diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..0c2b576e --- /dev/null +++ b/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "env", + { + "targets": { + "node": "current" + } + } + ] + ] +} diff --git a/.credo.exs b/.credo.exs new file mode 100644 index 00000000..f7a2bbb3 --- /dev/null +++ b/.credo.exs @@ -0,0 +1,114 @@ +# This file contains the configuration for Credo and you are probably reading +# this after creating it with `mix credo.gen.config`. +# +# If you find anything wrong or unclear in this file, please report an +# issue on GitHub: https://github.com/rrrene/credo/issues +# +%{ + # + # You can have as many configs as you like in the `configs:` field. + configs: [ + %{ + # + # Run any config using `mix credo -C `. If no config name is given + # "default" is used. + name: "default", + # + # These are the files included in the analysis: + files: %{ + # + # You can give explicit globs or simply directories. + # In the latter case `**/*.{ex,exs}` will be used. + included: ["lib/", "src/", "web/", "apps/"], + excluded: [~r"/_build/", ~r"/deps/"] + }, + # + # If you create your own checks, you must specify the source files for + # them here, so they can be loaded by Credo before running the analysis. + requires: [], + # + # Credo automatically checks for updates, like e.g. Hex does. + # You can disable this behaviour below: + check_for_updates: true, + # + # If you want to enforce a style guide and need a more traditional linting + # experience, you can change `strict` to `true` below: + strict: false, + # + # If you want to use uncolored output by default, you can change `color` + # to `false` below: + color: true, + # + # You can customize the parameters of any check by adding a second element + # to the tuple. + # + # To disable a check put `false` as second element: + # + # {Credo.Check.Design.DuplicatedCode, false} + # + checks: [ + {Credo.Check.Consistency.ExceptionNames}, + {Credo.Check.Consistency.LineEndings}, + {Credo.Check.Consistency.MultiAliasImportRequireUse}, + {Credo.Check.Consistency.ParameterPatternMatching}, + {Credo.Check.Consistency.SpaceAroundOperators}, + {Credo.Check.Consistency.SpaceInParentheses, false}, + {Credo.Check.Consistency.TabsOrSpaces}, + {Credo.Check.Design.DuplicatedCode, excluded_macros: []}, + {Credo.Check.Design.TagTODO, exit_status: 2}, + {Credo.Check.Design.TagFIXME}, + {Credo.Check.Design.AliasUsage, false}, + + {Credo.Check.Readability.FunctionNames}, + {Credo.Check.Readability.LargeNumbers}, + {Credo.Check.Readability.MaxLineLength, + priority: :low, max_length: 80, ignore_specs: true + }, + {Credo.Check.Readability.ModuleAttributeNames}, + {Credo.Check.Readability.ModuleDoc}, + {Credo.Check.Readability.ModuleNames}, + {Credo.Check.Readability.ParenthesesOnZeroArityDefs}, + {Credo.Check.Readability.ParenthesesInCondition}, + {Credo.Check.Readability.PredicateFunctionNames}, + {Credo.Check.Readability.PreferImplicitTry}, + {Credo.Check.Readability.RedundantBlankLines}, + {Credo.Check.Readability.Specs, false}, + {Credo.Check.Readability.StringSigils}, + {Credo.Check.Readability.TrailingBlankLine}, + {Credo.Check.Readability.TrailingWhiteSpace}, + {Credo.Check.Readability.VariableNames}, + {Credo.Check.Refactor.DoubleBooleanNegation}, + + {Credo.Check.Refactor.ABCSize}, + {Credo.Check.Refactor.CondStatements}, + {Credo.Check.Refactor.CyclomaticComplexity}, + {Credo.Check.Refactor.FunctionArity}, + {Credo.Check.Refactor.MatchInCondition}, + {Credo.Check.Refactor.NegatedConditionsInUnless}, + {Credo.Check.Refactor.NegatedConditionsWithElse}, + {Credo.Check.Refactor.Nesting}, + {Credo.Check.Refactor.PipeChainStart, false}, + {Credo.Check.Refactor.UnlessWithElse}, + {Credo.Check.Refactor.VariableRebinding, false}, + + {Credo.Check.Warning.BoolOperationOnSameValues}, + {Credo.Check.Warning.IExPry}, + {Credo.Check.Warning.IoInspect}, + {Credo.Check.Warning.NameRedeclarationByAssignment}, + {Credo.Check.Warning.NameRedeclarationByCase}, + {Credo.Check.Warning.NameRedeclarationByDef}, + {Credo.Check.Warning.NameRedeclarationByFn}, + {Credo.Check.Warning.OperationOnSameValues}, + {Credo.Check.Warning.OperationWithConstantResult}, + {Credo.Check.Warning.UnusedEnumOperation}, + {Credo.Check.Warning.UnusedFileOperation}, + {Credo.Check.Warning.UnusedKeywordOperation}, + {Credo.Check.Warning.UnusedListOperation}, + {Credo.Check.Warning.UnusedPathOperation}, + {Credo.Check.Warning.UnusedRegexOperation}, + {Credo.Check.Warning.UnusedStringOperation}, + {Credo.Check.Warning.UnusedTupleOperation}, + ] + } + ] +} diff --git a/.ebert.yml b/.ebert.yml new file mode 100644 index 00000000..148907f7 --- /dev/null +++ b/.ebert.yml @@ -0,0 +1,15 @@ +styleguide: elixirscript/elixirscript +engines: + credo: + enabled: true + fixme: + enabled: true + eslint: + enabled: true + remark-lint: + enabled: true +exclude_paths: +- config +- test +- priv/testrunner/vendor.build.js +- priv/testrunner/esm diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index fbe1a0a5..00000000 --- a/.eslintrc +++ /dev/null @@ -1,46 +0,0 @@ -{ - "ecmaFeatures":{ - "modules": true - }, - "rules": { - "indent": [ - 2, - 2 - ], - "quotes": [ - 0, - "double" - ], - "linebreak-style": [ - 2, - "unix" - ], - "semi": [ - 2, - "always" - ], - "no-var": true, - "camelcase": [ - 0, - { - "properties": "never" - } - ], - "no-underscore-dangle": false, - "new-cap": [ - 1, - { - "capIsNew": false - } - ], - "no-extend-native": [2, {"exceptions": ["Object", "String"]}], - "no-unused-vars": [2, {"vars": "all", "args": "none"}], - "space-infix-ops": [2, {"int32Hint": true}] - }, - "env": { - "es6": true, - "browser": true, - "mocha": true, - "node": true - } -} \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..df660be2 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,26 @@ +module.exports = { + rules: { + camelcase: 'off', + 'no-bitwise': 'off', + 'no-plusplus': 'off', + 'no-restricted-syntax': 'off', + 'no-underscore-dangle': 'off', + 'import/extensions': 'off', + 'import/no-extraneous-dependencies': ['error', {devDependencies: true}], + }, + 'overrides': [ + { + 'files': ['*spec.js', 'priv/testrunner/**/*'], + 'rules': { + 'no-console': 'off' + } + } + ], + extends: 'airbnb-base', + plugins: ['import'], + env: { + browser: true, + node: true, + mocha: true, + }, +} diff --git a/.flowconfig b/.flowconfig deleted file mode 100644 index 7f76ba60..00000000 --- a/.flowconfig +++ /dev/null @@ -1,12 +0,0 @@ -[ignore] -.*/dist/.* -.*/build/.* -.*/dist_build/.* -.*/node_modules/.* - -[include] -./priv/javascript - -[libs] - -[options] diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 00000000..562e8863 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,4 @@ +[ + import_deps: [:phoenix], + inputs: ["*.{ex,exs}", "{config,lib,priv,test}/**/*.{ex,exs}"] +] diff --git a/.gitignore b/.gitignore index b2d12146..1a813296 100644 --- a/.gitignore +++ b/.gitignore @@ -3,18 +3,25 @@ erl_crash.dump *.ez ex2js +elixirscript node_modules -priv/test_project/dest -priv/javascript/build -priv/javascript/dist -priv/javascript/dist_build /dist deploy.sh .DS_Store -priv/javascript/scratchpad.js sample/dest fprof.trace -index.js /doc /bench/snapshots - +.tern-port +test/std_lib_compile_test.exs +src/elixirscript +stdlib_state.bin +*.log +.nyc_output +test/app/build +.vscode +cover +/priv/build +/tmp +.esm-cache +.elixir_ls diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 00000000..000eeb3e --- /dev/null +++ b/.tool-versions @@ -0,0 +1,3 @@ +erlang 22.0 +elixir 1.9.1-otp-22 +nodejs 12.8.1 diff --git a/.travis.yml b/.travis.yml index 18001dc9..43916696 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,27 @@ sudo: false language: elixir elixir: - - 1.1.0 + - 1.9 otp_release: - - 18.0 \ No newline at end of file + - 22.0 +cache: + directories: + - _build + - deps + - node_modules +install: + - nvm install 12.8.1 && nvm use 12.8.1 + - npm install + - mix local.hex --force + - mix local.rebar --force + - mix deps.get +script: + - make + - make test +notifications: + webhooks: + urls: + - https://webhooks.gitter.im/e/fbd8944d285c0696dc41 + on_success: always # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: never # options: [always|never|change] default: always diff --git a/CHANGELOG.md b/CHANGELOG.md index 15aab538..89d3cd1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,143 +1,700 @@ -# v0.12.0 -* Enhancement - * Updated tuple implementation. It's now a class. - * Replaced pattern matching library with custom one - * Added PostOffice. Only thing that current uses it is Agent - * Removed erlang.js. Moved data types to Kernel.SpecialForms - * `else` now works for try expressions - * for now works with `into` for lists - -# v0.11.0 -* Enhancement - * `import` works with all options - * Added `JS` module with `new`, `mutate`, `import` macros - * All Standard libraries are rolled up into one elixir.js file and imported from that - * Added `Keyword` module with functions, `has_key?` and `get` - * Added `Agent` module with functions, `start`, `get`, `update`, and `get_and_update` - * Map keys are now correctly turned into their atom counterparts if atom keys are used - * Modules no longer export a default object - * `alias` now translates to a namespace import unless `default` option is given - -* Breaking - * `Mutable.update` has been replaced by `JS.update` - * `transpile`, `transpile_quoted`, and `transpile_path` are now `compile`, `compile_quoted`, and `compile_path` - -# v0.10.0 -* Enhancements - * Added `env` option for `ElixirScript.transpile` adding macros for compilation - * Fixed `case` implementation to add `this` to call - * Updated `Kernel` module to translate some functions to it's JavaScript equivalent - * Added `Logger` that translates Logger functions to console - -# v0.9.0 -* Enhancements - * an implementation for quote. Currently ignores `:location` and `:context` options - * an implementation for unquote and unquote_splicing - -# v0.8.0 -* Enhancements - * Can now support catch blocks in try expressions - * Wrapped try's in function closure to make sure they return a value; - * Added receive - * Updated pattern matching implementation - -# v0.7.0 -* Enhancements - * Can now support rescue and after blocks in try expressions - -# v0.6.5 -* Enhancements - * Now using the JS code generator from elixir-estree for code generation, improving speed of transpilation - * the parse functions in the ElixirScript module have been renamed to transpile - -# v0.6.0 - * Enhancements - * Made the Tuple, Range and BitString data structures more immutable - * Now replacing characters that can't be used in variable and function names in JavaScript with - something that it (i.e. `match?` -> `match__qmark__`) - * Implemented Integer module - * Atom now translates to an ES6 Symbol - * List now translates to a frozen JS Array - * Added iterators for Range and BitString - * Updated the pattern match binding to use ES6 destructuring for lists and tuples - * Inner modules are now split out into their own files - * Standard lib is now exported with file output from cli - * Standard lib modules are now automatically imported - * No longer have to define modules via aliases ahead of time. They will be automatically be resolved - and made into JavaScript import statements - -# v0.5.0 - * Enhancements - * For statements now work with pattern matching tuples - * Improved function chaining - * added `from` clause to `import`, `alias`, and `require` so that the import path can be overridden - * `alias` now acts like `require` in that it is translated into an import default statement - * modules now export a default object with def functions added as properties on it. - * for function closures, now calling by using `.call(this)` so that `this` is available inside of it - -# v0.4.0 - * Enhancements - * bitstrings - * Better Pattern Matching (Does not support bitstrings yet) - * Capture Operator - * Fixed multi arity implementation - * Updated variable implementation to match Elixir's (i.e. Reusing the same variable name creates a new one in the background) - * Throwing ParseError for SpecialForms currently not supported - * Added more functions from the list standard library - -# v0.3.0 - * Enhancements - * function and case guards - * function and case pattern matching - * Can now use ^ on a variable during assignment - -# v0.2.1 - * Enhancements - * Renamed project to ElixirScript - * Reduced escript file size - -# v0.2.0 - * Enhancements - * Pipe operator - * String interpolation - * Adding more functions to the Kernel module - * Now checking to see if a function is a Kernel function and prepending Kernel to it - * Now turning Atoms into an Atom javascript object instead of a Symbol - * Now turning tuples into a Tuple javascript object - * Fully implemented Tuple module - * Fully implemented Atom module - * Fully implemented Range module - * Can now call properties and zero parameter functions correctly - * case, cond, and if are now turned into if statements wrapped in function closures - * Anonymous functions are now turned into anonymous functions in javascript insteed of arrow functions - -# v0.1.0 - -* Enhancements - * From standard library implemented: - * Enum.map - * Kernel.tl - * Kernel.hd - * Logger - - * Implemented language features: - * All primatives except bitstrings - * defmodule - * import, alias, and require - * case, cond, if - * def, defp - * defstruct, defexception - * raise - * multiple arity functions - * basic binary operations - * for without into - - * Missing features: - * bitstrings - * pattern matching - * macros - * actors - * try - * regular expressions - * string interpolation - * Most of standard library +# Change Log + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/) +and this project adheres to [Semantic Versioning](http://semver.org/). + +## [0.32.1] - 2018-03-17 + +### Fixed + +* Global JavaScript modules not compiling correctly + +## [0.32.0] - 2018-02-10 + +### Added + +* `ElixirScript.Test` for testing ElixirScript modules in JavaScript. ElixirScript.Test is for unit testing modules that interact with JavaScript in some way. For modules that are can be used in both Elixir and ElixirScript, ExUnit is still preferred. Tests that use ElixirScript.Test must be placed in a `test_elixir_script` folder in the root of your project. These tests are run using node.js. The API for ElixirScript.Test is meant to be as close to ExUnit as possible. + +### Changed + +* ElixirScript now requires Elixir 1.6. This is so that ElixirScript can use the new `Mix.Task.Compiler` behaviour. +* `mix clean` will now correctly clean up ElixirScript output. +* Compiler will now output a JavaScript file per Elixir module. +* Modules with a start function **must** be started directly. + + ```elixir + # Before ElixirScript 0.32.0: + import Elixir from './elixirscript.build.js' + Elixir.start(Elixir.Main, [1, 2, 3]) + + # ElixirScript 0.32.0 and later: + import Main from './Elixir.Main.js' + Main.start(Symbol.for('normal'), [1, 2, 3]) + ``` + +## [0.31.1] - 2017-09-27 + +### Fixed + +* Compiler error when `receive` is used as variable name + +## [0.31.0] - 2017-09-24 + +### Added + +* [Compiler will now accept a path to Elixir Files to compile](https://github.com/elixirscript/elixirscript/issues/420) +* [Added `ElixirScript.JS.map_to_object/2` with options [keys: :string, symbols: false]](https://github.com/elixirscript/elixirscript/issues/362) +* [Added `ElixirScript.JS.object_to_map/1|2` with options [keys: :atom, recurse_array: true]](https://github.com/elixirscript/elixirscript/issues/381) +* [Fully implement `__info__` on modules](https://github.com/elixirscript/elixirscript/pull/378) +* [Concurrent Compilation](https://github.com/elixirscript/elixirscript/issues/376) +* [The following erlang functions have been implemented](https://github.com/elixirscript/elixirscript/issues/306): +* :erlang.nodes/0 +* :erlang.nodes/1 +* :math.log2/1 +* :binary.copy/1 +* :binary.copy/2 +* :binary.part/2 +* :binary.part/3 +* :binary.replace/3 +* :binary.replace/4 (some options still missing) + +### Fixed + +* Make sure not to add underscores to erlang functions +* [Make sure any variable names that are javascript keywords are handled properly](https://github.com/elixirscript/elixirscript/issues/355) +* [Make sure variables that begin with `_` are available](https://github.com/elixirscript/elixirscript/issues/356) +* [Finding the use of functions within anonymous functions](https://github.com/elixirscript/elixirscript/issues/358) +* [Reimplement `String.split_at/2` to make sure Unicode library isn't compiled](https://github.com/elixirscript/elixirscript/issues/353) +* [byte_size does not work on binaries that started via "" elixir string syntax](https://github.com/elixirscript/elixirscript/issues/384) +* [using . (dot) reference syntax on a map fails when value is a function](https://github.com/elixirscript/elixirscript/issues/380) +* [Make sure that remote ast works correctly with variables](https://github.com/elixirscript/elixirscript/issues/390) +* [Make sure == works as expected](https://github.com/elixirscript/elixirscript/issues/382) +* [Make sure that erlang function names that are also JavaScript keywords are not filters](https://github.com/elixirscript/elixirscript/issues/359) +* [erlang.error now throws errors resembling those in Elixir](https://github.com/elixirscript/elixirscript/pull/397) +* [Map.get fails if key is tuple or list](https://github.com/elixirscript/elixirscript/issues/406) + +## [0.30.0] - 2017-08-15 + +### Added + +* ElixirScript now has a Foreign Function Interface (FFI) for interoperability with JavaScript. For more details, see documentation at `ElixirScript.FFI` +* `ElixirScript.JS.mutate/3` +* `ElixirScript.JS.map_to_object/1` +* `root` option for specifying the root import path for FFI JavaScript modules. Defaults to `"."` + +### Changed + +* Compiler has been completely rewritten. ElixirScript now requires Erlang 20+ and Elixir 1.5+ +* `JS` module renamed to `ElixirScript.JS` +* Default output path is now `priv/elixir_script/build` + +### Removed + +* Support for CommonJS and UMD output formats has been removed. Output will be in ES module format +* The `js_modules` option has been removed in favor of the new FFI +* ElixirScript.Watcher has been removed + +## [0.28.0] - 2017-06-11 + +### Added + +* `remove-unused` option that will remove all unused modules from output +* reimplemented structs to avoid creating JavaScript classes + +## [0.27.0] - 2017-03-17 + +### Added + +* `super` +* `defoverridable` +* `IO.inspect\1`, `IO.puts\1`, `IO.puts\2`, `IO.warn\1` +* `Elixir.load` for loading generated JavaScript modules in bundled output. + Unlike `Elixir.start`, this will only call `__load` on the module and return the functions on it + +```javascript +const exports = Elixir.load(Elixir.MyApp); +exports.hello(); +``` + +### Changed + +* `-ex` alias is now `-e` +* A filename can be specified for output +* To access global JavaScript functions, modules, and properties, use the `JS` module + +```elixir +JS.length # translates to 'length' +JS.alert() # translates to 'alert()' +JS.String.raw("hi") # translate to String.raw('hi') +JS.console.log("hi") # translates to console.log('hi') +``` + +### Fixed + +* Make sure mix compiler works in umbrella apps + +## [0.26.1] - 2017-02-27 + +### Fixed + +* Fixed `for` translation +* Updated documentation + +## [0.26.0] - 2017-02-27 + +### Added + +* Multiple `when` clauses in guards +* Kernel.defdelegate/2 +* `js_modules` configuration option has been added. This is a list of JavaScript modules that will be used. + + ``` + js_modules: [ + {React, "react"}, + {ReactDOM, "react-dom"} + ] + ``` + +* `js-module` flag has been added to the CLI in order to pass js modules. + +``` +elixirscript "app/elixirscript" -o dist --js-module React:react --js-module ReactDOM:react-dom +``` + +### Removed + +* `@on_js_load` has been removed in favor of having a `start/2` function defined. More info below +* `JS.import` has been removed in favor of defining JavaScript modules used in configuration + +### Changed + +* Now bundles all output, including the boostrap code. + The exported object has Elixir modules in JavaScript namespaces that are lazily loaded when called. + + To start your application import the bundle according to whichever module format was selected and + then call start giving it the module and the initial args + + ```javascript + //ES module example + import Elixir from './Elixir.App'; + Elixir.start(Elixir.App, []); + ``` + + The `start` function will look for a `start/2` function there. + This is analogous to a [Application module callback](https://hexdocs.pm/elixir/Application.html#module-application-module-callback) + +## [0.25.0] - 2017-02-19 + +### Added + +* Updated elixir_script mix compiler to support compiling elixir_script paths in dependencies if dependency has mix compiler defined as well +* Add `Collectable` protocol implementations +* Updated `for` implementation to use `Collectable` +* `format` option. Can now specify the module format of output. + Choices are: + _ `:es` (default) for ES Modules + _ `:umd` for UMD \* `:common` for CommonJS + +* Default input, output and format for elixirscript mix compiler. In a mix project by default the elixirscript compiler will look in `lib/elixirscript` and input and place output in `priv/elixirscript`. The default format is `:es` + +### Removed + +* `receive` +* `Process` module + +### Fixed + +* JS module functions not translated properly when imported +* Update fs dependency to 2.12 +* Incorrect handling of function heads with guards + +## [0.24.0] - 2017-01-15 + +### Added + +* Support for `sigil_r` +* `Regex` module +* Better JavaScript formatting + +### Fixed + +* CLI now allows a comma-separated or space-separated list of paths +* Struct not properly referenced +* Tail call optimization + +## [0.23.3] - 2016-11-18 + +### Added + +* `@load_only`: lets the compiler know to load in the module, but not to compile it + +## [0.23.2] - 2016-11-17 + +### Fixed + +* Agent not functioning properly. Now uses internal store instead of making a process and using that to put data in store +* Protocol incorrectly handling strings +* `defgen` and `defgenp` functions not being recognized by Elixir compiler. + +## [0.23.1] - 2016-11-16 + +### Fixed + +* Incorrectly sending standard lib when using compile or compile_path by default + +## [0.23.0] - 2016-11-15 + +### Added + +* [`with` now supports `else`](https://github.com/bryanjos/elixirscript/pull/207) +* [Implement `context` option on `quote`](https://github.com/bryanjos/elixirscript/pull/208) +* New compiler pipeline +* `@on_js_load`. Expects a 0 arity function. This function will be called when the compiled module is loaded in JavaScript +* `JS.import\3`. Just like `JS.import\2` but expects options to decide if the import should be a default one or a namespace on. Only option allowed is `default`. Set to `true` by default + + ```elixir + # translates to "import A from 'a'" + JS.import A, "a" + + #translates to "import * as A from 'a'" + JS.import A, "a", default: false + ``` + +### Removed + +* The form of `JS.import` that accepted a list of atoms as the first arg. Used `JS.import\3` with `default: false` instead to create a namespace import +* `env` and `root` are no longer options for `ElixirScript`'s compile functions and cli +* Syntax once supported by Elixirscript `JQuery.("#element")`, is no longer supported + +### Changed + +* [Changed CHANGELOG.md to adhere the format from Keep a Changelog](https://github.com/bryanjos/elixirscript/pull/205) +* `defmacro` now supported. No longer have to separate macros from functions in separate files. `defmacrop` still unsupported +* To use anything in the `JS` module, you must `require` the `JS` module first +* Elixirscript files must now contain valid Elixir syntax. +* Now compiles `exjs` and `ex` files within the path can be compiled all the same. Dependencies from hex are still unsupported so these files must not rely on any code outside of the path. What this does mean is that it is now possible to share code between Elixir and Elixirscript as long as the Elixir files functionality fall within what Elixirscript currently supports. +* `defgen`, `defgenp`, `yield`, `yield_to`, and `object` are now in the `JS` module +* To access functions in the global JavaScript scope, either use `JS.global\0` or use the erlang module call syntax + + ```elixir + #calling alert + JS.global().alert("hi") + + #calling alert + :window.alert("hi") + ``` + + Calling JavaScript modules in the global scope works without using the above methods + + ```elixir + #calls window.Date.now() + Date.now() + ``` + +## [0.22.0] - 2016-10-16 + +### Added + +* `defgen` and `defgenp` for defining public and private generators +* `yield/0`, `yield/1`, and `yield_to\1` to `Kernel` + +### Changed + +* Updated output folder structure. stdlib code will now go in an `elxiir` folder under the output paths while generated app code will go into an `app` folder under the output path +* All process macros and functions now expect to receive and/or work using generators as entry points. Using functions defined with `def` or `defp` will not work correctly with them + +### Fixed + +* Correctly returning list if list is only item in body + +## [0.21.0] - 2016-06-28 + +### Added + +* This is the first release with early support for processes in elixirscript. Creating a process only works currently using `spawn/1`, `spawn_link/1`, and `spawn_monitor/1`. Inside of a process, you can use functions such as `send` and `receive`, along with some defined in the `Process` module. From outside of a process, you can send messages to a process, but you cannot receive a message from a process. Eventually all code will run inside processes and this restriction will naturally lift. +* The `Process` module has been implemented with the following functions: + * `alive?/1` + * `delete/1` + * `demonitor/1` + * `exit/2` + * `flag/2` + * `flag/3` + * `get/0` + * `get_keys/0` + * `get_keys/1` + * `link/1` + * `list/0` + * `monitor/1` + * `put/2` + * `register/2` + * `registered/0` + * `send/3` + * `sleep/1` + * `unlink/1` + * `unregister/1` + * `whereis/1` +* The `receive` special form has been implemented with the above caveat +* The following have been implemented on `Kernel`: + * `spawn/1` + * `spawn_link/1` + * `spawn_monitor/1` + * `send/2` + * `make_ref/0` + +## Fixed + +* Scoping on `fn` and `def` + +## [0.20.0] - 2016-05-14 + +### Added + +* `ElixirScript.Watcher` module and `elixirscript.watch` mix task +* logging MatchError exceptions to better show terms that don't match + +## [0.19.0] - 2016-04-30 + +### Added + +* elixir_script mix compiler + +### Removed + +* `Html`, `View`, and `VDom` modules have been removed + +## [0.18.0] - 2016-04-08 + +### Changed + +* Better support for macros. Macros should be defined in .ex or .exs files. ElixirScript code should be in .exjs files + +**NOTE**: The above functionality will cause either compiler errors or no output. Please change extensions of ElixirScript code to .exjs + +### Deprecated + +* `Html`, `View`, and `VDom` modules will be removed in the next version as they can now be replicated using macros + +## [0.17.0] - 2016-03-31 + +### Added + +* `output` as an option for compiler functions. This controls whether output is returned as a list of tuples, send to stdout, or saved to a file path +* `:full_build` as an option for compiler functions and `--full-build` option to CLI. These force the compiler to perform a full build +* `--version` option to CLI. Outputs current version of elixirscript +* `--std-lib` option to CLI. Takes a path and adds the stdlib to that path + +### Changed + +* Renamed `copy_core_to_destination` to `copy_stdlib_to_destination` +* Incremental Compilation: ElixirScript will now only build files and modules that have changed since the last build + +### Removed + +* `--core` option from CLI and `:core` compiler option. + +## [0.16.0] 2016-02-27 + +### Added + +* Bitstring pattern matching +* Bitstrings in for comprehensions +* Functions with catch, after, else clauses +* `with` special form +* Pin operator in map keys and function clauses +* Added `Kernel.object/1` function to make it more natural to create a JavaScript object with string keys. Elixirscript, by default turns the following, `%{a:"b"}` into `{[Symbol.for("a")]: "b"}` in JavaScript. In order to get string keys, one would have to do `%{"a" => "b"}` which turns into `{a: "b"}` in JavaScript. With `Kernel.object`, you can create string keyed maps conveniently, `object(a: "b")` which turns into `{a: "b"}`. + + **NOTE**: when updating the created by, you still have to use the string form `%{ my_map | "a" => "c" }` + +### Removed + +* `JS.update(object, property, value)` has been removed and replaced with `JS.update(object, map)`. This allows you to update multiple values on a javascript object at once. + +### Fixed + +* Optional parameters should now work as expected + +## [0.15.2] - 2016-02-21 + +### Addded + +* Support for variables as map keys + +### Fixed + +* Protocol implementations for Integer and Float which where not recognized +* Calling properties on non-objects + +## [0.15.1] - 2016-02-19 + +### Removed + +* Removed `catch` as a javascript keyword to filter + +### Fixed + +* Fixed View module so that an element can have multiple elements within +* struct implementation so that lists of atoms for fields are compiled correctly +* head-tail pattern match to allow for more complicated scenarios +* ModuleCollector to properly alias inner modules +* Raise translation to properly translate when string messages are given + +## [0.15.0] - 2016-01-26 + +### Added + +* `__ENV__` and `__CALLER__` are now supported +* `JS.import/1`, `JS.typeof/1`,`JS.instanceof/1`, and `JS.global/1` +* Support for multi alias/require/imports statements + +### Changed + +* `alias`, `require`, and `import` now work inside lexical scopes +* Some of the standard library originally written in JavaScript has been rewritten in Elixir. +* Generated JavaScript export statements are now default exports +* When output is sent to standard out, there are now markers to specify where each module begins as well as what the file name would be. For the end of a file, `//:ENDFILE` is used. For the file name, `//:ENDFILENAME` is used where `` is the name of the file +* `compile`, `compile_path`, and `compile_quoted` opts parameter now expects a map +* The `stdlib` compiler option is now `core`. The `stdlib_path` compiler options is now `core_path` + +## [0.14.1] - 2015-12-07 + +### Removed + +* .DS_Store and LICENSE from output + +## [0.14.0] - 2015-12-06 + +### Added + +* Can now implement protocols using JavaScript types + ```elixir + defimpl MyProtocol, for: HTMLElement + ``` +* virtual-dom JavaScript library +* ElixirScript.Html module for defining a virtual-dom tree +* ElixirScript.VDom module for manipulating the virtual-dom tree created using the ElixirScript.Html module +* Added ElixirScript.View module for handling view state and rendering virtual-dom +* Added `stdlib_path` compiler option to specify the es6 path to the standard library. If used, elixir.js will not be exported with the compiled modules + +### Changed + +* Renamed `ex2js` to `elixirscript`. This effects the escript as well as the + mix task +* Structs are now translated into classes +* Structs and Tuples now match on their types +* Can now match on JavaScript classes. Works just like matching on structs: + ```elixir + def my_func(%HTMLElement{id: "myId"}) + ``` +* Moved non-elixir JavaScript code into `core` es6 module. This will hopefully + make it so ElixirScript Standard Library modules can be defined in Elixir soon. + +## [0.13.0] - 2015-10-26 + +### Added + +* `Base` module with function: encode64, decode64, and decode64! +* `String` module +* `Bitwise` module +* `Map` module +* `MapSet` module +* `Set` module +* Protocol support +* Added `Collectable`, `Enumerable`, `Inspect`, `List.Chars`, and `String.Chars` protocols. The only one currently being used in the Standard Library, however, is String.Chars + +## [0.12.0] - 2015-09-23 + +### Added + +* Added PostOffice. Only thing that current uses it is Agent + +### Changed + +* Updated tuple implementation. It's now a class. +* Replaced pattern matching library with custom one +* Moved data types to Kernel.SpecialForms +* `else` now works for try expressions +* for now works with `into` for lists + +### Removed + +* Removed erlang.js. + +## [0.11.0] - 2015-09-17 + +### Added + +* Added `JS` module with `new`, `mutate`, `import` macros +* Added `Keyword` module with functions, `has_key?` and `get` +* Added `Agent` module with functions, `start`, `get`, `update`, and `get_and_update` + +### Changed + +* Map keys are now correctly turned into their atom counterparts if atom keys are used +* `import` works with all options +* `Mutable.update` has been replaced by `JS.update` +* `transpile`, `transpile_quoted`, and `transpile_path` are now `compile`, `compile_quoted`, and `compile_path` +* All Standard libraries are rolled up into one elixir.js file and imported from that +* Modules no longer export a default object +* `alias` now translates to a namespace import unless `default` option is given + +## [0.10.0] - 2015-09-02 + +### Added + +* Added `env` option for `ElixirScript.transpile` adding macros for compilation +* Added `Logger` that translates Logger functions to console + +### Changed + +* Updated `Kernel` module to translate some functions to it's JavaScript equivalent + +### Fixed + +* Fixed `case` implementation to add `this` to call + +## [0.9.0] - 2015-08-30 + +### Added + +* an implementation for quote. Currently ignores `:location` and `:context` options +* an implementation for unquote and unquote_splicing + +## [0.8.0] - 2015-08-15 + +### Added + +* Can now support catch blocks in try expressions +* Added receive + +### Changed + +* Updated pattern matching implementation +* Wrapped try's in function closure to make sure they return a value; + +## [0.7.0] - 2015-08-01 + +### Added + +* Can now support rescue and after blocks in try expressions + +## [0.6.5] - 2015-07-13 + +### Changed + +* Now using the JS code generator from elixir-estree for code generation, improving speed of transpilation +* the parse functions in the ElixirScript module have been renamed to transpile + +## [0.6.0] - 2015-07-02 + +### Added + +* Added iterators for Range and BitString +* Now replacing characters that can't be used in variable and function names in JavaScript with something that it (i.e. `match?` -> `match__qmark__`) +* Implemented Integer module + +### Changed + +* Made the Tuple, Range and BitString data structures more immutable +* Atom now translates to an ES6 Symbol +* List now translates to a frozen JS Array +* Updated the pattern match binding to use ES6 destructuring for lists and tuples +* Inner modules are now split out into their own files + * Standard lib is now exported with file output from cli + * Standard lib modules are now automatically imported + * No longer have to define modules via aliases ahead of time. They will be automatically be resolved + and made into JavaScript import statements + +## [0.5.0] - 2015-05-31 + +### Added + +* added `from` clause to `import`, `alias`, and `require` so that the import path can be overridden + +### Changed + +* For statements now work with pattern matching tuples +* Improved function chaining +* `alias` now acts like `require` in that it is translated into an import default statement +* modules now export a default object with def functions added as properties on it. +* for function closures, now calling by using `.call(this)` so that `this` is available inside of it + +## [0.4.0] - 2015-05-05 + +### Added + +* bitstrings +* Better Pattern Matching (Does not support bitstrings yet) +* Capture Operator +* Added more functions from the list standard library + +### Changed + +* Updated variable implementation to match Elixir's (i.e. Reusing the same variable name creates a new one in the background) + +### Fixed + +* Fixed multi arity implementation + +## [0.3.0] - 2015-04-23 + +### Added + +* function and case guards +* function and case pattern matching + +### Changed + +* Can now use ^ on a variable during assignment + +## [0.2.1] - 2015-04-14 + +### Changed + +* Renamed project to ElixirScript +* Reduced escript file size + +## [0.2.0] - 2015-04-12 + +### Added + +* Pipe operator +* String interpolation +* Adding more functions to the Kernel module +* Fully implemented Tuple module +* Fully implemented Atom module +* Fully implemented Range module + +### Changed + +* Now checking to see if a function is a Kernel function and prepending Kernel to it +* Now turning Atoms into an Atom javascript object instead of a Symbol +* Now turning tuples into a Tuple javascript object +* Can now call properties and zero parameter functions correctly +* case, cond, and if are now turned into if statements wrapped in function closures +* Anonymous functions are now turned into anonymous functions in javascript insteed of arrow functions + +## [0.1.0] - 2015-04-04 + +### Added + +* From standard library implemented: + +- Enum.map +- Kernel.tl +- Kernel.hd +- Logger + +* Implemented language features: + +- All primitives except bitstrings +- defmodule +- import, alias, and require +- case, cond, if +- def, defp +- defstruct, defexception +- raise +- multiple arity functions +- basic binary operations +- for without into diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..56fcb028 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,175 @@ +# Contributing to ElixirScript + +This contributing guide is based on the contributing for Elixir with changes suitable for this project. + +## Using the issue tracker + +Use the issues tracker for: + +- [bug reports](#bug-reports) +- [submitting pull requests](#pull-requests) +- [feature requests](#feature-requests) + +## Bug reports + +A bug is a _demonstrable problem_ that is caused by the code in the repository. +Good bug reports are extremely helpful - thank you! + +Guidelines for bug reports: + +1. **Use the GitHub issue search** — [check if the issue has already been + reported](https://github.com/elixirscript/elixirscript/search?type=Issues). + +2. **Check if the issue has been fixed** — try to reproduce it using the + `master` branch in the repository. + +3. **Isolate and report the problem** — ideally create a reduced test + case. + +Please try to be as detailed as possible in your report. Please provide steps to +reproduce the issue as well as the outcome you were expecting! All these details +will help developers to fix any potential bugs. + +Example: + +> Short and descriptive example bug report title +> +> A summary of the issue and the environment in which it occurs. If suitable, +> include the steps required to reproduce the bug. +> +> 1. This is the first step +> 2. This is the second step +> 3. Further steps, etc. +> +> `` - a link to the reduced test case (e.g. a GitHub Gist) +> +> Any other information you want to share that is relevant to the issue being +> reported. This might include the lines of code that you have identified as +> causing the bug, and potential solutions (and your opinions on their +> merits). + +## Feature requests + +Feature requests are welcome. But take a moment to find +out whether your idea fits with the scope and aims of the project. It's up to _you_ +to make a strong case to convince the community of the merits of this feature. +Please provide as much detail and context as possible. + +## Contributing + +Contributions to ElixirScript are welcomed! These contributions can be +in the form or code changes, documentation, or ideas of how to implement features +found in Elixir in ElixirScript. + +ElixirScript is broken up into the following parts: + +- The compiler, written in Elixir +- The JavaScript core + +The ElixirScript compiler is in the `lib` folder of the project. +Here is where Elixir code is converted into JavaScript. + +The JavaScript code is in the `src/javascript`. +This is where the special forms and the Erlang Compatibility Layer are defined + +The ElixirScript tests are ran using `mix test` + +The JavaScript tests are ran using `npm test` + +Please make sure all tests pass after making changes. Also make sure to include tests for +the changes you made. + +## Pull requests + +Good pull requests - patches, improvements, new features - are a fantastic +help. They should remain focused in scope and avoid containing unrelated +commits. + +**NOTE**: Do not send code style changes as pull requests like changing +the indentation of some particular code snippet or how a function is called. +Those will not be accepted as they pollute the repository history with non +functional changes and are often based on personal preferences. + +**IMPORTANT**: By submitting a patch, you agree that your work will be +licensed under the license used by the project. + +If you have any large pull request in mind (e.g. implementing features, +refactoring code, etc), **please ask first** otherwise you risk spending +a lot of time working on something that the project's developers might +not want to merge into the project. + +Please adhere to the coding conventions in the project (indentation, +accurate comments, etc.) and don't forget to add your own tests and +documentation. When working with Git, we recommend the following process +in order to craft an excellent pull request: + +1. [Fork](https://help.github.com/fork-a-repo/) the project, clone your fork, + and configure the remotes: + +```sh +# Clone your fork of the repo into the current directory +git clone https://github.com//elixirscript +# Navigate to the newly cloned directory +cd elixirscript +# Assign the original repo to a remote called "upstream" +git remote add upstream https://github.com/elixirscript/elixirscript +``` + +2. If you cloned a while ago, get the latest changes from upstream: + +```sh +git checkout master +git pull upstream master +``` + +3. Create a new topic branch (off of `master`) to contain your feature, change, + or fix. + + **IMPORTANT**: Making changes in `master` is discouraged. You should always + keep your local `master` in sync with upstream `master` and make your + changes in topic branches. + +```sh +git checkout -b +``` + +4. Commit your changes in logical chunks. Keep your commit messages organized, + with a short description in the first line and more detailed information on + the following lines. Feel free to use Git's + [interactive rebase](https://help.github.com/articles/interactive-rebase) + feature to tidy up your commits before making them public. + +5. Make sure all the tests are still passing. + +```sh +mix test +npm test +``` + +This is needed to ensure your changes can +pass all the tests. + +6. Push your topic branch up to your fork: + +```sh +git push origin +``` + +7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) + with a clear title and description. + +8. If you haven't updated your pull request for a while, you should consider + rebasing on master and resolving any conflicts. + + **IMPORTANT**: _Never ever_ merge upstream `master` into your branches. You + should always `git rebase` on `master` to bring your changes up to date when + necessary. + +```sh +git checkout master +git pull upstream master +git checkout +git rebase master +``` + +Thank you for your contributions! diff --git a/CompilerInternals.md b/CompilerInternals.md new file mode 100644 index 00000000..c729ee09 --- /dev/null +++ b/CompilerInternals.md @@ -0,0 +1,51 @@ +# Compiler Internals + +This is a document describing how ElixirScript works. This is intended for those who would like to contribute to ElixirScript or those who are curious how it works. + +## Input + +[ElixrScript.Compiler](https://github.com/elixirscript/elixirscript/blob/master/lib/elixir_script/compiler.ex) is the entry point of the compiler. It takes in either a module or a list of modules. These are what are called the `entry modules` or the entry points into your application. These are the places ElixirScript will start it's compilation process. It will traverse what is used and only compile those things. This is the first step in the compilation process. Finding used modules to compile. + +## Finding Used Modules + +[ElixirScript.FindUsedModules](https://github.com/elixirscript/elixirscript/blob/master/lib/elixir_script/passes/find_used_modules.ex) looks at our entry modules and recursively crawls them to find all the modules used. It firsts exacts the Abstract Syntax Tree (AST) from the Beam file and then looks for references to modules that haven't been crawled yet. This information is stored in [ElixirScript.State](https://github.com/elixirscript/elixirscript/blob/master/lib/elixir_script/state.ex) + +## AST Extraction from Beam Files + +ElixirScript requires at Erlang 20+ and Elixir 1.6+. The reason why is that in Erlang 20 there is a new feature that allows for debug information to be stored in beam files. Any of the beam languages can use this. Elixir uses it by storing the AST for the module in there. This is a special version of the AST where all of the macros are expanded. This means ElixirScript does not have to worry about macro expansion itself. This AST is what ElixirScript works with. + +The code for this is in the [ElixirScript.Beam](https://github.com/elixirscript/elixirscript/blob/master/lib/elixir_script/beam.ex) module. + +`ElixirScript.debug_info/1` takes in a module name and returns the AST for that module. For a normal module, `{:ok, map}` are returned. If a protocol is given, `{:ok, atom, map, list}` is returned. The `atom` is the name of the protocol, The `map` is the protocol's AST and the `list` is the list of all of the implementation modules. + +This module handles the `String` and `Agent` modules a little bit differenly. Because of how Elixir compiles the unicode library, ElixirScript has to be careful not to compile the entire unicode library in JavaScript. So here, `debug_info` will get the AST from `String`, but replace some functions with the AST from `ElixirScript.String`. This ensures ElixirScript uses versions of functions in the standard lib that won't bring in the unicode module. The ame thing happens for `Agent` for different reasons. `Agent` is the only OTP module ElixirScript supports. ElixirScript hacks together a version of `Agent` that stores state in a way that allows ElixirScript users to use `Agent` just like they would with Elixir. + +## Finding Used Functions + +[ElixirScript.FindUsedFunctions](https://github.com/elixirscript/elixirscript/blob/master/lib/elixir_script/passes/find_used_functions.ex) is our second process in shrinking our compilation suface. In this process, we crawl through the modules we have found for compilation and see which functions are actually being called. This information is also stored in [ElixirScript.State](https://github.com/elixirscript/elixirscript/blob/master/lib/elixir_script/state.ex) for each module. + +**Note**: Because of the way protocols work, it is impossible to know what is used and what isn't. So for protocols and their implementations, we have to take in everything. + +Now we have what we need to compile to the JavaScript AST. + +## JavaScript AST (ESTree) + +Before going further, here is a brief intro into the JavaScript AST we use. The [ESTree spec](https://github.com/estree/estree) is a specification based on SpiderMonkey's JavaScript AST. This is used by several tools in the JavaScript ecosystem. There are many other versions of JavaScript ASTs, but the reason ElixirScript uses this one is because there are popular tools in the JavaScript ecosystem that understand it. ElixirScript uses the [ESTree](https://github.com/elixirscript/elixir-estree) Hex package. This package has structs that represent ESTree Nodes. It can also turn those into JavaScript code. + +## Translation + +[ElixirScript.Translate](https://github.com/elixirscript/elixirscript/blob/master/lib/elixir_script/passes/translate.ex) starts off the translation process. All this module does though is call [ElixirScript.Translate.Module](https://github.com/elixirscript/elixirscript/blob/master/lib/elixir_script/passes/translate/module.ex) on each of our modules. Here is where we take in the module info for each module and start translating to JavaScript AST. We compile the function definitions into JavaScript. Here is where we process the information gained from `ElixirScript.FindUsedFunctions` to remove any unused functions. In Elixir, function names are made up of the name and the arity. In JavaScript, that is not the case. ElixirScript combines function arities here into one definition. From here, ElixirScript compiles each function and places the translated AST back into `ElixirScript.State`. + +Functions comprise of clauses. Clauses have guards and blocks. Blocks being the blocks of code that make up the implementation. + +[ElixirScript.Translate.Function](https://github.com/elixirscript/elixirscript/blob/master/lib/elixir_script/passes/translate/function.ex) handles function translation. `ElixirScript.Translate.Function.compile_block\2` handles compilation of blocks. for each item in the block, `ElixirScript.Translate.Form.compile\2` is called. This is what is responsible for a bulk of the translation. + +Another aside to talk about function translation. Elixir supports tail call recursion. JavaScript does not. To allow our ElixirScript-translated functions to do so, we use a technique called `trampolining`. ElixirScript implementation still has some bugs, but it works for the most part. + +## Pattern Matching Translation + +Patterns are processed using [ElixirScript.Translate.Forms.Pattern](https://github.com/elixirscript/elixirscript/blob/master/lib/elixir_script/passes/translate/forms/pattern.ex). It takes all the forms of patterns and compiles them into JavaScript AST. The AST represents calls to the [Tailored](https://github.com/elixirscript/tailored) JavaScript library. This library is responsible for pattern matching at run time. + +## Output + +[ElixirScript.Output](https://github.com/elixirscript/elixirscript/blob/master/lib/elixir_script/passes/output.ex) is the last step in compilation. This modules is responsible for creating JavaScript modules and writing them to the file system. Each Elixir module is translated into a JavaScript module. diff --git a/JavascriptInterop.md b/JavascriptInterop.md new file mode 100644 index 00000000..4426b0df --- /dev/null +++ b/JavascriptInterop.md @@ -0,0 +1,107 @@ +# JavaScript Interoperability + +## Data Type Conversions + +ElixirScript translates Elixir primitive types to the following: + +| Elixir | JavaScript | +|--------|------------| +| Integer | Number | +| Float | Number | +| Binary | String | +| Atom | Symbol | +| List | Array | +| Map | Map | +| Tuple | ErlangTypes.Tuple | +| Bitstring | ErlangTypes.Bitstring | +| PID | ErlangTypes.PID | +| Reference | ErlangTypes.Reference | + +The ErlangTypes library can be found [here](https://github.com/elixirscript/erlang-types) + +## ElixirScript Calling JavaScript + +### ElixirScript.JS module + +The `ElixirScript.JS` module has functions and macros that help with interacting with JavaScript. +Most of them correspond to JavaScript keywords that may be useful. + +```elixir +# Calling the JavaScript Debugger +ElixirScript.JS.debugger() + +# Getting the type of a value +ElixirScript.JS.typeof(my_value) +``` + +### Foreign Function Interface + +ElixirScript calls JavaScript modules through a Foreign Function Interface (FFI). A foreign module is defined by creating a new Elixir module and adding `use ElixirScript.FFI` to it. + +Here is an example of a foreign module for a JSON module + +```elixir +defmodule MyApp.JSON do + use ElixirScript.FFI + + defexternal stringify(map) + defexternal parse(string) +end +``` + +Foreign modules map to JavaScript files that export functions defined with the `defexternal` macro. +ElixirScript expects JavaScript modules to be in the `priv/elixir_script` directory. +These modules are copied to the output directory upon compilation. + +For our example, a JavaScript file must be placed at `priv/elixir_script/my_app/json.js`. + +It looks like this +```javascript +export default { + stringify: JSON.stringify, + parse: JSON.parse +} +``` + +For more information and options. Check the documentation for `ElixirScript.FFI` + +## JavaScript Calling ElixirScript + + In order to start an ElixirScript application, you must first import it using whichever JavaScript module system you are using and then call `Elixir.start` + + ```Elixir + # Our ElixirScript module + + defmodule Main do + def start(:normal, args) do + args + end + end + + ``` + + ```javascript + import Main from './Elixir.Main.js' + Main.start(Symbol.for('normal'), [1, 2, 3]) + ``` + + In the above example, we have an ElixirScript module, `Main` with a `start/2` function. + + + If you want to use an ElixirScript module inside of your JavaScript code, you can do so like below. + + ```Elixir + # Our ElixirScript module + + defmodule MyModule do + def hi() do + "hello" + end + end + ``` + + + ```javascript + import MyModule from './Elixir.MyModule.js' + MyModule.hi() + ``` diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..9ede1be1 --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +.PHONY: compile test clean js_compile elixir_compile elixir_test js_test deps elixir_deps js_deps + +default: deps compile + +compile: js_compile elixir_compile + +js_compile: + npm run build + +elixir_compile: + mix compile + +test: js_compile elixir_test js_test + +js_test: + npm test + +elixir_test: + mix test --cover + mix elixirscript.test + +clean: + rm -rf priv/build + mix clean + +deps: elixir_deps js_deps + +elixir_deps: + mix deps.get + +js_deps: + npm install diff --git a/README.md b/README.md index 7fc9600c..6d464386 100644 --- a/README.md +++ b/README.md @@ -1,176 +1,80 @@ -## ElixirScript [![Documentation](https://img.shields.io/badge/docs-hexpm-blue.svg)](http://hexdocs.pm/elixir_script/) [![Build](https://travis-ci.org/bryanjos/elixirscript.svg?branch=master)](https://travis-ci.org/bryanjos/elixirscript) +## ElixirScript [![Documentation](https://img.shields.io/badge/docs-hexpm-blue.svg)](http://hexdocs.pm/elixir_script/) [![Build](https://travis-ci.org/elixirscript/elixirscript.svg?branch=master)](https://travis-ci.org/elixirscript/elixirscript) -The goal is to convert a subset (or full set) of Elixir code to JavaScript, providing the ability to write JavaScript in Elixir. This is done by taking the Elixir AST and converting it into JavaScript AST and then to JavaScript code. This is done using the [Elixir-ESTree](https://github.com/bryanjos/elixir-estree) library. +The goal is to convert a subset (or full set) of Elixir code to JavaScript, providing the ability to write JavaScript in Elixir. This is done by taking the Elixir AST and converting it into JavaScript AST and then to JavaScript code. This is done using the [Elixir-ESTree](https://github.com/elixirscript/elixir-estree) library. -Requirements -=========== -* Elixir -* Node or io.js (only for development) +[Documentation for current release](http://hexdocs.pm/elixir_script/) -Usage -======== +# Requirements -ElixirScript can be used in the following ways: +* Erlang 20 or greater +* Elixir 1.6 or greater (must be compiled with Erlang 20 or greater) +* Node 8.2.1 or greater (only for development) +# Usage -* If using as part of a project, you can add the following to your deps +Add dependency to your deps in mix.exs: - ```elixir - {:elixir_script, "~> 0.12"} - ``` - - From there you can either use the ElixirScript module directly or the mix command, `mix ex2js` - -* CLI Client - - You can download the latest release from the [releases](https://github.com/bryanjos/elixirscript/releases) page and use the included `ex2js` escript. - - - -Development -=========== - -Clone the repo - - git clone git@github.com:bryanjos/elixirscript.git - -Get dependencies - - mix deps.get - npm install - -Compile - - mix compile - -Test - - mix test - - -Usage -=== - -``` - usage: ex2js [options] - - path to elixir files or - the elixir code string if the -ex flag is used - - options: - -o --output [path] places output at the given path - -ex --elixir read input as elixir code string - -r --root [path] root path for standard libs - -h --help this message -``` - -A note on `-r`. The standard lib modules are included in the output as `elixir.js`. They are by default included by importing them like so - -```javascript -import {...} from 'elixir' -``` - -Depending on your setup that may not work. With `-r` you can specify the root path that will be prepended to the default path. - -Ex. -```bash -mix ex2js "my/elixir/dir/**/*.ex" -r "js" -o my/js/dir -``` - -Will make the standard lib imports look like so -```javascript -import {...} from 'js/elixir' +```elixir +{:elixir_script, "~> x.x"} ``` - -## Examples - - * Using the included mix command, converting a folder of elixir files to JavaScript - ```bash - mix ex2js "my/elixir/dir/**/*.ex" -o my/js/dir - ``` - - * Using the included mix command, if you want to give it some elixir code and output JavaScript in the terminal - ```bash - mix ex2js -ex "[1, 2, 3, 4]" - ``` - - * Using the included the ElixirScript module to turn Elixir code into JavaScript - ```elixir - iex(1)> ElixirScript.compile("[1, 2, 3, 4]") - ["Kernel.SpecialForms.list(1,2,3,4)"] - ``` - -# Macros - -Macros can only be used when using ElixirScript as a library if the Macros are loaded into the current environment or if you give it a custom environment with the `env` option +Add `elixir_script` to list of mix compilers in mix.exs +Also add `elixir_script` configuration ```elixir -#module with macro defined -defmodule Math do - defmacro squared(x) do - quote do - unquote(x) * unquote(x) - end + def project do + [ + app: :my_app, + # ... + # Add elixir_script as a compiler + compilers: Mix.compilers ++ [:elixir_script], + # Our elixir_script configuration + elixir_script: [ + # Entry module. Can also be a list of modules + input: MyEntryModule, + # Output path. Either a path to a js file or a directory + output: "priv/elixir_script/build/elixirscript.build.js" + ] + ] end -end - -#create an env with the module required if not already in the current enviroment -def make_custom_env do - require Logger - require Math - __ENV__ -end - - -#Now pass it to `ElixirScript.compile` -ElixirScript.compile(""" - Math.squared(1) -""", env: make_custom_env) - -# returns ["1 * 1"] ``` -You should be able to use `use` in modules now as well, but modules that have `__using__` macros must also be require'd so that they can be expanded. +Run `mix compile` +# Examples -# Using JavaScript libraries +### Application -You can use `alias`, `import`, and `require` as you would in Elixir (sans macros). +[ElixirScript Todo Example](https://github.com/elixirscript/todo-elixirscript) -For JavaScript modules, use `JS.import` +### Library -```elixir -JS.import A, "a" #translates to "import {default as A} from 'a'" - -JS.import [A, B, C], "a" #translates to "import {A, B, C} from 'a'" -``` +[ElixirScript React](https://github.com/elixirscript/elixirscript_react) -# Limitations +### Starter kit -#### Not all of the Kernel.SpecialForms module is defined +[Elixirscript Starter Kit](https://github.com/harlantwood/elixirscript-starter-kit) -The following aren't defined (yet): +# Development -* `__CALLER__` -* `__ENV__` -* super +```bash +# Clone the repo +git clone git@github.com:bryanjos/elixirscript.git -The following are defined but incomplete: +#Get dependencies +make deps -* quote - Currently ignores `:location` and `:context` options -* bitstring - Implemented, but no pattern matching support yet +# Compile +make -#### Most of the Standard Library isn't defined yet -A lot of functions in the Kernel module are implemented. The Enum, Atom, List, Tuple, Logger, and Range modules are either fully defined are not complete. The rest still need to be implemented. Some modules like System or File may not be useful or function in the browser and may end up being only useful when using ElixirScript outside of the browser. +# Test +make test +``` +# Communication -### Example projects -* [phoenix_chat_example](https://github.com/bryanjos/phoenix_chat_example) The Pheonix Chat App using ElixirScript. -* [MobileElixir](https://github.com/bryanjos/MobileElixir) Using ElixirScript with React Native -* [color_bar_spike](https://github.com/bryanjos/color_bar_spike) A canvas drawing example using ElixirScript, React and Delorean +[#elixirscript](https://elixir-lang.slack.com/messages/elixirscript/) on the elixir-lang Slack -#### Using with Brunch -There is a plugin for using ElixirScript in your Brunch project -[here](https://www.npmjs.com/package/elixirscript-brunch) +# Contributing +Please check the [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/bench/elixir_script_bench.exs b/bench/elixir_script_bench.exs deleted file mode 100644 index b1a3ad6c..00000000 --- a/bench/elixir_script_bench.exs +++ /dev/null @@ -1,34 +0,0 @@ -defmodule ElixirScript.Bench do - use Benchfella - - bench "compile number" do - ElixirScript.compile("1") - :ok - end - - bench "compile string" do - ElixirScript.compile("\"1\"") - :ok - end - - bench "compile atom" do - ElixirScript.compile(":atom") - :ok - end - - bench "compile list" do - ElixirScript.compile("[1, 2, 3, 4]") - :ok - end - - bench "compile tuple" do - ElixirScript.compile("{1, 2, 3, 4}") - :ok - end - - bench "compile map" do - ElixirScript.compile("%{ a: 1, b: 2, c: :atom }") - :ok - end - -end \ No newline at end of file diff --git a/config/config.exs b/config/config.exs index a45f173b..32ca3de0 100644 --- a/config/config.exs +++ b/config/config.exs @@ -15,10 +15,12 @@ use Mix.Config format: "$date $time [$level] $metadata$message\n", metadata: [:user_id] + config :elixir_script, + lib_path: nil #The path to the elixirscript core and standard library JavaScript files # It is also possible to import configuration files, relative to this # directory. For example, you can emulate configuration per environment # by uncommenting the line below and defining dev.exs, test.exs and such. # Configuration from the imported file will override the ones defined # here (which is why it is important to import them last). # -# import_config "#{Mix.env}.exs" \ No newline at end of file + import_config "#{Mix.env}.exs" diff --git a/config/dev.exs b/config/dev.exs new file mode 100644 index 00000000..d2d855e6 --- /dev/null +++ b/config/dev.exs @@ -0,0 +1 @@ +use Mix.Config diff --git a/config/prod.exs b/config/prod.exs new file mode 100644 index 00000000..d2d855e6 --- /dev/null +++ b/config/prod.exs @@ -0,0 +1 @@ +use Mix.Config diff --git a/config/test.exs b/config/test.exs new file mode 100644 index 00000000..d2d855e6 --- /dev/null +++ b/config/test.exs @@ -0,0 +1 @@ +use Mix.Config diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index 72397e0c..00000000 --- a/gulpfile.js +++ /dev/null @@ -1,57 +0,0 @@ -var gulp = require('gulp'); -var mocha = require('gulp-mocha'); -var concat = require('gulp-concat'); -var babel = require('gulp-babel'); -var eslint = require('gulp-eslint'); -var rollup = require('gulp-rollup'); -var sourcemaps = require('gulp-sourcemaps'); - -var path = './priv/javascript'; - -var stdLibPath = path + '/lib/**/*.js'; - -var testPath = path + '/build/tests/**/*.spec.js'; - -var libPath = path + '/lib'; - -gulp.task('build', function() { - return gulp.src([libPath + '/**/*.js']) - .pipe(babel({sourceMap: false, modules:'common'})) - .pipe(gulp.dest('./priv/javascript/build/lib')); -}); - -gulp.task('build_test', function() { - return gulp.src(['./priv/javascript/tests/**/*.spec.js']) - .pipe(babel({sourceMap: false, modules:'common'})) - .pipe(gulp.dest('./priv/javascript/build/tests')); -}); - -gulp.task('test',['build', 'build_test'], function () { - return gulp.src(testPath, {read: false}) - .pipe(mocha({reporter: 'nyan'})); -}); - -gulp.task('lint', function () { - return gulp.src([stdLibPath, testPath, '!./priv/javascript/build/**/*.js']) - .pipe(eslint()) - .pipe(eslint.format()) - .pipe(eslint.failOnError()); -}); - - -gulp.task('dist_build', function() { - return gulp.src(['./priv/javascript/**/*.js', '!./priv/javascript/build/**/*.js', '!./priv/javascript/dist/**/*.js', '!./priv/javascript/dist_build/**/*.js', '!./priv/javascript/tests/**/*.js']) - .pipe(babel({whitelist: ['flow'], optional: ["minification.deadCodeElimination"]})) - .pipe(gulp.dest('./priv/javascript/dist_build')); -}); - - -gulp.task('dist_add_source_map', function() { - return gulp.src(['./priv/javascript/dist/elixir.js']) - .pipe(sourcemaps.init()) - .pipe(sourcemaps.write()) - .pipe(gulp.dest('./priv/javascript/dist')); -}); - - -gulp.task('default', ['lint', 'test']); diff --git a/lib/elixir_script.ex b/lib/elixir_script.ex index eabf0875..cfb1b9a6 100644 --- a/lib/elixir_script.ex +++ b/lib/elixir_script.ex @@ -1,214 +1,78 @@ defmodule ElixirScript do - alias ElixirScript.Translator - alias ElixirScript.Translator.JSModule - alias ESTree.Tools.Builder - alias ESTree.Tools.Generator - require Logger - @moduledoc """ - Translates Elixir into JavaScript. - - All compile functions return a list of - transpiled javascript code or a tuple consisting of - the file name for the code and the transpiled javascript code. - All compile functions also take an optional opts parameter - that controls transpiler output. + ElixirScript acts as a mix compiler. This means that when you execute `mix compile`, + ElixirScript's compiler will run as well. Make sure to add ElixirScript to your + list of compilers in mix.exs. + + ElixirScript must be told which modules to use as the entry to your ElixirScript application. + This is done by adding an `elixir_script` key to your project configuration whose value is a keyword list of options. + Add an `input` key and make the value either the name of a module or a list of modules + that are the entry modules you wish to compile to JavaScript. ElixirScript will use + those modules to find what other modules and functions it needs to convert to JavaScript. + ElixirScript by default places output in `priv/elixir_script/build`. If you wish to change this, + add an `output` key to your ElixirScript configuration. + + An example configuration for a project is shown below + + ``` elixir + def project do + [ + app: :my_app, + version: "0.1.0", + elixir: "~> 1.0", + deps: deps, + # Add elixir_script as a compilter + compilers: Mix.compilers() ++ [:elixir_script], + # Our elixir_script configuration + elixir_script: [ + # Entry module. Can also be a list of modules + input: MyEntryModule, + # Output path. Either a path to a js file or a directory + output: "priv/elixir_script/build" + ] + ] + end + ``` Available options are: - * `:include_path` - a boolean controlling whether to return just the JavaScript code - or a tuple of the file name and the JavaScript code - * `:root` - a binary path prepended to the path of the standard lib imports if needed - * `:env` - a Macro.env struct to use. This is most useful when using macros. Make sure that the - given env has the macros required. Defaults to __ENV__. - """ + * `input` (required): The entry module(s) for your application or library - @doc """ - Compiles the given Elixir code string - """ - @spec compile(binary, Dict.t) :: [binary | { binary, binary }] - def compile(elixir_code, opts \\ []) do - elixir_code - |> Code.string_to_quoted! - |> compile_quoted(opts) - end - - @doc """ - Compiles the given Elixir code in quoted form - """ - @spec compile_quoted(Macro.t, Dict.t) :: [binary | { binary, binary }] - def compile_quoted(quoted, opts \\ []) do - include_path = Dict.get(opts, :include_path, false) - root = Dict.get(opts, :root) - env = Dict.get(opts, :env, custom_env) - import_standard_libs? = Dict.get(opts, :import_standard_libs, true) - - - ElixirScript.State.start_link(root, env) - build_environment([quoted]) - - if Set.size(ElixirScript.State.get().modules) > 0 do - create_code(include_path, import_standard_libs?) - else - result = case Translator.translate(quoted, env) do - modules when is_list(modules) -> - List.flatten(modules) - |> Enum.map(fn(x) -> - convert_to_code(x, root, include_path, env, import_standard_libs?) - end) - module -> - List.wrap( - convert_to_code(module, root, include_path, env, import_standard_libs?) - ) - end + * `output`: The path of the generated JavaScript files. (defaults to `priv/elixir_script/build`) - ElixirScript.State.stop + This should be a directory. If given a file, it will dump JavaScript files into the same directory as the given file path - result - end - end + * `root`: Optional root for imports of FFI JavaScript modules. Defaults to `.`. If using output directly in a browser, you may want to make it something like `/js` or some uri. - def make_defmodule({:defmodule, _, _} = ast) do - ast - end + Now run `mix compile` and you should see a JavaScript file named `elixirscript.build.js` in the `priv/elixir_script/build/` directory. ElixirScript outputs JavaScript in the ES Module format. If your browser supports it, you can include the output in a script tag with the type "module" - def make_defmodule(ast) do - {:defmodule, [], [{:__aliases__, [], [:Temp]}, [do: { :__block__, [], [ast] }]]} - end + ```html + + ``` + + If your browser does not yet support ES modules directly, use a tool such as [webpack](https://webpack.js.org/) or [brunch](http://brunch.io/) to convert it into something that can be used in the browser + + ### JavaScript Interop + + Check out the [JavaScript Interoperability](javascriptinterop.html) documentation + ### Dependencies + + ElixirScript can use many of the same Hex packages and dependencies that currently exist. + It is also possible to make packages that are specific to ElixirScript. If you decide to + make such a package, please prepend `elixir_script` to the app name. For instance is making + a package for ElixirScript with FFI modules for interacting with React, the name would be + `elixir_script_react`. This is to make sure that other developers know that a package in Hex + is specifically for use with ElixirScript. + + ### Limitations + + ElixirScript does not support `receive` or any of OTP at this time. + """ end diff --git a/lib/elixir_script/beam.ex b/lib/elixir_script/beam.ex new file mode 100644 index 00000000..0a727ef1 --- /dev/null +++ b/lib/elixir_script/beam.ex @@ -0,0 +1,146 @@ +defmodule ElixirScript.Beam do + @moduledoc false + + @doc """ + Takes a module and finds the expanded AST + from the debug info inside the beam file. + For protocols, this will return a list of + all the protocol implementations + """ + @spec debug_info(atom | bitstring) :: {:ok, map} | {:ok, atom, map, list} | {:error, binary} + def debug_info(module) + + # We get debug info from String and then replace + # functions in it with equivalents in ElixirScript.String. + # This is so that we don't include the unicode database + # in our output + def debug_info(String) do + {:ok, info} = do_debug_info(String) + {:ok, ex_string_info} = do_debug_info(ElixirScript.String) + + definitions = replace_definitions(info.definitions, ex_string_info.definitions) + + info = %{info | definitions: definitions} + + {:ok, info} + end + + # Replace some modules with ElixirScript versions + def debug_info(module) when module in [Agent] do + case do_debug_info(Module.concat(ElixirScript, module)) do + {:ok, info} -> + {:ok, Map.put(info, :module, module)} + + e -> + e + end + end + + def debug_info(module) when is_atom(module) do + do_debug_info(module) + end + + def debug_info(beam) when is_bitstring(beam) do + do_debug_info(beam) + end + + defp do_debug_info(module, path \\ nil) + + defp do_debug_info(module, _) when is_atom(module) do + case :code.get_object_code(module) do + {_, beam, beam_path} -> + do_debug_info(beam, beam_path) + + :error -> + {:error, "Unknown module"} + end + end + + defp do_debug_info(beam, beam_path) do + with {:ok, {module, [debug_info: {:debug_info_v1, backend, data}]}} <- + :beam_lib.chunks(beam, [:debug_info]), + {:ok, {^module, attribute_info}} = :beam_lib.chunks(beam, [:attributes]) do + if Keyword.get(attribute_info[:attributes], :protocol) do + get_protocol_implementations(module, beam_path) + else + backend.debug_info(:elixir_v1, module, data, []) + |> process_debug_info(beam_path) + end + else + :error -> + {:error, "Unknown module"} + + {:error, :beam_lib, {:unknown_chunk, "non_existing.beam", :debug_info}} -> + {:error, "Unsupported version of Erlang"} + + {:error, :beam_lib, {:missing_chunk, _, _}} -> + {:error, "Debug info not available"} + + {:error, :beam_lib, {:file_error, "non_existing.beam", :enoent}} -> + {:error, "Debug info not available"} + end + end + + defp process_debug_info({:ok, info}, nil) do + info = Map.put(info, :last_modified, nil) + {:ok, info} + end + + defp process_debug_info({:ok, info}, beam_path) do + info = + case File.stat(beam_path, time: :posix) do + {:ok, file_info} -> + Map.put(info, :last_modified, file_info.mtime) + + _ -> + Map.put(info, :last_modified, nil) + end + + info = Map.put(info, :beam_path, beam_path) + + {:ok, info} + end + + defp process_debug_info(error, _) do + error + end + + defp get_protocol_implementations(module, beam_path) do + {:ok, protocol_module_info} = process_debug_info({:ok, %{}}, beam_path) + + implementations = + module + |> Protocol.extract_impls(:code.get_path()) + |> Enum.map(fn x -> Module.concat([module, x]) end) + |> Enum.map(fn x -> + case debug_info(x) do + {:ok, info} -> + {x, info} + + _ -> + raise ElixirScript.CompileError, + message: "Unable to compile protocol implementation #{inspect(x)}", + severity: :error + end + end) + + {:ok, module, protocol_module_info, implementations} + end + + defp replace_definitions(original_definitions, replacement_definitions) do + Enum.map(original_definitions, fn {{function, arity}, type, _, _} = ast -> + ex_ast = + Enum.find(replacement_definitions, fn {{ex_function, ex_arity}, ex_type, _, _} -> + ex_function == function and ex_arity == arity and ex_type == type + end) + + case ex_ast do + nil -> + ast + + _ -> + ex_ast + end + end) + end +end diff --git a/lib/elixir_script/cli.ex b/lib/elixir_script/cli.ex index 54fae872..1eedfc37 100644 --- a/lib/elixir_script/cli.ex +++ b/lib/elixir_script/cli.ex @@ -1,84 +1,85 @@ defmodule ElixirScript.CLI do @moduledoc false - @switches [ - output: :binary, elixir: :boolean, root: :binary, - help: :boolean + @app_version Mix.Project.config()[:version] + + @switches [ + output: :string, + help: :boolean, + version: :boolean, + root: :string ] - @aliases [ - o: :output, ex: :elixir, h: :help, r: :root + @aliases [ + o: :output, + h: :help, + v: :version ] - def main(argv) do - argv - |> parse_args - |> process - end + def parse_args(args) do + {options, input, errors} = OptionParser.parse(args, switches: @switches, aliases: @aliases) + + cond do + length(errors) > 0 -> + :help + + Keyword.get(options, :help, false) -> + :help + + Keyword.get(options, :version, false) -> + :version - def parse_args(args) do - parse = OptionParser.parse(args, switches: @switches, aliases: @aliases) + length(input) == 0 -> + :help - case parse do - { [help: true] , _ , _ } -> :help - { options , [input], _ } -> { input, options } - { [], [], [] } -> :help + true -> + {input, options} end + end + defp help_message do + """ + usage: elixirscript [options] + the entry module of your application + the path to .ex(s) files to compile + + options: + -o --output [path] places output at the given path. + Can be a directory or filename. + -v --version the current version number + -h --help this message + --root The root import path for FFI imports + """ end def process(:help) do - IO.write """ - usage: ex2js [options] - path to elixir files or - the elixir code string if the -ex flag is used - options: - -o --output [path] places output at the given path - -ex --elixir read input as elixir code string - -r --root [path] root path for standard libs - -h --help this message - """ + IO.write(help_message()) end - def process({ input, options }) do + def process(:version) do + IO.write(@app_version) + end + + def process({input, options}) do if options_contains_unknown_values(options) do - process(:help) + process(:help) else - do_process(input, options) + do_process(input, options) end end - def do_process(input, options) do - compile_opts = [ - root: options[:root], - include_path: options[:output] != nil + defp do_process(input, options) do + compile_opts = [ + output: Keyword.get(options, :output, :stdout), + root: Keyword.get(options, :root, ".") ] - compile_output = case options[:elixir] do - true -> - ElixirScript.compile(input, compile_opts) - _ -> - ElixirScript.compile_path(input, compile_opts) - end - - case options[:output] do - nil -> - Enum.each(compile_output, - fn - ({_path, code})-> IO.write(code) - (code)-> IO.write(code) - end) - output_path -> - Enum.each(compile_output, fn(x) -> - write_to_file(x, output_path) - end) - - ElixirScript.copy_standard_libs_to_destination(output_path) - end + input = handle_input(input) + ElixirScript.Compiler.compile(input, compile_opts) end defp options_contains_unknown_values(options) do - Enum.any?(options, fn({key, _value}) -> + Enum.any?(options, fn {key, _value} -> if key in Keyword.keys(@switches) do false else @@ -87,13 +88,10 @@ defmodule ElixirScript.CLI do end) end - def write_to_file({ file_path, js_code }, destination) do - file_name = Path.join([destination, file_path]) - - if !File.exists?(Path.dirname(file_name)) do - File.mkdir_p!(Path.dirname(file_name)) - end - - File.write!(file_name, js_code) + defp handle_input(input) do + input + |> Enum.map(fn x -> String.split(x, [" ", ","], trim: true) end) + |> List.flatten() + |> Enum.map(fn x -> Module.concat([x]) end) end end diff --git a/lib/elixir_script/compile_error.ex b/lib/elixir_script/compile_error.ex new file mode 100644 index 00000000..d670552b --- /dev/null +++ b/lib/elixir_script/compile_error.ex @@ -0,0 +1,3 @@ +defmodule ElixirScript.CompileError do + defexception [:message, :severity] +end diff --git a/lib/elixir_script/compiler.ex b/lib/elixir_script/compiler.ex new file mode 100644 index 00000000..9e36d5a8 --- /dev/null +++ b/lib/elixir_script/compiler.ex @@ -0,0 +1,186 @@ +defmodule ElixirScript.Compiler do + @moduledoc """ + The entry point for the ElixirScript compilation process. + Takes the given module(s) and compiles them and all modules + and functions they use into JavaScript. + + Will also take a path to Elixir files + """ + + @doc """ + Takes either a module name, list of module names, or a path as + the entry point(s) of an application/library. From there + it will determine which modules and functions are needed + to be compiled. + + Available options are: + * `output`: The path of the generated JavaScript file. + + If output is `nil`, then generated code is sent to standard out + + If output is a path, the generated code placed in that path. + If path ends in `.js` then that will be the name of the file. + If a directory is given, file will be named `elixirscript.build.js` + + * `root`: Optional root for imports of FFI JavaScript modules. Defaults to `.`. + """ + alias ElixirScript.{ + State, + Translate, + FindUsedModules, + FindUsedFunctions, + Output + } + + alias ElixirScript.ModuleSystems.ES + alias Kernel.ParallelCompiler + + @type compiler_input :: + atom + | [atom] + | binary + + @spec compile(compiler_input, []) :: map + def compile(path, opts \\ []) + + def compile(path, opts) when is_binary(path) do + opts = build_compiler_options(opts) + {:ok, pid} = State.start_link(opts) + + path = + if String.ends_with?(path, [".ex", ".exs"]) do + path + else + Path.join([path, "**", "*.{ex,exs}"]) + end + + files = Path.wildcard(path) + + ParallelCompiler.compile(files, each_module: &on_module_compile(pid, &1, &2, &3)) + + entry_modules = + pid + |> State.get_in_memory_modules() + |> Keyword.keys() + + do_compile(entry_modules, pid, opts) + end + + def compile(entry_modules, opts) do + opts = build_compiler_options(opts) + {:ok, pid} = State.start_link(opts) + + entry_modules = List.wrap(entry_modules) + + do_compile(entry_modules, pid, opts) + end + + defp do_compile(entry_modules, pid, opts) do + FindUsedModules.execute(entry_modules, pid) + + if opts.remove_unused_functions do + FindUsedFunctions.execute(entry_modules, pid) + end + + modules = State.list_modules(pid) + Translate.execute(modules, pid) + + modules = State.list_modules(pid) + result = Output.execute(modules, pid, opts) + + State.stop(pid) + + transform_output(modules, result, opts) + end + + defp build_compiler_options(opts) do + remove_used_functions? = Keyword.get(opts, :remove_unused_functions, true) + + default_options = + Map.new() + |> Map.put(:output, Keyword.get(opts, :output)) + |> Map.put(:format, :es) + |> Map.put(:root, Keyword.get(opts, :root, ".")) + |> Map.put(:remove_unused_functions, remove_used_functions?) + + options = default_options + Map.put(options, :module_formatter, ES) + end + + defp on_module_compile(pid, _file, module, beam) do + State.put_in_memory_module(pid, module, beam) + end + + defp transform_output(modules, compiled_js, opts) do + output_path = + cond do + opts.output == nil or opts.output == :stdout -> + "" + + File.dir?(opts.output) -> + opts.output + + true -> + Path.dirname(opts.output) + end + + data = %{ + ElixirScript.Core => %{ + references: [], + last_modified: nil, + beam_path: nil, + source: nil, + js_path: Path.join(output_path, "ElixirScript.Core.js"), + diagnostics: [], + js_code: nil, + type: :ffi + } + } + + Enum.reduce(modules, data, fn {module, info}, current_data -> + diagnostics = + Map.get(info, :diagnostics, []) + |> Enum.map(fn x -> + Map.put(x, :file, Map.get(info, :file)) + end) + + info = %{ + references: Map.get(info, :used_modules, []), + last_modified: Map.get(info, :last_modified, nil), + beam_path: Map.get(info, :beam_path), + source: Map.get(info, :file), + js_path: Path.join(output_path, "#{module}.js"), + diagnostics: diagnostics + } + + info = + case Keyword.get(compiled_js, module) do + [js_input_path, js_output_path] -> + last_modified = + case File.stat(js_input_path, time: :posix) do + {:ok, file_info} -> + file_info.mtime + + _ -> + nil + end + + info + |> Map.put(:last_modified, last_modified) + |> Map.put(:beam_path, nil) + |> Map.put(:source, js_input_path) + |> Map.put(:js_path, js_output_path) + |> Map.put(:js_code, nil) + |> Map.put(:type, :ffi) + + js_code -> + info + |> Map.put(:js_path, Path.join(output_path, "#{module}.js")) + |> Map.put(:js_code, js_code) + |> Map.put(:type, :module) + end + + Map.put(current_data, module, info) + end) + end +end diff --git a/lib/elixir_script/env.ex b/lib/elixir_script/env.ex deleted file mode 100644 index c212be2d..00000000 --- a/lib/elixir_script/env.ex +++ /dev/null @@ -1,5 +0,0 @@ -defmodule ElixirScript.Env do - @moduledoc false - - defstruct env: __ENV__, modules: HashSet.new, root: "" -end \ No newline at end of file diff --git a/lib/elixir_script/ffi.ex b/lib/elixir_script/ffi.ex new file mode 100644 index 00000000..71249fe0 --- /dev/null +++ b/lib/elixir_script/ffi.ex @@ -0,0 +1,107 @@ +defmodule ElixirScript.FFI do + @moduledoc """ + The Foreign Function Interface (FFI) for interacting with JavaScript + + To define a foreign module, make a new module and add `use ElixirScript.FFI` to it. + To define external functions, use the `defexternal` macro. + + Here is an example of a foreign module for a JSON module + + ```elixir + defmodule MyApp.JSON do + use ElixirScript.FFI + + defexternal stringify(map) + defexternal parse(string) + end + ``` + + Foreign modules map to JavaScript files that export functions defined with the `defexternal` macro. + ElixirScript expects JavaScript modules to be in the `priv/elixir_script` directory. + These modules are copied to the output directory upon compilation. + + For our example, a JavaScript file must be placed in the `priv/elixir_script` folder. + In our example, it could either be `priv/elixir_script/my_app/json.js` or + `priv/elixir_script/my_app.json.js`. ElixirScript will look for either path + + It looks like this + ```javascript + export default { + stringify: JSON.stringify, + parse: JSON.parse + } + ``` + + `ElixirScript.FFI` takes the following options + * `global`: If the module is defined in the global state or not. If this is set to `true`, + nothing is imported and instead ElixirScript will use the name of the module to call a module and + function in the global scope. + * `name`: Only applicable with `global` is set to `true`. This will use the name defined here + instead of the module name for calling modules and functions in the global scope + + An example using the global option to reference the JSON module in browsers + + ```elixir + defmodule JSON do + use ElixirScript.FFI, global: true + + defexternal stringify(map) + defexternal parse(string) + end + ``` + + The calls above are translated to calls to the `JSON` module in the global scope + + An example using global and name options + + ```elixir + defmodule Console do + use ElixirScript.FFI, global: true, name: :console + + defexternal log(term) + end + ``` + + With the above, calls in ElixirScript to `Console.log` will translate to `console.log` in JavaScript + """ + + defmacro __using__(opts) do + quote do + import ElixirScript.FFI + Module.register_attribute __MODULE__, :__foreign_info__, persist: true + @__foreign_info__ %{ + path: Macro.underscore(__MODULE__), + name: unquote(Keyword.get(opts, :name, nil)), + global: unquote(Keyword.get(opts, :global, false)) + } + end + end + + @doc """ + Defines a JavaScript function to be called from Elixir modules + + To define an external function, pass the name and arguments to `defexternal` + + ```elixir + defexternal my_js_function(arg1, arg2, arg3) + ``` + """ + defmacro defexternal({name, _, args}) do + args = Enum.map(args, fn + {:\\, meta0, [{name, meta, atom}, value]} -> + name = String.to_atom("_" <> Atom.to_string(name)) + {:\\, meta0, [{name, meta, atom}, value]} + + {name, meta, atom} -> + name = String.to_atom("_" <> Atom.to_string(name)) + {name, meta, atom} + + other -> + other + end) + + quote do + def unquote(name)(unquote_splicing(args)), do: nil + end + end +end diff --git a/lib/elixir_script/lib/agent.ex b/lib/elixir_script/lib/agent.ex new file mode 100644 index 00000000..4048350f --- /dev/null +++ b/lib/elixir_script/lib/agent.ex @@ -0,0 +1,49 @@ +defmodule ElixirScript.Agent do + @moduledoc false + + def start(fun, options \\ []) do + name = if Keyword.has_key?(options, :name) do + Keyword.get(options, :name) + else + nil + end + + pid = ElixirScript.Core.Store.create(fun.(), name) + { :ok, pid } + end + + def start_link(fun, options \\ []) do + name = if Keyword.has_key?(options, :name) do + Keyword.get(options, :name) + else + nil + end + + pid = ElixirScript.Core.Store.create(fun.(), name) + { :ok, pid } + end + + def stop(agent) do + ElixirScript.Core.Store.remove(agent) + :ok + end + + def update(agent, fun) do + current_state = ElixirScript.Core.Store.read(agent) + ElixirScript.Core.Store.update(agent, fun.(current_state)) + :ok + end + + def get(agent, fun) do + current_state = ElixirScript.Core.Store.read(agent) + fun.(current_state) + end + + def get_and_update(agent, fun) do + current_state = ElixirScript.Core.Store.read(agent) + {val, new_state} = fun.(current_state) + ElixirScript.Core.Store.update(agent, new_state) + val + end + +end diff --git a/lib/elixir_script/lib/functions.ex b/lib/elixir_script/lib/functions.ex new file mode 100644 index 00000000..acc12525 --- /dev/null +++ b/lib/elixir_script/lib/functions.ex @@ -0,0 +1,8 @@ +defmodule ElixirScript.Core.Functions do + @moduledoc false + use ElixirScript.FFI, global: true + + defexternal split_at(value, position) + + defexternal graphemes(str) +end diff --git a/lib/elixir_script/lib/js.ex b/lib/elixir_script/lib/js.ex index f3e1901e..a8713880 100644 --- a/lib/elixir_script/lib/js.ex +++ b/lib/elixir_script/lib/js.ex @@ -1,50 +1,94 @@ -defmodule ElixirScript.Lib.JS do +defmodule ElixirScript.JS do + @moduledoc """ + This module defines macros and functions which implement + JavaScript functionality that may not translate easily to + Elixir. For instance, creating a new object + """ + + use ElixirScript.FFI, global: true @doc """ Creates new JavaScript objects. - ex: - JS.new User, ["first_name", "last_name"] + ```elixir + ElixirScript.JS.new User, ["first_name", "last_name"] + ``` """ - defmacro new(module, params) + defexternal new(module, params) + @doc """ + Returns the type of the given value + """ + defexternal typeof(value) @doc """ - Updates an existing JavaScript object. + Determines if value is an instance of type. + """ + defexternal instanceof(value, type) - ex: - JS.update elem, "width", 100 + @doc """ + Throws the term given """ - defmacro update(object, property, value) + defexternal throw(term) + @doc """ + Creates a breakpoint for JavaScript debuggers to stop at + """ + defexternal debugger() @doc """ - Imports a JavaScript module. + The current JavaScript context + """ + defexternal this() - Elixir modules can use the normal `import`, `alias` and `require`, - but JavaScript modules work differently and have to be imported - using this. + @doc """ + Mutates an existing JavaScript object. - If module is not a list, then it is treated as a default import, - otherwise it is not. + ```elixir + ElixirScript.JS.mutate elem, "width", 100 + ``` + """ + defexternal mutate(object, key, value) - ex: - JS.import A, "a" #translates to "import {default as A} from 'a'" + @doc """ + Takes the given map and returns an object + Throws an error if any key is not a + number, binary, or atom - JS.import [A, B, C], "a" #translates to "import {A, B, C} from 'a'" + ```elixir + ElixirScript.JS.map_to_object(%{my: "map"}) + ``` """ - defmacro import(module, from) - + defexternal map_to_object(map) @doc """ - Turns an ElixirScript data structure into a JavaScript one. + Takes the given map and returns an object + Throws an error if any key is not a + number, binary, or atom + + ```elixir + ElixirScript.JS.map_to_object(%{my: "map"}, keys: :string) + ``` """ - defmacro to_js(value) + defexternal map_to_object(map, options) + @doc """ + Takes the given object and returns a map + Options include [{:keys, :atom}, {:recurse_array, true}] - @doc """ - Turns an ElixirScript data structure into JSON. + ```elixir + ElixirScript.JS.object_to_object({my: "object"}) + ``` """ - defmacro to_json(value) + defexternal object_to_map(object) + + @doc """ + Takes the given object and returns a map + Options include [{:keys, :atom}, {:recurse_array, true}] -end \ No newline at end of file + ```elixir + ElixirScript.JS.object_to_object({my: "map"}, keys: :atom) + ``` + """ + defexternal object_to_map(object, options) +end diff --git a/lib/elixir_script/lib/store.ex b/lib/elixir_script/lib/store.ex new file mode 100644 index 00000000..9e98bce3 --- /dev/null +++ b/lib/elixir_script/lib/store.ex @@ -0,0 +1,12 @@ +defmodule ElixirScript.Core.Store do + @moduledoc false + use ElixirScript.FFI, global: true + + defexternal create(value, name \\ nil) + + defexternal update(key, value) + + defexternal read(key) + + defexternal remove(key) +end diff --git a/lib/elixir_script/lib/string.ex b/lib/elixir_script/lib/string.ex new file mode 100644 index 00000000..f5227fc5 --- /dev/null +++ b/lib/elixir_script/lib/string.ex @@ -0,0 +1,204 @@ +defmodule ElixirScript.String do + @moduledoc false + import Kernel, except: [length: 1] + + def to_atom(str) do + :erlang.binary_to_atom(str, :utf8) + end + + def to_existing_atom(str) do + :erlang.binary_to_existing_atom(str, :utf8) + end + + def to_char_list(str) do + str.split("") + end + + def to_float(str) do + :erlang.binary_to_float(str) + end + + def to_integer(str) do + :erlang.binary_to_integer(str) + end + + def to_integer(str, base) do + :erlang.binary_to_integer(str, base) + end + + def upcase(str) do + str.toUpperCase() + end + + def downcase(str) do + str.toLowerCase() + end + + def at(str, pos) do + case pos > length(str) do + true -> + nil + _ -> + str[pos] + end + end + + def capitalize(str) do + first = str[0].toUpperCase() + rest = str.substr(1).toLowerCase() + + first <> rest + end + + def split(str) do + str.split() + end + + def split(str, replace, options \\ []) do + limit = Keyword.get(options, :parts, -1) + trim = Keyword.get(options, :trim, false) + split = str.split(replace, limit) + + Enum.map(split, fn(x) -> + if trim do + x.trim() + else + x + end + end) + end + + def next_grapheme(nil), do: nil + def next_grapheme(""), do: nil + + def next_grapheme(str) do + { str[0], str.substr(1) } + end + + def first(nil), do: nil + def first(str) do + str[0] + end + + def last(nil), do: nil + def last(str) do + str[length(str) - 1] + end + + def graphemes(str) do + ElixirScript.Core.Functions.graphemes(str) + end + + def length(str) do + graphemes(str).length() + end + + def match?(str, regex) do + str.match(regex) != nil + end + + def next_codepoint(nil), do: nil + def next_codepoint(""), do: nil + def next_codepoint(str) do + { str[0].codePointAt(0), str.substr(1) } + end + + def reverse(str) do + do_reverse(str, "") + end + + defp do_reverse("", str) do + str + end + + defp do_reverse(str, reverse_str) do + do_reverse(str.substr(1), reverse_str <> last(str)) + end + + def starts_with?(str, prefix) when is_binary(prefix) do + str.startsWith(prefix) + end + + def starts_with?(str, prefixes) when is_list(prefixes) do + do_starts_with?(str, prefixes) + end + + def do_starts_with?(_, []) do + false + end + + def do_starts_with?(str, prefixes) do + case starts_with?(str, hd(prefixes)) do + true -> + true + _ -> + do_starts_with?(str, tl(prefixes)) + end + end + + def ends_with?(str, suffix) when is_binary(suffix) do + str.endsWith(suffix) + end + + def ends_with?(str, suffixes) when is_list(suffixes) do + do_ends_with?(str, suffixes) + end + + def do_ends_with?(_, []) do + false + end + + def do_ends_with?(str, suffixes) do + case ends_with?(str, hd(suffixes)) do + true -> + true + _ -> + do_ends_with?(str, tl(suffixes)) + end + end + + def duplicate(str, n) do + str.repeat(n) + end + + def contains?(str, s) when is_binary(s) do + str.indexOf(s) > -1 + end + + def contains?(str, s) when is_list(s) do + do_contains?(str, s) + end + + def do_contains?(_, []) do + false + end + + def do_contains?(str, prefixes) do + case contains?(str, hd(prefixes)) do + true -> + true + _ -> + do_contains?(str, tl(prefixes)) + end + end + + def codepoints(str) do + do_codepoints(str, []) + end + + def do_codepoints("", codepoint_list) do + codepoint_list + end + + def do_codepoints(str, codepoint_list) do + do_codepoints(str.substr(1), codepoint_list ++ [first(str).codePointAt(0)]) + end + + def valid?(str) do + is_binary(str) + end + + def split_at(value, position) do + ElixirScript.Core.Functions.split_at(value, position) + end +end diff --git a/lib/elixir_script/manifest.ex b/lib/elixir_script/manifest.ex new file mode 100644 index 00000000..a7974cb1 --- /dev/null +++ b/lib/elixir_script/manifest.ex @@ -0,0 +1,28 @@ +defmodule ElixirScript.Manifest do + @moduledoc false + + @spec read_manifest(binary) :: nil + def read_manifest(manifest_path) do + if File.exists?(manifest_path) do + manifest_path + |> File.read!() + |> :erlang.binary_to_term() + else + %{} + end + end + + @spec write_manifest(binary, map) :: :ok + def write_manifest(manifest_path, modules) do + data = + Enum.reduce(modules, %{}, fn {module, info}, current_data -> + Map.put(current_data, module, Map.drop(info, [:js_code])) + end) + + data = :erlang.term_to_binary(data, [:compressed]) + File.mkdir_p!(Path.dirname(manifest_path)) + File.write!(manifest_path, data) + + :ok + end +end diff --git a/lib/elixir_script/module.ex b/lib/elixir_script/module.ex deleted file mode 100644 index 66d948e5..00000000 --- a/lib/elixir_script/module.ex +++ /dev/null @@ -1,5 +0,0 @@ -defmodule ElixirScript.Module do - @moduledoc false - - defstruct name: nil, functions: [], macros: [], body: nil -end \ No newline at end of file diff --git a/lib/elixir_script/module_systems/es.ex b/lib/elixir_script/module_systems/es.ex new file mode 100644 index 00000000..c5c9ef9d --- /dev/null +++ b/lib/elixir_script/module_systems/es.ex @@ -0,0 +1,51 @@ +defmodule ElixirScript.ModuleSystems.ES do + @moduledoc false + alias ESTree.Tools.Builder, as: JS + + def build(js_imports, body, exports) do + imports = js_imports + |> Enum.filter(fn + {_module, _name, nil, _import_path} -> false + _ -> true + end) + |> Enum.map(fn + {_module, name, _path, import_path} -> import_module(name, import_path) + end) + + export = if is_nil(exports), do: [], else: [export_module(exports)] + imports ++ body ++ export + end + + def build_imports(js_imports) do + js_imports + |> Enum.map(fn + {_module, name, _path, import_path} -> import_module(name, import_path) + end) + end + + def build_export(exports) do + if is_nil(exports), do: [], else: [export_module(exports)] + end + + defp import_module(import_name, from) do + js_module_name = JS.identifier(import_name) + + import_specifier = JS.import_default_specifier( + js_module_name + ) + + do_import_module([import_specifier], from) + end + + defp do_import_module(import_specifiers, file_path) do + JS.import_declaration( + import_specifiers, + JS.literal(file_path) + ) + end + + defp export_module(exported_object) do + JS.export_default_declaration(exported_object) + end + +end diff --git a/lib/elixir_script/module_systems/namespace.ex b/lib/elixir_script/module_systems/namespace.ex new file mode 100644 index 00000000..c98eee87 --- /dev/null +++ b/lib/elixir_script/module_systems/namespace.ex @@ -0,0 +1,65 @@ +defmodule ElixirScript.ModuleSystems.Namespace do + @moduledoc false + alias ESTree.Tools.Builder, as: JS + alias ElixirScript.Translate.Helpers + alias ElixirScript.Translate.Identifier + + def build(module_name, body, exports) do + List.wrap(make_namespace_body(module_name, body, exports)) + end + + defp module_name_function_call(module_name, function) do + members = ["Elixir"] ++ Module.split(module_name) ++ [function] + Identifier.make_namespace_members(members) + end + + defp build_namespace() do + JS.member_expression( + JS.identifier("ElixirScript"), + JS.member_expression( + JS.identifier(:Core), + JS.member_expression( + JS.identifier(:Functions), + JS.identifier(:build_namespace) + ) + ) + ) + end + + defp make_namespace_body(module_name, body, exports) do + values = module_name_function_call(module_name, "__exports") + + js_if = JS.if_statement( + values, + JS.return_statement(values) + ) + + exports = if is_nil(exports) do + JS.object_expression([]) + else + exports + end + + declaration = Helpers.declare("__exports", exports) + + assign = Helpers.assign(values, JS.identifier("__exports")) + + exports = [JS.return_statement(JS.identifier("__exports"))] + + make = JS.member_expression( + Helpers.call( + build_namespace(), + [JS.identifier("Elixir"), JS.literal(Enum.join(["Elixir"] ++ Module.split(module_name), "."))] + ), + JS.identifier("__load") + ) + + func_body = JS.block_statement([js_if] ++ body ++ [declaration, assign] ++ exports) + + func = Helpers.function([JS.identifier("Elixir")], func_body) + Helpers.assign( + make, + func + ) + end +end diff --git a/lib/elixir_script/passes/find_used_functions.ex b/lib/elixir_script/passes/find_used_functions.ex new file mode 100644 index 00000000..861d7c44 --- /dev/null +++ b/lib/elixir_script/passes/find_used_functions.ex @@ -0,0 +1,316 @@ +defmodule ElixirScript.FindUsedFunctions do + @moduledoc false + alias ElixirScript.State, as: ModuleState + + @doc """ + Takes a list of entry modules and finds modules they use along with + documenting the functions used. The data collected about used functions + is used to filter only the used functions for compilation + """ + @spec execute([atom], pid) :: nil + def execute(entry_modules, pid) do + entry_modules + |> List.wrap() + |> Enum.each(fn module -> + walk_module(module, pid) + end) + + pid + |> ElixirScript.State.list_modules() + |> Enum.each(fn {module, info} -> + if get_in(info, [:attributes, :protocol_impl]) do + walk_module(module, pid) + end + end) + end + + defp walk_module(module, pid) do + %{ + attributes: _attrs, + compile_opts: _compile_opts, + definitions: defs, + file: _file, + line: _line, + module: ^module, + unreachable: unreachable + } = ModuleState.get_module(pid, module) + + reachable_defs = + Enum.filter(defs, fn + {_, type, _, _} when type in [:defmacro, :defmacrop] -> + false + + {name, _, _, _} -> + name not in unreachable + + _ -> + true + end) + + state = %{ + pid: pid, + module: module + } + + Enum.each(reachable_defs, fn {name, _type, _, _clauses} -> + ModuleState.put_used(state.pid, module, name) + end) + + Enum.each(reachable_defs, &walk(&1, state)) + end + + defp walk_module(module, function, arity, pid) do + function = {function, arity} + + unless ModuleState.has_used?(pid, module, function) do + info = ModuleState.get_module(pid, module) + + state = %{ + pid: pid, + module: module + } + + reachable_def = + Enum.find(Map.get(info, :definitions, []), fn {name, _, _, _} -> name == function end) + + case reachable_def do + nil -> + nil + + {name, _type, _, _clauses} = func -> + ModuleState.put_used(state.pid, module, name) + walk(func, state) + end + end + end + + defp walk({{_name, _arity}, _type, _, clauses}, state) do + Enum.each(clauses, &walk(&1, state)) + end + + defp walk({_, _args, _guards, body}, state) do + walk_block(body, state) + end + + defp walk({:->, _, [[{:when, _, params}], body]}, state) do + guards = List.last(params) + params = params |> Enum.reverse() |> tl |> Enum.reverse() + + walk({[], params, guards, body}, state) + end + + defp walk({:->, _, [params, body]}, state) do + walk({[], params, [], body}, state) + end + + defp walk({:|, _, [head, tail]}, state) do + walk(head, state) + walk(tail, state) + end + + defp walk({:::, _, [target, _type]}, state) do + walk(target, state) + end + + defp walk(form, state) when is_list(form) do + Enum.each(form, &walk(&1, state)) + end + + defp walk({a, b}, state) do + walk({:{}, [], [a, b]}, state) + end + + defp walk({:{}, _, elements}, state) do + walk(elements, state) + end + + defp walk({:%{}, _, properties}, state) do + Enum.each(properties, fn val -> walk(val, state) end) + end + + defp walk({:<<>>, _, elements}, state) do + Enum.each(elements, fn val -> walk(val, state) end) + end + + defp walk({:=, _, [left, right]}, state) do + walk(left, state) + walk(right, state) + end + + defp walk({:%, _, [module, params]}, state) do + walk_module(module, :__struct__, 0, state.pid) + walk_module(module, :__struct__, 1, state.pid) + walk(params, state) + end + + defp walk({:for, _, generators}, state) when is_list(generators) do + Enum.each(generators, fn + {:<<>>, _, body} -> + walk(body, state) + + {:<-, _, [identifier, enum]} -> + walk(identifier, state) + walk(enum, state) + + [into: expression] -> + walk(expression, state) + + [into: expression, do: expression2] -> + walk(expression, state) + walk_block(expression2, state) + + [do: expression] -> + walk_block(expression, state) + + filter -> + walk(filter, state) + end) + end + + defp walk({:case, _, [condition, [do: clauses]]}, state) do + Enum.each(clauses, &walk(&1, state)) + walk(condition, state) + end + + defp walk({:cond, _, [[do: clauses]]}, state) do + Enum.each(clauses, fn {:->, _, [clause, clause_body]} -> + Enum.each(List.wrap(clause_body), &walk(&1, state)) + walk(hd(clause), state) + end) + end + + defp walk({:receive, _context, blocks}, state) do + do_block = Keyword.get(blocks, :do) + after_block = Keyword.get(blocks, :after, nil) + + walk_block(do_block, state) + + if after_block do + Enum.each(List.wrap(after_block), &walk(&1, state)) + end + end + + defp walk({:try, _, [blocks]}, state) do + try_block = Keyword.get(blocks, :do) + rescue_block = Keyword.get(blocks, :rescue, nil) + catch_block = Keyword.get(blocks, :catch, nil) + after_block = Keyword.get(blocks, :after, nil) + else_block = Keyword.get(blocks, :else, nil) + + walk_block(try_block, state) + + if rescue_block do + Enum.each(rescue_block, fn + {:->, _, [[{:in, _, [param, names]}], body]} -> + walk({[], [param], [{{:., [], [Enum, :member?]}, [], [param, names]}], body}, state) + + {:->, _, [[param], body]} -> + walk({[], [param], [], body}, state) + end) + end + + if catch_block do + walk({:fn, [], catch_block}, state) + end + + if after_block do + Enum.each(List.wrap(after_block), &walk(&1, state)) + end + + if else_block do + walk({:fn, [], else_block}, state) + end + end + + defp walk({:fn, _, clauses}, state) do + Enum.each(clauses, &walk(&1, state)) + end + + defp walk({:with, _, args}, state) do + Enum.each(args, fn + {:<-, _, [left, right]} -> + walk(left, state) + walk(right, state) + + {:=, _, [left, right]} -> + walk(left, state) + walk(right, state) + + [do: expression] -> + walk_block(expression, state) + + [do: expression, else: elses] -> + walk_block(expression, state) + + Enum.each(elses, fn {:->, _, [left, right]} -> + walk(left, state) + walk(right, state) + end) + end) + end + + defp walk({{:., _, [:erlang, :apply]}, _, [module, function, params]}, state) do + walk({{:., [], [module, function]}, [], params}, state) + end + + defp walk({{:., _, [:erlang, :apply]}, _, [function, params]}, state) do + walk({function, [], params}, state) + end + + defp walk({{:., _, [ElixirScript.JS, _]}, _, params}, state) do + walk(params, state) + end + + defp walk({{:., _, [module, function]}, _, params}, state) do + cond do + ElixirScript.Translate.Module.is_js_module(module, state) -> + nil + + ElixirScript.Translate.Module.is_elixir_module(module) -> + walk_module(module, function, length(params), state.pid) + + is_tuple(module) -> + walk(module, state) + + true -> + nil + end + + walk(params, state) + end + + defp walk({:super, _, [{_, function} | params]}, state) do + walk_module(state.module, function, length(params), state.pid) + walk(params, state) + end + + defp walk({function, _, params}, state) when is_atom(function) and is_list(params) do + walk_module(state.module, function, length(params), state.pid) + walk(params, state) + end + + defp walk({value, _, params}, state) when is_list(params) do + walk(value, state) + walk(params, state) + end + + defp walk(_, _) do + nil + end + + defp walk_block(block, state) do + case block do + nil -> + nil + + {:__block__, _, block_body} -> + Enum.each(block_body, &walk(&1, state)) + + b when is_list(b) -> + Enum.each(b, &walk(&1, state)) + + _ -> + walk(block, state) + end + end +end diff --git a/lib/elixir_script/passes/find_used_modules.ex b/lib/elixir_script/passes/find_used_modules.ex new file mode 100644 index 00000000..e294c7c1 --- /dev/null +++ b/lib/elixir_script/passes/find_used_modules.ex @@ -0,0 +1,402 @@ +defmodule ElixirScript.FindUsedModules do + @moduledoc false + alias ElixirScript.State, as: ModuleState + require Logger + + @doc """ + Takes a list of entry modules and finds modules they use. + """ + @spec execute([atom], pid) :: :ok + def execute(modules, pid) do + modules + |> List.wrap() + |> Enum.each(fn module -> + do_execute(module, pid) + end) + end + + defp do_execute(module, result, pid) do + case result do + {:ok, info} -> + walk_module(module, info, pid) + + {:ok, module, module_info, implementations} -> + walk_protocol(module, module_info, implementations, pid) + + {:error, "Unknown module"} -> + Logger.warn(fn -> + "ElixirScript: #{inspect(module)} is missing or unavailable" + end) + + ModuleState.put_diagnostic(pid, module, %{ + severity: :warning, + message: "#{inspect(module)} is missing or unavailable" + }) + + {:error, error} -> + raise ElixirScript.CompileError, + message: "An error occurred while compiling #{inspect(module)}: #{error}", + severity: :error + end + end + + defp do_execute(module, pid) do + result = get_debug_info(module, pid) + do_execute(module, result, pid) + end + + defp get_debug_info(module, pid) do + case ModuleState.get_in_memory_module(pid, module) do + nil -> + ElixirScript.Beam.debug_info(module) + + beam -> + ElixirScript.Beam.debug_info(beam) + end + end + + defp walk_module( + module, + %{attributes: [__foreign_info__: %{path: path, name: name, global: global}]} = info, + pid + ) do + {name, path} = + if global do + name = if name, do: name, else: module + path = nil + {name, path} + else + name = Enum.join(Module.split(module), "_") + path = path <> ".js" + {name, path} + end + + ModuleState.put_javascript_module(pid, module, name, path) + ModuleState.put_module(pid, module, info) + + nil + end + + defp walk_module(module, info, pid) do + %{ + attributes: _attrs, + compile_opts: _compile_opts, + definitions: defs, + file: _file, + line: _line, + module: ^module, + unreachable: unreachable + } = info + + ModuleState.put_module(pid, module, info) + + reachable_defs = + Enum.filter(defs, fn + {_, type, _, _} when type in [:defmacro, :defmacrop] -> false + {name, _, _, _} -> name not in unreachable + _ -> true + end) + + state = %{ + pid: pid, + module: module + } + + Enum.each(reachable_defs, fn x -> + walk(x, state) + end) + end + + defp walk_protocol(module, module_info, implementations, pid) do + impls = + Enum.map(implementations, fn {impl, %{attributes: attrs}} -> + protocol_impl = Keyword.fetch!(attrs, :protocol_impl) + impl_for = Keyword.fetch!(protocol_impl, :for) + {impl, impl_for} + end) + + first_implementation_functions = implementations |> hd |> elem(1) |> Map.get(:definitions) + + functions = Enum.map(first_implementation_functions, fn {name, _, _, _} -> name end) + + module_info = Map.merge(module_info, %{protocol: true, impls: impls, functions: functions}) + + ModuleState.put_module(pid, module, module_info) + + Enum.each(implementations, fn {impl, info} -> + ModuleState.put_used_module(pid, module, impl) + walk_module(impl, info, pid) + end) + end + + defp walk({{_name, _arity}, _type, _, clauses}, state) do + Enum.each(clauses, &walk(&1, state)) + end + + defp walk({_, args, guards, body}, state) do + walk(args, state) + walk(guards, state) + walk_block(body, state) + end + + defp walk({:->, _, [[{:when, _, params}], body]}, state) do + guards = List.last(params) + params = params |> Enum.reverse() |> tl |> Enum.reverse() + + walk({[], params, guards, body}, state) + end + + defp walk({:->, _, [params, body]}, state) do + walk({[], params, [], body}, state) + end + + defp walk({:|, _, [head, tail]}, state) do + walk(head, state) + walk(tail, state) + end + + defp walk({:::, _, [target, _type]}, state) do + walk(target, state) + end + + defp walk(form, state) when is_list(form) do + Enum.each(form, &walk(&1, state)) + end + + defp walk(form, state) + when is_atom(form) and form not in [BitString, Function, PID, Port, Reference, Any, Elixir] do + if ElixirScript.Translate.Module.is_elixir_module(form) and + !ElixirScript.Translate.Module.is_js_module(form, state) do + if ModuleState.get_module(state.pid, form) == nil do + case get_debug_info(form, state.pid) do + {:ok, _} = result -> + ModuleState.put_used_module(state.pid, state.module, form) + do_execute(form, result, state.pid) + + result -> + do_execute(form, result, state.pid) + end + else + ModuleState.put_used_module(state.pid, state.module, form) + end + end + end + + defp walk({a, b}, state) do + walk({:{}, [], [a, b]}, state) + end + + defp walk({:{}, _, elements}, state) do + Enum.each(elements, &walk(&1, state)) + end + + defp walk({:%{}, _, properties}, state) do + Enum.each(properties, fn val -> walk(val, state) end) + end + + defp walk({:<<>>, _, elements}, state) do + Enum.each(elements, fn val -> walk(val, state) end) + end + + defp walk({:=, _, [left, right]}, state) do + walk(left, state) + walk(right, state) + end + + defp walk({:%, _, [module, params]}, state) do + if ElixirScript.Translate.Module.is_elixir_module(module) and + !ElixirScript.Translate.Module.is_js_module(module, state) do + if ModuleState.get_module(state.pid, module) == nil do + case get_debug_info(module, state.pid) do + {:ok, _} = result -> + ModuleState.put_used_module(state.pid, state.module, module) + do_execute(module, result, state.pid) + + result -> + do_execute(module, result, state.pid) + end + else + ModuleState.put_used_module(state.pid, state.module, module) + end + end + + walk(params, state) + end + + defp walk({:for, _, generators}, state) when is_list(generators) do + walk(Collectable, state) + + Enum.each(generators, fn + {:<<>>, _, body} -> + walk(body, state) + + {:<-, _, [identifier, enum]} -> + walk(identifier, state) + walk(enum, state) + + [into: expression] -> + walk(expression, state) + + [into: expression, do: expression2] -> + walk(expression, state) + walk_block(expression2, state) + + [do: expression] -> + walk_block(expression, state) + + filter -> + walk(filter, state) + end) + end + + defp walk({:case, _, [condition, [do: clauses]]}, state) do + Enum.each(clauses, &walk(&1, state)) + walk(condition, state) + end + + defp walk({:cond, _, [[do: clauses]]}, state) do + Enum.each(clauses, fn {:->, _, [clause, clause_body]} -> + Enum.each(List.wrap(clause_body), &walk(&1, state)) + walk(hd(clause), state) + end) + end + + defp walk({:receive, _context, blocks}, state) when is_list(blocks) do + do_block = Keyword.get(blocks, :do) + after_block = Keyword.get(blocks, :after, nil) + + walk_block(do_block, state) + + if after_block do + Enum.each(List.wrap(after_block), &walk(&1, state)) + end + end + + defp walk({:try, _, [blocks]}, state) do + walk(Enum, state) + + try_block = Keyword.get(blocks, :do) + rescue_block = Keyword.get(blocks, :rescue, nil) + catch_block = Keyword.get(blocks, :catch, nil) + after_block = Keyword.get(blocks, :after, nil) + else_block = Keyword.get(blocks, :else, nil) + + walk_block(try_block, state) + + if rescue_block do + Enum.each(rescue_block, fn + {:->, _, [[{:in, _, [param, names]}], body]} -> + Enum.each(names, &walk(&1, state)) + walk({[], [param], [{{:., [], [Enum, :member?]}, [], [names, param]}], body}, state) + + {:->, _, [[param], body]} -> + walk({[], [param], [], body}, state) + end) + end + + if catch_block do + walk({:fn, [], catch_block}, state) + end + + if after_block do + Enum.each(List.wrap(after_block), &walk(&1, state)) + end + + if else_block do + walk({:fn, [], else_block}, state) + end + end + + defp walk({:fn, _, clauses}, state) do + Enum.each(clauses, &walk(&1, state)) + end + + defp walk({:with, _, args}, state) do + Enum.each(args, fn + {:<-, _, [left, right]} -> + walk(left, state) + walk(right, state) + + {:=, _, [left, right]} -> + walk(left, state) + walk(right, state) + + [do: expression] -> + walk_block(expression, state) + + [do: expression, else: elses] -> + walk_block(expression, state) + + Enum.each(elses, fn {:->, _, [left, right]} -> + walk(left, state) + walk(right, state) + end) + end) + end + + defp walk({{:., _, [:erlang, :apply]}, _, [module, function, params]}, state) do + walk({{:., [], [module, function]}, [], params}, state) + end + + defp walk({{:., _, [:erlang, :apply]}, _, [function, params]}, state) do + walk({function, [], params}, state) + end + + defp walk({{:., _, [_module, _function]} = ast, _, params}, state) do + walk(ast, state) + walk(params, state) + end + + defp walk({:., _, [ElixirScript.JS, _]}, _) do + nil + end + + defp walk({:., _, [module, function]}, state) do + if ElixirScript.Translate.Module.is_elixir_module(module) do + if ModuleState.get_module(state.pid, module) == nil do + case get_debug_info(module, state.pid) do + {:ok, _} = result -> + ModuleState.put_used_module(state.pid, state.module, module) + do_execute(module, result, state.pid) + + result -> + do_execute(module, result, state.pid) + end + else + ModuleState.put_used_module(state.pid, state.module, module) + end + else + walk(module, state) + walk(function, state) + end + end + + defp walk({:super, _, [{_, _} | params]}, state) do + walk(params, state) + end + + defp walk({function, _, params}, state) when is_list(params) do + walk(function, state) + walk(params, state) + end + + defp walk(_, _) do + nil + end + + defp walk_block(block, state) do + case block do + nil -> + nil + + {:__block__, _, block_body} -> + Enum.each(block_body, &walk(&1, state)) + + b when is_list(b) -> + Enum.each(b, &walk(&1, state)) + + _ -> + walk(block, state) + end + end +end diff --git a/lib/elixir_script/passes/output.ex b/lib/elixir_script/passes/output.ex new file mode 100644 index 00000000..2953ae0f --- /dev/null +++ b/lib/elixir_script/passes/output.ex @@ -0,0 +1,174 @@ +defmodule ElixirScript.Output do + @moduledoc false + + alias ElixirScript.State, as: ModuleState + alias ESTree.Tools.{Builder, Generator} + + @doc """ + Takes outputs the JavaScript code in the specified output + """ + @spec execute([atom], pid, map) :: [{atom, binary}] + def execute(modules, pid, opts) do + js_modules = + ModuleState.js_modules(pid) + |> Enum.filter(fn + {_module, _name, nil} -> false + _ -> true + end) + |> Enum.map(fn {module, name, path} -> + import_path = Path.join(opts.root, path) + {module, name, path, import_path} + end) + + prepared_modules = + modules + |> Enum.filter(fn {_, info} -> Map.has_key?(info, :js_ast) end) + |> Enum.map(fn {module, info} -> + {module, info.js_ast, filter_used_modules(info.used_modules, pid)} + end) + + create_modules(prepared_modules, opts, js_modules) + end + + defp filter_used_modules(used_modules, pid) do + used_modules + |> Enum.filter(fn module -> + module in ModuleState.list_javascript_modules(pid) == false + end) + end + + defp concat(code) do + """ + 'use strict'; + import ElixirScript from './ElixirScript.Core.js'; + #{code} + """ + end + + defp create_modules(modules, opts, js_modules) do + modules + |> Task.async_stream( + fn {module, [body, exports], used_modules} -> + modules = modules_to_import(used_modules) ++ js_modules + + imports = opts.module_formatter.build_imports(modules) + exports = opts.module_formatter.build_export(exports) + + js_parts = List.wrap(imports) ++ body ++ List.wrap(exports) + + js_parts + |> Builder.program() + |> Generator.generate() + |> concat + |> output(module, Map.get(opts, :output), js_modules) + end, + timeout: 10_000 + ) + |> Stream.map(fn {:ok, code} -> code end) + |> Enum.to_list() + |> List.flatten() + |> Enum.uniq() + end + + defp modules_to_import(modules) do + Enum.map(modules, &module_to_import(&1)) + end + + defp module_to_import(module) do + {module, module_to_name(module), "", "./Elixir.#{inspect(module)}.js"} + end + + def module_to_name(module) do + "$#{inspect(module)}$" + |> String.replace(".", "$") + end + + defp output(code, module, nil, _), do: [{module, code}] + + defp output(code, module, :stdout, _) do + IO.puts(code) + [{module, code}] + end + + defp output(code, module, path, js_modules) do + output_dir = + case Path.extname(path) do + "" -> + path + + _ -> + Path.dirname(path) + end + + file_name = Path.join(output_dir, "Elixir.#{inspect(module)}.js") + + if !File.exists?(output_dir) do + File.mkdir_p!(output_dir) + end + + apps = get_app_names() + + ffi_modules = + Enum.reduce(js_modules, [], fn {module, _, path, _}, acc -> + acc ++ copy_javascript_module(apps, output_dir, module, path) + end) + + copy_bootstrap_js(output_dir) + File.write!(file_name, code) + + [{module, code}] ++ ffi_modules + end + + defp copy_bootstrap_js(directory) do + operating_path = Path.join([Mix.Project.build_path(), "lib", "elixir_script", "priv"]) + path = Path.join([operating_path, "build", "es", "ElixirScript.Core.js"]) + File.cp!(path, Path.join([directory, "ElixirScript.Core.js"])) + end + + defp get_app_names() do + Mix.Project.config()[:app] + + deps = + Mix.Project.deps_paths() + |> Map.keys() + + [Mix.Project.config()[:app]] ++ deps + end + + defp copy_javascript_module(apps, output_dir, module, js_module_path) do + Enum.reduce(apps, [], fn app, acc -> + base_path = Path.join([:code.priv_dir(app), "elixir_script"]) + + js_input_path = + cond do + File.exists?(Path.join([base_path, js_module_path])) -> + Path.join([base_path, js_module_path]) + + File.exists?(Path.join([base_path, slashes_to_dots(js_module_path)])) -> + Path.join([base_path, slashes_to_dots(js_module_path)]) + + true -> + nil + end + + if js_input_path != nil do + js_output_path = Path.join(output_dir, js_module_path) + js_output_dir = Path.dirname(js_output_path) + + if !File.exists?(js_output_dir) do + File.mkdir_p!(js_output_dir) + end + + File.cp(js_input_path, js_output_path) + acc ++ [{module, [js_input_path, js_output_path]}] + else + acc + end + end) + end + + defp slashes_to_dots(js_module_path) do + js_module_path + |> String.replace("/", ".") + end +end diff --git a/lib/elixir_script/passes/output/js_module.ex b/lib/elixir_script/passes/output/js_module.ex new file mode 100644 index 00000000..9005dafc --- /dev/null +++ b/lib/elixir_script/passes/output/js_module.ex @@ -0,0 +1,13 @@ +defmodule ElixirScript.Output.JSModule do + @moduledoc false + + alias ESTree.Tools.Builder, as: J + + def compile(body, opts, js_modules) do + imports = opts.module_formatter.build_imports(js_modules) + exports = opts.module_formatter.build_export(J.identifier("Elixir")) + + [imports] ++ body ++ [exports] + end + +end diff --git a/lib/elixir_script/passes/translate.ex b/lib/elixir_script/passes/translate.ex new file mode 100644 index 00000000..0b6490fc --- /dev/null +++ b/lib/elixir_script/passes/translate.ex @@ -0,0 +1,19 @@ +defmodule ElixirScript.Translate do + @moduledoc false + + @doc """ + Takes a list of modules and translates their ast into + JavaScript AST. The modules are the ones collected from + the FindUsed pass. + """ + @spec execute([atom], pid) :: nil + def execute(modules, pid) do + modules + |> List.wrap() + |> Task.async_stream(fn + {module, info} -> + ElixirScript.Translate.Module.compile(module, info, pid) + end, timeout: 10_000) + |> Stream.run() + end +end diff --git a/lib/elixir_script/passes/translate/clause.ex b/lib/elixir_script/passes/translate/clause.ex new file mode 100644 index 00000000..38c42898 --- /dev/null +++ b/lib/elixir_script/passes/translate/clause.ex @@ -0,0 +1,125 @@ +defmodule ElixirScript.Translate.Clause do + @moduledoc false + + # Handles translation of all of the clause ASTs + + alias ESTree.Tools.Builder, as: J + alias ElixirScript.Translate.Helpers + alias ElixirScript.Translate.Form + alias ElixirScript.Translate.Forms.Pattern + alias ElixirScript.Translate.Function + + def compile({ _, args, guards, body}, state) do + {patterns, params, state} = Pattern.compile(args, state) + guard = compile_guard(params, guards, state) + + {body, _state} = Function.compile_block(body, state) + + body = body + |> return_last_statement + |> Function.update_last_call(state) + + ast = Helpers.call( + J.member_expression( + Helpers.patterns(), + J.identifier("clause") + ), + [ + J.array_expression(patterns), + Helpers.arrow_function( + params, + J.block_statement(body) + ), + guard + ] + ) + + { ast, state } + end + + def compile({:->, _, [[{:when, _, params}], body ]}, state) do + guards = List.last(params) + params = params |> Enum.reverse |> tl |> Enum.reverse + + compile({[], params, guards, body}, state) + end + + def compile({:->, _, [params, body]}, state) do + compile({[], params, [], body}, state) + end + + def return_last_statement(body) do + body + |> List.wrap + |> Enum.reverse + |> do_return_last_statement + |> Enum.reverse + end + + defp do_return_last_statement([%ESTree.ThrowStatement{} = ast]) do + [ast] + end + + defp do_return_last_statement([%ESTree.VariableDeclaration{} = head | tail]) do + declaration = hd(head.declarations).id + + return_statement = case declaration do + %ESTree.ArrayPattern{elements: elements} -> + if length(elements) == 1 do + J.return_statement(hd(declaration.elements)) + else + J.return_statement(J.array_expression(declaration.elements)) + end + _ -> + J.return_statement(declaration) + end + + [return_statement, head] ++ tail + end + + defp do_return_last_statement([head]) do + [J.return_statement(head)] + end + + defp do_return_last_statement([%ESTree.ThrowStatement{} = head | tail]) do + [head] ++ tail + end + + defp do_return_last_statement([head | tail]) do + [J.return_statement(head)] ++ tail + end + + defp do_return_last_statement([]) do + [J.return_statement(J.identifier("null"))] + end + + def compile_guard(params, guards, state) do + state = Map.put(state, :in_guard, true) + + guards = guards + |> List.wrap + |> Enum.reverse + |> process_guards + |> Form.compile!(state) + + Helpers.arrow_function( + params, + J.block_statement([ + J.return_statement(guards) + ]) + ) + + end + + defp process_guards([]) do + true + end + + defp process_guards([guard]) do + guard + end + + defp process_guards([head | tail]) do + {{:., [], [:erlang, :orelse]}, [], [process_guards(tail), head]} + end +end diff --git a/lib/elixir_script/passes/translate/form.ex b/lib/elixir_script/passes/translate/form.ex new file mode 100644 index 00000000..129d1723 --- /dev/null +++ b/lib/elixir_script/passes/translate/form.ex @@ -0,0 +1,467 @@ +defmodule ElixirScript.Translate.Form do + @moduledoc false + + # Handles translation of all forms that are not functions or clauses + + alias ESTree.Tools.Builder, as: J + alias ElixirScript.Translate.Helpers + alias ElixirScript.Translate.Forms.{Bitstring, Match, Try, For, Receive, Remote, Pattern, With} + alias ElixirScript.Translate.Clause + alias ElixirScript.State, as: ModuleState + require Logger + + @spec compile!(any, map) :: ESTree.Node.t() + def compile!(ast, state) do + {js_ast, _} = compile(ast, state) + + js_ast + end + + @spec compile(any, map) :: {ESTree.Node.t(), map} + def compile(ast, state) + + def compile(nil, state) do + {J.identifier("null"), state} + end + + def compile(map, state) when is_map(map) do + quoted = Code.string_to_quoted!("#{inspect(map)}") + compile(quoted, state) + end + + def compile(form, state) + when is_boolean(form) or is_integer(form) or is_float(form) or is_binary(form) do + {J.literal(form), state} + end + + def compile([{:|, _, [_head, _tail]} = ast], state) do + compile(ast, state) + end + + def compile({:|, _, [head, tail]}, state) do + ast = + Helpers.call( + J.member_expression( + Helpers.functions(), + J.identifier("concat") + ), + [compile!(head, state), compile!(tail, state)] + ) + + {ast, state} + end + + def compile(form, state) when is_list(form) do + ast = J.array_expression(Enum.map(form, &compile!(&1, state))) + + {ast, state} + end + + def compile(form, state) when is_atom(form) do + ast = + cond do + ElixirScript.Translate.Module.is_elixir_module(form) and + ModuleState.get_module(state.pid, form) != nil -> + Remote.process_module_name(form, state) + + ElixirScript.Translate.Module.is_elixir_module(form) -> + J.object_expression([]) + + true -> + Helpers.symbol(form) + end + + {ast, state} + end + + def compile({a, b}, state) do + compile({:{}, [], [a, b]}, state) + end + + def compile({:{}, _, elements}, state) do + ast = + Helpers.new( + Helpers.tuple(), + Enum.map(elements, &compile!(&1, state)) |> List.flatten() + ) + + {ast, state} + end + + def compile({:&, _, [{:/, _, [{{:., _, [_module, _function]} = ast, [], []}, _]}]}, state) do + Remote.compile(ast, state) + end + + def compile({:&, _, [{:/, _, [{var, _, _}, _]}]}, state) do + ast = ElixirScript.Translate.Identifier.make_function_name(var) + {ast, state} + end + + def compile({:%{}, _, _} = map, state) do + ElixirScript.Translate.Forms.Map.compile(map, state) + end + + def compile({:<<>>, _, []} = bitstring, state) do + Bitstring.compile(bitstring, state) + end + + def compile({:<<>>, _, elements} = bitstring, state) do + is_interpolated_string = + Enum.all?(elements, fn x -> + case x do + b when is_binary(b) -> + true + + {:::, _, [_target, {:binary, _, _}]} -> + true + + _ -> + false + end + end) + + if is_interpolated_string do + Bitstring.make_interpolated_string(elements, state) + else + Bitstring.compile(bitstring, state) + end + end + + def compile({:=, _, [_, _]} = match, state) do + Match.compile(match, state) + end + + def compile({:%, _, [module, params]}, state) do + ast = + Helpers.call( + J.member_expression( + Remote.process_module_name(module, state), + J.identifier("__struct__") + ), + [compile!(params, state)] + ) + + {ast, state} + end + + def compile({:for, _, generators} = ast, state) when is_list(generators) do + For.compile(ast, state) + end + + def compile({:case, _, [{:=, _, [left, _]} = match, [do: clauses]]}, state) do + {match_ast, state} = compile(match, state) + {case_ast, state} = compile({:case, [], [left, [do: clauses]]}, state) + + match_ast = List.wrap(match_ast) + + {match_ast ++ [case_ast], state} + end + + def compile({:case, _, [condition, [do: clauses]]}, state) do + func = + Helpers.call( + J.member_expression( + Helpers.patterns(), + J.identifier("defmatch") + ), + Enum.map(clauses, fn x -> Clause.compile(x, state) |> elem(0) end) |> List.flatten() + ) + + ast = + Helpers.call(J.member_expression(func, J.identifier("call")), [ + J.identifier(:this), + compile!(condition, state) + ]) + + {ast, state} + end + + def compile({:cond, _, [[do: clauses]]}, state) do + processed_clauses = + Enum.map(clauses, fn {:->, _, [clause, clause_body]} -> + {translated_body, state} = + ElixirScript.Translate.Function.compile_block(clause_body, state) + + translated_body = + translated_body + |> Clause.return_last_statement() + + translated_body = Helpers.arrow_function([], J.block_statement(translated_body)) + + {translated_clause, _} = compile(hd(clause), state) + + J.array_expression([translated_clause, translated_body]) + end) + + cond_function = + J.member_expression( + Helpers.special_forms(), + J.identifier("cond") + ) + + ast = + Helpers.call( + cond_function, + processed_clauses + ) + + {ast, state} + end + + def compile({:receive, context, [blocks]}, state) do + line = Keyword.get(context, :line, 1) + module = Map.get(state, :module) + pid = Map.get(state, :pid) + {function, _arity} = Map.get(state, :function) + + ModuleState.put_diagnostic(pid, module, %{ + severity: :warning, + message: "receive not supported, Module: #{inspect(state.module)}, Function: #{function}", + position: line + }) + + Logger.warn(fn -> + "ElixirScript: receive not supported, Module: #{inspect(state.module)}, Function: #{ + function + }, Line: #{line}" + end) + + Receive.compile(blocks, state) + end + + def compile({:try, _, [blocks]}, state) do + Try.compile(blocks, state) + end + + def compile({:with, _, args}, state) do + With.compile(args, state) + end + + def compile({:fn, _, _} = ast, state) do + ElixirScript.Translate.Function.compile(ast, state) + end + + def compile({{:., _, [:erlang, op]}, _, [item]}, state) when op in [:+, :-] do + ast = + J.unary_expression( + op, + true, + compile!(item, state) + ) + + {ast, state} + end + + def compile({{:., _, [:erlang, op]}, _, [left, right]}, state) when op in [:==, :===] do + ast = + Helpers.call( + J.member_expression( + Helpers.core_module("erlang"), + J.identifier("equals") + ), + [compile!(left, state), compile!(right, state)] + ) + + {ast, state} + end + + def compile({{:., _, [:erlang, op]}, _, [left, right]}, state) + when op in [:+, :-, :*, :/, :>, :<, :>=] do + ast = + J.binary_expression( + op, + compile!(left, state), + compile!(right, state) + ) + + {ast, state} + end + + def compile({{:., _, [:erlang, :"=<"]}, _, [left, right]}, state) do + ast = + J.binary_expression( + :<=, + compile!(left, state), + compile!(right, state) + ) + + {ast, state} + end + + def compile({{:., _, [:erlang, :"=:="]}, _, [left, right]}, state) do + ast = + J.binary_expression( + :===, + compile!(left, state), + compile!(right, state) + ) + + {ast, state} + end + + def compile({{:., _, [:erlang, :"=/="]}, _, [left, right]}, state) do + ast = + J.binary_expression( + :!==, + compile!(left, state), + compile!(right, state) + ) + + {ast, state} + end + + def compile({{:., _, [:erlang, :"/="]}, _, [left, right]}, state) do + ast = + J.binary_expression( + :!=, + compile!(left, state), + compile!(right, state) + ) + + {ast, state} + end + + def compile({{:., _, [:erlang, op]}, _, [left, right]}, state) when op in [:andalso, :and] do + ast = + J.binary_expression( + :&&, + compile!(left, state), + compile!(right, state) + ) + + {ast, state} + end + + def compile({{:., _, [:erlang, op]}, _, [left, right]}, state) when op in [:orelse, :or] do + ast = + J.binary_expression( + :||, + compile!(left, state), + compile!(right, state) + ) + + {ast, state} + end + + def compile({{:., _, [var, func_or_prop]}, _, []}, state) when not is_atom(var) do + ast = + Helpers.call(ElixirScript.Translate.Forms.JS.call_property(), [ + compile!(var, state), + J.literal(to_string(func_or_prop)) + ]) + + {ast, state} + end + + def compile({{:., _, [ElixirScript.JS, _]}, _, _} = ast, state) do + ElixirScript.Translate.Forms.JS.compile(ast, state) + end + + def compile({:., _, call} = ast, state) when is_list(call) do + Remote.compile(ast, state) + end + + def compile({:super, _, [_ | params]}, state) when is_list(params) do + {function_name, _} = Map.get(state, :function) + {var_decs, params} = compile_params(params, state) + + ast = + Helpers.call( + ElixirScript.Translate.Identifier.make_function_name(function_name), + params + ) + + case var_decs do + [] -> + {ast, state} + + _ -> + {var_decs ++ List.wrap(ast), state} + end + end + + def compile({:__block__, _, _} = ast, state) do + ElixirScript.Translate.Function.compile_block(ast, state) + end + + def compile({var, _, params}, state) when is_list(params) and is_atom(var) do + {var_decs, params} = compile_params(params, state) + + ast = + Helpers.call( + ElixirScript.Translate.Identifier.make_function_name(var), + params + ) + + case var_decs do + [] -> + {ast, state} + + _ -> + {var_decs ++ List.wrap(ast), state} + end + end + + def compile({function, _, []}, state) do + ast = + Helpers.call(ElixirScript.Translate.Forms.JS.call_property(), [compile!(function, state)]) + + {ast, state} + end + + def compile({function, _, params}, state) when is_list(params) do + {var_decs, params} = compile_params(params, state) + + ast = + Helpers.call( + compile!(function, state), + params + ) + + case var_decs do + [] -> + {ast, state} + + _ -> + {var_decs ++ List.wrap(ast), state} + end + end + + def compile({var, meta, _}, state) do + counter = Pattern.get_counter(meta) + + var = ElixirScript.Translate.Identifier.filter_name(var) + var = Pattern.get_variable_name(var <> counter, state) + {ElixirScript.Translate.Identifier.make_identifier(var), state} + end + + defp compile_params(params, state) do + {params, var_decs} = + Enum.map_reduce(params, [], fn + {:=, _, [{left_var, _, atom} = left, right]} = ast, acc when is_atom(atom) -> + case Atom.to_string(left_var) do + "_" <> _ -> + {compile!(right, state), acc} + + _ -> + {ast, state} = compile(ast, state) + left = compile!(left, state) + + {left, acc ++ List.wrap(ast)} + end + + {:=, _, [left, _]} = ast, acc -> + {ast, state} = compile(ast, state) + left = compile!(left, state) + + {left, acc ++ List.wrap(ast)} + + x, acc -> + compiled = compile!(x, state) + + {compiled, acc} + end) + + {var_decs, params} + end +end diff --git a/lib/elixir_script/passes/translate/forms/bitstring.ex b/lib/elixir_script/passes/translate/forms/bitstring.ex new file mode 100644 index 00000000..e4d2cc2a --- /dev/null +++ b/lib/elixir_script/passes/translate/forms/bitstring.ex @@ -0,0 +1,141 @@ +defmodule ElixirScript.Translate.Forms.Bitstring do + @moduledoc false + alias ESTree.Tools.Builder, as: JS + alias ElixirScript.Translate.Helpers + alias ElixirScript.Translate.Form + + + def compile({:<<>>, _, elements}, state) do + js_ast = Helpers.new( + Helpers.bitstring(), + Enum.map(elements, &compile_element(&1, state)) + ) + + { js_ast, state } + end + + def compile_element(element, state) when is_number(element) do + do_compile_element({:integer, Form.compile!(element, state)}) + end + + def compile_element(element, state) when is_binary(element) do + do_compile_element({:binary, Form.compile!(element, state)}) + end + + def compile_element({:<<>>, _, _} = ast, state) do + {ast, _} = compile(ast, state) + ast + end + + def compile_element({:::, _, [element, {type, _, _}]}, state) when type in [:integer, :float, :bitstring, :bits, :binary, :bytes, :utf8, :utf16, :utf32, :signed, :unsigned] do + do_compile_element({type, translate_element(element, state)}) + end + + def compile_element({:::, _, [element, {type, _, params}]}, state) when type in [:size, :unit] do + do_compile_element({type, translate_element(element, state), Enum.map(params, &translate_element(&1, state))}) + end + + def compile_element({:::, _, [element, {:*, _, [size, unit]}]}, state) do + size_ast = do_compile_element({:size, translate_element(element, state), [translate_element(size, state)]}) + do_compile_element({:unit, size_ast, [translate_element(unit, state)]}) + end + + def compile_element({:::, _, [element, {:-, _, types}]}, state) do + handle_type_adjectives({:-, [], types}, translate_element(element, state), state) + end + + def compile_element({:::, _, [element, size]}, state) do + do_compile_element({:size, translate_element(element, state), [translate_element(size, state)]}) + end + + def compile_element(element, state) do + do_compile_element({:binary, translate_element(element, state)}) + end + + def translate_element(ElixirScript.Translate.Forms.Pattern.Patterns, _) do + JS.object_expression([JS.property( + JS.literal("value"), + ElixirScript.Translate.Forms.Pattern.Patterns.parameter() + ) + ]) + end + + def translate_element(element, state) do + Form.compile!(element, state) + end + + defp handle_type_adjectives({:-, _, types}, ast, state) do + Enum.reduce(types, ast, fn(type, current_ast) -> + case type do + {:-, _, sub_types} -> + handle_type_adjectives({:-, [], sub_types}, current_ast, state) + {:*, _, [size, unit]} -> + size_ast = do_compile_element({:size, current_ast, [Form.compile!(size, state)]}) + do_compile_element({:unit, size_ast, [Form.compile!(unit, state)]}) + {the_type, _, params} when is_list(params) -> + do_compile_element({the_type, current_ast, Enum.map(params, &Form.compile!(&1, state))}) + {the_type, _, _} -> + do_compile_element({the_type, current_ast}) + end + end) + end + + defp do_compile_element({type, ast}) do + Helpers.call( + JS.member_expression( + Helpers.bitstring(), + JS.identifier(type) + ), + [ + ast + ] + ) + end + + defp do_compile_element({type, ast, params}) when is_list(params) do + Helpers.call( + JS.member_expression( + Helpers.bitstring(), + JS.identifier(type) + ), + [ + ast + ] ++ params + ) + end + + def make_interpolated_string(elements, state) do + translated_elements = Enum.map(elements, fn(x) -> + case x do + elem when is_binary(elem) -> + Form.compile!(elem, state) + {:::, _, data} -> + Form.compile!(hd(data), state) + end + end) + + result = case translated_elements do + [] -> + JS.literal('') + [_] -> + do_make_interpolated_string([], hd(translated_elements)) + elements -> + do_make_interpolated_string(tl(elements), hd(elements)) + end + + {result, state} + end + + defp do_make_interpolated_string([], ast) do + ast + end + + defp do_make_interpolated_string(elements, ast) do + JS.binary_expression( + :+, + ast, + do_make_interpolated_string(tl(elements), hd(elements)) + ) + end + +end diff --git a/lib/elixir_script/passes/translate/forms/for.ex b/lib/elixir_script/passes/translate/forms/for.ex new file mode 100644 index 00000000..a641d480 --- /dev/null +++ b/lib/elixir_script/passes/translate/forms/for.ex @@ -0,0 +1,114 @@ +defmodule ElixirScript.Translate.Forms.For do + @moduledoc false + + alias ESTree.Tools.Builder, as: JS + alias ElixirScript.Translate.{Form, Clause, Helpers} + alias ElixirScript.Translate.Forms.Pattern + + def compile({:for, _, generators}, state) do + args = handle_args(generators, state) + + generators = JS.array_expression(args.generators) + + into = args.into || JS.array_expression([]) + filter = args.filter || Helpers.arrow_function([], JS.block_statement([JS.return_statement(JS.identifier("true"))])) + fun = args.fun + + + expression = Helpers.call( + JS.member_expression( + Helpers.patterns(), + JS.identifier("clause") + ), + [JS.array_expression(args.patterns), fun, filter] + ) + + collectable = JS.identifier("Collectable") + + ast = Helpers.call( + JS.member_expression( + Helpers.special_forms(), + JS.identifier("_for") + ), + [expression, generators, collectable, into] + ) + + {ast, state} + end + + defp handle_args(nil, _) do + %{generators: [], args: [], filter: nil, fun: nil, into: nil, patterns: []} + end + + defp handle_args(generators, module_state) do + Enum.reduce(generators, %{generators: [], args: [], filter: nil, fun: nil, into: nil, patterns: []}, fn + + ({:<<>>, _, body}, state) -> + {bs_parts, collection} = Enum.map_reduce(body, nil, fn + {:::, _, _} = ast, state -> + {ast, state} + {:<-, _, [var, collection]}, _ -> + {var, collection} + end) + + {patterns, params, module_state} = Pattern.compile([{:<<>>, [], bs_parts}], module_state) + + gen = Helpers.call( + JS.member_expression( + Helpers.patterns(), + JS.identifier("bitstring_generator") + ), + [hd(patterns), Form.compile!(collection, module_state)] + ) + + %{state | generators: state.generators ++ [gen], args: state.args ++ params, patterns: state.patterns ++ patterns} + + ({:<-, _, [identifier, enum]}, state) -> + {patterns, params, module_state} = Pattern.compile([identifier], module_state) + + gen = Helpers.call( + JS.member_expression( + Helpers.patterns(), + JS.identifier("list_generator") + ), + [hd(patterns), Form.compile!(enum, module_state)] + ) + + %{state | generators: state.generators ++ [gen], args: state.args ++ params, patterns: state.patterns ++ patterns} + ([into: expression], state) -> + %{state | into: Form.compile!(expression, module_state)} + + ([do: expression2, into: expression], state) -> + fun = create_function_expression(expression2, state, module_state) + + %{state | into: Form.compile!(expression, module_state), fun: fun} + + ([into: expression, do: expression2], state) -> + fun = create_function_expression(expression2, state, module_state) + + %{state | into: Form.compile!(expression, module_state), fun: fun} + + ([do: expression], state) -> + fun = create_function_expression(expression, state, module_state) + + %{state | fun: fun} + (filter, state) -> + fun = create_function_expression(filter, state, module_state) + + %{state | filter: fun} + end) + end + + + defp create_function_expression(ast, state, module_state) do + ast = ast + |> ElixirScript.Translate.Function.compile_block(module_state) + |> elem(0) + |> Clause.return_last_statement + + Helpers.arrow_function( + state.args, + JS.block_statement(ast) + ) + end +end diff --git a/lib/elixir_script/passes/translate/forms/js.ex b/lib/elixir_script/passes/translate/forms/js.ex new file mode 100644 index 00000000..03fb0f0a --- /dev/null +++ b/lib/elixir_script/passes/translate/forms/js.ex @@ -0,0 +1,151 @@ +defmodule ElixirScript.Translate.Forms.JS do + @moduledoc false + alias ESTree.Tools.Builder, as: J + alias ElixirScript.Translate.Helpers + alias ElixirScript.Translate.Form + + def call_property() do + J.member_expression( + Helpers.functions(), + J.identifier("call_property") + ) + end + + def compile({{:., _, [ElixirScript.JS, :debugger]}, _, _}, state) do + ast = J.debugger_statement() + {ast, state} + end + + def compile({{:., _, [ElixirScript.JS, :this]}, _, _}, state) do + ast = J.this_expression() + {ast, state} + end + + def compile({{:., _, [ElixirScript.JS, :new]}, _, [module, params]}, state) do + members = Module.split(module) + + members = case members do + ["JS" | rest] -> + rest + x -> + x + end + + params = case params do + p when is_list(p) -> + Enum.map(params, &Form.compile!(&1, state)) + _ -> + [J.rest_element(Form.compile!(params, state))] + end + + ast = Helpers.new( + ElixirScript.Translate.Identifier.make_namespace_members(members), + params + ) + + {ast, state} + end + + def compile({{:., _, [ElixirScript.JS, :throw]}, _, [term]}, state) do + ast = J.throw_statement( + Form.compile!(term, state) + ) + + {ast, state} + end + + def compile({{:., _, [ElixirScript.JS, :import]}, _, [term]}, state) do + ast = Helpers.call( + J.identifier("import"), + [Form.compile!(term, state)] + ) + + {ast, state} + end + + def compile({{:., _, [ElixirScript.JS, :mutate]}, _, [object, map]}, state) do + ast = Helpers.call( + J.member_expression( + J.identifier("Object"), + J.identifier("assign") + ), + [ + Form.compile!(object, state), + Form.compile!(map, state) + ] + ) + + {ast, state} + end + + def compile({{:., _, [ElixirScript.JS, :mutate]}, _, [object, key, value]}, state) do + ast = Helpers.assign( + J.member_expression( + Form.compile!(object, state), + Form.compile!(key, state), + true + ), + Form.compile!(value, state) + ) + + {ast, state} + end + + def compile({{:., _, [ElixirScript.JS, :map_to_object]}, _, [map]}, state) do + ast = Helpers.call( + J.member_expression( + Helpers.functions(), + J.identifier("map_to_object") + ), + [ + Form.compile!(map, state) + ] + ) + + {ast, state} + end + + def compile({{:., _, [ElixirScript.JS, :map_to_object]}, _, [map, options]}, state) do + ast = Helpers.call( + J.member_expression( + Helpers.functions(), + J.identifier("map_to_object") + ), + [ + Form.compile!(map, state), + Form.compile!(options, state) + ] + ) + + {ast, state} + end + + def compile({{:., _, [ElixirScript.JS, :object_to_map]}, _, [object]}, state) do + ast = Helpers.call( + J.member_expression( + Helpers.functions(), + J.identifier("object_to_map") + ), + [ + Form.compile!(object, state) + ] + ) + + {ast, state} + end + + def compile({{:., _, [ElixirScript.JS, :object_to_map]}, _, [object, options]}, state) do + ast = Helpers.call( + J.member_expression( + Helpers.functions(), + J.identifier("object_to_map") + ), + [ + Form.compile!(object, state), + Form.compile!(options, state) + ] + ) + + {ast, state} + end +end diff --git a/lib/elixir_script/passes/translate/forms/map.ex b/lib/elixir_script/passes/translate/forms/map.ex new file mode 100644 index 00000000..19ae42e4 --- /dev/null +++ b/lib/elixir_script/passes/translate/forms/map.ex @@ -0,0 +1,71 @@ +defmodule ElixirScript.Translate.Forms.Map do + @moduledoc false + alias ESTree.Tools.Builder, as: J + alias ElixirScript.Translate.{Form, Helpers} + + def compile({:%{}, _, [{:|, _, [map, new_values]}]}, state) do + { map, state } = Form.compile(map, state) + data = Enum.map(new_values, fn {x, y} -> + J.array_expression([ + Form.compile!(x, state), + Form.compile!(y, state) + ]) + end) + + ast = Helpers.new( + J.identifier("Map"), + [ + J.array_expression( + [J.spread_element(map)] ++ data + ) + ] + ) + + { ast, state } + end + + def compile({:%{}, _, properties}, state) do + ast = Helpers.new( + J.identifier("Map"), + [ + J.array_expression( + Enum.map(properties, fn + {x, y} -> + J.array_expression( + [ + Form.compile!(x, state), + Form.compile!(y, state) + ] + ) + end) + ) + ] + ) + + {ast, state} + end + + def make_property(%ESTree.Identifier{} = key, value) do + J.property(key, value) + end + + def make_property(%ESTree.Literal{value: k}, value) when is_binary(k) do + key = case String.contains?(k, "-") do + true -> + J.literal(k) + false -> + ElixirScript.Translate.Identifier.make_identifier(k) + end + + J.property(key, value) + end + + def make_property(key, value) do + J.property(key, value, :init, false, false, true) + end + + def make_shorthand_property(%ESTree.Identifier{} = key) do + J.property(key, key, :init, true) + end + +end diff --git a/lib/elixir_script/passes/translate/forms/match.ex b/lib/elixir_script/passes/translate/forms/match.ex new file mode 100644 index 00000000..f09a3259 --- /dev/null +++ b/lib/elixir_script/passes/translate/forms/match.ex @@ -0,0 +1,146 @@ +defmodule ElixirScript.Translate.Forms.Match do + @moduledoc false + + alias ESTree.Tools.Builder, as: J + alias ElixirScript.Translate.Helpers + alias ElixirScript.Translate.Form + alias ElixirScript.Translate.Forms.Pattern + + def compile({:=, _, [left, right]}, state) do + build_matches(left, right, %{patterns: []}) + |> compile_match(state) + end + + defp make_list_ref(array_pattern, params) do + {ref, params} = make_params(params) + ref_declaration = Helpers.declare(ref, J.array_expression(params)) + + [array_pattern, ref_declaration] + end + + defp make_tuple_ref(array_pattern, params) do + {ref, params} = make_params(params) + + ref_declaration = Helpers.declare(ref, Helpers.new( + Helpers.tuple(), + params + )) + [array_pattern, ref_declaration] + end + + + defp make_params(params) do + ref = J.identifier("_ref#{:rand.uniform(10000000)}") + + params = Enum.map(params, fn + (nil) -> J.identifier(:undefined) + (x) -> x + end) + + { ref, params } + end + + defp build_matches(pattern, {:=, _, [left, right]}, parts) do + parts = parts + |> Map.update!(:patterns, fn(x) -> x ++ [pattern] end) + + build_matches(left, right, parts) + end + + defp build_matches(left, right, parts) do + parts + |> Map.update!(:patterns, fn(x) -> x ++ [left] end) + |> Map.put(:expression, right) + end + + defp compile_match(%{patterns: [left], expression: right}, state) do + { right_ast, state } = Form.compile(right, state) + + {var_decs, right_ast} = case right_ast do + x when is_list(x) -> + l = Enum.reverse(x) + [head | tail] = l + l = Enum.reverse(tail) + + {l, head} + x -> + {[], x} + end + + { patterns, params, state } = Pattern.compile([left], state) + + array_pattern = Helpers.declare(params, Helpers.call( + J.member_expression( + Helpers.patterns(), + J.identifier("match") + ), + [hd(patterns), right_ast] + )) + + js_ast = case left do + list when is_list(list) -> + make_list_ref(array_pattern, params) + { _, _ } -> + make_tuple_ref(array_pattern, params) + {:{}, _, _ } -> + make_tuple_ref(array_pattern, params) + _ -> + List.wrap(array_pattern) + end + + { var_decs ++ js_ast, state } + end + + defp compile_match(%{patterns: lefts, expression: right}, state) do + { right_ast, state } = Form.compile(right, state) + + {var_dec, right_ast} = case right_ast do + [variable_declaration, x] -> + {variable_declaration, x} + x -> + {nil, x} + end + + intermediate_assign = Helpers.assign( + J.identifier("__intermediate__"), + right_ast + ) + + {js_ast, state} = Enum.map_reduce(lefts, state, fn(left, state) -> + { patterns, params, state } = Pattern.compile([left], state) + + array_pattern = Helpers.declare(params, Helpers.call( + J.member_expression( + Helpers.patterns(), + J.identifier("match") + ), + [hd(patterns), J.identifier("__intermediate__")] + )) + + js_ast = case left do + list when is_list(list) -> + make_list_ref(array_pattern, params) + { _, _ } -> + make_tuple_ref(array_pattern, params) + {:{}, _, _ } -> + make_tuple_ref(array_pattern, params) + _ -> + List.wrap(array_pattern) + end + + js_ast = case var_dec do + nil -> + js_ast + x -> + [x] ++ js_ast + end + + { js_ast, state } + end) + + js_ast = [intermediate_assign] ++ List.flatten(js_ast) + + {js_ast, state} + end + +end diff --git a/lib/elixir_script/passes/translate/forms/pattern.ex b/lib/elixir_script/passes/translate/forms/pattern.ex new file mode 100644 index 00000000..a671178a --- /dev/null +++ b/lib/elixir_script/passes/translate/forms/pattern.ex @@ -0,0 +1,255 @@ +defmodule ElixirScript.Translate.Forms.Pattern do + alias ElixirScript.Translate.Forms.Pattern.Patterns, as: PM + alias ESTree.Tools.Builder, as: J + alias ElixirScript.Translate.Helpers + alias ElixirScript.Translate.Form + alias ElixirScript.Translate.Forms.Bitstring + + @moduledoc false + + @doc """ + Handles all pattern matching translations + """ + @spec compile(list(), map()) :: { list(), list(), map() } + def compile(patterns, state) do + patterns + |> do_compile(state) + |> update_env(state) + end + + defp do_compile(patterns, state) do + Enum.reduce(patterns, {[], []}, fn + x, { patterns, params } -> + {pattern, param} = process_pattern(x, state) + { patterns ++ List.wrap(pattern), params ++ List.wrap(param) } + end) + end + + defp update_env({ patterns, params }, state) do + { params, state } = Enum.map_reduce(params, state, fn + (%ESTree.Identifier{name: :undefined} = param, state) -> + { param, state } + + (%ESTree.Identifier{} = param, state) -> + state = update_variable(param.name, state) + new_name = get_variable_name(param.name, state) + + { %{ param | name: new_name }, state } + + (param, state) -> + { param, state } + end) + + { patterns, params, state } + end + + @spec get_variable_name(atom(), map()) :: atom() + def get_variable_name(function, state) do + number = Map.get(state.vars, function) + String.to_atom("#{function}#{number}") + end + + defp update_variable(name, state) do + vars = Map.update(state.vars, name, 0, fn val -> val + 1 end) + Map.put(state, :vars, vars) + end + + defp process_pattern(term, state) when is_number(term) or is_binary(term) or is_boolean(term) or is_atom(term) or is_nil(term) do + { [Form.compile!(term, state)], [] } + end + + defp process_pattern({:^, _, [value]}, state) do + { [PM.bound(Form.compile!(value, state))], [] } + end + + defp process_pattern({:_, _, _}, _) do + { [PM.parameter(J.literal("_"))], [] } + end + + defp process_pattern({a, b}, state) do + process_pattern({:{}, [], [a, b] }, state) + end + + defp process_pattern({:{}, _, elements }, state) do + { patterns, params } = elements + |> Enum.map(&do_compile([&1], state)) + |> reduce_patterns(state) + + pattern = J.object_expression([ + J.property( + J.identifier("values"), + J.array_expression(patterns) + ) + ]) + + tuple = Helpers.tuple() + + { [PM.type(tuple, pattern)], params } + end + + defp process_pattern([{:|, _, [head, tail]}], state) do + { head_patterns, head_params } = process_pattern(head, state) + { tail_patterns, tail_params } = process_pattern(tail, state) + params = head_params ++ tail_params + + { [PM.head_tail(hd(head_patterns), hd(tail_patterns))], params } + end + + defp process_pattern(list, state) when is_list(list) do + { patterns, params } = list + |> Enum.map(&do_compile([&1], state)) + |> reduce_patterns(state) + + {[J.array_expression(patterns)], params} + end + + defp process_pattern({:|, _, [head, tail]}, state) do + { head_patterns, head_params } = process_pattern(head, state) + { tail_patterns, tail_params } = process_pattern(tail, state) + params = head_params ++ tail_params + + { [PM.head_tail(hd(head_patterns), hd(tail_patterns))], params } + end + + defp process_pattern({{:., _, [:erlang, :++]}, context, [head, tail]}, state) do + process_pattern({:|, context, [head, tail]}, state) + end + + defp process_pattern({:%, _, [module, {:%{}, _, props}]}, state) do + process_pattern({:%{}, [], [__module__struct__: module] ++ props}, state) + end + + defp process_pattern({:%{}, _, props}, state) do + properties = Enum.map(props, fn + {:__module__struct__, {_, _, nil} = var } -> + {pattern, params} = process_pattern(var, state) + + a = J.object_expression([%ESTree.Property{ + key: J.identifier("__MODULE__"), + value: hd(List.wrap(pattern)) + }]) + + property = J.array_expression([ + Form.compile!(:__struct__, state), + a + ]) + + { property, params } + + {:__module__struct__, module} -> + a = J.object_expression([%ESTree.Property{ + key: J.identifier("__MODULE__"), + value: Helpers.symbol(to_string(module)) + }]) + + property = J.array_expression([ + Form.compile!(:__struct__, state), + a + ]) + + { property, [] } + + {key, value} -> + {pattern, params} = process_pattern(value, state) + property = case key do + {:^, _, [the_key]} -> + J.array_expression([ + Form.compile!(the_key, state), + hd(List.wrap(pattern)) + ]) + _ -> + J.array_expression([ + Form.compile!(key, state), + hd(List.wrap(pattern)) + ]) + end + + { property, params } + end) + + {props, params} = Enum.reduce(properties, {[], []}, fn({prop, param}, {props, params}) -> + { props ++ [prop], params ++ param } + end) + + ast = Helpers.new( + J.identifier("Map"), + [ + J.array_expression(List.wrap(props)) + ] + ) + + { [ast], params } + end + + defp process_pattern({:<<>>, _, elements}, state) do + params = Enum.reduce(elements, [], fn + ({:::, _, [{ _, _, params } = ast, _]}, state) when is_nil(params) + when is_list(params) and length(params) == 0 -> + + var_str = make_identifier(ast) + var_atom = String.to_atom(var_str) + state ++ [ElixirScript.Translate.Identifier.make_identifier(var_atom)] + _, state -> + state + end) + + elements = Enum.map(elements, fn + ({:::, context, [{ _, _, params }, options]}) when is_atom(params) -> + Bitstring.compile_element({:::, context, [ElixirScript.Translate.Forms.Pattern.Patterns, options]}, state) + x -> + Bitstring.compile_element(x, state) + end) + + { [PM.bitstring_match(elements)], params } + end + + defp process_pattern({:<>, _, [prefix, value]}, state) do + { [PM.starts_with(prefix)], [Form.compile!(value, state)] } + end + + defp process_pattern({:=, _, [{name, _, _} = target, right]}, state) when name not in [:%, :{}, :^, :%{}, :<<>>] do + unify(target, right, state) + end + + defp process_pattern({:=, _, [left, {name, _, _} = target]}, state) when name not in [:%, :{}, :^, :%{}, :<<>>] do + unify(target, left, state) + end + + defp process_pattern({_, _, a} = ast, _) when is_atom(a) do + var_str = make_identifier(ast) + var_atom = String.to_atom(var_str) + { [PM.parameter(J.literal(var_str))], [ElixirScript.Translate.Identifier.make_identifier(var_atom)] } + end + + defp process_pattern(ast, state) do + { [Form.compile!(ast, state)], [] } + end + + defp reduce_patterns(patterns, _) do + patterns + |> Enum.reduce({ [], [] }, fn({ pattern, new_param }, { patterns, new_params }) -> + { patterns ++ List.wrap(pattern), new_params ++ List.wrap(new_param) } + end) + end + + defp unify(target, source, state) do + { patterns, params } = do_compile([source], state) + { [_] , [param] } = process_pattern(target, state) + { [PM.capture(hd(patterns))], params ++ [param] } + end + + def get_counter(meta) do + case Keyword.get(meta, :counter, nil) do + nil -> "" + {module, value} -> + value + |> Kernel.abs() + |> to_string() + end + end + + defp make_identifier({var, meta, _}) do + counter = get_counter(meta) + to_string(var) <> counter + end +end diff --git a/lib/elixir_script/passes/translate/forms/pattern/patterns.ex b/lib/elixir_script/passes/translate/forms/pattern/patterns.ex new file mode 100644 index 00000000..eb873149 --- /dev/null +++ b/lib/elixir_script/passes/translate/forms/pattern/patterns.ex @@ -0,0 +1,97 @@ +defmodule ElixirScript.Translate.Forms.Pattern.Patterns do + @moduledoc false + + alias ESTree.Tools.Builder, as: J + alias ElixirScript.Translate.Helpers + + @parameter J.member_expression( + Helpers.patterns(), + J.identifier(:variable) + ) + + @head_tail J.member_expression( + Helpers.patterns(), + J.identifier(:headTail) + ) + + @starts_with J.member_expression( + Helpers.patterns(), + J.identifier(:startsWith) + ) + + @capture J.member_expression( + Helpers.patterns(), + J.identifier(:capture) + ) + + @bound J.member_expression( + Helpers.patterns(), + J.identifier(:bound) + ) + + @_type J.member_expression( + Helpers.patterns(), + J.identifier(:type) + ) + + @bitstring_match J.member_expression( + Helpers.patterns(), + J.identifier(:bitStringMatch) + ) + + def parameter() do + Helpers.call( + @parameter, + [] + ) + end + + def parameter(name) do + Helpers.call( + @parameter, + [name] + ) + end + + def head_tail(headParameter, tailParameter) do + Helpers.call( + @head_tail, + [headParameter, tailParameter] + ) + end + + def starts_with(prefix) do + Helpers.call( + @starts_with, + [J.literal(prefix)] + ) + end + + def capture(value) do + Helpers.call( + @capture, + [value] + ) + end + + def bound(value) do + Helpers.call( + @bound, + [value] + ) + end + + def type(prototype, value) do + Helpers.call( + @_type, + [prototype, value] + ) + end + + def bitstring_match(values) do + Helpers.call( + @bitstring_match, + values + ) + end +end diff --git a/lib/elixir_script/passes/translate/forms/receive.ex b/lib/elixir_script/passes/translate/forms/receive.ex new file mode 100644 index 00000000..1186c285 --- /dev/null +++ b/lib/elixir_script/passes/translate/forms/receive.ex @@ -0,0 +1,51 @@ +defmodule ElixirScript.Translate.Forms.Receive do + @moduledoc false + alias ESTree.Tools.Builder, as: J + alias ElixirScript.Translate.{Helpers, Form, Function, Clause} + + @doc """ + receive is not supported just yet, but we compile it + to a stub function for now + """ + def compile(blocks, state) do + receive_block = blocks + |> Keyword.get(:do, []) + |> Enum.map(fn x -> + Clause.compile(x, state) + |> elem(0) + end) + |> List.flatten + |> J.array_expression() + + receive_function = J.member_expression( + Helpers.special_forms(), + J.identifier("receive") + ) + + after_block = Keyword.get(blocks, :after, nil) + args = [receive_block] ++ process_after(after_block, state) + + ast = Helpers.call( + receive_function, + args + ) + + { ast, state } + end + + defp process_after(nil, _) do + [] + end + + defp process_after([{:->, _, [[timeout], body]}], state) do + timeout = Form.compile!(timeout, state) + {body, _state} = Function.compile_block(body, state) + + function = Helpers.arrow_function( + [], + J.block_statement(List.wrap(body)) + ) + + [timeout, function] + end +end diff --git a/lib/elixir_script/passes/translate/forms/remote.ex b/lib/elixir_script/passes/translate/forms/remote.ex new file mode 100644 index 00000000..fda121fe --- /dev/null +++ b/lib/elixir_script/passes/translate/forms/remote.ex @@ -0,0 +1,166 @@ +defmodule ElixirScript.Translate.Forms.Remote do + @moduledoc false + + alias ESTree.Tools.Builder, as: J + alias ElixirScript.Translate.{Form, Helpers} + alias ElixirScript.State, as: ModuleState + + @erlang_modules [ + :erlang, + :maps, + :lists, + :gen, + :elixir_errors, + :elixir_config, + :supervisor, + :application, + :code, + :elixir_utils, + :file, + :io, + :binary, + :unicode, + :math, + :calendar, + :filename, + :epp, + :re, + :ets, + :sys, + :global, + :os, + :rand, + :orddict, + :filelib, + :net_adm, + :net_kernel + ] + + @doc """ + Compiles functions into JavaScript AST. + These are not actual function calls, but + the function identifiers themselves. Also + includes function heads for converting some + erlang functions into JavaScript functions. + """ + + def compile({:., _, [:erlang, :++]}, state) do + ast = erlang_compat_function("erlang", "list_concatenation") + {ast, state} + end + + def compile({:., _, [:erlang, :--]}, state) do + ast = erlang_compat_function("erlang", "list_substraction") + {ast, state} + end + + def compile({:., _, [:erlang, :"=<"]}, state) do + ast = erlang_compat_function("erlang", "lessThanEqualTo") + {ast, state} + end + + def compile({:., _, [:erlang, :+]}, state) do + ast = erlang_compat_function("erlang", "add") + {ast, state} + end + + def compile({:., _, [module, function]}, state) when module in @erlang_modules do + ast = + J.member_expression( + Helpers.core_module(module), + J.identifier(function) + ) + + {ast, state} + end + + def compile({:., _, [function_name]}, state) do + Form.compile(function_name, state) + end + + def compile({:., _, [module, function]}, state) do + function_name = ElixirScript.Translate.Identifier.make_function_name(function) + + ast = + J.member_expression( + process_module_name(module, state), + function_name + ) + + {ast, state} + end + + def process_module_name(module, state) when is_atom(module) do + cond do + ElixirScript.Translate.Module.is_js_module(module, state) and + ModuleState.is_global_module(state.pid, module) -> + process_global_js_module_name(module, state) + + ElixirScript.Translate.Module.is_js_module(module, state) -> + process_js_module_name(module, state) + + module === Elixir -> + module + |> ElixirScript.Output.module_to_name() + |> J.identifier() + + module === :ElixirScript -> + module + |> ElixirScript.Output.module_to_name() + |> J.identifier() + + ElixirScript.Translate.Module.is_elixir_module(module) -> + module + |> ElixirScript.Output.module_to_name() + |> J.identifier() + + true -> + ElixirScript.Translate.Identifier.make_identifier(module) + end + end + + def process_module_name(module, state) do + Form.compile!(module, state) + end + + defp process_global_js_module_name(module, state) do + case ModuleState.get_js_module_name(state.pid, module) do + name when is_binary(name) -> + J.identifier(name) + + name when is_atom(name) -> + case to_string(name) do + "Elixir." <> _ -> + ElixirScript.Translate.Identifier.make_alias(Module.split(name) |> Enum.reverse()) + + x -> + J.identifier(x) + end + end + end + + defp process_js_module_name(module, state) do + case ModuleState.get_js_module_name(state.pid, module) do + name when is_binary(name) -> + J.identifier(name) + + name when is_atom(name) -> + case to_string(name) do + "Elixir." <> _ -> + module + |> ElixirScript.Output.module_to_name() + |> J.identifier() + + x -> + J.identifier(x) + end + end + end + + defp erlang_compat_function(module, function) do + J.member_expression( + Helpers.core_module(module), + ElixirScript.Translate.Identifier.make_function_name(function) + ) + end +end diff --git a/lib/elixir_script/passes/translate/forms/try.ex b/lib/elixir_script/passes/translate/forms/try.ex new file mode 100644 index 00000000..3d3c1283 --- /dev/null +++ b/lib/elixir_script/passes/translate/forms/try.ex @@ -0,0 +1,122 @@ +defmodule ElixirScript.Translate.Forms.Try do + @moduledoc false + alias ESTree.Tools.Builder, as: JS + alias ElixirScript.Translate.{Form, Function, Clause, Helpers} + + def compile(blocks, state) do + try_block = Keyword.get(blocks, :do) + rescue_block = Keyword.get(blocks, :rescue, nil) + catch_block = Keyword.get(blocks, :catch, nil) + after_block = Keyword.get(blocks, :after, nil) + else_block = Keyword.get(blocks, :else, nil) + + translated_body = prepare_function_body(try_block, state) + + translated_body = JS.block_statement(translated_body) + try_block = Helpers.arrow_function([], translated_body) + + rescue_block = if rescue_block do + process_rescue_block(rescue_block, state) + else + JS.identifier(:null) + end + + catch_block = if catch_block do + Form.compile!({:fn, [], catch_block}, state) + else + JS.identifier(:null) + end + + after_block = if after_block do + process_after_block(after_block, state) + else + JS.identifier(:null) + end + + else_block = if else_block do + Form.compile!({:fn, [], else_block}, state) + else + JS.identifier(:null) + end + + js_ast = Helpers.call( + JS.member_expression( + Helpers.special_forms(), + JS.identifier("_try") + ), + [ + try_block, + rescue_block, + catch_block, + else_block, + after_block + ] + ) + + { js_ast, state } + end + + defp process_rescue_block(rescue_block, state) do + processed_clauses = Enum.map(rescue_block, fn + {:->, _, [ [{:in, _, [{:_, context, atom}, names]}], body]} -> + names = Enum.map(names, &make_exception_ast(&1)) + + param = {:_e, context, atom} + reason_call = {{:., [], [param, :__reason]}, [], []} + reason_call = {{:., [], [reason_call, :__struct__]}, [], []} + reason_call = {{:., [], [reason_call, :__MODULE__]}, [], []} + + {ast, _} = Clause.compile({ + [], + [param], + [{{:., [], [Enum, :member?]}, [], [names, reason_call]}], + body}, + state) + ast + {:->, _, [ [{:in, _, [param, names]}], body]} -> + names = Enum.map(names, &make_exception_ast(&1)) + + reason_call = {{:., [], [param, :__reason]}, [], []} + reason_call = {{:., [], [reason_call, :__struct__]}, [], []} + reason_call = {{:., [], [reason_call, :__MODULE__]}, [], []} + + {ast, _} = Clause.compile({ + [], + [param], + [{{:., [], [Enum, :member?]}, [], [names, reason_call]}], + body}, + state) + ast + {:->, _, [ [param], body]} -> + {ast, _} = Clause.compile({[], [param], [], body}, state) + ast + end) + + + Helpers.call( + JS.member_expression( + Helpers.patterns(), + JS.identifier("defmatch") + ), + processed_clauses + ) + + end + + defp make_exception_ast(name) do + {{:., [], [name, :__MODULE__]}, [], []} + end + + defp process_after_block(after_block, state) do + translated_body = prepare_function_body(after_block, state) + translated_body = JS.block_statement(translated_body) + + Helpers.arrow_function([], translated_body) + end + + defp prepare_function_body(body, state) do + {ast, _} = Function.compile_block(body, state) + + Clause.return_last_statement(ast) + end +end diff --git a/lib/elixir_script/passes/translate/forms/with.ex b/lib/elixir_script/passes/translate/forms/with.ex new file mode 100644 index 00000000..2bc5ea08 --- /dev/null +++ b/lib/elixir_script/passes/translate/forms/with.ex @@ -0,0 +1,55 @@ +defmodule ElixirScript.Translate.Forms.With do + @moduledoc false + alias ESTree.Tools.Builder, as: JS + alias ElixirScript.Translate.{Function, Clause, Helpers} + alias ElixirScript.Translate.Forms.Pattern + + + def compile(args, module_state) do + result = Enum.reduce(args, %{ expressions: [], arguments: [], module_state: module_state }, fn + {symbol, _, [pattern, body] }, state when symbol in [:<-, :=] -> + {ast, module_state} = Function.compile_block(body, state.module_state) + body = Clause.return_last_statement(ast) + expr_function = Helpers.arrow_function(state.arguments, JS.block_statement(body)) + + { patterns, params, module_state } = Pattern.compile([pattern], module_state) + + %{state | arguments: state.arguments ++ params, + expressions: state.expressions ++ [ JS.array_expression([hd(patterns), expr_function]) ], + module_state: module_state + } + + [do: expr], state -> + expr_function = process_block(expr, state.arguments, state.module_state) + + %{state | expressions: state.expressions ++ [ expr_function ] } + [do: do_expr, else: else_expr], state -> + do_function = process_block(do_expr, state.arguments, state.module_state) + + { else_function, _ } = Function.compile({:fn, [], else_expr}, state.module_state) + + %{state | expressions: state.expressions ++ [ do_function, else_function ] } + end) + + expressions = result.expressions + + js_ast = Helpers.call( + JS.member_expression( + Helpers.special_forms(), + JS.identifier("_with") + ), + expressions + ) + + { js_ast, module_state } + + end + + + defp process_block(body, arguments, module_state) do + {ast, _} = Function.compile_block(body, module_state) + + body = Clause.return_last_statement(ast) + Helpers.arrow_function(arguments, JS.block_statement(body)) + end +end diff --git a/lib/elixir_script/passes/translate/function.ex b/lib/elixir_script/passes/translate/function.ex new file mode 100644 index 00000000..dc0ff282 --- /dev/null +++ b/lib/elixir_script/passes/translate/function.ex @@ -0,0 +1,234 @@ +defmodule ElixirScript.Translate.Function do + @moduledoc false + + # Translates the given Elixir function AST into the + # equivalent JavaScript AST. + + alias ESTree.Tools.Builder, as: J + alias ElixirScript.Translate.{Clause, Form, Helpers} + alias ElixirScript.Translate.Forms.Pattern + + @spec compile(any, map) :: {ESTree.Node.t, map} + def compile({:fn, _, clauses}, state) do + anonymous? = Map.get(state, :anonymous_fn, false) + + state = Map.put(state, :anonymous_fn, true) + |> Map.put(:in_guard, false) + + clauses = compile_clauses(clauses, state) + + arg_matches_declaration = Helpers.declare_let("__arg_matches__", J.identifier("null")) + + function_recur_dec = Helpers.function( + "recur", + [J.rest_element(J.identifier("__function_args__"))], + J.block_statement([ + arg_matches_declaration, + clauses, + J.throw_statement( + Helpers.new( + J.member_expression( + Helpers.patterns(), + J.identifier("MatchError") + ), + [J.identifier("__function_args__")] + ) + ) + ]) + ) + + function_dec = Helpers.arrow_function( + [J.rest_element(J.identifier("__function_args__"))], + J.block_statement([ + function_recur_dec, + J.return_statement( + trampoline() + ) + ]) + ) + + state = Map.put(state, :anonymous_fn, anonymous?) + { function_dec, state } + end + + def compile({{name, arity}, _type, _, clauses}, state) do + + state = Map.put(state, :function, {name, arity}) + |> Map.put(:anonymous_fn, false) + |> Map.put(:in_guard, false) + + clauses = compile_clauses(clauses, state) + + arg_matches_declaration = Helpers.declare_let("__arg_matches__", J.identifier("null")) + intermediate_declaration = Helpers.declare_let("__intermediate__", J.identifier("null")) + + function_recur_dec = Helpers.function( + "recur", + [J.rest_element(J.identifier("__function_args__"))], + J.block_statement([ + arg_matches_declaration, + intermediate_declaration, + clauses, + J.throw_statement( + Helpers.new( + J.member_expression( + Helpers.patterns(), + J.identifier("MatchError") + ), + [J.identifier("__function_args__")] + ) + ) + ]) + ) + + function_dec = Helpers.function( + ElixirScript.Translate.Identifier.make_function_name(name), + [J.rest_element(J.identifier("__function_args__"))], + J.block_statement([ + function_recur_dec, + J.return_statement( + trampoline() + ) + ]) + ) + + { function_dec, state } + end + + defp compile_clauses(clauses, state) do + clauses + |> Enum.map(&compile_clause(&1, state)) + |> Enum.map(fn {patterns, _params, guards, body} -> + match_or_default_call = Helpers.call( + J.member_expression( + Helpers.patterns(), + J.identifier("match_or_default") + ), + [J.array_expression(patterns), J.identifier("__function_args__"), guards] + ) + + J.if_statement( + J.binary_expression( + :!==, + Helpers.assign(J.identifier("__arg_matches__"), match_or_default_call), + J.identifier("null") + ), + J.block_statement(body) + ) + end) + |> Enum.reverse + |> Enum.reduce(nil, fn + if_ast, nil -> + if_ast + if_ast, ast -> + %{if_ast | alternate: ast} + end) + end + + defp compile_clause({ _, args, guards, body}, state) do + state = if Map.has_key?(state, :vars) do + state + else + Map.put(state, :vars, %{}) + end + + {patterns, params, state} = Pattern.compile(args, state) + guard = Clause.compile_guard(params, guards, state) + + {body, _state} = compile_block(body, state) + + body = body + |> Clause.return_last_statement + |> update_last_call(state) + + declaration = Helpers.declare_let(params, J.identifier("__arg_matches__")) + + body = [declaration] ++ body + {patterns, params, guard, body} + end + + defp compile_clause({:->, _, [[{:when, _, params}], body ]}, state) do + guards = List.last(params) + params = params |> Enum.reverse |> tl |> Enum.reverse + + compile_clause({[], params, guards, body}, state) + end + + defp compile_clause({:->, _, [params, body]}, state) do + compile_clause({[], params, [], body}, state) + end + + @spec compile_block(any, map) :: {ESTree.Node.t, map} + def compile_block(block, state) do + ast = case block do + nil -> + J.identifier("null") + {:__block__, _, block_body} -> + {list, _} = Enum.map_reduce(block_body, state, &Form.compile(&1, &2)) + List.flatten(list) + _ -> + Form.compile!(block, state) + end + + {ast, state} + end + + @spec update_last_call([ESTree.Node.t], map) :: list + def update_last_call(clause_body, %{function: {name, _}, anonymous_fn: anonymous?}) do + last_item = List.last(clause_body) + function_name = ElixirScript.Translate.Identifier.make_function_name(name) + + case last_item do + %ESTree.ReturnStatement{ argument: %ESTree.CallExpression{ callee: ^function_name, arguments: arguments } } -> + if anonymous? do + clause_body + else + new_last_item = J.return_statement( + recurse( + recur_bind(arguments) + ) + ) + + List.replace_at(clause_body, length(clause_body) - 1, new_last_item) + end + _ -> + clause_body + end + end + + defp recur_bind(args) do + Helpers.call( + J.member_expression( + J.identifier("recur"), + J.identifier("bind") + ), + [J.identifier("null")] ++ args + ) + end + + defp recurse(func) do + Helpers.new( + J.member_expression( + Helpers.functions(), + J.identifier("Recurse") + ), + [ + func + ] + ) + end + + defp trampoline() do + Helpers.call( + J.member_expression( + Helpers.functions(), + J.identifier("trampoline") + ), + [ + recurse( + recur_bind([J.rest_element(J.identifier("__function_args__"))]) + ) + ] + ) + end +end diff --git a/lib/elixir_script/passes/translate/helpers.ex b/lib/elixir_script/passes/translate/helpers.ex new file mode 100644 index 00000000..227eab39 --- /dev/null +++ b/lib/elixir_script/passes/translate/helpers.ex @@ -0,0 +1,140 @@ +defmodule ElixirScript.Translate.Helpers do + @moduledoc false + alias ESTree.Tools.Builder, as: J + + def symbol(value) do + J.call_expression( + J.member_expression( + J.identifier("Symbol"), + J.identifier("for") + ), + [J.literal(value)] + ) + end + + def new(callee, arguments) do + J.new_expression( + callee, + arguments + ) + end + + def call(callee, arguments) do + J.call_expression( + callee, + arguments + ) + end + + def arrow_function(params, body) do + J.arrow_function_expression( + params, + [], + body + ) + end + + def function(%ESTree.Identifier{} = name, params, body) do + J.function_declaration( + name, + params, + [], + body + ) + end + + def function(name, params, body) when is_binary(name) do + function(J.identifier(name), params, body) + end + + def function(params, body) do + J.function_expression( + params, + [], + body + ) + end + + def core do + J.member_expression( + J.identifier("ElixirScript"), + J.identifier("Core") + ) + end + + def core_module(module) do + J.member_expression( + core(), + J.identifier(module) + ) + end + + def tuple do + core_module("Tuple") + end + + def bitstring do + core_module("BitString") + end + + def patterns do + core_module("Patterns") + end + + def functions do + core_module("Functions") + end + + def special_forms do + core_module("SpecialForms") + end + + def declare(%ESTree.Identifier{} = name, value) do + declarator = J.variable_declarator( + name, + value + ) + + J.variable_declaration([declarator], :const) + end + + def declare(names, value) when is_list(names) do + declarator = J.variable_declarator( + J.array_pattern(names), + value + ) + + J.variable_declaration([declarator], :const) + end + + def declare(name, value) when is_binary(name) do + declare(J.identifier(name), value) + end + + def declare_let(names, value) when is_list(names) do + declarator = J.variable_declarator( + J.array_pattern(names), + value + ) + + J.variable_declaration([declarator], :let) + end + + def declare_let(name, value) do + declarator = J.variable_declarator( + J.identifier(name), + value + ) + + J.variable_declaration([declarator], :let) + end + + def assign(name, value) do + J.assignment_expression( + :=, + name, + value + ) + end + +end diff --git a/lib/elixir_script/passes/translate/identifier.ex b/lib/elixir_script/passes/translate/identifier.ex new file mode 100644 index 00000000..a04f35b8 --- /dev/null +++ b/lib/elixir_script/passes/translate/identifier.ex @@ -0,0 +1,88 @@ +defmodule ElixirScript.Translate.Identifier do + @moduledoc false + alias ESTree.Tools.Builder, as: J + + @js_reserved_words [ + :break, + :case, + :class, + :const, + :continue, + :debugger, + :default, + :delete, + :do, + :else, + :export, + :extends, + :finally, + :function, + :if, + :import, + :in, + :instanceof, + :new, + :return, + :super, + :switch, + :throw, + :try, + :typeof, + :var, + :void, + :while, + :with, + :yield + ] + + def make_identifier(ast) do + ast + |> filter_name + |> J.identifier() + end + + def filter_name(reserved_word) when reserved_word in @js_reserved_words do + "__#{Atom.to_string(reserved_word)}__" + end + + def filter_name(name) do + name = to_string(name) + + if String.contains?(name, ["?", "!"]) do + name + |> String.replace("?", "__qmark__") + |> String.replace("!", "__emark__") + else + name + end + end + + def make_alias([x]) do + make_identifier(x) + end + + def make_alias([h | t]) do + J.member_expression(make_alias(t), make_identifier(h)) + end + + def make_namespace_members(module_name) do + case module_name do + m when is_list(m) -> + m + + m when is_atom(m) -> + Module.split(m) + end + |> Enum.reverse() + |> make_alias + end + + def make_function_name(name) do + name = filter_name(name) + J.identifier(name) + end + + def js_reserved_words() do + @js_reserved_words + end +end diff --git a/lib/elixir_script/passes/translate/module.ex b/lib/elixir_script/passes/translate/module.ex new file mode 100644 index 00000000..831c6848 --- /dev/null +++ b/lib/elixir_script/passes/translate/module.ex @@ -0,0 +1,308 @@ +defmodule ElixirScript.Translate.Module do + @moduledoc false + alias ESTree.Tools.Builder, as: J + alias ElixirScript.Translate.Helpers + alias ElixirScript.Translate.Function + alias ElixirScript.State, as: ModuleState + alias ElixirScript.Translate.Form + + @operators [ + :+, + :-, + :*, + :/, + :!=, + :==, + :===, + :<=, + :>=, + :=~, + :++, + :!==, + :--, + :<, + :>, + :=~ + ] + + @doc """ + Translate the given module's ast to + JavaScript AST + """ + def compile(module, %{protocol: true} = info, pid) do + ElixirScript.Translate.Protocol.compile(module, info, pid) + end + + def compile(_module, %{attributes: [__foreign_info__: %{path: _, name: _, global: _}]}, _) do + nil + end + + def compile(module, info, pid) do + %{ + attributes: attrs, + compile_opts: _compile_opts, + definitions: defs, + file: _file, + line: _line, + module: ^module, + unreachable: unreachable + } = info + + used = Map.get(info, :used) + remove_unused_functions = ModuleState.remove_unused_functions(pid) + + state = %{ + module: module, + pid: pid + } + + # Filter so that we only have the + # Used functions to compile + reachable_defs = + Enum.filter(defs, fn + {_, type, _, _} when type in [:defmacro, :defmacrop] -> false + {name, _, _, _} -> name not in unreachable + _ -> true + end) + + used_defs = + if Keyword.has_key?(attrs, :protocol_impl) or used == nil do + reachable_defs + else + Enum.filter(reachable_defs, fn + {{:start, 2}, _, _, _} -> + true + + {{:__struct__, _}, _, _, _} -> + true + + {{name, _}, _, _, _} when name in @operators -> + false + + {name, _, _, _} -> + if remove_unused_functions do + name in used + else + true + end + + _ -> + false + end) + end + + # we combine our function arities + combined_defs = combine_defs(used_defs) + exports = make_exports(module, combined_defs) + + {compiled_functions, _} = Enum.map_reduce(combined_defs, state, &Function.compile(&1, &2)) + + info_function = make_info_function(module, state) + compiled_functions = [info_function] ++ compiled_functions + + js_ast = [compiled_functions, exports] + + ModuleState.put_module(pid, module, Map.put(info, :js_ast, js_ast)) + end + + defp combine_defs(used_defs) do + used_defs + |> Enum.sort(fn {{name1, arity1}, _, _, _}, {{name2, arity2}, _, _, _} -> + "#{name1}#{arity1}" < "#{name2}#{arity2}" + end) + |> Enum.group_by(fn {{name, _}, _, _, _} -> name end) + |> Enum.map(fn {group, funs} -> + {_, type, _, _} = hd(funs) + + Enum.reduce(funs, {{group, nil}, type, [], []}, fn {_, _, _, clauses}, + {name, type, context, acc_clauses} -> + {name, type, context, acc_clauses ++ clauses} + end) + end) + end + + defp make_exports(module, reachable_defs) do + exports = + Enum.reduce(reachable_defs, [], fn + {{name, _arity}, :def, _, _}, list -> + function_name = ElixirScript.Translate.Identifier.make_identifier(name) + list ++ [J.property(function_name, function_name, :init, true)] + + _, list -> + list + end) + + # Add an attribute to use to determine if this is a module + # Will be used by the is_atom implementation + exports = + exports ++ + [ + %ESTree.Property{ + key: J.identifier("__MODULE__"), + value: Helpers.symbol(to_string(module)) + }, + J.property( + J.identifier("__info__"), + J.identifier("__info__"), + :init, + true + ) + ] + + J.object_expression(exports) + end + + @doc """ + Determins if the given atom + is an Elixir function + """ + def is_elixir_module(module) when is_atom(module) do + str_module = Atom.to_string(module) + + case str_module do + "Elixir" <> _ -> + true + + _ -> + false + end + end + + def is_elixir_module(_) do + false + end + + @doc """ + Determines is given function is a JS module. + A JS module is either one that begins with "JS" + or is a module defined from the js_modules compiler + opt + """ + def is_js_module(module, state) do + cond do + module in ModuleState.list_javascript_modules(state.pid) -> + true + + module === Elixir -> + false + + true -> + false + end + end + + defp make_info_map(module, state) do + functions = + module.__info__(:functions) + |> Form.compile!(state) + + macros = + module.__info__(:macros) + |> Form.compile!(state) + + attributes = + module.__info__(:attributes) + |> Form.compile!(state) + + compile = + module.__info__(:compile) + |> Keyword.update(:source, "", fn x -> :erlang.list_to_binary(x) end) + |> Form.compile!(state) + + md5 = + module.__info__(:md5) + |> :erlang.binary_to_list() + + md5 = Form.compile!({:<<>>, [], md5}, state) + + module = Helpers.symbol(to_string(module)) + + map_entries = + J.array_expression([ + J.array_expression([ + Helpers.symbol("functions"), + functions + ]), + J.array_expression([ + Helpers.symbol("macros"), + macros + ]), + J.array_expression([ + Helpers.symbol("attributes"), + attributes + ]), + J.array_expression([ + Helpers.symbol("compile"), + compile + ]), + J.array_expression([ + Helpers.symbol("md5"), + md5 + ]), + J.array_expression([ + Helpers.symbol("module"), + module + ]) + ]) + + map = + Helpers.new(J.identifier("Map"), [ + map_entries + ]) + + Helpers.declare("__info__map__", map) + end + + # Builds the __info__ function that Elixir modules + # have. + defp make_info_function(module, state) do + info_map = make_info_map(module, state) + + get_call = + Helpers.call( + J.member_expression( + J.identifier("__info__map__"), + J.identifier("get") + ), + [ + J.identifier("kind") + ] + ) + + value = Helpers.declare("value", get_call) + + body = + J.if_statement( + J.binary_expression( + :!==, + J.identifier("value"), + J.identifier("null") + ), + J.block_statement([ + J.return_statement(J.identifier("value")) + ]) + ) + + body = + J.block_statement([ + info_map, + value, + body, + J.throw_statement( + Helpers.new( + J.member_expression( + Helpers.patterns(), + J.identifier("MatchError") + ), + [J.identifier("kind")] + ) + ) + ]) + + Helpers.function( + "__info__", + [J.identifier("kind")], + body + ) + end +end diff --git a/lib/elixir_script/passes/translate/protocol.ex b/lib/elixir_script/passes/translate/protocol.ex new file mode 100644 index 00000000..ceaddcad --- /dev/null +++ b/lib/elixir_script/passes/translate/protocol.ex @@ -0,0 +1,115 @@ +defmodule ElixirScript.Translate.Protocol do + @moduledoc false + alias ESTree.Tools.Builder, as: J + alias ElixirScript.Translate.Helpers + alias ElixirScript.Translate.{Function, Identifier} + alias ElixirScript.State, as: ModuleState + + + @doc """ + This compiles and consolidates the given protocol + """ + def compile(module, %{protocol: true, impls: impls, functions: functions} = info, pid) do + object = Enum.map(functions, fn {function, _} -> + {Identifier.make_function_name(function), Helpers.function([], J.block_statement([]))} + end) + |> Enum.map(fn({key, value}) -> ElixirScript.Translate.Forms.Map.make_property(key, value) end) + |> J.object_expression + + declaration = Helpers.declare( + "protocol", + Helpers.call( + J.member_expression( + Helpers.functions(), + J.identifier(:defprotocol) + ), + [object] + ) + ) + + body = build_implementations(impls) + + body = [declaration] ++ body + + js_ast = [body, J.identifier("protocol")] + + ModuleState.put_module(pid, module, Map.put(info, :js_ast, js_ast)) + end + + defp build_implementations(impls) do + Enum.map(impls, fn({impl, impl_for}) -> + ast = impl + |> ElixirScript.Output.module_to_name() + |> J.identifier + + Helpers.call( + J.member_expression( + Helpers.functions(), + J.identifier(:defimpl) + ), + [ + J.identifier("protocol"), + map_to_js(impl_for), + ast + ] + ) + end) + end + + defp map_to_js(Integer) do + Helpers.core_module("Integer") + end + + defp map_to_js(Tuple) do + Helpers.tuple() + end + + defp map_to_js(Atom) do + J.identifier(:Symbol) + end + + defp map_to_js(List) do + J.identifier(:Array) + end + + defp map_to_js(BitString) do + Helpers.bitstring() + end + + defp map_to_js(Float) do + Helpers.core_module("Float") + end + + defp map_to_js(Function) do + J.identifier(:Function) + end + + defp map_to_js(PID) do + Helpers.core_module("PID") + end + + defp map_to_js(Port) do + Helpers.core_module("Port") + end + + defp map_to_js(Reference) do + Helpers.core_module("Reference") + end + + defp map_to_js(Map) do + J.identifier(:Map) + end + + defp map_to_js(Any) do + J.identifier(:null) + end + + defp map_to_js(module) when is_atom(module) do + case Module.split(module) do + ["JS" | rest] -> + Identifier.make_namespace_members(rest) + _ -> + Helpers.symbol(module) + end + end +end diff --git a/lib/elixir_script/pattern_matching/match.ex b/lib/elixir_script/pattern_matching/match.ex deleted file mode 100644 index 8251f4b6..00000000 --- a/lib/elixir_script/pattern_matching/match.ex +++ /dev/null @@ -1,170 +0,0 @@ -defmodule ElixirScript.PatternMatching.Match do - @moduledoc false - - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Primitive - alias ElixirScript.Translator.Utils - alias ElixirScript.Translator.Map - - @wildcard JS.member_expression( - JS.identifier(:Patterns), - JS.identifier(:wildcard) - ) - - @parameter JS.member_expression( - JS.identifier(:Patterns), - JS.identifier(:variable) - ) - - @head_tail JS.member_expression( - JS.identifier(:Patterns), - JS.identifier(:headTail) - ) - - @starts_with JS.member_expression( - JS.identifier(:Patterns), - JS.identifier(:startsWith) - ) - - @capture JS.member_expression( - JS.identifier(:Patterns), - JS.identifier(:capture) - ) - - @bound JS.member_expression( - JS.identifier(:Patterns), - JS.identifier(:bound) - ) - - def wildcard() do - JS.call_expression( - @wildcard, - [] - ) - end - - def parameter() do - JS.call_expression( - @parameter, - [] - ) - end - - def head_tail() do - JS.call_expression( - @head_tail, - [] - ) - end - - def starts_with(prefix) do - JS.call_expression( - @starts_with, - [JS.literal(prefix)] - ) - end - - def capture(value) do - JS.call_expression( - @capture, - [value] - ) - end - - def bound(value) do - JS.call_expression( - @bound, - [value] - ) - end - - def build_match(params, env) do - Enum.map(params, &do_build_match(&1, env)) - |> reduce_patterns - end - - defp do_build_match({:^, _, [value]}, env) do - { [bound(Translator.translate(value, env))], [nil] } - end - - defp do_build_match({:_, _, _}, env) do - { [wildcard()], [JS.identifier(:undefined)] } - end - - defp do_build_match([{:|, _, [head, tail]}], env) do - { [head_tail()], [Translator.translate(head, env), Translator.translate(tail, env)] } - end - - defp do_build_match({:<>, _, [prefix, value]}, env) do - { [starts_with(prefix)], [Translator.translate(value, env)] } - end - - defp do_build_match({:%{}, _, props}, env) do - properties = Enum.map(props, fn({key, value}) -> - {pattern, params} = do_build_match(value, env) - { Map.make_property(Translator.translate(key, env), hd(List.wrap(pattern))), params } - end) - - {props, params} = Enum.reduce(properties, {[], []}, fn({prop, param}, {props, params}) -> - { props ++ [prop], params ++ param } - end) - - { JS.object_expression(List.wrap(props)), params } - end - - defp do_build_match({:%, _, [{:__aliases__, _, name}, {:%{}, meta, props}]}, env) do - props = [{:__struct__ , List.last(name)}] ++ props - do_build_match({:%{}, meta, props}, env) - end - - defp do_build_match({:=, _, [{name, _, _}, right]}, env) when not name in [:%, :{}, :__aliases__, :^, :%{}] do - unify(name, right, env) - end - - defp do_build_match({:=, _, [left, {name, _, _}]}, env) when not name in [:%, :{}, :__aliases__, :^, :%{}] do - unify(name, left, env) - end - - defp do_build_match(list, env) when is_list(list) do - { patterns, params } = list - |> Enum.map(&build_match([&1], env)) - |> reduce_patterns - - {[Primitive.make_list_no_translate(patterns)], params} - end - - defp do_build_match(term, env) when is_number(term) or is_binary(term) or is_boolean(term) or is_atom(term) or is_nil(term) do - { [Translator.translate(term, env)], [] } - end - - defp do_build_match({ one, two }, env) do - do_build_match({:{}, [], [one, two]}, env) - end - - defp do_build_match({:{}, _, list}, env) do - { patterns, params } = list - |> Enum.map(&build_match([&1], env)) - |> reduce_patterns - - {[Primitive.make_tuple_no_translate(patterns)], params} - end - - defp do_build_match({name, _, _}, env) do - name = Utils.filter_name(name) - { [parameter()], [JS.identifier(name)] } - end - - defp reduce_patterns(patterns) do - patterns - |> Enum.reduce({ [], [] }, fn({ pattern, new_param }, { patterns, new_params }) -> - { patterns ++ List.wrap(pattern), new_params ++ List.wrap(new_param) } - end) - end - - defp unify(target, source, env) do - {patterns, params} = build_match([source], env) - { [capture(hd(patterns))], params ++ [JS.identifier(Utils.filter_name(target))] } - end - -end \ No newline at end of file diff --git a/lib/elixir_script/preprocess/aliases.ex b/lib/elixir_script/preprocess/aliases.ex deleted file mode 100644 index a3df468e..00000000 --- a/lib/elixir_script/preprocess/aliases.ex +++ /dev/null @@ -1,60 +0,0 @@ -defmodule ElixirScript.Preprocess.Aliases do - @moduledoc false - - @doc """ - Takes the ast, records any uses of outer modules, and updates the calls - to make them look like as if using an alias - - ex. - Hello.World.hi() - - #would turn into - World.hi() - - #the "Hello.World" would be placed in a set and later used for building an alias - """ - def process(ast, env) do - - state = %{ add: HashSet.new, defined: HashSet.new } - - {new_ast, state } = Macro.prewalk(ast, state, fn(x, acc) -> - process_aliases(x, acc, env) - end) - - { new_ast, state.add } - end - - def process_aliases({:alias, _, [{:__aliases__, _, _name}, [as: {:__aliases__, _, alias_name}]]} = ast, state, _) do - { ast, %{state | defined: HashSet.put(state.defined, List.last(alias_name)) } } - end - - def process_aliases({:alias, _, [{:__aliases__, _, name}]} = ast, state, _) do - { ast, %{state | defined: HashSet.put(state.defined, List.last(name)) } } - end - - def process_aliases({{:., meta1, [{:__aliases__, meta2, aliases}, function]}, meta3, params} = ast, state, env) do - expanded_ast = Macro.expand(ast, env) - - if expanded_ast == ast do - if ElixirScript.State.module_listed?(aliases) do - new_ast = {{:., meta1, [{:__aliases__, meta2, List.last(aliases) |> List.wrap }, function]}, meta3, params} - - new_state = if !HashSet.member?(state.defined, List.last(aliases)) do - %{ state | add: HashSet.put(state.add, aliases) } - else - state - end - - { new_ast, new_state } - else - { ast, state } - end - else - process_aliases(expanded_ast, state, env) - end - end - - def process_aliases(ast, state, _) do - {ast, state} - end -end \ No newline at end of file diff --git a/lib/elixir_script/preprocess/modules.ex b/lib/elixir_script/preprocess/modules.ex deleted file mode 100644 index b12a7f0b..00000000 --- a/lib/elixir_script/preprocess/modules.ex +++ /dev/null @@ -1,118 +0,0 @@ -defmodule ElixirScript.Preprocess.Modules do - @moduledoc false - - alias ElixirScript.State - - def get_info(modules) do - Enum.each(modules, fn(m) -> - Macro.postwalk(m, fn(x) -> - do_get_info(x) - end) - end) - end - - defp do_get_info({:defmodule, _, [{:__aliases__, meta, module_name_list}, [do: body]]} = ast) do - #TODO: expand macro here - body = Macro.expand(body, State.get().env) - body = make_inner_module_aliases(module_name_list, body) - - functions = get_functions_from_module(body) - macros = get_macros_from_module(body) - - mod = %ElixirScript.Module{ name: module_name_list , body: body, functions: functions, macros: macros } - State.add_module(mod) - - ast - end - - defp do_get_info(ast) do - ast - end - - defp make_inner_module_aliases(module_name_list, body) do - case body do - nil -> - { :__block__, [], [] } - - {:__block__, meta2, list2 } -> - list2 = Enum.map(list2, fn(x) -> - case x do - {:defmodule, meta1, [{:__aliases__, meta2, module_name_list2}, [do: body2]]} -> - body2 = make_inner_module_aliases(module_name_list2, body2) - - functions = get_functions_from_module(body2) - macros = get_macros_from_module(body2) - - mod = %ElixirScript.Module{ name: module_name_list2, body: body2, functions: functions, macros: macros } - - if State.module_listed?(module_name_list2) do - State.delete_module(mod) - end - - mod = %ElixirScript.Module{ name: module_name_list ++ module_name_list2, body: body2, functions: functions, macros: macros } - State.add_module(mod) - - [ - {:alias, meta1, [{:__aliases__, [alias: false], module_name_list ++ module_name_list2}]} - ] - _ -> - x - end - end) - |> List.flatten - - {:__block__, meta2, list2} - {:defmodule, meta1, [{:__aliases__, meta2, module_name_list2}, [do: body2]]} -> - body2 = make_inner_module_aliases(module_name_list2, body2) - - functions = get_functions_from_module(body2) - macros = get_macros_from_module(body2) - - mod = %ElixirScript.Module{ name: module_name_list2, body: body2, functions: functions, macros: macros } - - if State.module_listed?(module_name_list2) do - State.delete_module(mod) - end - - mod = %ElixirScript.Module{ name: module_name_list ++ module_name_list2, body: body2, functions: functions, macros: macros } - State.add_module(mod) - - {:__block__, meta2, [ - {:alias, meta1, [{:__aliases__, [alias: false], module_name_list ++ module_name_list2}]} - ] - } - _ -> - body - end - end - - - defp get_functions_from_module({:__block__, meta, list}) do - Enum.reduce(list, HashSet.new, fn - ({:def, _, [{:when, _, [{name, _, _params} | _guards] }, [do: _body]] }, state) -> - Set.put(state, name) - ({:def, _, [{name, _, _params}, [do: _body]]}, state) -> - Set.put(state, name) - _, state -> - state - end) - end - - defp get_functions_from_module(ast) do - [] - end - - defp get_macros_from_module({:__block__, meta, list}) do - Enum.reduce(list, HashSet.new, fn - ({:defmacro, _, [{:when, _, [{name, _, _params} | _guards] }, [do: _body]] }, state) -> - Set.put(state, name) - ({:defmacro, _, [{name, _, _params}, [do: _body]]}, state) -> - Set.put(state, name) - _, state -> - state - end) - end - - defp get_macros_from_module(ast) do - [] - end -end \ No newline at end of file diff --git a/lib/elixir_script/preprocess/using.ex b/lib/elixir_script/preprocess/using.ex deleted file mode 100644 index 1b1a8317..00000000 --- a/lib/elixir_script/preprocess/using.ex +++ /dev/null @@ -1,24 +0,0 @@ -defmodule ElixirScript.Preprocess.Using do - @moduledoc false - - def process(ast, env) do - Macro.prewalk(ast, fn(x) -> - process_using(x, env) - end) - end - - def process_using({:use, _, _} = ast, env) do - ast - |> Macro.expand(env) - |> expand__using__(env) - end - - def process_using(ast, _) do - ast - end - - defp expand__using__({:__block__, _, [{:require, _, _}, {{:., _, [_, :__using__]}, _, _} = using_ast]}, env) do - Macro.expand_once(using_ast, env) - end - -end \ No newline at end of file diff --git a/lib/elixir_script/preprocess/variables.ex b/lib/elixir_script/preprocess/variables.ex deleted file mode 100644 index 55c45180..00000000 --- a/lib/elixir_script/preprocess/variables.ex +++ /dev/null @@ -1,152 +0,0 @@ -defmodule ElixirScript.Preprocess.Variables do - @moduledoc false - - @doc """ - Creates a new variable binding whenever an - identifier is reused. - - ex. - a = 1 - a = 2 - - #becomes - - a0 = 1 - a1 = 2 - """ - def process(ast) do - {new_ast, _ } = Macro.prewalk(ast, %{}, fn(x, acc) -> - process_variables(x, acc) - end) - - new_ast - end - - def process_variables({:::, meta, [{{:., meta1, [Kernel, :to_string]}, meta2, params}, {:binary, meta3, context}]}, state) do - params = Enum.map(params, fn(x) -> - {value, _} = process_variables(x, state) - value - end) - - { {:::, meta, [{{:., meta1, [Kernel, :to_string]}, meta2, params}, {:binary, meta3, context}]}, state } - end - - def process_variables({:=, meta, [{var1, var2}, value]}, state) do - - { value, _ } = process_variables(value, state) - - { [var1, var2], state } = Enum.map_reduce([var1, var2], state, fn(x, current_state) -> - case x do - {variable_name, meta2, context} when not(variable_name in [:%, :{}, :^, :&, :%{}]) -> - {new_variable_name, new_state} = get_new_variable_name(variable_name, current_state) - { {new_variable_name, meta2, context} , new_state } - _ -> - {x, current_state} - end - end) - - { {:=, meta, [{var1, var2}, value]}, state } - end - - def process_variables({:=, meta, [{:{}, meta2, variables}, value]}, state) do - - { value, _ } = process_variables(value, state) - - { variables, state } = Enum.map_reduce(variables, state, fn(x, current_state) -> - case x do - {variable_name, meta3, context} when not(variable_name in [:%, :{}, :^, :&, :_, :%{}]) -> - {new_variable_name, new_state} = get_new_variable_name(variable_name, current_state) - { {new_variable_name, meta3, context} , new_state } - _ -> - {x, current_state} - end - end) - - { {:=, meta, [{:{}, meta2, variables}, value]}, state } - end - - def process_variables({:=, meta, [variables, value]}, state) when is_list(variables) do - - { value, _ } = process_variables(value, state) - - { variables, state } = Enum.map_reduce(variables, state, fn(x, current_state) -> - case x do - {variable_name, meta3, context} when not(variable_name in [:%, :{}, :^, :&, :_, :%{}]) -> - {new_variable_name, new_state} = get_new_variable_name(variable_name, current_state) - { {new_variable_name, meta3, context} , new_state } - _ -> - {x, current_state} - end - end) - - { {:=, meta, [variables, value]}, state } - end - - def process_variables({:=, meta, [{variable_name, meta2, context}, value]}, state) when not(variable_name in [:%, :{}, :^, :&, :., :%{}]) do - { value, _ } = process_variables(value, state) - - {new_variable_name, new_state} = get_new_variable_name(variable_name, state) - { {:=, meta, [{new_variable_name, meta2, context}, value]}, new_state } - end - - def process_variables({:<<>>, meta, params}, state) do - params = Enum.map(params, fn(x) -> - {value, _} = process_variables(x, state) - value - end) - - { {:<<>>, meta, params}, state } - end - - def process_variables({{:., meta, [{:__aliases__, context, module}, function]}, meta2, params}, state) do - params = Enum.map(params, fn(x) -> - {value, _} = process_variables(x, state) - value - end) - - { {{:., meta, [{:__aliases__, context, module}, function]}, meta2, params}, state } - end - - def process_variables({{:., meta, [variable, function]}, meta2, params}, state) do - {variable, _} = process_variables(variable, state) - - params = Enum.map(params, fn(x) -> - {value, _} = process_variables(x, state) - value - end) - - { {{:., meta, [variable, function]}, meta2, params}, state } - end - - def process_variables(variables, state) when is_list(variables) do - variables = Enum.map(variables, fn(x) -> - {value, _} = process_variables(x, state) - value - end) - - { variables, state } - end - - def process_variables({variable_name, meta, context}, state) do - cond do - Map.has_key?(state, variable_name) -> - new_variable_name = String.to_atom("#{variable_name}#{Map.get(state, variable_name)}") - { {new_variable_name, meta, context}, state } - true -> - { {variable_name, meta, context}, state } - end - end - - def process_variables(ast, state) do - { ast, state } - end - - defp get_new_variable_name(variable_name, state) do - current = Map.get(state, variable_name, -1) + 1 - new_variable_name = String.to_atom("#{variable_name}#{current}") - new_state = Map.put(state, variable_name, current) - - { new_variable_name, new_state } - end - -end \ No newline at end of file diff --git a/lib/elixir_script/state.ex b/lib/elixir_script/state.ex index 594ee565..37d0473f 100644 --- a/lib/elixir_script/state.ex +++ b/lib/elixir_script/state.ex @@ -1,52 +1,175 @@ defmodule ElixirScript.State do @moduledoc false - def start_link(root, env \\ __ENV__) do - Agent.start_link(fn -> %ElixirScript.Env{ root: root, env: env } end, name: __MODULE__) + # Holds the state for the ElixirScript compiler + + def start_link(compiler_opts) do + Agent.start_link(fn -> + %{ + modules: Keyword.new(), + js_modules: [], + in_memory_modules: [], + compiler_opts: compiler_opts + } + end) + end + + def stop(pid) do + Agent.stop(pid) + end + + def get_module(pid, module) do + Agent.get(pid, fn state -> + Keyword.get(state.modules, module) + end) + end + + def put_module(pid, module, value) do + Agent.update(pid, fn state -> + value = + Map.put_new(value, :used, []) + |> Map.put_new(:used_modules, []) + + modules = Keyword.put(state.modules, module, value) + %{state | modules: modules} + end) + end + + def put_used_module(pid, module, used_module) do + Agent.update(pid, fn state -> + module_info = Keyword.get(state.modules, module) + + used_modules = Map.get(module_info, :used_modules, []) + used_modules = Enum.uniq([used_module | used_modules]) + + module_info = Map.put(module_info, :used_modules, used_modules) + modules = Keyword.put(state.modules, module, module_info) + + %{state | modules: modules} + end) + end + + def has_used?(pid, module, func) do + Agent.get(pid, fn state -> + module_info = Keyword.get(state.modules, module) + used = Map.get(module_info, :used, []) + + Enum.find(used, fn x -> x == func end) != nil + end) + end + + def put_used(pid, module, {_function, _arity} = func) do + Agent.update(pid, fn state -> + module_info = Keyword.get(state.modules, module) + + used = Map.get(module_info, :used, []) + used = [func | used] + + module_info = Map.put(module_info, :used, used) + modules = Keyword.put(state.modules, module, module_info) + + %{state | modules: modules} + end) end - def update_env(env) do - Agent.update(__MODULE__, fn(state) -> - %{state | env: env } + def put_javascript_module(pid, module, name, path) do + Agent.update(pid, fn state -> + js_modules = Map.get(state, :js_modules, []) + js_modules = [{module, name, path} | js_modules] + %{state | js_modules: js_modules} end) end - def add_module(module) do - Agent.update(__MODULE__, fn(state) -> - %{ state | modules: Set.put(state.modules, module) } + def put_diagnostic(pid, module, diagnostic) do + Agent.update(pid, fn state -> + module_info = Keyword.get(state.modules, module) + + if module_info do + diagnostics = Map.get(module_info, :diagnostics, []) + diagnostics = [diagnostic | diagnostics] + + module_info = Map.put(module_info, :diagnostics, diagnostics) + modules = Keyword.put(state.modules, module, module_info) + + %{state | modules: modules} + else + state + end end) end - def delete_module(module) do - Agent.update(__MODULE__, fn(state) -> - %{ state | modules: Set.delete(state.modules, module) } + def list_javascript_modules(pid) do + Agent.get(pid, fn state -> + state.js_modules + |> Enum.map(fn {module, _name, _path} -> + module + end) end) end - def module_listed?(module_name) do - Agent.get(__MODULE__, fn(state) -> - Enum.any?(state.modules, fn(x) -> x.name == module_name end) + def js_modules(pid) do + Agent.get(pid, fn state -> + state.js_modules end) end - def get() do - Agent.get(__MODULE__, fn(state) -> - state + def is_global_module(pid, module) do + Agent.get(pid, fn state -> + result = + Enum.find(state.js_modules, fn {mod, _name, path} -> mod == module and path == nil end) + + if result == nil, do: false, else: true end) end - def get_module(module_name_list) do - state = Agent.get(__MODULE__, fn(state) -> - state + def get_global_module_name(pid, module) do + Agent.get(pid, fn state -> + result = + Enum.find(state.js_modules, fn {mod, _name, path} -> mod == module and path == nil end) + + if result == nil, do: nil, else: elem(result, 1) end) + end - Enum.find(Set.to_list(state.modules), fn(x) -> - x.name == module_name_list + def remove_unused_functions(pid) do + Agent.get(pid, fn state -> + state.compiler_opts.remove_unused_functions end) end - def stop() do - Agent.stop(__MODULE__) + def get_js_module_name(pid, module) do + Agent.get(pid, fn state -> + {_, name, _} = + state.js_modules + |> Enum.find(fn {m, _, _} -> module == m end) + + name + end) end -end \ No newline at end of file + def list_modules(pid) do + Agent.get(pid, fn state -> + state.modules + end) + end + + def get_in_memory_module(pid, module) do + Agent.get(pid, fn state -> + Keyword.get(state.in_memory_modules, module) + end) + end + + def get_in_memory_modules(pid) do + Agent.get(pid, fn state -> + state.in_memory_modules + end) + end + + def put_in_memory_module(pid, module, beam) do + Agent.update(pid, fn state -> + in_memory_modules = Map.get(state, :in_memory_modules, []) + in_memory_modules = Keyword.put(in_memory_modules, module, beam) + %{state | in_memory_modules: in_memory_modules} + end) + end +end diff --git a/lib/elixir_script/translator.ex b/lib/elixir_script/translator.ex deleted file mode 100644 index 793d5d52..00000000 --- a/lib/elixir_script/translator.ex +++ /dev/null @@ -1,307 +0,0 @@ -defmodule ElixirScript.Translator do - @moduledoc """ - Translates the given Elixir AST into JavaScript AST - """ - alias ElixirScript.Translator.Primitive - alias ElixirScript.Translator.Assignment - alias ElixirScript.Translator.Map - alias ElixirScript.Translator.Function - alias ElixirScript.Translator.Capture - alias ElixirScript.Translator.Import - alias ElixirScript.Translator.Cond - alias ElixirScript.Translator.Case - alias ElixirScript.Translator.For - alias ElixirScript.Translator.Try - alias ElixirScript.Translator.Block - alias ElixirScript.Translator.Struct - alias ElixirScript.Translator.Module - alias ElixirScript.Translator.Utils - alias ElixirScript.Translator.Bitstring - alias ElixirScript.Translator.Receive - alias ElixirScript.Translator.Quote - alias ElixirScript.Translator.Utils - alias ElixirScript.Translator.Kernel, as: KernelLib - alias ElixirScript.Translator.Logger - alias ElixirScript.Translator.JS, as: JSLib - alias ESTree.Tools.Builder, as: JS - - @doc """ - Translates Elixir AST to JavaScript AST - """ - def translate(ast, env) do - do_translate(ast, env) - end - - defp do_translate(ast, _) when is_number(ast) or is_binary(ast) or is_boolean(ast) or is_nil(ast) do - Primitive.make_literal(ast) - end - - defp do_translate(ast, _) when is_atom(ast) do - Primitive.make_atom(ast) - end - - defp do_translate(ast, env) when is_list(ast) do - Primitive.make_list(ast, env) - end - - defp do_translate({ one, two }, env) do - Primitive.make_tuple({one, two}, env) - end - - defp do_translate({:&, [], [number]}, _) when is_number(number) do - Primitive.make_identifier(String.to_atom("__#{number}")) - end - - defp do_translate({:&, _, [{:/, _, [{{:., _, [{:__aliases__, _, module_name}, function_name]}, _, []}, arity]}]}, env) do - function_name = Utils.filter_name(function_name) - Capture.make_capture(List.last(module_name), function_name, arity, env) - end - - defp do_translate({:&, _, [{:/, _, [{function_name, _, _}, arity]}]}, env) do - function_name = Utils.filter_name(function_name) - Capture.make_capture(function_name, arity, env) - end - - defp do_translate({:&, _, body}, env) do - params = Capture.find_value_placeholders(body) |> List.flatten - Function.make_anonymous_function([{:->, [], [params, body]}], env) - end - - defp do_translate({:@, _, [{name, _, [value]}]}, env) do - name = Utils.filter_name(name) - Module.make_attribute(name, value, env) - end - - defp do_translate({:@, _, [{name, _, _}]}, _) do - name = Utils.filter_name(name) - Primitive.make_identifier(name) - end - - defp do_translate({:%, _, [alias_info, data]}, env) do - {_, _, name} = alias_info - {_, _, data} = data - Struct.make_struct(name, data, env) - end - - defp do_translate({:%{}, _, [{:|, _, [map, data]}]}, env) do - Map.make_map_update(map, data, env); - end - - defp do_translate({:%{}, _, properties}, env) do - Map.make_object(properties, env) - end - - defp do_translate({:<<>>, _, elements}, env) do - is_interpolated_string = Enum.all?(elements, fn(x) -> - case x do - b when is_binary(b) -> - true - {:::, _, [_target, {:binary, _, _}]} -> - true - _ -> - false - end - end) - - case is_interpolated_string do - true -> - Bitstring.make_interpolated_string(elements, env) - _ -> - Bitstring.make_bitstring(elements, env) - end - end - - defp do_translate({{:., _, [{:__aliases__, _, [:Logger]}, function_name]}, _, params }, env) do - Logger.make_logger(function_name, params, env) - end - - defp do_translate({{:., _, [Access, :get]}, _, [target, property]}, env) do - Map.make_get_property(target, property, env) - end - - defp do_translate({{:., _, [function_name]}, _, params}, env) do - Function.make_function_call(function_name, params, env) - end - - defp do_translate({:., _, [module_name, function_name]} = ast, env) do - expanded_ast = Macro.expand(ast, env) - - if expanded_ast == ast do - Function.make_function_or_property_call(module_name, function_name, env) - else - translate(expanded_ast, env) - end - end - - defp do_translate({{:., _, [module_name, function_name]}, _, [] } = ast, env) do - expanded_ast = Macro.expand(ast, env) - - if expanded_ast == ast do - Function.make_function_or_property_call(module_name, function_name, env) - else - translate(expanded_ast, env) - end - end - - defp do_translate({{:., _, [{:__aliases__, _, module_name}]}, _, params} = ast, env) do - expanded_ast = Macro.expand(ast, env) - if expanded_ast == ast do - Function.make_function_call(hd(module_name), params, env) - else - translate(expanded_ast, env) - end - end - - defp do_translate({{:., context, [module_name, function_name]}, _, params } = ast, env) do - case module_name do - Kernel -> - KernelLib.translate_kernel_function(function_name, params, env) - {:__aliases__, _, [:JS]} -> - JSLib.translate_js_function(function_name, params, env) - _ -> - expanded_ast = Macro.expand(ast, env) - if expanded_ast == ast do - Function.make_function_call(module_name, function_name, params, env) - else - translate(expanded_ast, env) - end - end - end - - defp do_translate({:_, _, _}, _env) do - Primitive.make_identifier(:undefined) - end - - defp do_translate({:__aliases__, _, aliases}, _) do - Primitive.make_identifier({:__aliases__, [], aliases}) - end - - defp do_translate({:__block__, _, expressions }, env) do - Block.make_block(expressions, env) - end - - defp do_translate({:__DIR__, _, _}, _) do - JS.call_expression( - JS.member_expression( - Primitive.special_forms(), - JS.identifier(:__DIR__) - ), - [] - ) - end - - defp do_translate({:try, _, [ blocks ]}, env) do - Try.make_try(blocks, env) - end - - defp do_translate({:receive, _, [expressions] }, env) do - Receive.make_receive(expressions, env); - end - - defp do_translate({:super, _, _expressions }, _) do - raise ElixirScript.UnsupportedError, "super" - end - - defp do_translate({:__CALLER__, _, _expressions }, _) do - raise ElixirScript.UnsupportedError, "__CALLER__" - end - - defp do_translate({:__ENV__, _, _expressions }, _) do - raise ElixirScript.UnsupportedError, "__ENV__" - end - - defp do_translate({:quote, _, [[do: expr]]}, env) do - Quote.make_quote([], expr, env) - end - - defp do_translate({:quote, _, [opts, [do: expr]]}, env) do - Quote.make_quote(opts, expr, env) - end - - defp do_translate({:import, _, [{:__aliases__, _, module_name_list}, params ]}, env) do - Import.make_import(module_name_list, params, env) - end - - defp do_translate({:import, _, [{:__aliases__, _, module_name_list}]}, env) do - Import.make_import(module_name_list, [], env) - end - - defp do_translate({:alias, _, [alias_info, options]}, _) when is_tuple(alias_info) do - Import.make_alias_import(alias_info, options) - end - - defp do_translate({:alias, _, [alias_info]}, _) when is_tuple(alias_info) do - Import.make_alias_import(alias_info, []) - end - - defp do_translate({:require, _, [alias_info, options]}, _) do - Import.make_alias_import(alias_info, options) - end - - defp do_translate({:require, _, [alias_info]}, _) do - Import.make_alias_import(alias_info, []) - end - - defp do_translate({:case, _, [condition, [do: clauses]]}, env) do - Case.make_case(condition, clauses, env) - end - - defp do_translate({:cond, _, [[do: clauses]]}, env) do - Cond.make_cond(clauses, env) - end - - defp do_translate({:for, _, generators}, env) do - For.make_for(generators, env) - end - - defp do_translate({:fn, _, clauses}, env) do - Function.make_anonymous_function(clauses, env) - end - - defp do_translate({:{}, _, elements}, env) do - Primitive.make_tuple(elements, env) - end - - defp do_translate({:=, _, [left, right]}, env) do - Assignment.make_assignment(left, right, env) - end - - defp do_translate({function, _, [{:when, _, [{name, _, _params} | _guards] }, [do: _body]] } = ast, env) when function in [:def, :defp] do - Function.process_function(Utils.filter_name(name), [ast], env) - end - - defp do_translate({function, _, [{name, _, _params}, [do: _body]]} = ast, env) when function in [:def, :defp] do - Function.process_function(Utils.filter_name(name), [ast], env) - end - - defp do_translate({:defstruct, _, attributes}, env) do - Struct.make_defstruct(attributes, env) - end - - defp do_translate({:defexception, _, attributes}, env) do - Struct.make_defexception(attributes, env) - end - - defp do_translate({:defmodule, _, [{:__aliases__, _, module_name_list}, [do: body]]}, env) do - Module.make_module(module_name_list, body, env) - end - - defp do_translate({name, metadata, params} = ast, env) when is_list(params) do - if KernelLib.is_defined_in_kernel(name, length(params)) do - KernelLib.translate_kernel_function(name, params, env) - else - expanded_ast = Macro.expand(ast, env) - if expanded_ast == ast do - Function.make_function_call(name, params, env) - else - translate(expanded_ast, env) - end - end - end - - defp do_translate({ name, _, _ }, _) do - name = Utils.filter_name(name) - Primitive.make_identifier(name) - end - -end diff --git a/lib/elixir_script/translator/assignment.ex b/lib/elixir_script/translator/assignment.ex deleted file mode 100644 index 668e546f..00000000 --- a/lib/elixir_script/translator/assignment.ex +++ /dev/null @@ -1,58 +0,0 @@ -defmodule ElixirScript.Translator.Assignment do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Primitive - alias ElixirScript.PatternMatching.Match - - def make_assignment(left, right, env) do - { patterns, params } = Match.build_match([left], env) - - declarator = JS.variable_declarator( - JS.array_pattern(params), - JS.call_expression( - JS.member_expression( - JS.identifier("Patterns"), - JS.identifier("match") - ), - [hd(patterns), Translator.translate(right, env)] - ) - ) - - array_pattern = JS.variable_declaration([declarator], :let) - - case left do - list when is_list(list) -> - make_ref(array_pattern, params, "list") - {_left1, _left2} -> - make_ref(array_pattern, params, "tuple") - {:{}, _, _} -> - make_ref(array_pattern, params, "tuple") - _ -> - array_pattern - end - end - - defp make_ref(array_pattern, params, type) do - ref = JS.identifier("_ref") - - params = Enum.map(params, fn - (nil) -> JS.identifier(:undefined) - (x) -> x - end) - - ref_declarator = JS.variable_declarator( - ref, - JS.call_expression( - JS.member_expression( - Primitive.special_forms(), - JS.identifier(type) - ), - params - ) - ) - - ref_declaration = JS.variable_declaration([ref_declarator], :let) - %ElixirScript.Translator.Group{ body: [array_pattern, ref_declaration] } - end -end \ No newline at end of file diff --git a/lib/elixir_script/translator/bitstring.ex b/lib/elixir_script/translator/bitstring.ex deleted file mode 100644 index 101e1a7f..00000000 --- a/lib/elixir_script/translator/bitstring.ex +++ /dev/null @@ -1,116 +0,0 @@ -defmodule ElixirScript.Translator.Bitstring do - @moduledoc false - alias ESTree.Tools.Builder - alias ElixirScript.Translator - alias ElixirScript.Translator.Primitive - - - def make_bitstring(elements, env) do - Builder.call_expression( - Builder.member_expression( - Primitive.special_forms(), - Builder.identifier("bitstring") - ), - Enum.map(elements, &make_bitstring_element(&1, env)) - ) - end - - defp make_bitstring_element(element, env) when is_number(element) do - do_make_bitstring_element({:integer, Translator.translate(element, env)}) - end - - defp make_bitstring_element(element, env) when is_binary(element) do - do_make_bitstring_element({:binary, Translator.translate(element, env)}) - end - - defp make_bitstring_element({:<<>>, [], elements}, env) do - make_bitstring(elements, env) - end - - defp make_bitstring_element({:::, _, [element, {type, _, _}]}, env) when type in [:integer, :float, :bitstring, :bits, :binary, :bytes, :utf8, :utf16, :utf32] do - do_make_bitstring_element({type, Translator.translate(element, env)}) - end - - defp make_bitstring_element({:::, _, [element, {type, _, params}]}, env) when type in [:size, :unit] do - do_make_bitstring_element({type, Translator.translate(element, env), Enum.map(params, &Translator.translate(&1, env))}) - end - - defp make_bitstring_element({:::, _, [element, {:*, _, [size, unit]}]}, env) do - size_ast = do_make_bitstring_element({:size, Translator.translate(element, env), [Translator.translate(size, env)]}) - do_make_bitstring_element({:unit, size_ast, [Translator.translate(unit, env)]}) - end - - defp make_bitstring_element({:::, _, [element, {:-, _, types}]}, env) do - handle_type_adjectives({:-, [], types}, Translator.translate(element, env), env) - end - - defp make_bitstring_element({:::, _, [element, size]}, env) do - do_make_bitstring_element({:size, Translator.translate(element, env), [Translator.translate(size, env)]}) - end - - defp handle_type_adjectives({:-, _, types}, ast, env) do - Enum.reduce(types, ast, fn(type, current_ast) -> - case type do - {:-, _, sub_types} -> - handle_type_adjectives({:-, [], sub_types}, current_ast, env) - {:*, _, [size, unit]} -> - size_ast = do_make_bitstring_element({:size, current_ast, [Translator.translate(size, env)]}) - do_make_bitstring_element({:unit, size_ast, [Translator.translate(unit, env)]}) - {the_type, _, params} when is_list(params) -> - do_make_bitstring_element({the_type, current_ast, Enum.map(params, &Translator.translate(&1, env))}) - {the_type, _, _} -> - do_make_bitstring_element({the_type, current_ast}) - end - end) - end - - defp do_make_bitstring_element({type, ast}) do - Builder.call_expression( - Builder.member_expression( - Builder.identifier("BitString"), - Builder.identifier(type) - ), - [ - ast - ] - ) - end - - defp do_make_bitstring_element({type, ast, params}) when is_list(params) do - Builder.call_expression( - Builder.member_expression( - Builder.identifier("BitString"), - Builder.identifier(type) - ), - [ - ast - ] ++ params - ) - end - - def make_interpolated_string(elements, env) do - translated_elements = Enum.map(elements, fn(x)-> - case x do - elem when is_binary(elem) -> - Translator.translate(elem, env) - {:::, _, data} -> - Translator.translate(hd(data), env) - end - end) - - do_make_interpolated_string(tl(translated_elements), hd(translated_elements), env) - end - - def do_make_interpolated_string([], ast, _) do - ast - end - - def do_make_interpolated_string(elements, ast, env) do - Builder.binary_expression( - :+, - ast, - do_make_interpolated_string(tl(elements), hd(elements), env) - ) - end - -end \ No newline at end of file diff --git a/lib/elixir_script/translator/block.ex b/lib/elixir_script/translator/block.ex deleted file mode 100644 index 5bd958f9..00000000 --- a/lib/elixir_script/translator/block.ex +++ /dev/null @@ -1,10 +0,0 @@ -defmodule ElixirScript.Translator.Block do - @moduledoc false - alias ESTree.Tools.Builder - alias ElixirScript.Translator - - def make_block(expressions, env) do - Builder.block_statement(Enum.map(expressions, &Translator.translate(&1, env))) - end - -end \ No newline at end of file diff --git a/lib/elixir_script/translator/capture.ex b/lib/elixir_script/translator/capture.ex deleted file mode 100644 index ec7e5313..00000000 --- a/lib/elixir_script/translator/capture.ex +++ /dev/null @@ -1,63 +0,0 @@ -defmodule ElixirScript.Translator.Capture do - @moduledoc false - - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.PatternMatching.Match - alias ElixirScript.Translator.Utils - alias ElixirScript.Translator.Function - - def make_capture(function_name, arity, env) do - params = Enum.map(1..arity, fn(x) -> {String.to_atom("__#{x}"), [], ElixirScript.Translator.Capture} end) - - { patterns, params } = Match.build_match(params, env) - - body = JS.block_statement([ - JS.return_statement( - JS.call_expression( - JS.identifier(function_name), - params - ) - ) - ]) - - - Function.make_defmatch([ - Function.do_make_function_clause(patterns, params, body) - ]) - end - - def make_capture(module_name, function_name, arity, env) do - params = Enum.map(1..arity, fn(x) -> {String.to_atom("__#{x}"), [], ElixirScript.Translator.Capture} end) - - { patterns, params } = Match.build_match(params, env) - - body = JS.block_statement([ - JS.return_statement( - JS.call_expression( - Utils.make_member_expression(module_name, function_name, env), - params - ) - ) - ]) - - - Function.make_defmatch([ - Function.do_make_function_clause(patterns, params, body) - ]) - - - end - - def find_value_placeholders(ast) do - case ast do - list when is_list(list) -> - Enum.map(list, &find_value_placeholders(&1)) - {:&, _, [number]} when is_number(number) -> - [{String.to_atom("__#{number}"), [], ElixirScript.Translator.Capture}] - tuple when is_tuple(tuple) -> - Enum.map(Tuple.to_list(tuple), &find_value_placeholders(&1)) - _ -> - [] - end - end -end \ No newline at end of file diff --git a/lib/elixir_script/translator/case.ex b/lib/elixir_script/translator/case.ex deleted file mode 100644 index befef37d..00000000 --- a/lib/elixir_script/translator/case.ex +++ /dev/null @@ -1,16 +0,0 @@ -defmodule ElixirScript.Translator.Case do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Function - - def make_case(condition, clauses, env) do - JS.call_expression( - JS.member_expression( - Function.make_anonymous_function(clauses, env), - JS.identifier("call") - ), - [JS.identifier(:this), Translator.translate(condition, env)] - ) - end -end \ No newline at end of file diff --git a/lib/elixir_script/translator/cond.ex b/lib/elixir_script/translator/cond.ex deleted file mode 100644 index 819e03cd..00000000 --- a/lib/elixir_script/translator/cond.ex +++ /dev/null @@ -1,31 +0,0 @@ -defmodule ElixirScript.Translator.Cond do - @moduledoc false - - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Primitive - alias ElixirScript.Translator.Function - alias ElixirScript.Translator.Utils - - def make_cond(clauses, env) do - JS.call_expression( - JS.member_expression( - Primitive.special_forms(), - JS.identifier("cond") - ), - process_cond(clauses, env) - ) - end - - defp process_cond(clauses, env) do - Enum.map(clauses, fn({:->, _, [clause, clause_body]}) -> - translated_body = Function.prepare_function_body(clause_body, env) |> JS.block_statement - function = JS.function_expression([], [], translated_body) - translated_clause = Translator.translate(hd(clause), env) - - - Primitive.make_list_no_translate([translated_clause, function]) - end) - end - -end \ No newline at end of file diff --git a/lib/elixir_script/translator/expression.ex b/lib/elixir_script/translator/expression.ex deleted file mode 100644 index e3341266..00000000 --- a/lib/elixir_script/translator/expression.ex +++ /dev/null @@ -1,14 +0,0 @@ -defmodule ElixirScript.Translator.Expression do - @moduledoc false - alias ESTree.Tools.Builder - alias ElixirScript.Translator - - def make_unary_expression(operator, expr, env) do - Builder.unary_expression(operator, true, Translator.translate(expr, env)) - end - - def make_binary_expression(operator, left, right, env) do - Builder.binary_expression(operator, Translator.translate(left, env), Translator.translate(right, env)) - end - -end \ No newline at end of file diff --git a/lib/elixir_script/translator/for.ex b/lib/elixir_script/translator/for.ex deleted file mode 100644 index 1f9d0d05..00000000 --- a/lib/elixir_script/translator/for.ex +++ /dev/null @@ -1,66 +0,0 @@ -defmodule ElixirScript.Translator.For do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Utils - alias ElixirScript.PatternMatching.Match - alias ElixirScript.Translator.Primitive - alias ElixirScript.Translator.Function - - - def make_for(generators, env) do - args = handle_args(generators, env) - - collections = Primitive.make_list_no_translate(args.collections) - into = args.into || Primitive.make_list_no_translate([]) - filter = args.filter || JS.function_expression([], [], JS.block_statement([JS.return_statement(JS.identifier("true"))])) - fun = args.fun - - JS.call_expression( - JS.member_expression( - Primitive.special_forms(), - JS.identifier("_for") - ), - [collections, fun, filter, into] - ) - end - - defp handle_args(generators, env) do - Enum.reduce(generators, %{collections: [], args: [], filter: nil, fun: nil, into: nil}, fn - ({:<-, [], [identifier, enum]}, state) -> - { patterns, params } = Match.build_match([identifier], env) - - list = Primitive.make_list_no_translate([hd(patterns), Translator.translate(enum, env)]) - - %{state | collections: state.collections ++ [list], args: state.args ++ params } - ([into: expression], state) -> - %{ state | into: Translator.translate(expression, env) } - ([into: expression, do: expression2], state) -> - fun = JS.function_expression( - state.args, - [], - Function.make_function_body(expression2, env) - ) - - %{ state | into: Translator.translate(expression, env), fun: fun } - - ([do: expression], state) -> - - fun = JS.function_expression( - state.args, - [], - Function.make_function_body(expression, env) - ) - - %{ state | fun: fun } - (filter, state) -> - fun = JS.function_expression( - state.args, - [], - Function.make_function_body(filter, env) - ) - - %{ state | filter: fun } - end) - end -end \ No newline at end of file diff --git a/lib/elixir_script/translator/function.ex b/lib/elixir_script/translator/function.ex deleted file mode 100644 index 9024c0b3..00000000 --- a/lib/elixir_script/translator/function.ex +++ /dev/null @@ -1,252 +0,0 @@ -defmodule ElixirScript.Translator.Function do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Utils - alias ElixirScript.PatternMatching.Match - alias ElixirScript.Preprocess.Variables - alias ElixirScript.Translator.Map - - - def process_function(name, functions, env) do - result = make_anonymous_function(functions, env) - - declarator = JS.variable_declarator( - JS.identifier(name), - result - ) - - JS.variable_declaration([declarator], :let) - end - - def make_anonymous_function(functions, env) do - clauses = functions - |> Stream.map(fn(x) -> Variables.process(x) end) - |> Stream.map(fn - {:->, _, [ [{:when, _, [params | guards]}], body ]} -> - { patterns, params } = Match.build_match(List.wrap(params), env) - params = make_params(params) - body = make_function_body(body, env) - guard_body = make_guards(guards, env) - do_make_function_clause(patterns, params, body, guard_body) - - ({:->, _, [params, body]}) -> - { patterns, params } = Match.build_match(params, env) - params = make_params(params) - body = make_function_body(body, env) - do_make_function_clause(patterns, params, body) - - ({_, _, [{:when, _, [{_, _, params} | guards] }, [do: body]]}) -> - { patterns, params } = Match.build_match(params, env) - params = make_params(params) - body = make_function_body(body, env) - guard_body = make_guards(guards, env) - do_make_function_clause(patterns, params, body, guard_body) - - ({_, _, [{_, _, params}, [do: body]]}) -> - { patterns, params } = Match.build_match(params, env) - params = make_params(params) - body = make_function_body(body, env) - do_make_function_clause(patterns, params, body) - - end) - |> Enum.to_list - - make_defmatch(clauses) - end - - def make_defmatch(clauses) do - JS.call_expression( - JS.member_expression( - JS.identifier("Patterns"), - JS.identifier("defmatch") - ), - clauses - ) - end - - def make_function_body(body, env) do - body - |> prepare_function_body(env) - |> JS.block_statement - end - - defp make_guards(guards, env) do - hd(List.wrap(guards)) - |> prepare_function_body(env) - |> JS.block_statement - end - - defp make_params(params) do - Enum.filter(params, fn - (%ESTree.Identifier{name: :undefined}) -> false - (_) -> true - end) - end - - def do_make_function_clause(patterns, params, body, guard_body) do - JS.call_expression( - JS.member_expression( - JS.identifier("Patterns"), - JS.identifier("make_case") - ), - [ - JS.array_expression(patterns), - JS.function_expression(params, [], body), - JS.function_expression(params, [], guard_body) - ] - ) - end - - def do_make_function_clause(patterns, params, body) do - JS.call_expression( - JS.member_expression( - JS.identifier("Patterns"), - JS.identifier("make_case") - ), - [ - JS.array_expression(patterns), - JS.function_expression(params, [], body) - ] - ) - end - - def make_function_or_property_call(module_name, function_name, env) do - the_name = case module_name do - {:__aliases__, _, name} -> - name - {name, _, _} when is_atom(name) -> - name - {{:., _, [_module_name, _function_name]}, _, _params } = ast -> - ast - name -> - case to_string(name) do - "Elixir." <> actual_name -> - actual_name - _ -> - name - end - end - - JS.call_expression( - JS.member_expression( - JS.identifier("JS"), - JS.identifier("get_property_or_call_function") - ), - [ - Utils.make_module_expression_tree(the_name, false, env), - Translator.translate(to_string(function_name), env) - ] - ) - end - - def make_function_call(function_name, params, env) when is_tuple(function_name) do - Utils.make_call_expression(function_name, params, env) - end - - def make_function_call(function_name, params, env) do - Utils.make_call_expression(Utils.filter_name(function_name), params, env) - end - - def make_function_call(module_name, function_name, params, env) do - the_name = case module_name do - {:__aliases__, _, name} -> - name - {name, _, _} when is_atom(name) -> - name - {{:., _, [_, _]}, _, _ } = ast -> - ast - {{:., _, [{:__aliases__, _, _}]}, _, _} = ast -> - ast - name -> - case to_string(name) do - "Elixir." <> actual_name -> - actual_name - _ -> - name - end - end - - Utils.make_call_expression(the_name, Utils.filter_name(function_name), params, env) - end - - def prepare_function_body(body, env) do - case body do - nil -> - [] - list when is_list(list) -> - Enum.map(list, &Translator.translate(&1, env)) - {:__block__, _, list} -> - Enum.map(list, &Translator.translate(&1, env)) - _ -> - [Translator.translate(body, env)] - end - |> Utils.inflate_groups - |> return_last_expression - end - - def return_last_expression(nil) do - nil - end - - def return_last_expression([]) do - [JS.return_statement(JS.literal(nil))] - end - - def return_last_expression(%ESTree.BlockStatement{} = block) do - %ESTree.BlockStatement{ block | body: return_last_expression(block.body) } - end - - def return_last_expression(list) when is_list(list) do - last_item = List.last(list) - - last_item = case last_item do - %ESTree.Literal{} -> - JS.return_statement(last_item) - %ESTree.Identifier{} -> - JS.return_statement(last_item) - %ESTree.VariableDeclaration{} -> - declaration = hd(last_item.declarations).id - - return_statement = case declaration do - %ESTree.ArrayPattern{elements: elements} -> - if(length(elements) == 1) do - JS.return_statement(hd(declaration.elements)) - else - JS.return_statement(JS.array_expression(declaration.elements)) - end - _ -> - JS.return_statement(declaration) - end - - [last_item, return_statement] - %ESTree.BlockStatement{} -> - last_item = %ESTree.BlockStatement{ last_item | body: return_last_expression(last_item.body) } - _ -> - if String.contains?(last_item.type, "Expression") do - JS.return_statement(last_item) - else - [last_item, JS.return_statement(JS.literal(nil))] - end - end - - - list = Enum.take(list, length(list)-1) - |> Enum.map(fn(x) -> - case x do - %ESTree.MemberExpression{} -> - JS.expression_statement(x) - %ESTree.CallExpression{} -> - JS.expression_statement(x) - _ -> - x - end - end) - - if is_list(last_item) do - list ++ last_item - else - list ++ [last_item] - end - end -end \ No newline at end of file diff --git a/lib/elixir_script/translator/group.ex b/lib/elixir_script/translator/group.ex deleted file mode 100644 index a16665b5..00000000 --- a/lib/elixir_script/translator/group.ex +++ /dev/null @@ -1,9 +0,0 @@ -defmodule ElixirScript.Translator.Group do - @moduledoc false - - @type t :: %ElixirScript.Translator.Group{ - type: binary, - body: [ESTree.Statement.t] - } - defstruct type: "Group", body: [] -end \ No newline at end of file diff --git a/lib/elixir_script/translator/import.ex b/lib/elixir_script/translator/import.ex deleted file mode 100644 index f2b4916b..00000000 --- a/lib/elixir_script/translator/import.ex +++ /dev/null @@ -1,215 +0,0 @@ -defmodule ElixirScript.Translator.Import do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.State - - def make_alias_import(alias_info, options) do - {_, _, name} = alias_info - - default = Dict.get(options, :default, false) - - - import_specifier = if default == false do - if options[:as] do - {_, _, alt} = options[:as] - - JS.import_namespace_specifier( - JS.identifier(List.last(alt)) - ) - else - JS.import_namespace_specifier( - JS.identifier(List.last(name)) - ) - end - else - if options[:as] do - {_, _, alt} = options[:as] - - JS.import_specifier( - JS.identifier("default"), - JS.identifier(List.last(alt)) - ) - else - JS.import_default_specifier( - JS.identifier(List.last(name)), - JS.identifier(List.last(name)) - ) - end - end - - import_path = make_source(name) - - JS.import_declaration( - [import_specifier], - JS.identifier(import_path) - ) - end - - def make_import(module_name_list, [], env) do - mod = List.last(module_name_list) |> JS.identifier - - functions = State.get_module(module_name_list).functions - - functions = Enum.map(functions, fn - ({name, _arity}) -> - name - (name) -> - name - end) - - specifiers = Enum.map(functions, fn - ({name, _arity}) -> - name = JS.identifier(name) - JS.import_specifier( - name, - name - ) - (name) -> - name = JS.identifier(name) - JS.import_specifier( - name, - name - ) - end) - - import_path = make_source(module_name_list) - - JS.import_declaration(specifiers, JS.identifier(import_path)) - end - - def make_import(module_name_list, [only: :functions], env) do - mod = List.last(module_name_list) |> JS.identifier - - functions = State.get_module(module_name_list).functions - - functions = Enum.map(functions, fn - ({name, _arity}) -> - name - (name) -> - name - end) - - specifiers = Enum.map(functions, fn - (name) -> - name = JS.identifier(name) - JS.import_specifier( - name, - name - ) - end) - - import_path = make_source(module_name_list) - - JS.import_declaration(specifiers, JS.identifier(import_path)) - end - - def make_import(module_name_list, [only: only], env) do - mod = List.last(module_name_list) |> JS.identifier - - only = Enum.map(only, fn - ({name, _arity}) -> - name - (name) -> - name - end) - - functions = State.get_module(module_name_list) - |> get_functions_from_module([only: only]) - - specifiers = Enum.map(functions, fn - (name) -> - name = JS.identifier(name) - JS.import_specifier( - name, - name - ) - end) - - import_path = make_source(module_name_list) - - JS.import_declaration(specifiers, JS.identifier(import_path)) - end - - def make_import(module_name_list, [except: except], env) do - mod = List.last(module_name_list) |> JS.identifier - - except = Enum.map(except, fn - ({name, _arity}) -> - name - (name) -> - name - end) - - functions = State.get_module(module_name_list) - |> get_functions_from_module([except: except]) - - specifiers = Enum.map(functions, fn - (name) -> - name = JS.identifier(name) - JS.import_specifier( - name, - name - ) - end) - - import_path = make_source(module_name_list) - - JS.import_declaration(specifiers, JS.identifier(import_path)) - end - - defp make_source(name) do - root = ElixirScript.State.get().root - "'#{root(root)}#{make_file_path(name)}'" - end - - def make_file_path(name) do - Enum.map(name, fn(x) -> - x - |> Atom.to_string - |> Inflex.underscore - |> String.downcase - end) - |> Enum.join("/") - end - - def get_functions_from_module(module, [only: only]) do - Set.intersection(Enum.into(only, HashSet.new), Enum.into(module.functions, HashSet.new)) - end - - def get_functions_from_module(module, [except: except]) do - Set.difference(Enum.into(module.functions, HashSet.new), Enum.into(except, HashSet.new)) - end - - - def create_standard_lib_imports(root, env) do - module_names = [ - :Patterns, :Kernel, :Atom, :Enum, :Integer, - :JS, :List, :Range, :Tuple, :Agent, :Keyword, - :BitString - ] - - import_specifiers = Enum.map(module_names, fn(x) -> - JS.import_specifier( - JS.identifier(x), - JS.identifier(x) - ) - end) - - import_declaration = JS.import_declaration( - import_specifiers, - JS.identifier("'#{root(root) <> "elixir"}'") - ) - - [import_declaration] - end - - defp root(nil) do - "" - end - - defp root(root) do - root <> "/" - end - -end diff --git a/lib/elixir_script/translator/js.ex b/lib/elixir_script/translator/js.ex deleted file mode 100644 index 23154f71..00000000 --- a/lib/elixir_script/translator/js.ex +++ /dev/null @@ -1,89 +0,0 @@ -defmodule ElixirScript.Translator.JS do - @moduledoc false - - alias ESTree.Tools.Builder - alias ElixirScript.Translator - alias ElixirScript.Translator.Utils - - @doc false - def translate_js_function(name, params, env) do - do_translate({name, [], params}, env) - end - - defp do_translate({:new, _, [{:__aliases__, _, module_name}, params]}, env) do - Builder.new_expression( - Utils.make_module_expression_tree(module_name, false, env), - Enum.map(params, &Translator.translate(&1, env)) - ) - end - - defp do_translate({:new, _, [module_name, params]}, env) do - Builder.new_expression( - Translator.translate(module_name, env), - Enum.map(params, &Translator.translate(&1, env)) - ) - end - - defp do_translate({:update, _, [object, property, value]}, env) do - Builder.assignment_expression( - :=, - Builder.member_expression( - Translator.translate(object, env), - Translator.translate(property, env), - true - ), - Translator.translate(value, env) - ) - end - - defp do_translate({:import, _, [module_names, from]}, env) when is_list(module_names) do - import_specifiers = Enum.map(module_names, fn(x) -> - Builder.import_specifier( - Translator.translate(x, env), - Translator.translate(x, env) - ) - end) - - build_import_declaration(import_specifiers, from) - end - - defp do_translate({:import, _, [module_name, from]}, env) do - - import_specifier = Builder.import_specifier( - Builder.identifier("default"), - Translator.translate(module_name, env) - ) - - build_import_declaration([import_specifier], from) - end - - defp build_import_declaration(import_specifiers, from) do - Builder.import_declaration( - import_specifiers, - Builder.identifier("'#{from}'") - ) - end - - - defp do_translate({:to_js, _, [value]}, env) do - quoted = quote do - if is_list(unquote(value)) || is_map(unquote(value)) || is_tuple(unquote(value)) do - value.toJS() - else - value - end - end - - Translator.translate(quoted, env) - end - - - defp do_translate({:to_json, _, [value]}, env) do - quoted = quote do - JSON.stringify(JS.to_js(unquote(value))) - end - - Translator.translate(quoted, env) - end - -end \ No newline at end of file diff --git a/lib/elixir_script/translator/js_module.ex b/lib/elixir_script/translator/js_module.ex deleted file mode 100644 index f1fb023d..00000000 --- a/lib/elixir_script/translator/js_module.ex +++ /dev/null @@ -1,4 +0,0 @@ -defmodule ElixirScript.Translator.JSModule do - @moduledoc false - defstruct name: [], body: [] -end diff --git a/lib/elixir_script/translator/kernel.ex b/lib/elixir_script/translator/kernel.ex deleted file mode 100644 index 7851bc70..00000000 --- a/lib/elixir_script/translator/kernel.ex +++ /dev/null @@ -1,219 +0,0 @@ -defmodule ElixirScript.Translator.Kernel do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Map - alias ElixirScript.Translator.Function - alias ElixirScript.Translator.Expression - alias ElixirScript.Translator.Raise - - @kernel_definitions Kernel.__info__(:functions) ++ Kernel.__info__(:macros) - - def is_defined_in_kernel(name, arity) do - { name, arity } in @kernel_definitions - end - - def translate_kernel_function(name, params, env) do - do_translate({name, [], params}, env) - end - - defp do_translate({operator, _, [value]}, env) when operator in [:-, :!, :+] do - Expression.make_unary_expression(operator, value, env) - end - - defp do_translate({operator, _, [left, right]}, env) when operator in [:+, :-, :/, :*, :==, :!=, :&&, :||, :>, :<, :>=, :<=, :===, :!==] do - Expression.make_binary_expression(operator, left, right, env) - end - - defp do_translate({:<>, _, [left, right]}, env) do - Expression.make_binary_expression(:+, left, right, env) - end - - defp do_translate({:++, _, [left, right]}, env) do - JS.call_expression( - JS.member_expression( - Translator.translate(left, env), - JS.identifier(:concat) - ), - [Translator.translate(right, env)] - ) - end - - defp do_translate({:.., _, [first, last]}, env) do - quoted_range = quote do: Range.(unquote(first), unquote(last)) - - Translator.translate(quoted_range, env) - end - - defp do_translate({:abs, _, [number]}, env) do - quoted = quote do - Math.abs(unquote(number)) - end - - Translator.translate(quoted, env) - end - - defp do_translate({:apply, _, [fun, args]}, env) do - quoted = quote do - unquote(fun)(unquote_splicing(args)) - end - - Translator.translate(quoted, env) - end - - defp do_translate({:apply, _, [module, fun, args]}, env) do - quoted = quote do - unquote(module).unquote(fun)(unquote_splicing(args)) - end - - Translator.translate(quoted, env) - end - - defp do_translate({:and, _, [left, right]}, env) do - Expression.make_binary_expression(:&&, left, right, env) - end - - defp do_translate({:div, _, [left, right]}, env) do - Expression.make_binary_expression(:/, left, right, env) - end - - defp do_translate({:or, _, [left, right]}, env) do - Expression.make_binary_expression(:||, left, right, env) - end - - defp do_translate({:not, _, [value]}, env) do - Expression.make_unary_expression(:!, value, env) - end - - defp do_translate({:rem, _, [left, right]}, env) do - Expression.make_binary_expression(:%, left, right, env) - end - - defp do_translate({:round, _, [value]}, env) do - quoted = quote do - Math.round(unquote(value)) - end - - Translator.translate(quoted, env) - end - - defp do_translate({:self, _, []}, _) do - JS.identifier(:self) - end - - defp do_translate({:tuple_size, _, [tuple]}, env) do - quoted = quote do - unquote(tuple).count() - end - - Translator.translate(quoted, env) - end - - defp do_translate({:map_size, _, [map]}, env) do - quoted = quote do - Object.keys(unquote(map)).length - end - - Translator.translate(quoted, env) - end - - defp do_translate({:max, _, [first, second]}, env) do - quoted = quote do - Math.max(unquote(first), unquote(second)) - end - - Translator.translate(quoted, env) - end - - defp do_translate({:min, _, [first, second]}, env) do - quoted = quote do - Math.min(unquote(first), unquote(second)) - end - - Translator.translate(quoted, env) - end - - defp do_translate({:if, _, _} = ast, env) do - Macro.expand(ast, env) - |> Translator.translate(env) - end - - defp do_translate({:unless, _, _} = ast, env) do - Macro.expand(ast, env) - |> Translator.translate(env) - end - - defp do_translate({:|>, _, [left, right]}, env) do - case right do - {{:., meta, [module, fun]}, meta2, params} -> - Translator.translate({{:., meta, [module, fun]}, meta2, [left] ++ params}, env) - {fun, meta, params} -> - Translator.translate({fun, meta, [left] ++ params}, env) - end - end - - defp do_translate({:hd, _, [list]}, env) do - quoted = quote do - unquote(list)[0] - end - - Translator.translate(quoted, env) - end - - defp do_translate({:tl, _, [list]}, env) do - JS.call_expression( - JS.member_expression( - Translator.translate(list, env), - JS.identifier(:slice) - ), - [JS.literal(1)] - ) - end - - defp do_translate({:length, _, [list]}, env) when is_list(list) do - JS.member_expression( - Translator.translate(list, env), - JS.identifier(:length) - ) - end - - defp do_translate({:raise, _, [alias_info, attributes]}, env) do - {_, _, name} = alias_info - - JS.throw_statement( - JS.call_expression( - JS.member_expression( - JS.identifier(List.last(name)), - JS.identifier(:defexception) - ), - Enum.map(attributes, fn({k, v})-> - JS.assignment_expression( - :=, - JS.identifier(k), - Translator.translate(v, env) - ) - end) - ) - ) - end - - defp do_translate({:raise, _, [message]}, env) do - JS.throw_statement( - JS.object_expression( - [ - Map.make_property(Translator.translate(:__struct__, env), Translator.translate(:RuntimeError, env)), - Map.make_property(Translator.translate(:message, env), JS.literal(message)) - ] - ) - ) - end - - defp do_translate({:to_string, _, [param]}, env) when is_binary(param) do - Translator.translate(param, env) - end - - defp do_translate({name, _, params}, env) do - Function.make_function_call(:Kernel, name, params, env) - end - -end \ No newline at end of file diff --git a/lib/elixir_script/translator/logger.ex b/lib/elixir_script/translator/logger.ex deleted file mode 100644 index 2537c1c5..00000000 --- a/lib/elixir_script/translator/logger.ex +++ /dev/null @@ -1,24 +0,0 @@ -defmodule ElixirScript.Translator.Logger do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - - - def make_logger(:log, params, env) do - do_make_logger(hd(params), tl(params), env) - end - - def make_logger(level, params, env) do - do_make_logger(level, params, env) - end - - defp do_make_logger(level, params, env) do - JS.call_expression( - JS.member_expression( - JS.identifier("console"), - JS.identifier(level) - ), - [Translator.translate(hd(params), env)] - ) - end -end \ No newline at end of file diff --git a/lib/elixir_script/translator/map.ex b/lib/elixir_script/translator/map.ex deleted file mode 100644 index 75fcd613..00000000 --- a/lib/elixir_script/translator/map.ex +++ /dev/null @@ -1,68 +0,0 @@ -defmodule ElixirScript.Translator.Map do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Primitive - alias ElixirScript.Translator.Utils - - def make_map(object_expression) do - JS.call_expression( - JS.member_expression( - Primitive.special_forms(), - JS.identifier("map") - ), - [object_expression] - ) - end - - def make_get_property(target, property, env) do - JS.member_expression( - Translator.translate(target, env), - Translator.translate(property, env), - true - ) - end - - def make_object(properties, env) do - object = properties - |> Enum.map(fn - ({x, {:__aliases__, _, [value]}}) -> make_property(Translator.translate(x, env), JS.identifier(value)) - ({x, y}) -> make_property(Translator.translate(x, env), Translator.translate(y, env)) - end) - |> JS.object_expression - |> make_map - end - - def make_property(%ESTree.Identifier{} = key, value) do - JS.property(key, value) - end - - def make_property(%ESTree.Literal{value: k} = key, value) when is_binary(k) do - JS.property(JS.identifier(k), value) - end - - def make_property(key, value) do - JS.property(key, value, :init, false, false, true) - end - - def make_map_update(map, data, env) do - map = Translator.translate(map, env) - - map_update = JS.object_expression( - Enum.map(data, - fn({key, value}) -> - make_property(Translator.translate(key, env), Translator.translate(value, env)) - end - ) - ) - - JS.call_expression( - JS.member_expression( - Primitive.special_forms(), - JS.identifier("map_update") - ), - [map, map_update] - ) - end - -end diff --git a/lib/elixir_script/translator/module.ex b/lib/elixir_script/translator/module.ex deleted file mode 100644 index 0c50784f..00000000 --- a/lib/elixir_script/translator/module.ex +++ /dev/null @@ -1,221 +0,0 @@ -defmodule ElixirScript.Translator.Module do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Utils - alias ElixirScript.Translator.JSModule - alias ElixirScript.Preprocess.Aliases - alias ElixirScript.Preprocess.Using - alias ElixirScript.Translator.Function - - def make_module(module_name_list, nil, env) do - [%JSModule{ name: module_name_list, body: List.wrap(create__module__(module_name_list, env)) }] - end - - def make_module(module_name_list, body, env) do - body = Using.process(body, env) - { body, aliases } = Aliases.process(body, env) - - { body, functions } = extract_functions_from_module(body) - { exported_functions, private_functions } = process_functions(functions, env) - - #Translate body - body = Translator.translate(body, env) - - body = case body do - [%ESTree.BlockStatement{ body: body }] -> - body - %ESTree.BlockStatement{ body: body } -> - body - _ -> - List.wrap(body) - end - - {imports, body} = extract_imports_from_body(body) - {structs, body} = extract_structs_from_body(body) - - - #Add imports found from walking the ast - #and make sure to only put one declaration per alias - imports = process_imports(imports, aliases) - imports = imports.imports - - #Collect all the functions so that we can process their arity - body = Enum.map(body, fn(x) -> - case x do - %ESTree.CallExpression{} -> - JS.expression_statement(x) - _ -> - x - end - end) - - body = Utils.inflate_groups(body) - - exported_object = JS.object_expression( - make_defstruct_property(structs) ++ - Enum.map(exported_functions, fn({key, _value}) -> - JS.property(JS.identifier(key), JS.identifier(key), :init, true) - end) - ) - - exported_functions = Enum.map(exported_functions, fn({_key, value}) -> value end) - private_functions = Enum.map(private_functions, fn({_key, value}) -> value end) - - default = JS.export_named_declaration(exported_object) - {modules, body} = Enum.partition(body, fn(x) -> - case x do - %JSModule{} -> - true - _ -> - false - end - end) - - result = [ - %JSModule{ - name: module_name_list, - body: imports ++ List.wrap(create__module__(module_name_list, env)) ++ structs ++ private_functions ++ exported_functions ++ body ++ [default] - } - ] ++ List.flatten(modules) - - result - end - - defp extract_functions_from_module({:__block__, meta, body_list}) do - { body_list, functions } = Enum.map_reduce(body_list, - %{exported: HashDict.new(), private: HashDict.new()}, fn - ({:def, _, [{:when, _, [{name, _, _} | _guards] }, _] } = function, state) -> - { - nil, - %{ state | exported: HashDict.put(state.exported, name, HashDict.get(state.exported, name, []) ++ [function]) } - } - ({:def, _, [{name, _, _}, _]} = function, state) -> - { - nil, - %{ state | exported: HashDict.put(state.exported, name, HashDict.get(state.exported, name, []) ++ [function]) } - } - ({:defp, _, [{:when, _, [{name, _, _} | _guards] }, _] } = function, state) -> - { - nil, - %{ state | private: HashDict.put(state.private, name, HashDict.get(state.private, name, []) ++ [function]) } - } - ({:defp, _, [{name, _, _}, _]} = function, state) -> - { - nil, - %{ state | private: HashDict.put(state.private, name, HashDict.get(state.private, name, []) ++ [function]) } - } - (x, state) -> - { x, state } - end) - - body_list = Enum.filter(body_list, fn(x) -> !is_nil(x) end) - body = {:__block__, meta, body_list} - - { body, functions } - end - - defp extract_functions_from_module(body) do - extract_functions_from_module({:__block__, [], List.wrap(body)}) - end - - defp extract_imports_from_body(body) do - Enum.partition(body, fn(x) -> - case x do - %ESTree.ImportDeclaration{} -> - true - _ -> - false - end - end) - end - - defp extract_structs_from_body(body) do - Enum.partition(body, fn(x) -> - case x do - %ESTree.FunctionDeclaration{} -> - true - _ -> - false - end - end) - end - - defp make_defstruct_property([]) do - [] - end - - defp make_defstruct_property([the_struct]) do - case the_struct do - %ESTree.FunctionDeclaration{id: %ESTree.Identifier{name: :defstruct}} -> - [JS.property(JS.identifier(:defstruct), JS.identifier(:defstruct), :init, true )] - %ESTree.FunctionDeclaration{id: %ESTree.Identifier{name: :defexception}} -> - [JS.property(JS.identifier(:defexception), JS.identifier(:defexception), :init, true )] - end - end - - defp process_imports(imports, aliases) do - imports ++ make_imports(aliases) - |> Enum.reduce(HashSet.new, fn(x, acc)-> - HashSet.put(acc, x) - end) - |> HashSet.to_list - |> Enum.reduce(%{ identifiers: HashSet.new, imports: [] }, fn(x, state) -> - case x do - %ESTree.ImportDeclaration{ specifiers: [%ESTree.ImportSpecifier{ local: id }] } -> - if HashSet.member?(state.identifiers, id.name) do - state - else - %{ state | identifiers: HashSet.put(state.identifiers, id.name), imports: state.imports ++ [x] } - end - %ESTree.ImportDeclaration{ specifiers: [%ESTree.ImportDefaultSpecifier{ local: id }] } -> - if HashSet.member?(state.identifiers, id.name) do - state - else - %{ state | identifiers: HashSet.put(state.identifiers, id.name), imports: state.imports ++ [x] } - end - _ -> - %{ state | imports: state.imports ++ [x] } - end - end) - end - - defp process_functions(%{ exported: exported, private: private }, env) do - exported_functions = Enum.map(Dict.keys(exported), fn(key) -> - functions = Dict.get(exported, key) - { key, Function.process_function(key, functions, env) } - end) - - private_functions = Enum.map(Dict.keys(private), fn(key) -> - functions = Dict.get(private, key) - { key, Function.process_function(key, functions, env) } - end) - - { exported_functions, private_functions } - end - - def make_attribute(name, value, env) do - declarator = JS.variable_declarator( - JS.identifier(name), - ElixirScript.Translator.translate(value, env) - ) - - JS.variable_declaration([declarator], :const) - end - - defp create__module__(module_name_list, env) do - declarator = JS.variable_declarator( - JS.identifier(:__MODULE__), - ElixirScript.Translator.translate(List.last(module_name_list), env) - ) - - JS.variable_declaration([declarator], :const) - end - - def make_imports(enum) do - Enum.map(enum, fn(x) -> - ElixirScript.Translator.Import.make_alias_import({ nil, nil, x }, []) - end) - end - -end diff --git a/lib/elixir_script/translator/primitive.ex b/lib/elixir_script/translator/primitive.ex deleted file mode 100644 index c4021ffe..00000000 --- a/lib/elixir_script/translator/primitive.ex +++ /dev/null @@ -1,112 +0,0 @@ -defmodule ElixirScript.Translator.Primitive do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Quote - alias ElixirScript.Translator.Utils - - def special_forms() do - JS.member_expression( - JS.identifier("Kernel"), - JS.identifier("SpecialForms") - ) - end - - def make_wildcard() do - JS.member_expression( - JS.identifier("fun"), - JS.identifier("wildcard") - ) - end - - def make_identifier({:__aliases__, _, aliases}) do - Utils.make_module_expression_tree(aliases, false, __ENV__) - end - - def make_identifier([ast]) do - JS.identifier(ast) - end - - def make_identifier(ast) do - JS.identifier(ast) - end - - def make_literal(ast) when is_number(ast) or is_binary(ast) or is_boolean(ast) or is_nil(ast) do - JS.literal(ast) - end - - def make_atom(ast) when is_atom(ast) do - JS.call_expression( - JS.member_expression( - special_forms(), - JS.identifier("atom") - ), - [JS.literal(ast)] - ) - end - - def make_list(ast, env) when is_list(ast) do - JS.call_expression( - JS.member_expression( - special_forms(), - JS.identifier("list") - ), - Enum.map(ast, fn(x) -> Translator.translate(x, env) end) - ) - end - - def make_list_quoted(opts, ast, env) when is_list(ast) do - JS.call_expression( - JS.member_expression( - special_forms(), - JS.identifier("list") - ), - Enum.map(ast, fn(x) -> Quote.make_quote(opts, x, env) end) - ) - end - - def make_list_no_translate(ast) when is_list(ast) do - JS.call_expression( - JS.member_expression( - special_forms(), - JS.identifier("list") - ), - ast - ) - end - - def make_tuple({ one, two }, env) do - make_tuple([one, two], env) - end - - def make_tuple(elements, env) do - JS.call_expression( - JS.member_expression( - special_forms(), - JS.identifier("tuple") - ), - Enum.map(elements, fn(x) -> Translator.translate(x, env) end) - ) - end - - def make_tuple_no_translate(elements) do - JS.call_expression( - JS.member_expression( - special_forms(), - JS.identifier("tuple") - ), - elements - ) - end - - def make_tuple_quoted(opts, elements, env) do - JS.call_expression( - JS.member_expression( - special_forms(), - JS.identifier("tuple") - ), - Enum.map(elements, fn(x) -> Quote.make_quote(opts, x, env) end) - ) - end - -end diff --git a/lib/elixir_script/translator/quote.ex b/lib/elixir_script/translator/quote.ex deleted file mode 100644 index 09bdf4c5..00000000 --- a/lib/elixir_script/translator/quote.ex +++ /dev/null @@ -1,79 +0,0 @@ -defmodule ElixirScript.Translator.Quote do - @moduledoc false - - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Primitive - - def make_quote(_opts, expr, env) when is_number(expr) or is_binary(expr) or is_boolean(expr) or is_nil(expr) or is_atom(expr) do - Translator.translate(expr, env) - end - - def make_quote(opts, expr, env) when is_list(expr) do - has_unquote_splicing = Enum.any?(expr, fn - ({:unquote_splicing, _, _}) -> true - (_) -> false - end) - - if(has_unquote_splicing) do - expr = Enum.map(expr, fn - ({:unquote_splicing, _, [param]}) -> - make_unquote_slicing(param, env) - (x) -> - Primitive.make_list_no_translate([make_quote(opts, x, env)]) - end - ) - - JS.call_expression( - JS.member_expression( - JS.identifier("Enum"), - JS.identifier("concat") - ), - expr - ) - else - Primitive.make_list_quoted(opts, expr, env) - end - end - - def make_quote(opts, {one, two}, env) do - Primitive.make_tuple_quoted(opts, [one, two], env) - end - - def make_quote([unquote: false] = opts, {:unquote, context, params}, env) do - Primitive.make_tuple_quoted(opts, [:unquote, context, params], env) - end - - def make_quote(_, {:alias!, _, [_alias]}, _) do - _alias - end - - def make_quote(_, {:unquote, _, [param]}, env) do - make_unquote(param, env) - end - - def make_quote(opts, {name, context, elements }, env) do - if is_in_bind_quoted(opts[:bind_quoted], name) do - Translator.translate({name, context, elements }, env) - else - Primitive.make_tuple_quoted(opts, [name, context, elements], env) - end - end - - def make_unquote(expr, env) do - Translator.translate(expr, env) - end - - def make_unquote_slicing(expr, env) do - Translator.translate(expr, env) - end - - defp is_in_bind_quoted(nil, _) do - false - end - - defp is_in_bind_quoted(binds, name) do - binds[name] != nil - end - -end \ No newline at end of file diff --git a/lib/elixir_script/translator/receive.ex b/lib/elixir_script/translator/receive.ex deleted file mode 100644 index a71142d3..00000000 --- a/lib/elixir_script/translator/receive.ex +++ /dev/null @@ -1,48 +0,0 @@ -defmodule ElixirScript.Translator.Receive do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Primitive - - def make_receive([do: clauses], env) do - JS.call_expression( - JS.member_expression( - Primitive.special_forms(), - JS.identifier("receive") - ), - [ - JS.function_expression( - [JS.identifier(:message)], - [], - JS.block_statement([ - JS.return_statement( - ElixirScript.Translator.Case.make_case({:__aliases__, [], [:message]}, clauses, env) - ) - ]) - ) - ] - ) - end - - def make_receive([do: clauses, after: [{:->, _, [[time], _body]}] = after_clause], env) do - JS.call_expression( - JS.member_expression( - Primitive.special_forms(), - JS.identifier("receive") - ), - [ - JS.function_expression( - [JS.identifier(:message)], - [], - JS.block_statement([ - JS.return_statement( - ElixirScript.Translator.Case.make_case({:__aliases__, [], [:message]}, clauses, env) - ) - ]) - ), - Translator.translate(time, env), - ElixirScript.Translator.Function.make_anonymous_function(after_clause, env) - ] - ) - end -end \ No newline at end of file diff --git a/lib/elixir_script/translator/struct.ex b/lib/elixir_script/translator/struct.ex deleted file mode 100644 index f3c6e8ed..00000000 --- a/lib/elixir_script/translator/struct.ex +++ /dev/null @@ -1,91 +0,0 @@ -defmodule ElixirScript.Translator.Struct do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - - def make_struct(module_name, data, env) do - JS.call_expression( - JS.member_expression( - JS.identifier(List.last(module_name)), - JS.identifier(:defstruct) - ), - Enum.map(data, fn({k, v})-> - JS.assignment_expression( - :=, - JS.identifier(k), - Translator.translate(v, env) - ) - end) - ) - end - - def make_defstruct(attributes, env) when length(attributes) == 1 do - attributes = Enum.flat_map(attributes, fn(x) -> x end) - - params = Enum.map(attributes, fn({x,_y}) -> x end) - defaults = Enum.map(attributes, fn({_x,y}) -> y end) - - do_make_defstruct(:defstruct, params, defaults, env) - end - - def make_defstruct(attributes, env) do - params = Enum.map(attributes, fn(x) -> x end) - defaults = [] - - do_make_defstruct(:defstruct, params, defaults, env) - end - - def make_defexception(attributes, env) when length(attributes) == 1 do - attributes = Enum.flat_map(attributes, fn(x) -> x end) - - params = Enum.map(attributes, fn(x) -> - - case x do - a when is_tuple(a) -> - elem(a, 0) - _ -> - x - end - - end) - - defaults = Enum.map(attributes, fn(x) -> - - case x do - a when is_tuple(a) -> - elem(a, 1) - _ -> - nil - end - - end) - - do_make_defstruct(:defexception, params, defaults, env) - end - - def make_defexceptions(attributes, env) do - params = Enum.map(attributes, fn(x) -> x end) - defaults = [] - - do_make_defstruct(:defexception, params, defaults, env) - end - - defp do_make_defstruct(name, params, defaults, env) do - JS.function_declaration( - JS.identifier(name), - Enum.map(params, &JS.identifier(&1)), - Enum.map(defaults, &Translator.translate(&1, env)), - JS.block_statement([ - JS.return_statement( - ElixirScript.Translator.Map.make_map( - JS.object_expression( - [ElixirScript.Translator.Map.make_property(Translator.translate(:__struct__, env), JS.identifier(:__MODULE__))] ++ - Enum.map(params, fn(x) -> ElixirScript.Translator.Map.make_property(Translator.translate(x, env), JS.identifier(x)) end) - ) - ) - ) - ]) - ) - end - -end diff --git a/lib/elixir_script/translator/try.ex b/lib/elixir_script/translator/try.ex deleted file mode 100644 index 16fb2740..00000000 --- a/lib/elixir_script/translator/try.ex +++ /dev/null @@ -1,102 +0,0 @@ -defmodule ElixirScript.Translator.Try do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator.Function - alias ElixirScript.Translator.Primitive - - def make_try(blocks, env) do - try_block = Dict.get(blocks, :do) - rescue_block = Dict.get(blocks, :rescue, nil) - catch_block = Dict.get(blocks, :catch, nil) - after_block = Dict.get(blocks, :after, nil) - else_block = Dict.get(blocks, :else, nil) - - translated_body = Function.prepare_function_body(try_block, env) |> JS.block_statement - try_block = JS.function_expression([], [], translated_body) - - if rescue_block do - rescue_block = process_rescue_block(rescue_block, env) - else - rescue_block = JS.identifier(:null) - end - - if catch_block do - catch_block = process_catch_block(catch_block, env) - else - catch_block = JS.identifier(:null) - end - - if after_block do - after_block = process_after_block(after_block, env) - else - after_block = JS.identifier(:null) - end - - if else_block do - else_block = Function.make_anonymous_function(else_block, env) - else - else_block = JS.identifier(:null) - end - - JS.call_expression( - JS.member_expression( - Primitive.special_forms(), - JS.identifier("_try") - ), - [ - try_block, - rescue_block, - catch_block, - else_block, - after_block - ] - ) - - end - - defp process_rescue_block(rescue_block, env) do - Enum.map(rescue_block, fn(x) -> - case x do - {:->, _, [[{value, _, module}], block]} when not is_list(module) -> - {:->, [], [[{value, [], convert_to_struct(module)}], block]} - {:->, _, [[{:in, meta, [value, error_names]}], block]} -> - error_names = Enum.map(error_names, fn(x) -> - convert_to_struct(x) - end) - - guards = {:in, meta, [value, error_names]} - - {:->, [], [ [{:when, [], [value | [guards]]}], block ]} - {:->, _, [error_names, block]} -> - Enum.map(error_names, fn(x) -> - {:->, [], [[convert_to_struct(x)], block]} - end) - end - end) - |> List.flatten - |> Function.make_anonymous_function(env) - end - - defp process_catch_block(catch_block, env) do - catch_block - |> Function.make_anonymous_function(env) - end - - defp process_after_block(after_block, env) do - translated_body = Function.prepare_function_body(after_block, env) |> JS.block_statement - JS.function_expression([], [], translated_body) - end - - defp convert_to_struct([module]) do - convert_to_struct(module) - end - - defp convert_to_struct(module) do - case module do - {:__aliases__, _, _} = alias_ast-> - {:%, [], [alias_ast, {:%{}, [], []}]} - ast -> - ast - end - end -end \ No newline at end of file diff --git a/lib/elixir_script/translator/utils.ex b/lib/elixir_script/translator/utils.ex deleted file mode 100644 index c1d085a4..00000000 --- a/lib/elixir_script/translator/utils.ex +++ /dev/null @@ -1,186 +0,0 @@ -defmodule ElixirScript.Translator.Utils do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - - def inflate_groups(body) do - Enum.map(body, fn(x) -> - case x do - %ElixirScript.Translator.Group{body: group_body} -> - group_body - %ESTree.BlockStatement{} -> - %ESTree.BlockStatement{ body: inflate_groups(x.body) } - %ESTree.IfStatement{} -> - %{x | consequent: inflate_groups(x.consequent), alternate: inflate_groups(x.alternate) } - _ -> - x - end - end) - |> List.flatten - end - - def make_throw_statement(error_name, message) do - JS.throw_statement( - JS.new_expression( - JS.identifier(error_name), - [ - JS.literal(message) - ] - ) - ) - end - - def make_module_expression_tree([module], computed, env) do - make_module_expression_tree(module, computed, env) - end - - def make_module_expression_tree(modules, computed, _) when is_list(modules) do - Enum.reduce(modules, nil, fn(x, ast) -> - case ast do - nil -> - JS.member_expression(JS.identifier(x), nil, computed) - %ESTree.MemberExpression{ property: nil } -> - %{ ast | property: JS.identifier(x) } - _ -> - JS.member_expression(ast, JS.identifier(x), computed) - end - end) - end - - def make_module_expression_tree(module, _computed, _) when is_binary(module) or is_atom(module) do - JS.identifier(module) - end - - def make_module_expression_tree(module, _computed, env) do - Translator.translate(module, env) - end - - def make_call_expression_with_ast_params(module_name, function_name, params, env) do - JS.call_expression( - make_member_expression(module_name, function_name, env), - params - ) - end - - def make_call_expression(module_name, function_name, params, env) do - JS.call_expression( - make_member_expression(module_name, function_name, env), - Enum.map(params, &Translator.translate(&1, env)) - ) - end - - def make_call_expression(function_name, params, env) when is_tuple(function_name) do - JS.call_expression( - Translator.translate(function_name, env), - Enum.map(params, &Translator.translate(&1, env)) - ) - end - - def make_call_expression(function_name, params, env) do - JS.call_expression( - JS.identifier(function_name), - Enum.map(params, &Translator.translate(&1, env)) - ) - end - - def make_member_expression(module_name, function_name, env, computed \\ false) do - case module_name do - modules when is_list(modules) and length(modules) > 1 -> - ast = make_module_expression_tree(modules, computed, env) - JS.member_expression( - ast, - build_function_name_ast(function_name), - computed - ) - modules when is_list(modules) and length(modules) == 1 -> - JS.member_expression( - JS.identifier(hd(modules)), - build_function_name_ast(function_name), - computed - ) - {{:., _, [_module_name, _function_name]}, _, _params } = ast -> - JS.member_expression( - Translator.translate(ast, env), - build_function_name_ast(function_name), - computed - ) - {{:., _, [{:__aliases__, _, _}]}, _, _} = ast -> - JS.member_expression( - Translator.translate(ast, env), - build_function_name_ast(function_name), - computed - ) - {:., _, _} = ast -> - JS.member_expression( - Translator.translate(ast, env), - build_function_name_ast(function_name), - computed - ) - _ -> - JS.member_expression( - JS.identifier(module_name), - build_function_name_ast(function_name), - computed - ) - end - end - - def build_function_name_ast(function_name) do - JS.identifier(function_name) - end - - def make_array_accessor_call(name, index) do - make_member_expression(name, index, true) - end - - def wrap_in_function_closure(body) do - the_body = case body do - b when is_list(b) -> - b - _ -> - [body] - end - - JS.call_expression( - JS.member_expression( - JS.function_expression([],[], - JS.block_statement(the_body) - ), - JS.identifier("call") - ), - [JS.identifier("this")] - ) - end - - def make_match(pattern, expr, env) do - JS.call_expression( - make_member_expression("Kernel", "match__qmark__", env), - [ - pattern, - expr - ] - ) - end - - def make_match(pattern, expr, guard, env) do - JS.call_expression( - make_member_expression("Kernel", "match__qmark__", env), - [ - pattern, - expr, - guard - ] - ) - end - - def filter_name(:in) do - "__in__" - end - - def filter_name(name) do - to_string(name) - |> String.replace("?", "__qmark__") - |> String.replace("!", "__emark__") - end - -end diff --git a/lib/elixir_script/unsupported_error.ex b/lib/elixir_script/unsupported_error.ex deleted file mode 100644 index cead0820..00000000 --- a/lib/elixir_script/unsupported_error.ex +++ /dev/null @@ -1,8 +0,0 @@ -defmodule ElixirScript.UnsupportedError do - defexception [:message] - - def exception(value) do - msg = "Currently unsupported #{inspect value}" - %ElixirScript.UnsupportedError{message: msg} - end -end \ No newline at end of file diff --git a/lib/elixir_script_test/test.ex b/lib/elixir_script_test/test.ex new file mode 100644 index 00000000..b531b700 --- /dev/null +++ b/lib/elixir_script_test/test.ex @@ -0,0 +1,111 @@ +defmodule ElixirScript.Test do + @moduledoc """ + Unit Testing Framework for ElixirScript. + + Requires node.js 8.3.0 and above + + Uses assertions from ExUnit as well as has a similar api to ExUnit with a few differences + + ## Example + + An basic setup of a test. Modified from ExUnit's example + + ```elixir + # File: assertion_test.exs + # 1) Create a new test module (test case) and use "ElixirScript.Test". + defmodule AssertionTest do + use ElixirScript.Test + + # 2) Use the "test" macro. + test "the truth" do + assert true + end + end + ``` + + To run the test above, use the `ElixirScript.Test.start/1` function, giving it the path to the test + ``` + ElixirScript.Test.start("assertion_test.exs") + ``` + + ## Integration with Mix + + To run tests using mix, run `mix elixirscript.test`. This will run all tests in the test_elixir_script directory. + + + ## Callbacks + + ElixirScript defines the following callbacks + + * setup/1 + * setup/2 + * setup_all/1 + * setup_all/2 + * teardown/1 + * teardown/2 + * teardown_all/1 + * teardown_all/2 + + The `setup` and `setup_all` callbacks work exactly as they would in ExUnit. Instead of having an `on_exit` callback, + ElixirScript.Test has `teardown` callbacks. `teardown` is called after each test and `teardown_all` after all tests + in the file have run. + + ```elixir + defmodule AssertionTest do + use ElixirScript.Test + + # run before test + setup do + admin = create_admin_function() + [admin: admin] + end + + test "the truth", %{admin: admin} do + assert admin.is_authenticated + end + + # run after test + teardown, %{admin: admin} do + destroy_admin_function(admin) + end + end + ``` + """ + + defmacro __using__(_opts) do + quote do + require ExUnit.Assertions + import ElixirScript.Test.{Callbacks, Assertions} + + def __elixirscript_test_module__, do: true + end + end + + @doc """ + Runs tests found in the given path. Accepts wildcards + """ + @spec start(binary(), map()) :: :ok | :error + def start(path, _opts \\ %{}) do + output = Path.join([System.tmp_dir!(), "elixirscript_tests"]) + File.mkdir_p!(output) + + ElixirScript.Compiler.compile(path, [output: output]) + + js_files = output + |> Path.expand + |> Path.join("Elixir.*.js") + |> Path.wildcard() + + exit_status = ElixirScript.Test.Runner.Node.run(js_files) + + # Delete directory at the end + File.rm_rf!(output) + + case exit_status do + 0 -> + :ok + _ -> + :error + end + end +end diff --git a/lib/elixir_script_test/test/assertion_error.ex b/lib/elixir_script_test/test/assertion_error.ex new file mode 100644 index 00000000..c591a95d --- /dev/null +++ b/lib/elixir_script_test/test/assertion_error.ex @@ -0,0 +1,14 @@ +defmodule ElixirScript.Test.AssertionError do + @moduledoc """ + Raised to signal an assertion error. + """ + + @no_value :ex_unit_no_meaningful_value + + defexception left: @no_value, + right: @no_value, + message: @no_value, + expr: @no_value, + file: @no_value, + line: @no_value +end diff --git a/lib/elixir_script_test/test/assertions.ex b/lib/elixir_script_test/test/assertions.ex new file mode 100644 index 00000000..7e8e53fe --- /dev/null +++ b/lib/elixir_script_test/test/assertions.ex @@ -0,0 +1,215 @@ +defmodule ElixirScript.Test.Assertions do + @moduledoc """ + Defines assertions for use in ElixirScript test. + These are a subset of [ExUnit.Assertions](https://hexdocs.pm/ex_unit/ExUnit.Assertions.html) + """ + + @doc false + def raise_elixir_script_assert(error, file, line) do + reraise(ElixirScript.Test.AssertionError, [ + left: error.left, + right: error.right, + message: error.message, + expr: error.expr, + file: file, + line: line + ], []) + end + + @doc """ + Asserts its argument is a truthy value + """ + defmacro assert(assertion) do + %{file: file, line: line} = __CALLER__ + + quote [file: file, line: line] do + try do + ExUnit.Assertions.assert(unquote(assertion)) + rescue + error in [ExUnit.AssertionError] -> + ElixirScript.Test.Assertions.raise_elixir_script_assert( + error, + unquote(file), + unquote(line) + ) + end + end + end + + @doc """ + Asserts `value` is `true`, displaying the given `message` otherwise. + """ + defmacro assert(value, message) do + %{file: file, line: line} = __CALLER__ + + quote [file: file, line: line] do + try do + ExUnit.Assertions.assert(unquote(value), unquote(message)) + rescue + error in [ExUnit.AssertionError] -> + ElixirScript.Test.Assertions.raise_elixir_script_assert( + error, + unquote(file), + unquote(line) + ) + end + end + end + + @doc """ + Asserts the `exception` is raised during `function` execution. + Returns the rescued exception, fails otherwise. + """ + defmacro assert_raise(exception, function) do + %{file: file, line: line} = __CALLER__ + + quote [file: file, line: line] do + try do + ExUnit.Assertions.assert(unquote(exception), unquote(function)) + rescue + error in [ExUnit.AssertionError] -> + ElixirScript.Test.Assertions.raise_elixir_script_assert( + error, + unquote(file), + unquote(line) + ) + end + end + end + + @doc """ + Asserts the `exception` is raised during `function` execution with + the expected `message`, which can be a `Regex` or an exact `String`. + Returns the rescued exception, fails otherwise. + """ + defmacro assert_raise(exception, message, function) do + %{file: file, line: line} = __CALLER__ + + quote [file: file, line: line] do + try do + ExUnit.Assertions.assert( + unquote(exception), + unquote(message), + unquote(function) + ) + rescue + error in [ExUnit.AssertionError] -> + ElixirScript.Test.Assertions.raise_elixir_script_assert( + error, + unquote(file), + unquote(line) + ) + end + end + end + + @doc """ + Asserts that `value1` and `value2` differ by no more than `delta` + """ + defmacro assert_in_delta(value1, value2, delta, message \\ nil) do + %{file: file, line: line} = __CALLER__ + + quote [file: file, line: line] do + try do + ExUnit.Assertions.assert_in_delta( + unquote(value1), + unquote(value2), + unquote(delta), + unquote(message) + ) + rescue + error in [ExUnit.AssertionError] -> + ElixirScript.Test.Assertions.raise_elixir_script_assert( + error, + unquote(file), + unquote(line) + ) + end + end + end + + @doc """ + A negative assertion, expects the expression to be `false` or `nil`. + """ + defmacro refute(assertion) do + %{file: file, line: line} = __CALLER__ + + quote [file: file, line: line] do + try do + ExUnit.Assertions.assert(unquote(assertion)) + rescue + error in [ExUnit.AssertionError] -> + ElixirScript.Test.Assertions.raise_elixir_script_assert( + error, + unquote(file), + unquote(line) + ) + end + end + end + + @doc """ + Asserts `value` is `nil` or `false` (that is, `value` is not truthy). + """ + defmacro refute(value, message) do + %{file: file, line: line} = __CALLER__ + + quote [file: file, line: line] do + try do + ExUnit.Assertions.assert(unquote(value), unquote(message)) + rescue + error in [ExUnit.AssertionError] -> + ElixirScript.Test.Assertions.raise_elixir_script_assert( + error, + unquote(file), + unquote(line) + ) + end + end + end + + @doc """ + Asserts that `value1` and `value2` are not within `delta` + """ + defmacro refute_in_delta(value1, value2, delta, message \\ nil) do + %{file: file, line: line} = __CALLER__ + + quote [file: file, line: line] do + try do + ExUnit.Assertions.refute_in_delta( + unquote(value1), + unquote(value2), + unquote(delta), + unquote(message) + ) + rescue + error in [ExUnit.AssertionError] -> + ElixirScript.Test.Assertions.raise_elixir_script_assert( + error, + unquote(file), + unquote(line) + ) + end + end + end + + @doc """ + Fails with a message. + """ + defmacro flunk(message \\ "Flunked!") do + %{file: file, line: line} = __CALLER__ + + quote [file: file, line: line] do + try do + ExUnit.Assertions.flunk(unquote(message)) + rescue + error in [ExUnit.AssertionError] -> + ElixirScript.Test.Assertions.raise_elixir_script_assert( + error, + unquote(file), + unquote(line) + ) + end + end + end +end diff --git a/lib/elixir_script_test/test/callbacks.ex b/lib/elixir_script_test/test/callbacks.ex new file mode 100644 index 00000000..f314c1c1 --- /dev/null +++ b/lib/elixir_script_test/test/callbacks.ex @@ -0,0 +1,117 @@ +defmodule ElixirScript.Test.Callbacks do + @moduledoc """ + Defines ElixirScript.Test callbacks + """ + + @doc """ + Called before all tests are run in a test file + """ + defmacro setup_all(context \\ quote(do: _), contents) do + do_setup(context, contents, :__elixirscript_test_setup_all) + end + + @doc """ + Called before each test is run in a test file + """ + defmacro setup(context \\ quote(do: _), contents) do + do_setup(context, contents, :__elixirscript_test_setup) + end + + defp do_setup(context, contents, name) do + contents = + case contents do + [do: block] -> + quote do + unquote(block) + end + _ -> + quote do + try(unquote(contents)) + end + end + + context = Macro.escape(context) + contents = Macro.escape(contents, unquote: true) + + quote bind_quoted: [context: context, contents: contents, name: name] do + def unquote(name)(unquote(context)) do + unquote(contents) + end + end + end + + @doc """ + Called after all tests are run in a test file + """ + defmacro teardown_all(context \\ quote(do: _), contents) do + do_teardown(context, contents, :__elixirscript_test_teardown_all) + end + + @doc """ + Called after each test is run in a test file + """ + defmacro teardown(context \\ quote(do: _), contents) do + do_teardown(context, contents, :__elixirscript_test_teardown) + end + + defp do_teardown(context, contents, name) do + contents = + case contents do + [do: block] -> + quote do + unquote(block) + :ok + end + _ -> + quote do + try(unquote(contents)) + :ok + end + end + + context = Macro.escape(context) + contents = Macro.escape(contents, unquote: true) + + quote bind_quoted: [context: context, contents: contents, name: name] do + def unquote(name)(unquote(context)) do + unquote(contents) + end + end + end + + @doc """ + Defines a test + """ + defmacro test(message, context \\ quote(do: _), contents) do + contents = + case contents do + [do: block] -> + quote do + unquote(block) + :ok + end + _ -> + quote do + try(unquote(contents)) + :ok + end + end + + context = Macro.escape(context) + contents = Macro.escape(contents, unquote: true) + name = message + |> String.replace(" ", "_") + |> String.replace(~r/[^A-Za-z0-9]/, "") + + name = String.to_atom("__elixirscript_test_case_#{name}") + + quote bind_quoted: [context: context, contents: contents, message: message, name: name] do + def unquote(name)() do + %{ + message: unquote(message), + test: fn(context) -> unquote(contents) end + } + end + end + end +end diff --git a/lib/elixir_script_test/test/runner.ex b/lib/elixir_script_test/test/runner.ex new file mode 100644 index 00000000..e746292b --- /dev/null +++ b/lib/elixir_script_test/test/runner.ex @@ -0,0 +1,14 @@ +defmodule ElixirScript.Test.Runner do + @moduledoc """ + Defines a behaviour for an ElixirScript Test runner + """ + + @doc """ + Callback for running the test runner. + Receives a list of JavaScript files from the + compiled Elixir code. Expects an exit status + representing the success or failure of the + tests + """ + @callback run([binary]) :: integer +end diff --git a/lib/elixir_script_test/test/runner/node.ex b/lib/elixir_script_test/test/runner/node.ex new file mode 100644 index 00000000..31e83667 --- /dev/null +++ b/lib/elixir_script_test/test/runner/node.ex @@ -0,0 +1,18 @@ +defmodule ElixirScript.Test.Runner.Node do + @moduledoc """ + Defines an ElixirScript Test runner using node + """ + @behaviour ElixirScript.Test.Runner + + def run(js_files) do + test_script_path = Path.join([:code.priv_dir(:elixir_script), "testrunner", "index.js"]) + test_script_path = [test_script_path] ++ js_files + {_, exit_status} = System.cmd( + "node", + test_script_path, + into: IO.stream(:stdio, :line) + ) + + exit_status + end +end diff --git a/lib/mix/tasks/compile.elixir_script.ex b/lib/mix/tasks/compile.elixir_script.ex new file mode 100644 index 00000000..b529251e --- /dev/null +++ b/lib/mix/tasks/compile.elixir_script.ex @@ -0,0 +1,130 @@ +defmodule Mix.Tasks.Compile.ElixirScript do + use Mix.Task.Compiler + alias ElixirScript.Manifest + alias ElixirScript.Compiler + + @recursive true + @manifest ".compile.elixir_script" + @manifest_vsn 1 + + @moduledoc """ + Mix compiler to allow mix to compile Elixirscript source files into JavaScript + + Looks for an `elixir_script` or `elixirscript` key in your mix project config + + def project do + [ + app: :my_app, + version: "0.1.0", + elixir: "~> 1.0", + deps: deps, + elixir_script: [ input: Example, output: "dest/js"], + compilers: Mix.compilers ++ [:elixir_script] + ] + end + + Available options are: + * `input`: The module or modules that are the entry to your application (required) + * `output`: The path of the generated JavaScript file. (defaults to `priv/elixir_script/build`) + + If path ends in `.js` then that will be the name of the file. If a directory is given, + file will be named `elixirscript.build.js` + + * `root`: Optional root for imports of FFI JavaScript modules. + Defaults to `.`. If using output directly in a browser, you may want to make it something like `/js` or some uri. + + The mix compiler will also compile any dependencies that have the elixirscript compiler in its mix compilers as well + """ + + @spec run([binary()]) :: + :ok | :noop | {:ok | :noop | :error, [Mix.Task.Compiler.Diagnostic.t()]} + def run(_) do + {input, opts} = get_compiler_params() + + diagnostics = + try do + result = Compiler.compile(input, opts) + Manifest.write_manifest(manifest(), result) + + result + |> Enum.map(fn {_module, info} -> + Map.get(info, :diagnostics, []) + end) + |> List.flatten() + |> Enum.map(fn x -> + %Mix.Task.Compiler.Diagnostic{ + compiler_name: "elixir_script", + file: x.file, + message: x.message, + position: x.position, + severity: x.severity + } + end) + rescue + x in [ElixirScript.CompileError] -> + [ + %Mix.Task.Compiler.Diagnostic{ + compiler_name: "elixir_script", + message: x.message, + severity: x.severity, + position: nil, + file: nil + } + ] + end + + case diagnostics do + [] -> :ok + x -> x + end + end + + def clean do + manifest() + |> Manifest.read_manifest() + |> Enum.each(fn {_module, info} -> + File.rm(info.js_path) + end) + + :ok + end + + def manifests, do: [manifest()] + defp manifest, do: Path.join(Mix.Project.manifest_path(), @manifest) + + @doc false + def get_compiler_params() do + elixirscript_config = get_elixirscript_config() + input = Keyword.fetch!(elixirscript_config, :input) + + opts = [ + output: Keyword.get(elixirscript_config, :output) + ] + + {input, opts} + end + + defp get_elixirscript_config() do + config = Mix.Project.config() + + exjs_config = + cond do + Keyword.has_key?(config, :elixir_script) -> + Keyword.get(config, :elixir_script, []) + + Keyword.has_key?(config, :elixirscript) -> + Keyword.get(config, :elixirscript, []) + + true -> + defaults() + end + + Keyword.merge(defaults(), exjs_config) + end + + defp defaults() do + [ + output: "priv/elixir_script/build" + ] + end +end diff --git a/lib/mix/tasks/elixirscript.ex b/lib/mix/tasks/elixirscript.ex new file mode 100644 index 00000000..7cfb3d33 --- /dev/null +++ b/lib/mix/tasks/elixirscript.ex @@ -0,0 +1,17 @@ +defmodule Mix.Tasks.Elixirscript do + @moduledoc """ + A command-line interface to the elixirscript compiler + """ + use Mix.Task + + @shortdoc "Translate Elixir to JavaScript" + + + def run(args) do + Mix.Task.run "app.start" + + args + |> ElixirScript.CLI.parse_args + |> ElixirScript.CLI.process + end +end diff --git a/lib/mix/tasks/elixirscript.test.ex b/lib/mix/tasks/elixirscript.test.ex new file mode 100644 index 00000000..e60899d6 --- /dev/null +++ b/lib/mix/tasks/elixirscript.test.ex @@ -0,0 +1,29 @@ +defmodule Mix.Tasks.Elixirscript.Test do + @moduledoc """ + Runs ElixirScript Tests + """ + use Mix.Task + + @shortdoc "Runs ElixirScript Tests" + @preferred_cli_env :test + + def run(_args) do + Mix.Task.run "app.start" + + path = Path.join([default_test_path(), "**", "*_test.exs"]) + case ElixirScript.Test.start(path) do + :error -> + System.at_exit(fn _ -> exit({:shutdown, 1}) end) + :ok -> + :ok + end + end + + defp default_test_path do + if File.dir?("test_elixir_script") do + "test_elixir_script" + else + "" + end + end +end diff --git a/lib/mix/tasks/ex2js.ex b/lib/mix/tasks/ex2js.ex deleted file mode 100644 index d96d35a2..00000000 --- a/lib/mix/tasks/ex2js.ex +++ /dev/null @@ -1,25 +0,0 @@ -defmodule Mix.Tasks.Ex2js do - @moduledoc """ - Translate Elixir code to javascript - - usage: mix ex2js [options] - path to elixir files or the elixir code string if the -ex flag is used - options: - -o --output [path] places output at the given path - -ex --elixir read input as elixir code string - -r --root [path] root path for standard libs - -h --help this message - """ - - use Mix.Task - - @shortdoc "Translate Elixir code to javascript" - - def run(args) do - Mix.Task.run "app.start" - - args - |> ElixirScript.CLI.parse_args - |> ElixirScript.CLI.process - end -end \ No newline at end of file diff --git a/mix.exs b/mix.exs index aef7120c..66184c98 100644 --- a/mix.exs +++ b/mix.exs @@ -4,104 +4,56 @@ defmodule ElixirScript.Mixfile do def project do [ app: :elixir_script, - version: "0.12.0", - elixir: "~> 1.0", - escript: escript_config, - deps: deps, - description: description, - package: package, - source_url: "https://github.com/bryanjos/elixirscript", - aliases: aliases, + version: "0.32.1", + elixir: "~> 1.6", + elixirc_paths: elixirc_paths(Mix.env()), + deps: deps(), + description: description(), + package: package(), + source_url: "https://github.com/elixirscript/elixirscript", test_coverage: [tool: ExCoveralls], - preferred_cli_env: [coveralls: :test] + docs: [ + main: "ElixirScript", + extras: ["JavaScriptInterop.md", "CompilerInternals.md"] + ] ] end def application do [ - applications: [:logger, :inflex, :estree] + extra_applications: [:logger] ] end defp deps do [ - {:inflex, "~> 1.4" }, - {:estree, "~> 2.0"}, - {:shouldi, only: :test }, - {:earmark, "~> 0.1", only: :dev }, - {:ex_doc, "~> 0.9", only: :dev }, - {:benchfella, "~> 0.2", only: :test}, - {:excoveralls, "~> 0.3", only: :test} + {:estree, "~> 2.6"}, + {:ex_doc, "~> 0.16", only: :dev}, + {:excoveralls, "~> 0.7", only: :test}, + {:credo, "~> 1.0", only: [:dev, :test]}, + {:stream_data, "~> 0.3", only: :test}, + {:poison, "~> 4.0", only: :test} ] end - defp escript_config do - [main_module: ElixirScript.CLI, name: "ex2js"] - end + defp elixirc_paths(:test), do: ["lib", "test/support"] + defp elixirc_paths(_), do: ["lib"] defp description do """ - converts Elixir to JavaScript + ElixirScript: compiles Elixir code to JavaScript """ end defp package do [ - files: ["lib", "priv/javascript/dist", "mix.exs", "README*", "readme*", "LICENSE*", "license*", "CHANGELOG*"], - contributors: ["Bryan Joseph"], + files: ["lib", "priv/**/*.*", "mix.exs", "README.md", "LICENSE", "CHANGELOG.md"], + maintainers: ["Bryan Joseph"], licenses: ["MIT"], - links: %{ - "GitHub" => "https://github.com/bryanjos/elixirscript" + links: %{ + "GitHub" => "https://github.com/elixirscript/elixirscript" }, build_tools: ["mix"] ] end - - defp aliases do - [dist: &dist/1, - install: &install/1] - end - - def dist(_) do - Mix.Task.run "app.start" - - dist_folder = "dist" - folder_name = "#{dist_folder}/ex2js" - archive_file_name = "#{dist_folder}/ex2js.tar.gz" - - File.mkdir_p("priv/javascript/dist") - - Mix.Tasks.Escript.Build.run([]) - - if File.exists?(dist_folder) do - File.rm_rf(dist_folder) - end - - { _ , _ } = System.cmd("node", ["node_modules/gulp/bin/gulp.js", "dist_build"]) - { elixir_js, _ } = System.cmd("node", ["node_modules/rollup/bin/rollup", "./priv/javascript/dist_build/elixir.js"]) - File.write!("priv/javascript/dist/elixir.js", elixir_js) - { _ , _ } = System.cmd("node", ["node_modules/gulp/bin/gulp.js", "dist_add_source_map"]) - - File.mkdir_p(folder_name <> "/bin") - File.cp!("ex2js", "#{folder_name}/bin/ex2js") - File.cp_r!("priv/javascript/dist", "#{folder_name}/dist") - File.cp_r!("LICENSE", "#{folder_name}/LICENSE") - - System.cmd("tar", ["czf", archive_file_name, folder_name]) - - File.rm_rf(folder_name) - end - - def install(_) do - Mix.Task.run "app.start" - - System.cmd("tar", ["-zxvf", "dist/ex2js.tar.gz"]) - - File.rm_rf!("/usr/local/ex2js") - - System.cmd("mv", ["dist/ex2js", "/usr/local/ex2js"]) - - IO.puts("installed at /usr/local/ex2js") - end - end diff --git a/mix.lock b/mix.lock index 48ef915a..3e10cac4 100644 --- a/mix.lock +++ b/mix.lock @@ -1,14 +1,24 @@ -%{"benchfella": {:hex, :benchfella, "0.2.1"}, - "dogma": {:hex, :dogma, "0.0.7"}, - "earmark": {:hex, :earmark, "0.1.17"}, - "estree": {:hex, :estree, "2.0.1"}, - "ex_doc": {:hex, :ex_doc, "0.9.0"}, - "excoveralls": {:hex, :excoveralls, "0.3.11"}, - "exjsx": {:hex, :exjsx, "3.2.0"}, - "hackney": {:hex, :hackney, "1.3.2"}, - "idna": {:hex, :idna, "1.0.2"}, - "inflex": {:hex, :inflex, "1.5.0"}, - "jsx": {:hex, :jsx, "2.6.2"}, - "poison": {:hex, :poison, "1.5.0"}, - "shouldi": {:hex, :shouldi, "0.2.3"}, - "ssl_verify_hostname": {:hex, :ssl_verify_hostname, "1.0.5"}} +%{ + "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"}, + "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"}, + "credo": {:hex, :credo, "1.1.3", "bf31887b8914a4b7e1810ae2b5aab7c657698abbf4cca6a2335a094d57995168", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, + "earmark": {:hex, :earmark, "1.3.5", "0db71c8290b5bc81cb0101a2a507a76dca659513984d683119ee722828b424f6", [:mix], [], "hexpm"}, + "estree": {:hex, :estree, "2.7.0", "32cb6ff05c85a93bf6c646b3b184e131f699fc1e7157d2b5be148c17f43ae566", [:mix], [], "hexpm"}, + "ex_doc": {:hex, :ex_doc, "0.21.1", "5ac36660846967cd869255f4426467a11672fec3d8db602c429425ce5b613b90", [:mix], [{:earmark, "~> 1.3", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, + "excoveralls": {:hex, :excoveralls, "0.11.2", "0c6f2c8db7683b0caa9d490fb8125709c54580b4255ffa7ad35f3264b075a643", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm"}, + "exjsx": {:hex, :exjsx, "4.0.0", "60548841e0212df401e38e63c0078ec57b33e7ea49b032c796ccad8cde794b5c", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm"}, + "hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, + "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"}, + "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, + "jsx": {:hex, :jsx, "2.8.3", "a05252d381885240744d955fbe3cf810504eb2567164824e19303ea59eef62cf", [:mix, :rebar3], [], "hexpm"}, + "makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, + "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"}, + "nimble_parsec": {:hex, :nimble_parsec, "0.5.1", "c90796ecee0289dbb5ad16d3ad06f957b0cd1199769641c961cfe0b97db190e0", [:mix], [], "hexpm"}, + "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"}, + "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"}, + "stream_data": {:hex, :stream_data, "0.4.3", "62aafd870caff0849a5057a7ec270fad0eb86889f4d433b937d996de99e3db25", [:mix], [], "hexpm"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"}, +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..58a35a9a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6511 @@ +{ + "name": "elixirscript", + "version": "0.26.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@ava/babel-plugin-throws-helper": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@ava/babel-plugin-throws-helper/-/babel-plugin-throws-helper-4.0.0.tgz", + "integrity": "sha512-3diBLIVBPPh3j4+hb5lo0I1D+S/O/VDJPI4Y502apBxmwEqjyXG4gTSPFUlm41sSZeZzMarT/Gzovw9kV7An0w==", + "dev": true + }, + "@ava/babel-preset-stage-4": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@ava/babel-preset-stage-4/-/babel-preset-stage-4-4.0.0.tgz", + "integrity": "sha512-lZEV1ZANzfzSYBU6WHSErsy7jLPbD1iIgAboASPMcKo7woVni5/5IKWeT0RxC8rY802MFktur3OKEw2JY1Tv2w==", + "dev": true, + "requires": { + "@babel/plugin-proposal-async-generator-functions": "^7.2.0", + "@babel/plugin-proposal-dynamic-import": "^7.5.0", + "@babel/plugin-proposal-optional-catch-binding": "^7.2.0", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/plugin-transform-modules-commonjs": "^7.5.0" + } + }, + "@ava/babel-preset-transform-test-files": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@ava/babel-preset-transform-test-files/-/babel-preset-transform-test-files-6.0.0.tgz", + "integrity": "sha512-8eKhFzZp7Qcq1VLfoC75ggGT8nQs9q8fIxltU47yCB7Wi7Y8Qf6oqY1Bm0z04fIec24vEgr0ENhDHEOUGVDqnA==", + "dev": true, + "requires": { + "@ava/babel-plugin-throws-helper": "^4.0.0", + "babel-plugin-espower": "^3.0.1" + } + }, + "@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/core": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.5.5.tgz", + "integrity": "sha512-i4qoSr2KTtce0DmkuuQBV4AuQgGPUcPXMr9L5MyYAtk06z068lQ10a4O009fe5OB/DfNV+h+qqT7ddNV8UnRjg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.5.5", + "@babel/helpers": "^7.5.5", + "@babel/parser": "^7.5.5", + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.5.5", + "@babel/types": "^7.5.5", + "convert-source-map": "^1.1.0", + "debug": "^4.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "json5": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", + "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.5.5.tgz", + "integrity": "sha512-ETI/4vyTSxTzGnU2c49XHv2zhExkv9JHLTwDAFz85kmcwuShvYG2H08FwgIguQf4JC75CBnXAUM5PqeF4fj0nQ==", + "dev": true, + "requires": { + "@babel/types": "^7.5.5", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + }, + "dependencies": { + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz", + "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-module-imports": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", + "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-module-transforms": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.5.5.tgz", + "integrity": "sha512-jBeCvETKuJqeiaCdyaheF40aXnnU1+wkSiUs/IQg3tB85up1LyL8x77ClY8qJpuRJUcXQo+ZtdNESmZl4j56Pw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/helper-simple-access": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/template": "^7.4.4", + "@babel/types": "^7.5.5", + "lodash": "^4.17.13" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", + "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.5.5.tgz", + "integrity": "sha512-CkCYQLkfkiugbRDO8eZn6lRuR8kzZoGXCg3149iTk5se7g6qykSpy3+hELSwquhu+TgHn8nkLiBwHvNX8Hofcw==", + "dev": true, + "requires": { + "lodash": "^4.17.13" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz", + "integrity": "sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-wrap-function": "^7.1.0", + "@babel/template": "^7.1.0", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-simple-access": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz", + "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==", + "dev": true, + "requires": { + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", + "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", + "dev": true, + "requires": { + "@babel/types": "^7.4.4" + } + }, + "@babel/helper-wrap-function": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz", + "integrity": "sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.1.0", + "@babel/template": "^7.1.0", + "@babel/traverse": "^7.1.0", + "@babel/types": "^7.2.0" + } + }, + "@babel/helpers": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.5.5.tgz", + "integrity": "sha512-nRq2BUhxZFnfEn/ciJuhklHvFOqjJUD5wpx+1bxUF2axL9C+v4DE/dmp5sT2dKnpOs4orZWzpAZqlCy8QqE/7g==", + "dev": true, + "requires": { + "@babel/template": "^7.4.4", + "@babel/traverse": "^7.5.5", + "@babel/types": "^7.5.5" + } + }, + "@babel/highlight": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + } + } + }, + "@babel/parser": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.5.5.tgz", + "integrity": "sha512-E5BN68cqR7dhKan1SfqgPGhQ178bkVKpXTPEXnFJBrEt8/DKRZlybmy+IgYLTeN7tp1R5Ccmbm2rBk17sHYU3g==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz", + "integrity": "sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-remap-async-to-generator": "^7.1.0", + "@babel/plugin-syntax-async-generators": "^7.2.0" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.5.0.tgz", + "integrity": "sha512-x/iMjggsKTFHYC6g11PL7Qy58IK8H5zqfm9e6hu4z1iH2IRyAp9u9dL80zA6R76yFovETFLKz2VJIC2iIPBuFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-dynamic-import": "^7.2.0" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz", + "integrity": "sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.2.0" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz", + "integrity": "sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.2.0.tgz", + "integrity": "sha512-mVxuJ0YroI/h/tbFTPGZR8cv6ai+STMKNBq0f8hFxsxWjl94qqhsb+wXbpNMDPU3cfR1TIsVFzU3nXyZMqyK4w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz", + "integrity": "sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.4.4.tgz", + "integrity": "sha512-P05YEhRc2h53lZDjRPk/OektxCVevFzZs2Gfjd545Wde3k+yFDbXORgl2e0xpbq8mLcKJ7Idss4fAg0zORN/zg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-regex": "^7.4.4", + "regexpu-core": "^4.5.4" + }, + "dependencies": { + "regexpu-core": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.5.tgz", + "integrity": "sha512-FpI67+ky9J+cDizQUJlIlNZFKual/lUkFr1AG6zOCpwZ9cLrg8UUVakyUQJD7fCDIe9Z2nwTQJNPyonatNmDFQ==", + "dev": true, + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.1.0", + "regjsgen": "^0.5.0", + "regjsparser": "^0.6.0", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.1.0" + } + }, + "regjsgen": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz", + "integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==", + "dev": true + }, + "regjsparser": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz", + "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + } + } + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.5.0.tgz", + "integrity": "sha512-xmHq0B+ytyrWJvQTc5OWAC4ii6Dhr0s22STOoydokG51JjWhyYo5mRPXoi+ZmtHQhZZwuXNN+GG5jy5UZZJxIQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.4.4", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/helper-simple-access": "^7.1.0", + "babel-plugin-dynamic-import-node": "^2.3.0" + } + }, + "@babel/template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.4.tgz", + "integrity": "sha512-CiGzLN9KgAvgZsnivND7rkA+AeJ9JB0ciPOD4U59GKbQP2iQl+olF1l76kJOupqidozfZ32ghwBEJDhnk9MEcw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.4.4", + "@babel/types": "^7.4.4" + } + }, + "@babel/traverse": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.5.5.tgz", + "integrity": "sha512-MqB0782whsfffYfSjH4TM+LMjrJnhCNEDMDIjeTpl+ASaUvxcjoiVCo/sM1GhS1pHOXYfWVCYneLjMckuUxDaQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.5.5", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/parser": "^7.5.5", + "@babel/types": "^7.5.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.5.5.tgz", + "integrity": "sha512-s63F9nJioLqOlW3UkyMd+BYhXt44YuaFm/VV0VwuteqjYwRrObkU7ra9pY4wAJR3oXi8hJrMcrcJdO/HH33vtw==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + }, + "dependencies": { + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, + "@comandeer/babel-plugin-banner": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@comandeer/babel-plugin-banner/-/babel-plugin-banner-5.0.0.tgz", + "integrity": "sha512-sR9Go0U6puXoXyW9UgIiIQhRcJ8jVOvGl4BptUiXAtheMs72WcakZ1udh6J0ZOivr3o8jAM+MTCHLP8FZMbVpQ==", + "dev": true + }, + "@concordance/react": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@concordance/react/-/react-2.0.0.tgz", + "integrity": "sha512-huLSkUuM2/P+U0uy2WwlKuixMsTODD8p4JVQBI4VKeopkiN0C7M3N9XYVawb4M+4spN5RrO/eLhk7KoQX6nsfA==", + "dev": true, + "requires": { + "arrify": "^1.0.1" + }, + "dependencies": { + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + } + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.1.tgz", + "integrity": "sha512-NT/skIZjgotDSiXs0WqYhgcuBKhUMgfekCmCGtkUAiLqZdOnrdjmZr9wRl3ll64J9NF79uZ4fk16Dx0yMc/Xbg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.1", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.1.tgz", + "integrity": "sha512-+RqhBlLn6YRBGOIoVYthsG0J9dfpO79eJyN7BYBkZJtfqrBwf2KK+rD/M/yjZR6WBmIhAgOV7S60eCgaSWtbFw==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.2.tgz", + "integrity": "sha512-J/DR3+W12uCzAJkw7niXDcqcKBg6+5G5Q/ZpThpGNzAUz70eOR6RV4XnnSN01qHZiVl0eavoxJsBypQoKsV2QQ==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.1", + "fastq": "^1.6.0" + } + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true + }, + "@std/esm": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/@std/esm/-/esm-0.8.3.tgz", + "integrity": "sha512-JZigVxIuy2mCkBZWxwS3Wu9eL0lJzR176Rmzb6hLjGIg3yBVBEK4XhEiFX6k5lXDY+e69XAvEVbp59PfUpfpBA==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" + }, + "@types/events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", + "dev": true + }, + "@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/node": { + "version": "12.7.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.2.tgz", + "integrity": "sha512-dyYO+f6ihZEtNPDcWNR1fkoTDf3zAK3lAABDze3mz6POyIercH0lEUawUFXlG8xaQZmm1yEBON/4TsYv/laDYg==", + "dev": true + }, + "@types/resolve": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", + "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "acorn": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.0.0.tgz", + "integrity": "sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ==", + "dev": true + }, + "acorn-jsx": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", + "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", + "dev": true + }, + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "dev": true, + "requires": { + "string-width": "^3.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "ansi-escapes": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.2.1.tgz", + "integrity": "sha512-Cg3ymMAdN10wOk/VYfLV7KCQyv7EDirJ64500sU7n9UlmioEtDuU5Gd+hj73hXSU/ex7tHJSssmyftDdkMLO8Q==", + "dev": true, + "requires": { + "type-fest": "^0.5.2" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.0.0.tgz", + "integrity": "sha512-8zjUtFJ3db/QoPXuuEMloS2AUf79/yeyttJ7Abr3hteopJu9HK8vsgGviGUMq+zyA6cZZO6gAyZoMTF6TgaEjA==", + "dev": true, + "requires": { + "color-convert": "^2.0.0" + }, + "dependencies": { + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "anymatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.0.3.tgz", + "integrity": "sha512-c6IvoeBECQlMVuYUjSwimnhmztImpErfxJzWZhIQinIvQWoGOnB0dLIgifbPHQt5heS6mNlaZG16f06H3C8t1g==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "append-transform": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", + "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "dev": true, + "requires": { + "default-require-extensions": "^2.0.0" + } + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-includes": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.7.0" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array-uniq": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-2.1.0.tgz", + "integrity": "sha512-bdHxtev7FN6+MXI1YFW0Q8mQ8dTJc2S8AMfju+ZR77pbg2yAdVyDlwkaUI7Har0LyOMRFPHrJ9lYdyjZZswdlQ==", + "dev": true + }, + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "ava": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ava/-/ava-2.3.0.tgz", + "integrity": "sha512-4VaaSnl13vpTZmqW3aMqioSolT0/ozRkjQxTLi3p8wtyRONuX/uLKL3uF0j50w2BNRoLsJqztnkX2h8xeVp2lg==", + "dev": true, + "requires": { + "@ava/babel-preset-stage-4": "^4.0.0", + "@ava/babel-preset-transform-test-files": "^6.0.0", + "@babel/core": "^7.5.5", + "@babel/generator": "^7.5.5", + "@concordance/react": "^2.0.0", + "ansi-escapes": "^4.2.1", + "ansi-styles": "^4.0.0", + "arr-flatten": "^1.1.0", + "array-union": "^2.1.0", + "array-uniq": "^2.1.0", + "arrify": "^2.0.1", + "bluebird": "^3.5.5", + "chalk": "^2.4.2", + "chokidar": "^3.0.2", + "chunkd": "^1.0.0", + "ci-parallel-vars": "^1.0.0", + "clean-stack": "^2.2.0", + "clean-yaml-object": "^0.1.0", + "cli-cursor": "^3.1.0", + "cli-truncate": "^2.0.0", + "code-excerpt": "^2.1.1", + "common-path-prefix": "^1.0.0", + "concordance": "^4.0.0", + "convert-source-map": "^1.6.0", + "currently-unhandled": "^0.4.1", + "debug": "^4.1.1", + "del": "^4.1.1", + "dot-prop": "^5.1.0", + "emittery": "^0.4.1", + "empower-core": "^1.2.0", + "equal-length": "^1.0.0", + "escape-string-regexp": "^2.0.0", + "esm": "^3.2.25", + "figures": "^3.0.0", + "find-up": "^4.1.0", + "get-port": "^5.0.0", + "globby": "^10.0.1", + "ignore-by-default": "^1.0.0", + "import-local": "^3.0.2", + "indent-string": "^4.0.0", + "is-ci": "^2.0.0", + "is-error": "^2.2.2", + "is-observable": "^2.0.0", + "is-plain-object": "^3.0.0", + "is-promise": "^2.1.0", + "lodash": "^4.17.15", + "loud-rejection": "^2.1.0", + "make-dir": "^3.0.0", + "matcher": "^2.0.0", + "md5-hex": "^3.0.1", + "meow": "^5.0.0", + "micromatch": "^4.0.2", + "ms": "^2.1.2", + "observable-to-promise": "^1.0.0", + "ora": "^3.4.0", + "package-hash": "^4.0.0", + "pkg-conf": "^3.1.0", + "plur": "^3.1.1", + "pretty-ms": "^5.0.0", + "require-precompiled": "^0.1.0", + "resolve-cwd": "^3.0.0", + "slash": "^3.0.0", + "source-map-support": "^0.5.13", + "stack-utils": "^1.0.2", + "strip-ansi": "^5.2.0", + "strip-bom-buf": "^2.0.0", + "supertap": "^1.0.0", + "supports-color": "^7.0.0", + "trim-off-newlines": "^1.0.1", + "trim-right": "^1.0.1", + "unique-temp-dir": "^1.0.0", + "update-notifier": "^3.0.1", + "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "dev": true, + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + }, + "dependencies": { + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", + "dev": true + } + } + }, + "babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", + "dev": true, + "requires": { + "babel-helper-explode-assignable-expression": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-evaluate-path": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-helper-evaluate-path/-/babel-helper-evaluate-path-0.5.0.tgz", + "integrity": "sha512-mUh0UhS607bGh5wUMAQfOpt2JX2ThXMtppHRdRU1kL7ZLRWIXxoV2UIV1r2cAeeNeU1M5SB5/RSUgUxrK8yOkA==", + "dev": true + }, + "babel-helper-explode-assignable-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-flip-expressions": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-flip-expressions/-/babel-helper-flip-expressions-0.4.3.tgz", + "integrity": "sha1-NpZzahKKwYvCUlS19AoizrPB0/0=", + "dev": true + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "dev": true, + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-is-nodes-equiv": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/babel-helper-is-nodes-equiv/-/babel-helper-is-nodes-equiv-0.0.1.tgz", + "integrity": "sha1-NOmzALFHnd2Y7HfqC76TQt/jloQ=", + "dev": true + }, + "babel-helper-is-void-0": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-is-void-0/-/babel-helper-is-void-0-0.4.3.tgz", + "integrity": "sha1-fZwBtFYee5Xb2g9u7kj1tg5nMT4=", + "dev": true + }, + "babel-helper-mark-eval-scopes": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-mark-eval-scopes/-/babel-helper-mark-eval-scopes-0.4.3.tgz", + "integrity": "sha1-0kSjvvmESHJgP/tG4izorN9VFWI=", + "dev": true + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-remap-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", + "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-remove-or-void": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.4.3.tgz", + "integrity": "sha1-pPA7QAd6D/6I5F0HAQ3uJB/1rmA=", + "dev": true + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "dev": true, + "requires": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-to-multiple-sequence-expressions": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.5.0.tgz", + "integrity": "sha512-m2CvfDW4+1qfDdsrtf4dwOslQC3yhbgyBFptncp4wvtdrDHqueW7slsYv4gArie056phvQFhT2nRcGS4bnm6mA==", + "dev": true + }, + "babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", + "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-espower": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-espower/-/babel-plugin-espower-3.0.1.tgz", + "integrity": "sha512-Ms49U7VIAtQ/TtcqRbD6UBmJBUCSxiC3+zPc+eGqxKUIFO1lTshyEDRUjhoAbd2rWfwYf3cZ62oXozrd8W6J0A==", + "dev": true, + "requires": { + "@babel/generator": "^7.0.0", + "@babel/parser": "^7.0.0", + "call-matcher": "^1.0.0", + "core-js": "^2.0.0", + "espower-location-detector": "^1.0.0", + "espurify": "^1.6.0", + "estraverse": "^4.1.1" + } + }, + "babel-plugin-minify-builtins": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-builtins/-/babel-plugin-minify-builtins-0.5.0.tgz", + "integrity": "sha512-wpqbN7Ov5hsNwGdzuzvFcjgRlzbIeVv1gMIlICbPj0xkexnfoIDe7q+AZHMkQmAE/F9R5jkrB6TLfTegImlXag==", + "dev": true + }, + "babel-plugin-minify-constant-folding": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-constant-folding/-/babel-plugin-minify-constant-folding-0.5.0.tgz", + "integrity": "sha512-Vj97CTn/lE9hR1D+jKUeHfNy+m1baNiJ1wJvoGyOBUx7F7kJqDZxr9nCHjO/Ad+irbR3HzR6jABpSSA29QsrXQ==", + "dev": true, + "requires": { + "babel-helper-evaluate-path": "^0.5.0" + } + }, + "babel-plugin-minify-dead-code-elimination": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-dead-code-elimination/-/babel-plugin-minify-dead-code-elimination-0.5.1.tgz", + "integrity": "sha512-x8OJOZIrRmQBcSqxBcLbMIK8uPmTvNWPXH2bh5MDCW1latEqYiRMuUkPImKcfpo59pTUB2FT7HfcgtG8ZlR5Qg==", + "dev": true, + "requires": { + "babel-helper-evaluate-path": "^0.5.0", + "babel-helper-mark-eval-scopes": "^0.4.3", + "babel-helper-remove-or-void": "^0.4.3", + "lodash": "^4.17.11" + } + }, + "babel-plugin-minify-flip-comparisons": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-flip-comparisons/-/babel-plugin-minify-flip-comparisons-0.4.3.tgz", + "integrity": "sha1-AMqHDLjxO0XAOLPB68DyJyk8llo=", + "dev": true, + "requires": { + "babel-helper-is-void-0": "^0.4.3" + } + }, + "babel-plugin-minify-guarded-expressions": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-guarded-expressions/-/babel-plugin-minify-guarded-expressions-0.4.4.tgz", + "integrity": "sha512-RMv0tM72YuPPfLT9QLr3ix9nwUIq+sHT6z8Iu3sLbqldzC1Dls8DPCywzUIzkTx9Zh1hWX4q/m9BPoPed9GOfA==", + "dev": true, + "requires": { + "babel-helper-evaluate-path": "^0.5.0", + "babel-helper-flip-expressions": "^0.4.3" + } + }, + "babel-plugin-minify-infinity": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-infinity/-/babel-plugin-minify-infinity-0.4.3.tgz", + "integrity": "sha1-37h2obCKBldjhO8/kuZTumB7Oco=", + "dev": true + }, + "babel-plugin-minify-mangle-names": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-mangle-names/-/babel-plugin-minify-mangle-names-0.5.0.tgz", + "integrity": "sha512-3jdNv6hCAw6fsX1p2wBGPfWuK69sfOjfd3zjUXkbq8McbohWy23tpXfy5RnToYWggvqzuMOwlId1PhyHOfgnGw==", + "dev": true, + "requires": { + "babel-helper-mark-eval-scopes": "^0.4.3" + } + }, + "babel-plugin-minify-numeric-literals": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-numeric-literals/-/babel-plugin-minify-numeric-literals-0.4.3.tgz", + "integrity": "sha1-jk/VYcefeAEob/YOjF/Z3u6TwLw=", + "dev": true + }, + "babel-plugin-minify-replace": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-replace/-/babel-plugin-minify-replace-0.5.0.tgz", + "integrity": "sha512-aXZiaqWDNUbyNNNpWs/8NyST+oU7QTpK7J9zFEFSA0eOmtUNMU3fczlTTTlnCxHmq/jYNFEmkkSG3DDBtW3Y4Q==", + "dev": true + }, + "babel-plugin-minify-simplify": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-simplify/-/babel-plugin-minify-simplify-0.5.1.tgz", + "integrity": "sha512-OSYDSnoCxP2cYDMk9gxNAed6uJDiDz65zgL6h8d3tm8qXIagWGMLWhqysT6DY3Vs7Fgq7YUDcjOomhVUb+xX6A==", + "dev": true, + "requires": { + "babel-helper-evaluate-path": "^0.5.0", + "babel-helper-flip-expressions": "^0.4.3", + "babel-helper-is-nodes-equiv": "^0.0.1", + "babel-helper-to-multiple-sequence-expressions": "^0.5.0" + } + }, + "babel-plugin-minify-type-constructors": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-minify-type-constructors/-/babel-plugin-minify-type-constructors-0.4.3.tgz", + "integrity": "sha1-G8bxW4f3qxCF1CszC3F2V6IVZQA=", + "dev": true, + "requires": { + "babel-helper-is-void-0": "^0.4.3" + } + }, + "babel-plugin-syntax-async-functions": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", + "dev": true + }, + "babel-plugin-syntax-exponentiation-operator": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", + "dev": true + }, + "babel-plugin-syntax-trailing-function-commas": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", + "dev": true + }, + "babel-plugin-transform-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-functions": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "dev": true, + "requires": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "dev": true, + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "dev": true, + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "dev": true, + "requires": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "dev": true, + "requires": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "dev": true, + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + } + }, + "babel-plugin-transform-exponentiation-operator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", + "dev": true, + "requires": { + "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", + "babel-plugin-syntax-exponentiation-operator": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-inline-consecutive-adds": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.4.3.tgz", + "integrity": "sha1-Mj1Ho+pjqDp6w8gRro5pQfrysNE=", + "dev": true + }, + "babel-plugin-transform-member-expression-literals": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-member-expression-literals/-/babel-plugin-transform-member-expression-literals-6.9.4.tgz", + "integrity": "sha1-NwOcmgwzE6OUlfqsL/OmtbnQOL8=", + "dev": true + }, + "babel-plugin-transform-merge-sibling-variables": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-merge-sibling-variables/-/babel-plugin-transform-merge-sibling-variables-6.9.4.tgz", + "integrity": "sha1-hbQi/DN3tEnJ0c3kQIcgNTJAHa4=", + "dev": true + }, + "babel-plugin-transform-minify-booleans": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-minify-booleans/-/babel-plugin-transform-minify-booleans-6.9.4.tgz", + "integrity": "sha1-rLs+VqNVXdI5KOS1gtKFFi3SsZg=", + "dev": true + }, + "babel-plugin-transform-property-literals": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-property-literals/-/babel-plugin-transform-property-literals-6.9.4.tgz", + "integrity": "sha1-mMHSHiVXNlc/k+zlRFn2ziSYXTk=", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "dev": true, + "requires": { + "regenerator-transform": "^0.10.0" + } + }, + "babel-plugin-transform-regexp-constructors": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regexp-constructors/-/babel-plugin-transform-regexp-constructors-0.4.3.tgz", + "integrity": "sha1-WLd3W2OvzzMyj66aX4j71PsLSWU=", + "dev": true + }, + "babel-plugin-transform-remove-console": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz", + "integrity": "sha1-uYA2DAZzhOJLNXpYjYB9PINSd4A=", + "dev": true + }, + "babel-plugin-transform-remove-debugger": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-debugger/-/babel-plugin-transform-remove-debugger-6.9.4.tgz", + "integrity": "sha1-QrcnYxyXl44estGZp67IShgznvI=", + "dev": true + }, + "babel-plugin-transform-remove-undefined": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-undefined/-/babel-plugin-transform-remove-undefined-0.5.0.tgz", + "integrity": "sha512-+M7fJYFaEE/M9CXa0/IRkDbiV3wRELzA1kKQFCJ4ifhrzLKn/9VCCgj9OFmYWwBd8IB48YdgPkHYtbYq+4vtHQ==", + "dev": true, + "requires": { + "babel-helper-evaluate-path": "^0.5.0" + } + }, + "babel-plugin-transform-simplify-comparison-operators": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-simplify-comparison-operators/-/babel-plugin-transform-simplify-comparison-operators-6.9.4.tgz", + "integrity": "sha1-9ir+CWyrDh9ootdT/fKDiIRxzrk=", + "dev": true + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "dev": true, + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-undefined-to-void": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-undefined-to-void/-/babel-plugin-transform-undefined-to-void-6.9.4.tgz", + "integrity": "sha1-viQcqBQEAwZ4t0hxcyK4nQyP4oA=", + "dev": true + }, + "babel-preset-env": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz", + "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", + "dev": true, + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-to-generator": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.23.0", + "babel-plugin-transform-es2015-classes": "^6.23.0", + "babel-plugin-transform-es2015-computed-properties": "^6.22.0", + "babel-plugin-transform-es2015-destructuring": "^6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", + "babel-plugin-transform-es2015-for-of": "^6.23.0", + "babel-plugin-transform-es2015-function-name": "^6.22.0", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.22.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-umd": "^6.23.0", + "babel-plugin-transform-es2015-object-super": "^6.22.0", + "babel-plugin-transform-es2015-parameters": "^6.23.0", + "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", + "babel-plugin-transform-exponentiation-operator": "^6.22.0", + "babel-plugin-transform-regenerator": "^6.22.0", + "browserslist": "^3.2.6", + "invariant": "^2.2.2", + "semver": "^5.3.0" + } + }, + "babel-preset-minify": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/babel-preset-minify/-/babel-preset-minify-0.5.1.tgz", + "integrity": "sha512-1IajDumYOAPYImkHbrKeiN5AKKP9iOmRoO2IPbIuVp0j2iuCcj0n7P260z38siKMZZ+85d3mJZdtW8IgOv+Tzg==", + "dev": true, + "requires": { + "babel-plugin-minify-builtins": "^0.5.0", + "babel-plugin-minify-constant-folding": "^0.5.0", + "babel-plugin-minify-dead-code-elimination": "^0.5.1", + "babel-plugin-minify-flip-comparisons": "^0.4.3", + "babel-plugin-minify-guarded-expressions": "^0.4.4", + "babel-plugin-minify-infinity": "^0.4.3", + "babel-plugin-minify-mangle-names": "^0.5.0", + "babel-plugin-minify-numeric-literals": "^0.4.3", + "babel-plugin-minify-replace": "^0.5.0", + "babel-plugin-minify-simplify": "^0.5.1", + "babel-plugin-minify-type-constructors": "^0.4.3", + "babel-plugin-transform-inline-consecutive-adds": "^0.4.3", + "babel-plugin-transform-member-expression-literals": "^6.9.4", + "babel-plugin-transform-merge-sibling-variables": "^6.9.4", + "babel-plugin-transform-minify-booleans": "^6.9.4", + "babel-plugin-transform-property-literals": "^6.9.4", + "babel-plugin-transform-regexp-constructors": "^0.4.3", + "babel-plugin-transform-remove-console": "^6.9.4", + "babel-plugin-transform-remove-debugger": "^6.9.4", + "babel-plugin-transform-remove-undefined": "^0.5.0", + "babel-plugin-transform-simplify-comparison-operators": "^6.9.4", + "babel-plugin-transform-undefined-to-void": "^6.9.4", + "lodash": "^4.17.11" + } + }, + "babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "dev": true, + "requires": { + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "dev": true, + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "dev": true, + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", + "dev": true + }, + "bluebird": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", + "dev": true + }, + "blueimp-md5": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.11.1.tgz", + "integrity": "sha512-4UiOAmql2XO0Sws07OVzYdCKK0K2Va5g6AVgYXoGhEQiKrdSOefjUCm1frPk6E+xiIOHRqaFg+TUGo7cClKg5g==", + "dev": true + }, + "boxen": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-3.2.0.tgz", + "integrity": "sha512-cU4J/+NodM3IHdSL2yN8bqYqnmlBTidDR4RC7nJs61ZmtGz8VZzM3HLQX0zY5mrSmPtR3xWwsq2jOUQqFZN8+A==", + "dev": true, + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^2.4.2", + "cli-boxes": "^2.2.0", + "string-width": "^3.0.0", + "term-size": "^1.2.0", + "type-fest": "^0.3.0", + "widest-line": "^2.0.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "type-fest": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", + "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "dev": true + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", + "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000844", + "electron-to-chromium": "^1.3.47" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "builtin-modules": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", + "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", + "dev": true + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } + }, + "caching-transform": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", + "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", + "dev": true, + "requires": { + "hasha": "^3.0.0", + "make-dir": "^2.0.0", + "package-hash": "^3.0.0", + "write-file-atomic": "^2.4.2" + }, + "dependencies": { + "hasha": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", + "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", + "dev": true, + "requires": { + "is-stream": "^1.0.1" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "package-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", + "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^3.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + } + } + }, + "call-matcher": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/call-matcher/-/call-matcher-1.1.0.tgz", + "integrity": "sha512-IoQLeNwwf9KTNbtSA7aEBb1yfDbdnzwjCetjkC8io5oGeOmK2CBNdg0xr+tadRYKO0p7uQyZzvon0kXlZbvGrw==", + "dev": true, + "requires": { + "core-js": "^2.0.0", + "deep-equal": "^1.0.0", + "espurify": "^1.6.0", + "estraverse": "^4.0.0" + } + }, + "call-signature": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/call-signature/-/call-signature-0.0.2.tgz", + "integrity": "sha1-qEq8glpV70yysCi9dOIFpluaSZY=", + "dev": true + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha1-oqpfsa9oh1glnDLBQUJteJI7m3c=", + "dev": true, + "requires": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + } + }, + "caniuse-lite": { + "version": "1.0.30000989", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000989.tgz", + "integrity": "sha512-vrMcvSuMz16YY6GSVZ0dWDTJP8jqk3iFQ/Aq5iqblPwxSVVZI+zxDyTX0VPqtQsDnfdrBDcsmhgTEOh5R8Lbpw==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chokidar": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.0.2.tgz", + "integrity": "sha512-c4PR2egjNjI1um6bamCQ6bUNPDiyofNQruHvKgHQ4gDUP/ITSVSzNsiI5OWtHOsX323i5ha/kk4YmOZ1Ktg7KA==", + "dev": true, + "requires": { + "anymatch": "^3.0.1", + "braces": "^3.0.2", + "fsevents": "^2.0.6", + "glob-parent": "^5.0.0", + "is-binary-path": "^2.1.0", + "is-glob": "^4.0.1", + "normalize-path": "^3.0.0", + "readdirp": "^3.1.1" + } + }, + "chunkd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/chunkd/-/chunkd-1.0.0.tgz", + "integrity": "sha512-xx3Pb5VF9QaqCotolyZ1ywFBgyuJmu6+9dLiqBxgelEse9Xsr3yUlpoX3O4Oh11M00GT2kYMsRByTKIMJW2Lkg==", + "dev": true + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "ci-parallel-vars": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ci-parallel-vars/-/ci-parallel-vars-1.0.0.tgz", + "integrity": "sha512-u6dx20FBXm+apMi+5x7UVm6EH7BL1gc4XrcnQewjcB7HWRcor/V5qWc3RG2HwpgDJ26gIi2DSEu3B7sXynAw/g==", + "dev": true + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "clean-yaml-object": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/clean-yaml-object/-/clean-yaml-object-0.1.0.tgz", + "integrity": "sha1-Y/sRDcLOGoTcIfbZM0h20BCui2g=", + "dev": true + }, + "cli-boxes": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", + "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.2.0.tgz", + "integrity": "sha512-tgU3fKwzYjiLEQgPMD9Jt+JjHVL9kW93FiIMX/l7rivvOD4/LL0Mf7gda3+4U2KJBloybwgj5KEoQgGRioMiKQ==", + "dev": true + }, + "cli-truncate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.0.0.tgz", + "integrity": "sha512-C4hp+8GCIFVsUUiXcw+ce+7wexVWImw8rQrgMBFsqerx9LvvcGlwm6sMjQYAEmV/Xb87xc1b5Ttx505MSpZVqg==", + "dev": true, + "requires": { + "slice-ansi": "^2.1.0", + "string-width": "^4.1.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "code-excerpt": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-2.1.1.tgz", + "integrity": "sha512-tJLhH3EpFm/1x7heIW0hemXJTUU5EWl2V0EIX558jp05Mt1U6DVryCgkp3l37cxqs+DNbNgxG43SkwJXpQ14Jw==", + "dev": true, + "requires": { + "convert-to-spaces": "^1.0.1" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", + "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "dev": true, + "optional": true + }, + "common-path-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-1.0.0.tgz", + "integrity": "sha1-zVL28HEuC6q5fW+XModPIvR3UsA=", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concordance": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/concordance/-/concordance-4.0.0.tgz", + "integrity": "sha512-l0RFuB8RLfCS0Pt2Id39/oCPykE01pyxgAFypWTlaGRgvLkZrtczZ8atEHpTeEIW+zYWXTBuA9cCSeEOScxReQ==", + "dev": true, + "requires": { + "date-time": "^2.1.0", + "esutils": "^2.0.2", + "fast-diff": "^1.1.2", + "js-string-escape": "^1.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.flattendeep": "^4.4.0", + "lodash.islength": "^4.0.1", + "lodash.merge": "^4.6.1", + "md5-hex": "^2.0.0", + "semver": "^5.5.1", + "well-known-symbols": "^2.0.0" + }, + "dependencies": { + "md5-hex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-2.0.0.tgz", + "integrity": "sha1-0FiOnxx0lUSS7NJKwKxs6ZfZLjM=", + "dev": true, + "requires": { + "md5-o-matic": "^0.1.1" + } + } + } + }, + "configstore": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-4.0.0.tgz", + "integrity": "sha512-CmquAXFBocrzaSM8mtGPMM/HiWmyIpr4CcJl/rgY2uCObZ/S7cKU0silxslqJejl+t/T9HS8E0PUNQD81JGUEQ==", + "dev": true, + "requires": { + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + }, + "dependencies": { + "dot-prop": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", + "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + } + } + }, + "confusing-browser-globals": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.8.tgz", + "integrity": "sha512-lI7asCibVJ6Qd3FGU7mu4sfG4try4LX3+GVS+Gv8UlrEf2AeW57piecapnog2UHZSbcX/P/1UDWVaTsblowlZg==", + "dev": true + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "convert-to-spaces": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-1.0.2.tgz", + "integrity": "sha1-fj5Iu+bZl7FBfdyihoIEtNPYVxU=", + "dev": true + }, + "core-js": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==", + "dev": true + }, + "cp-file": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", + "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "make-dir": "^2.0.0", + "nested-error-stacks": "^2.0.0", + "pify": "^4.0.1", + "safe-buffer": "^5.0.1" + }, + "dependencies": { + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "crypto-random-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", + "dev": true + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "date-time": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/date-time/-/date-time-2.1.0.tgz", + "integrity": "sha512-/9+C44X7lot0IeiyfgJmETtRMhBidBYM2QFFIkGa0U1k+hSyY87Nw7PY3eDqpvCBm7I3WCSfPeZskW/YYq6m4g==", + "dev": true, + "requires": { + "time-zone": "^1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + } + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "dev": true + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "default-require-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", + "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", + "dev": true, + "requires": { + "strip-bom": "^3.0.0" + } + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "defer-to-connect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.0.2.tgz", + "integrity": "sha512-k09hcQcTDY+cwgiwa6PYKLm3jlagNzQ+RSvhjzESOGOx+MNOuXkxTfEvPrO1IOQ81tArCFYQgi631clB70RpQw==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + }, + "dependencies": { + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + } + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dot-prop": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.1.0.tgz", + "integrity": "sha512-n1oC6NBF+KM9oVXtjmen4Yo7HyAVWV2UUl50dCYJdw2924K6dX9bf9TTTWaKtYlRn0FEtxG27KS80ayVLixxJA==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.236", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.236.tgz", + "integrity": "sha512-LWOvuJ80pLO3FtFqTcGuXB0dxdMtzSCkRmbXdY5mHUvXRQGor3sTVmyfU70aD2yF5i+fbHz52ncWr5T3xUYHlA==", + "dev": true + }, + "emittery": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.4.1.tgz", + "integrity": "sha512-r4eRSeStEGf6M5SKdrQhhLK5bOwOBxQhIE3YSTnZE3GpKiLfnnhE+tPtrJE79+eDJgm39BM6LSoI8SCx4HbwlQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "empower-core": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/empower-core/-/empower-core-1.2.0.tgz", + "integrity": "sha512-g6+K6Geyc1o6FdXs9HwrXleCFan7d66G5xSCfSF7x1mJDCes6t0om9lFQG3zOrzh3Bkb/45N0cZ5Gqsf7YrzGQ==", + "dev": true, + "requires": { + "call-signature": "0.0.2", + "core-js": "^2.0.0" + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "equal-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/equal-length/-/equal-length-1.0.1.tgz", + "integrity": "sha1-IcoRLUirJLTh5//A5TOdMf38J0w=", + "dev": true + }, + "erlang-types": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/erlang-types/-/erlang-types-1.1.3.tgz", + "integrity": "sha512-HZWcirSqKIdasr04DT9NnYgVyqvUaefDPp2LhNgtBnM2ovOkUhJ4DEKVQZ71POKsu4wiR64EWhahA8AE9r796g==" + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.2.1.tgz", + "integrity": "sha512-ES7BzEzr0Q6m5TK9i+/iTpKjclXitOdDK4vT07OqbkBT2/VcN/gO9EL1C4HlK3TAOXYv2ItcmbVR9jO1MR0fJg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.2", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.0", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.4.1", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "strip-json-comments": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", + "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "dev": true + } + } + }, + "eslint-config-airbnb-base": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.0.0.tgz", + "integrity": "sha512-2IDHobw97upExLmsebhtfoD3NAKhV4H0CJWP3Uprd/uk+cHuWYOczPVxQ8PxLFUAw7o3Th1RAU8u1DoUpr+cMA==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.7", + "object.assign": "^4.1.0", + "object.entries": "^1.1.0" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", + "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.5.0" + } + }, + "eslint-module-utils": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz", + "integrity": "sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==", + "dev": true, + "requires": { + "debug": "^2.6.8", + "pkg-dir": "^2.0.0" + } + }, + "eslint-plugin-import": { + "version": "2.18.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz", + "integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.4.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.0", + "read-pkg-up": "^2.0.0", + "resolve": "^1.11.0" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + } + } + }, + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz", + "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.0.0" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "dev": true + }, + "espower-location-detector": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/espower-location-detector/-/espower-location-detector-1.0.0.tgz", + "integrity": "sha1-oXt+zFnTDheeK+9z+0E3cEyzMbU=", + "dev": true, + "requires": { + "is-url": "^1.2.1", + "path-is-absolute": "^1.0.0", + "source-map": "^0.5.0", + "xtend": "^4.0.0" + } + }, + "espree": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.0.tgz", + "integrity": "sha512-boA7CHRLlVWUSg3iL5Kmlt/xT3Q+sXnKoRYYzj1YeM10A76TEJBbotV5pKbnK42hEUIr121zTv+QLRM5LsCPXQ==", + "dev": true, + "requires": { + "acorn": "^7.0.0", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "espurify": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/espurify/-/espurify-1.8.1.tgz", + "integrity": "sha512-ZDko6eY/o+D/gHCWyHTU85mKDgYcS4FJj7S+YD6WIInm7GQ6AnOjmcL4+buFV/JOztVLELi/7MmuGU5NHta0Mg==", + "dev": true, + "requires": { + "core-js": "^2.0.0" + } + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "fast-glob": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.0.4.tgz", + "integrity": "sha512-wkIbV6qg37xTJwqSsdnIphL1e+LaGz4AIQqr00mIubMaEhv1/HEmJ0uuCGZRNRUkZZmOB5mJKO0ZUTVq+SxMQg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.1", + "@nodelib/fs.walk": "^1.2.1", + "glob-parent": "^5.0.0", + "is-glob": "^4.0.1", + "merge2": "^1.2.3", + "micromatch": "^4.0.2" + } + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastq": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.0.tgz", + "integrity": "sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA==", + "dev": true, + "requires": { + "reusify": "^1.0.0" + } + }, + "figures": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.0.0.tgz", + "integrity": "sha512-HKri+WoWoUgr83pehn/SIgLOMZ9nAWC6dcGj26RY2R4F50u4+RTUz0RCrUlOV3nKRAICW1UGzyb+kcX2qK1S/g==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + } + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "dev": true + }, + "foreground-child": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", + "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", + "dev": true, + "requires": { + "cross-spawn": "^4", + "signal-exit": "^3.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", + "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "which": "^1.2.9" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.0.7.tgz", + "integrity": "sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-port": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.0.0.tgz", + "integrity": "sha512-imzMU0FjsZqNa6BqOjbbW6w5BivHIuQKopjpPqcnx0AVHJQKCxK1O+Ab3OrVXhrekqfVMjwA9ZYu062R+KcIsQ==", + "dev": true, + "requires": { + "type-fest": "^0.3.0" + }, + "dependencies": { + "type-fest": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", + "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "dev": true + } + } + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", + "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "dev": true, + "requires": { + "ini": "^1.3.4" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", + "dev": true + }, + "globby": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", + "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "dependencies": { + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + } + } + }, + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==" + }, + "handlebars": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", + "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "dev": true, + "requires": { + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true + }, + "hasha": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.0.0.tgz", + "integrity": "sha512-PqWdhnQhq6tqD32hZv+l1e5mJHNSudjnaAzgAHfkGiU0ABN6lmbZF8abJIulQHbZ7oiHhP8yL6O910ICMc+5pw==", + "dev": true, + "requires": { + "is-stream": "^1.1.0", + "type-fest": "^0.3.0" + }, + "dependencies": { + "type-fest": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", + "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "dev": true + } + } + }, + "home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "dev": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" + } + }, + "hosted-git-info": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", + "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", + "dev": true + }, + "http-cache-semantics": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", + "integrity": "sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", + "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", + "dev": true + }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", + "dev": true + }, + "import-fresh": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", + "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, + "import-local": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", + "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "inquirer": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.1.tgz", + "integrity": "sha512-uxNHBeQhRXIoHWTSNYUFhQVrHYFThIt6IVo2fFmSe8aBwdR3/w6b58hJpiL/fMukFkvGzjg+hSxFtwvVmKZmXw==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^2.4.2", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^4.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "irregular-plurals": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-2.0.0.tgz", + "integrity": "sha512-Y75zBYLkh0lJ9qxeHlMjQ7bSbyiSqNW/UOPWDmzC7cXskL1hekSITh1Oc6JV0XCWWZ9DE8VYSB71xocLk3gmGw==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-error": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-error/-/is-error-2.2.2.tgz", + "integrity": "sha512-IOQqts/aHWbiisY5DuPJQ0gcbvaLFCa7fBa9xoLfxBZvQ+ZI/Zh9xoI7Gk+G64N0FdK4AbibytHht2tWgpJWLg==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-finite": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", + "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-installed-globally": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "dev": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + }, + "dependencies": { + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + } + } + }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "dev": true + }, + "is-npm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-3.0.0.tgz", + "integrity": "sha512-wsigDr1Kkschp2opC4G3yA6r9EgVA6NjRpWzIi9axXqeIaAATPRJc4uLujXe3Nd9uO8KoDyA4MD6aZSeXTADhA==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-observable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-2.0.0.tgz", + "integrity": "sha512-fhBZv3eFKUbyHXZ1oHujdo2tZ+CNbdpdzzlENgCGZUC8keoGxUew2jYFLYcUB4qo7LDD03o4KK11m/QYD7kEjg==", + "dev": true + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "requires": { + "is-path-inside": "^2.1.0" + } + }, + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.2" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.0.tgz", + "integrity": "sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg==", + "dev": true, + "requires": { + "isobject": "^4.0.0" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-reference": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.1.3.tgz", + "integrity": "sha512-W1iHHv/oyBb2pPxkBxtaewxa1BC58Pn5J0hogyCdefwUIvb6R+TGbAcIa4qPNYLqLhb3EnOgUf2MQkkF76BcKw==", + "requires": { + "@types/estree": "0.0.39" + } + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "dev": true + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-4.0.0.tgz", + "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-hook": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", + "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", + "dev": true, + "requires": { + "append-transform": "^1.0.0" + } + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", + "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", + "dev": true, + "requires": { + "handlebars": "^4.1.2" + } + }, + "js-string-escape": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", + "integrity": "sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8=", + "dev": true + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", + "dev": true + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "requires": { + "package-json": "^6.3.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.flattendeep": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", + "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=", + "dev": true + }, + "lodash.islength": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.islength/-/lodash.islength-4.0.1.tgz", + "integrity": "sha1-Tpho1FJXXXUK/9NYyXlUPcIO1Xc=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "loud-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-2.1.0.tgz", + "integrity": "sha512-g/6MQxUXYHeVqZ4PGpPL1fS1fOvlXoi7bay0pizmjAd/3JhyXwxzwrnr74yzdmhuerlslbRJ3x7IOXzFz0cE5w==", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.2" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "magic-string": { + "version": "0.25.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.3.tgz", + "integrity": "sha512-6QK0OpF/phMz0Q2AxILkX2mFhi7m+WMwTRg0LQKq/WBB0cDP4rYH3Wp4/d3OTXlrPLVJT/RFqj8tFeAR4nk8AA==", + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "make-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz", + "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha1-plzSkIepJZi4eRJXpSPgISIqwfk=", + "dev": true + }, + "matcher": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-2.0.0.tgz", + "integrity": "sha512-nlmfSlgHBFx36j/Pl/KQPbIaqE8Zf0TqmSMjsuddHDg6PMSVgmyW9HpkLs0o0M1n2GIZ/S2BZBLIww/xjhiGng==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, + "md5-hex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz", + "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==", + "dev": true, + "requires": { + "blueimp-md5": "^2.10.0" + } + }, + "md5-o-matic": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/md5-o-matic/-/md5-o-matic-0.1.1.tgz", + "integrity": "sha1-givM1l4RfFFPqxdrJZRdVBAKA8M=", + "dev": true + }, + "meow": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", + "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", + "dev": true, + "requires": { + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0", + "yargs-parser": "^10.0.0" + }, + "dependencies": { + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + } + } + } + }, + "merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "merge2": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.4.tgz", + "integrity": "sha512-FYE8xI+6pjFOhokZu0We3S5NKCirLbCzSh2Usf3qEyr4X8U+0jNg9P8RZ4qz+V2UoECLVwSyzU3LxXBaLGtD3A==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + }, + "minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + }, + "dependencies": { + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true + }, + "nested-error-stacks": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", + "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-url": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.3.0.tgz", + "integrity": "sha512-0NLtR71o4k6GLP+mr6Ty34c5GA6CMoEsncKJxvQd8NzPxaHRJNnb5gZE8R1XF4CPIS7QPHLJ74IFszwtNVAHVQ==", + "dev": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "nyc": { + "version": "14.1.1", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", + "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", + "dev": true, + "requires": { + "archy": "^1.0.0", + "caching-transform": "^3.0.2", + "convert-source-map": "^1.6.0", + "cp-file": "^6.2.0", + "find-cache-dir": "^2.1.0", + "find-up": "^3.0.0", + "foreground-child": "^1.5.6", + "glob": "^7.1.3", + "istanbul-lib-coverage": "^2.0.5", + "istanbul-lib-hook": "^2.0.7", + "istanbul-lib-instrument": "^3.3.0", + "istanbul-lib-report": "^2.0.8", + "istanbul-lib-source-maps": "^3.0.6", + "istanbul-reports": "^2.2.4", + "js-yaml": "^3.13.1", + "make-dir": "^2.1.0", + "merge-source-map": "^1.1.0", + "resolve-from": "^4.0.0", + "rimraf": "^2.6.3", + "signal-exit": "^3.0.2", + "spawn-wrap": "^1.4.2", + "test-exclude": "^5.2.3", + "uuid": "^3.3.2", + "yargs": "^13.2.2", + "yargs-parser": "^13.0.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.entries": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", + "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "object.values": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", + "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.12.0", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "observable-to-promise": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/observable-to-promise/-/observable-to-promise-1.0.0.tgz", + "integrity": "sha512-cqnGUrNsE6vdVDTPAX9/WeVzwy/z37vdxupdQXU8vgTXRFH72KCZiZga8aca2ulRPIeem8W3vW9rQHBwfIl2WA==", + "dev": true, + "requires": { + "is-observable": "^2.0.0", + "symbol-observable": "^1.0.4" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "ora": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", + "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-spinners": "^2.0.0", + "log-symbols": "^2.2.0", + "strip-ansi": "^5.2.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + } + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "package-hash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "hasha": "^5.0.0", + "lodash.flattendeep": "^4.4.0", + "release-zalgo": "^1.0.0" + } + }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-2.1.0.tgz", + "integrity": "sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "picomatch": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz", + "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-conf": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz", + "integrity": "sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "load-json-file": "^5.2.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "load-json-file": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-5.3.0.tgz", + "integrity": "sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.15", + "parse-json": "^4.0.0", + "pify": "^4.0.1", + "strip-bom": "^3.0.0", + "type-fest": "^0.3.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "type-fest": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", + "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "dev": true + } + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "plur": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/plur/-/plur-3.1.1.tgz", + "integrity": "sha512-t1Ax8KUvV3FFII8ltczPn2tJdjqbd1sIzu6t4JL7nQ3EyeL/lTrj5PWKb06ic5/6XYDr65rQ4uzQEGN70/6X5w==", + "dev": true, + "requires": { + "irregular-plurals": "^2.0.0" + } + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true + }, + "pretty-ms": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-5.0.0.tgz", + "integrity": "sha512-94VRYjL9k33RzfKiGokPBPpsmloBYSf5Ri+Pq19zlsEcUKFob+admeXr5eFDRuPjFmEOcjJvPGdillYOJyvZ7Q==", + "dev": true, + "requires": { + "parse-ms": "^2.1.0" + } + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=", + "dev": true + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "readdirp": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.1.2.tgz", + "integrity": "sha512-8rhl0xs2cxfVsqzreYCvs8EwBfn/DhVdqtoLmw19uI3SC5avYX9teCurlErfpPXGmYtMHReGaP2RsLnFvz/lnw==", + "dev": true, + "requires": { + "picomatch": "^2.0.4" + } + }, + "redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo=", + "dev": true, + "requires": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + }, + "dependencies": { + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + } + } + }, + "regenerate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", + "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", + "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", + "dev": true, + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "dev": true, + "requires": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "dev": true, + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "registry-auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.0.0.tgz", + "integrity": "sha512-lpQkHxd9UL6tb3k/aHAVfnVtn+Bcs9ob5InuFLLEDqSqeq+AljB8GZW9xY0x7F+xYwEcjKe07nyoxzEYz6yvkw==", + "dev": true, + "requires": { + "rc": "^1.2.8", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", + "dev": true + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + } + }, + "release-zalgo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/release-zalgo/-/release-zalgo-1.0.0.tgz", + "integrity": "sha1-CXALflB0Mpc5Mw5TXFqQ+2eFFzA=", + "dev": true, + "requires": { + "es6-error": "^4.0.1" + } + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "require-precompiled": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/require-precompiled/-/require-precompiled-0.1.0.tgz", + "integrity": "sha1-WhtS63Dr7UPrmC6XTIWrWVceVvo=", + "dev": true + }, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "rollup": { + "version": "1.19.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-1.19.4.tgz", + "integrity": "sha512-G24w409GNj7i/Yam2cQla6qV2k6Nug8bD2DZg9v63QX/cH/dEdbNJg8H4lUm5M1bRpPKRUC465Rm9H51JTKOfQ==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "@types/node": "^12.6.9", + "acorn": "^6.2.1" + }, + "dependencies": { + "acorn": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", + "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", + "dev": true + } + } + }, + "rollup-plugin-babel": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/rollup-plugin-babel/-/rollup-plugin-babel-4.3.3.tgz", + "integrity": "sha512-tKzWOCmIJD/6aKNz0H1GMM+lW1q9KyFubbWzGiOG540zxPPifnEAHTZwjo0g991Y+DyOZcLqBgqOdqazYE5fkw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "rollup-pluginutils": "^2.8.1" + } + }, + "rollup-plugin-babel-minify": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-babel-minify/-/rollup-plugin-babel-minify-9.0.0.tgz", + "integrity": "sha512-5aJVWpuoZUbQrIaRF7Jvjo7bBnYqaChOhrhsGtz72wJ3lyo7ygIL85hsuPkvrk/3Fj5AUlNZV3IaSZ98fHyoTw==", + "dev": true, + "requires": { + "@babel/core": "^7.4.5", + "@babel/plugin-syntax-dynamic-import": "^7.2.0", + "@comandeer/babel-plugin-banner": "^5.0.0", + "babel-preset-minify": "^0.5.0", + "sourcemap-codec": "^1.4.4" + } + }, + "rollup-plugin-commonjs": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.0.2.tgz", + "integrity": "sha512-DxeR4QXTgTOFseYls1V7vgKbrSJmPYNdEMOs0OvH+7+89C3GiIonU9gFrE0u39Vv1KWm3wepq8KAvKugtoM2Zw==", + "requires": { + "estree-walker": "^0.6.1", + "is-reference": "^1.1.2", + "magic-string": "^0.25.2", + "resolve": "^1.11.0", + "rollup-pluginutils": "^2.8.1" + } + }, + "rollup-plugin-node-resolve": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz", + "integrity": "sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw==", + "dev": true, + "requires": { + "@types/resolve": "0.0.8", + "builtin-modules": "^3.1.0", + "is-module": "^1.0.0", + "resolve": "^1.11.1", + "rollup-pluginutils": "^2.8.1" + } + }, + "rollup-pluginutils": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.1.tgz", + "integrity": "sha512-J5oAoysWar6GuZo0s+3bZ6sVZAC0pfqKz68De7ZgDi5z63jOVZn1uJL/+z1jeKHNbGII8kAyHF5q8LnxSX5lQg==", + "requires": { + "estree-walker": "^0.6.1" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true + }, + "rxjs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", + "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "semver-diff": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "dev": true, + "requires": { + "semver": "^5.0.3" + } + }, + "serialize-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", + "integrity": "sha1-ULZ51WNc34Rme9yOWa9OW4HV9go=", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "dev": true, + "requires": { + "source-map": "^0.5.6" + } + }, + "sourcemap-codec": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz", + "integrity": "sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg==" + }, + "spawn-wrap": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.2.tgz", + "integrity": "sha512-vMwR3OmmDhnxCVxM8M+xO/FtIp6Ju/mNaDfCMMW7FDcLRTPFWUswec4LXJHTJE2hwTI9O0YBfygu4DalFl7Ylg==", + "dev": true, + "requires": { + "foreground-child": "^1.5.6", + "mkdirp": "^0.5.0", + "os-homedir": "^1.0.1", + "rimraf": "^2.6.2", + "signal-exit": "^3.0.2", + "which": "^1.3.0" + } + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", + "dev": true + }, + "string-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.1.0.tgz", + "integrity": "sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^5.2.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-bom-buf": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-2.0.0.tgz", + "integrity": "sha512-gLFNHucd6gzb8jMsl5QmZ3QgnUJmp7qn4uUSHNwEXumAp7YizoGYw19ZUVfuq4aBOQUtyn2k8X/CwzWB73W2lQ==", + "dev": true, + "requires": { + "is-utf8": "^0.2.1" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supertap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supertap/-/supertap-1.0.0.tgz", + "integrity": "sha512-HZJ3geIMPgVwKk2VsmO5YHqnnJYl6bV5A9JW2uzqV43WmpgliNEYbuvukfor7URpaqpxuw3CfZ3ONdVbZjCgIA==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "indent-string": "^3.2.0", + "js-yaml": "^3.10.0", + "serialize-error": "^2.1.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "supports-color": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.0.0.tgz", + "integrity": "sha512-WRt32iTpYEZWYOpcetGm0NPeSvaebccx7hhS/5M6sAiqnhedtFCHFxkjzZlJvFNCPowiKSFGiZk5USQDFy83vQ==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + } + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "tailored": { + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/tailored/-/tailored-2.7.5.tgz", + "integrity": "sha512-5zLODXUWx3P8SPVbFA9RgaUrpTGJMXg67jMaZJmJJwE9jQXuCfJb7bnO/1FuTsrFFZTDDmkYR/PPz9gAN26CmQ==", + "requires": { + "erlang-types": "^1.0.1" + } + }, + "term-size": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", + "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", + "dev": true, + "requires": { + "execa": "^0.7.0" + } + }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "time-zone": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", + "integrity": "sha1-mcW/VZWJZq9tBtg73zgA3IL67F0=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", + "dev": true + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA=", + "dev": true + }, + "trim-off-newlines": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", + "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", + "dev": true + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-fest": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.5.2.tgz", + "integrity": "sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "uglify-js": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", + "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "dev": true, + "optional": true, + "requires": { + "commander": "~2.20.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "uid2": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz", + "integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I=", + "dev": true + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", + "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", + "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==", + "dev": true + }, + "unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "dev": true, + "requires": { + "crypto-random-string": "^1.0.0" + } + }, + "unique-temp-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-temp-dir/-/unique-temp-dir-1.0.0.tgz", + "integrity": "sha1-bc6VsmgcoAPuv7MEpBX5y6vMU4U=", + "dev": true, + "requires": { + "mkdirp": "^0.5.1", + "os-tmpdir": "^1.0.1", + "uid2": "0.0.3" + } + }, + "update-notifier": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-3.0.1.tgz", + "integrity": "sha512-grrmrB6Zb8DUiyDIaeRTBCkgISYUgETNe7NglEbVsrLWXeESnlCSP50WfRSj/GmzMPl6Uchj24S/p80nP/ZQrQ==", + "dev": true, + "requires": { + "boxen": "^3.0.0", + "chalk": "^2.0.1", + "configstore": "^4.0.0", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.1.0", + "is-npm": "^3.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "requires": { + "prepend-http": "^2.0.0" + } + }, + "uuid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "well-known-symbols": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", + "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "widest-line": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", + "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "dev": true, + "requires": { + "string-width": "^2.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "write-file-atomic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.0.tgz", + "integrity": "sha512-EIgkf60l2oWsffja2Sf2AL384dx328c0B+cIYPTQq5q2rOYuDV00/iPFBOUiDKKwKMOhkymH8AidPaRvzfxY+Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "xdg-basedir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } +} diff --git a/package.json b/package.json index e287b863..3acb3b42 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,20 @@ { - "name": "elixir_script", - "version": "1.0.0", + "name": "elixirscript", + "version": "0.26.1", "description": "Convert Elixir to JavaScript", - "main": "index.js", - "directories": { - "test": "test" + "main": "elixir.js", + "bin": { + "elixirscript": "./elixirscript" + }, + "engines": { + "node": ">=7.1" }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "lint": "eslint src/javascript/lib/**/*.js src/javascript/tests/**/*.js", + "lint:fix": "eslint src/javascript/lib/**/*.js src/javascript/tests/**/*.js --fix", + "build": "node rollup.config.js", + "clean": "rm -rf priv/build", + "test": "nyc ava src/javascript/tests" }, "repository": { "type": "git", @@ -15,18 +22,36 @@ }, "author": "", "license": "MIT", - "dependencies": {}, + "dependencies": { + "erlang-types": "^1.0.1", + "grapheme-splitter": "^1.0.2", + "rollup-plugin-commonjs": "^10.0.2", + "tailored": "^2.7.4" + }, "devDependencies": { - "acorn": "^2.4.0", - "babel-core": "^5.2.12", - "chai": "^2.2.0", - "gulp": "^3.8.11", - "gulp-babel": "^5.1.0", - "gulp-concat": "^2.5.2", - "gulp-eslint": "^0.13.2", - "gulp-mocha": "^2.0.1", - "gulp-rollup": "^1.0.1", - "gulp-sourcemaps": "^1.5.2", - "rollup": "^0.14.1" + "@std/esm": "^0.8.3", + "ava": "^2.3.0", + "babel-core": "^6.26.0", + "babel-preset-env": "^1.6.0", + "babel-register": "^6.26.0", + "eslint": "^6.2.1", + "eslint-config-airbnb-base": "^14.0.0", + "eslint-plugin-import": "^2.7.0", + "nyc": "^14.1.1", + "rollup": "^1.19.4", + "rollup-plugin-babel": "^4.3.3", + "rollup-plugin-babel-minify": "^9.0.0", + "rollup-plugin-node-resolve": "^5.2.0" + }, + "ava": { + "require": [ + "babel-register" + ], + "babel": { + "babelrc": true + } + }, + "@std/esm": { + "esm": "js" } } diff --git a/priv/javascript/elixir.js b/priv/javascript/elixir.js deleted file mode 100644 index ebdb3eb2..00000000 --- a/priv/javascript/elixir.js +++ /dev/null @@ -1,17 +0,0 @@ -import PostOffice from './lib/post_office'; - - -self.post_office = self.post_office || new PostOffice(); - -export { default as Patterns } from './lib/patterns/patterns'; -export { default as BitString } from './lib/bit_string'; -export { default as Kernel } from './lib/kernel'; -export { default as Atom } from './lib/atom'; -export { default as Enum } from './lib/enum'; -export { default as Integer } from './lib/integer'; -export { default as JS } from './lib/js'; -export { default as List } from './lib/list'; -export { default as Range } from './lib/range'; -export { default as Tuple } from './lib/tuple'; -export { default as Agent } from './lib/agent'; -export { default as Keyword } from './lib/keyword'; \ No newline at end of file diff --git a/priv/javascript/lib/agent.js b/priv/javascript/lib/agent.js deleted file mode 100644 index 6dc0f40e..00000000 --- a/priv/javascript/lib/agent.js +++ /dev/null @@ -1,40 +0,0 @@ -import Kernel from './kernel'; -import Keyword from './keyword'; - -let Agent = {}; - -Agent.start = function(fun, options = []){ - const name = Keyword.has_key__qm__(options, Kernel.SpecialForms.atom("name")) ? Keyword.get(options, Kernel.SpecialForms.atom("name")) : Symbol(); - - self.post_office.add_mailbox(name); - self.post_office.send(name, fun()); - - return Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom("ok"), name); -} - -Agent.stop = function(agent, timeout = 5000){ - self.post_office.remove_mailbox(agent); - return Kernel.SpecialForms.atom("ok"); -} - -Agent.update = function(agent, fun, timeout = 5000){ - - const current_state = self.post_office.receive(agent); - self.post_office.send(agent, fun(current_state)); - - return Kernel.SpecialForms.atom("ok"); -} - -Agent.get = function(agent, fun, timeout = 5000){ - return fun(self.post_office.peek(agent)); -} - -Agent.get_and_update = function(agent, fun, timeout = 5000){ - - const get_and_update_tuple = fun(self.post_office.receive(agent)); - self.post_office.send(agent, Kernel.elem(get_and_update_tuple, 1)); - - return Kernel.elem(get_and_update_tuple, 0); -} - -export default Agent; diff --git a/priv/javascript/lib/atom.js b/priv/javascript/lib/atom.js deleted file mode 100644 index 354283b8..00000000 --- a/priv/javascript/lib/atom.js +++ /dev/null @@ -1,11 +0,0 @@ -let Atom = {}; - -Atom.to_string = function (atom) { - return Symbol.keyFor(atom); -}; - -Atom.to_char_list = function (atom) { - return Atom.to_string(atom).split(''); -}; - -export default Atom; diff --git a/priv/javascript/lib/bit_string.js b/priv/javascript/lib/bit_string.js deleted file mode 100644 index 16e7d268..00000000 --- a/priv/javascript/lib/bit_string.js +++ /dev/null @@ -1,304 +0,0 @@ -class BitString { - constructor(...args){ - this.raw_value = function(){ - return Object.freeze(args); - }; - - this.value = Object.freeze(this.process(args)); - } - - get(index){ - return this.value[index]; - } - - count(){ - return this.value.length; - } - - [Symbol.iterator]() { - return this.value[Symbol.iterator](); - } - - toString(){ - var i, s = ""; - for (i = 0; i < this.count(); i++) { - if (s !== "") { - s += ", "; - } - s += this[i].toString(); - } - - return "<<" + s + ">>"; - } - - process(){ - let processed_values = []; - - var i; - for (i = 0; i < this.raw_value().length; i++) { - let processed_value = this['process_' + this.raw_value()[i].type](this.raw_value()[i]); - - for(let attr of this.raw_value()[i].attributes){ - processed_value = this['process_' + attr](processed_value); - } - - processed_values = processed_values.concat(processed_value); - } - - return processed_values; - } - - process_integer(value){ - return value.value; - } - - process_float(value){ - if(value.size === 64){ - return BitString.float64ToBytes(value.value); - }else if(value.size === 32){ - return BitString.float32ToBytes(value.value); - } - - throw new Error('Invalid size for float'); - } - - process_bitstring(value){ - return value.value.value; - } - - process_binary(value){ - return BitString.toUTF8Array(value.value); - } - - process_utf8(value){ - return BitString.toUTF8Array(value.value); - } - - process_utf16(value){ - return BitString.toUTF16Array(value.value); - } - - process_utf32(value){ - return BitString.toUTF32Array(value.value); - } - - process_signed(value){ - return (new Uint8Array([value]))[0]; - } - - process_unsigned(value){ - return value; - } - - process_native(value){ - return value; - } - - process_big(value){ - return value; - } - - process_little(value){ - return value.reverse(); - } - - process_size(value){ - return value; - } - - process_unit(value){ - return value; - } - - static integer(value){ - return BitString.wrap(value, { 'type': 'integer', 'unit': 1, 'size': 8 }); - } - - static float(value){ - return BitString.wrap(value, { 'type': 'float', 'unit': 1, 'size': 64 }); - } - - static bitstring(value){ - return BitString.wrap(value, { 'type': 'bitstring', 'unit': 1, 'size': value.length }); - } - - static bits(value){ - return BitString.bitstring(value); - } - - static binary(value){ - return BitString.wrap(value, { 'type': 'binary', 'unit': 8, 'size': value.length}); - } - - static bytes(value){ - return BitString.binary(value); - } - - static utf8(value){ - return BitString.wrap(value, { 'type': 'utf8' }); - } - - static utf16(value){ - return BitString.wrap(value, { 'type': 'utf16' }); - } - - static utf32(value){ - return BitString.wrap(value, { 'type': 'utf32' }); - } - - static signed(value){ - return BitString.wrap(value, {}, 'signed'); - } - - static unsigned(value){ - return BitString.wrap(value, {}, 'unsigned'); - } - - static native(value){ - return BitString.wrap(value, {}, 'native'); - } - - static big(value){ - return BitString.wrap(value, {}, 'big'); - } - - static little(value){ - return BitString.wrap(value, {}, 'little'); - } - - static size(value, count){ - return BitString.wrap(value, {'size': count}); - } - - static unit(value, count){ - return BitString.wrap(value, {'unit': count}); - } - - static wrap(value, opt, new_attribute = null){ - let the_value = value; - - if(!(value instanceof Object)){ - the_value = {'value': value, 'attributes': []}; - } - - the_value = Object.assign(the_value, opt); - - if(new_attribute){ - the_value.attributes.push(new_attribute); - } - - - return the_value; - } - - static toUTF8Array(str) { - var utf8 = []; - for (var i = 0; i < str.length; i++) { - var charcode = str.charCodeAt(i); - if (charcode < 0x80){ - utf8.push(charcode); - } - else if (charcode < 0x800) { - utf8.push(0xc0 | (charcode >> 6), - 0x80 | (charcode & 0x3f)); - } - else if (charcode < 0xd800 || charcode >= 0xe000) { - utf8.push(0xe0 | (charcode >> 12), - 0x80 | ((charcode >> 6) & 0x3f), - 0x80 | (charcode & 0x3f)); - } - // surrogate pair - else { - i++; - // UTF-16 encodes 0x10000-0x10FFFF by - // subtracting 0x10000 and splitting the - // 20 bits of 0x0-0xFFFFF into two halves - charcode = 0x10000 + (((charcode & 0x3ff) << 10) - | (str.charCodeAt(i) & 0x3ff)); - utf8.push(0xf0 | (charcode >> 18), - 0x80 | ((charcode >> 12) & 0x3f), - 0x80 | ((charcode >> 6) & 0x3f), - 0x80 | (charcode & 0x3f)); - } - } - return utf8; - } - - static toUTF16Array(str) { - var utf16 = []; - for (var i = 0; i < str.length; i++) { - var codePoint = str.codePointAt(i); - - if(codePoint <= 255){ - utf16.push(0); - utf16.push(codePoint); - }else{ - utf16.push(((codePoint >> 8) & 0xFF)); - utf16.push((codePoint & 0xFF)); - } - } - return utf16; - } - - - static toUTF32Array(str) { - var utf32 = []; - for (var i = 0; i < str.length; i++) { - var codePoint = str.codePointAt(i); - - if(codePoint <= 255){ - utf32.push(0); - utf32.push(0); - utf32.push(0); - utf32.push(codePoint); - }else{ - utf32.push(0); - utf32.push(0); - utf32.push(((codePoint >> 8) & 0xFF)); - utf32.push((codePoint & 0xFF)); - } - } - return utf32; - } - - //http://stackoverflow.com/questions/2003493/javascript-float-from-to-bits - static float32ToBytes(f) { - var bytes = []; - - var buf = new ArrayBuffer(4); - (new Float32Array(buf))[0] = f; - - let intVersion = (new Uint32Array(buf))[0]; - - bytes.push(((intVersion >> 24) & 0xFF)); - bytes.push(((intVersion >> 16) & 0xFF)); - bytes.push(((intVersion >> 8) & 0xFF)); - bytes.push((intVersion & 0xFF)); - - return bytes; - } - - static float64ToBytes(f) { - var bytes = []; - - var buf = new ArrayBuffer(8); - (new Float64Array(buf))[0] = f; - - var intVersion1 = (new Uint32Array(buf))[0]; - var intVersion2 = (new Uint32Array(buf))[1]; - - bytes.push(((intVersion2 >> 24) & 0xFF)); - bytes.push(((intVersion2 >> 16) & 0xFF)); - bytes.push(((intVersion2 >> 8) & 0xFF)); - bytes.push((intVersion2 & 0xFF)); - - bytes.push(((intVersion1 >> 24) & 0xFF)); - bytes.push(((intVersion1 >> 16) & 0xFF)); - bytes.push(((intVersion1 >> 8) & 0xFF)); - bytes.push((intVersion1 & 0xFF)); - - return bytes; - } -} - -export default BitString; diff --git a/priv/javascript/lib/enum.js b/priv/javascript/lib/enum.js deleted file mode 100644 index 961da97e..00000000 --- a/priv/javascript/lib/enum.js +++ /dev/null @@ -1,163 +0,0 @@ -import Kernel from './kernel'; - -let Enum = { - - all__qmark__: function(collection, fun = (x) => x){ - return collection.every(fun); - }, - - any__qmark__: function(collection, fun = (x) => x){ - return collection.some(fun); - }, - - at: function(collection, n, the_default = null){ - if(n > this.count(collection) || n < 0){ - return the_default; - } - - return collection[n]; - }, - - concat: function(...enumables){ - return enumables[0].concat(enumables[1]); - }, - - count: function(collection, fun = null){ - if(fun == null){ - return collection.length; - } else { - return collection.filter(fun).length; - } - }, - - drop: function(collection, count){ - return collection.slice(count); - }, - - drop_while: function(collection, fun){ - let count = 0; - - for(let elem of collection){ - if(fun(elem)){ - count = count + 1; - }else{ - break; - } - } - - return collection.slice(count); - }, - - each: function(collection, fun){ - for(let elem of collection){ - fun(elem); - } - }, - - empty__qmark__: function(collection){ - return collection.length === 0; - }, - - fetch: function(collection, n){ - if(Kernel.is_list(collection)){ - if(n < this.count(collection) && n >= 0){ - return Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom("ok"), collection[n]); - }else{ - return Kernel.SpecialForms.atom("error"); - } - } - - throw new Error("collection is not an Enumerable"); - }, - - fetch__emark__: function(collection, n){ - if(Kernel.is_list(collection)){ - if(n < this.count(collection) && n >= 0){ - return collection[n]; - }else{ - throw new Error("out of bounds error"); - } - } - - throw new Error("collection is not an Enumerable"); - }, - - filter: function(collection, fun){ - return collection.filter(fun); - }, - - filter_map: function(collection, filter, mapper){ - return collection.filter(filter).map(mapper); - }, - - find: function(collection, if_none = null, fun){ - return collection.find(fun, null, if_none); - }, - - into: function(collection, list){ - return list.concat(collection); - }, - - map: function(collection, fun){ - return collection.map(fun); - }, - - map_reduce: function(collection, acc, fun){ - let mapped = Kernel.SpecialForms.list(); - let the_acc = acc; - - for (var i = 0; i < this.count(collection); i++) { - let tuple = fun(collection[i], the_acc); - - the_acc = Kernel.elem(tuple, 1); - mapped = Kernel.SpecialForms.list(...mapped.concat([Kernel.elem(tuple, 0)])); - } - - return Kernel.SpecialForms.tuple(mapped, the_acc); - }, - - member: function(collection, value){ - return collection.includes(value); - }, - - reduce: function(collection, acc, fun){ - return collection.reduce(fun, acc); - }, - - take: function(collection, count){ - return collection.slice(0, count); - }, - - take_every: function(collection, nth){ - let result = []; - let index = 0; - - for(let elem of collection){ - if(index % nth === 0){ - result.push(elem); - } - } - - return Kernel.SpecialForms.list(...result); - }, - - take_while: function(collection, fun){ - let count = 0; - - for(let elem of collection){ - if(fun(elem)){ - count = count + 1; - }else{ - break; - } - } - - return collection.slice(0, count); - }, - - to_list: function(collection){ - return collection; - } -}; - -export default Enum; diff --git a/priv/javascript/lib/integer.js b/priv/javascript/lib/integer.js deleted file mode 100644 index d169226f..00000000 --- a/priv/javascript/lib/integer.js +++ /dev/null @@ -1,38 +0,0 @@ -import Kernel from './kernel'; - -let Integer = { - - is_even: function(n){ - return n % 2 === 0; - }, - - is_odd: function(n){ - return n % 2 !== 0; - }, - - parse: function(bin){ - let result = parseInt(bin); - - if(isNaN(result)){ - return Kernel.SpecialForms.atom("error"); - } - - let indexOfDot = bin.indexOf("."); - - if(indexOfDot >= 0){ - return Kernel.SpecialForms.tuple(result, bin.substring(indexOfDot)); - } - - return Kernel.SpecialForms.tuple(result, ""); - }, - - to_char_list: function(number, base = 10){ - return number.toString(base).split(''); - }, - - to_string: function(number, base = 10){ - return number.toString(base); - } -}; - -export default Integer; diff --git a/priv/javascript/lib/js.js b/priv/javascript/lib/js.js deleted file mode 100644 index fcf82f63..00000000 --- a/priv/javascript/lib/js.js +++ /dev/null @@ -1,11 +0,0 @@ -let JS = { - get_property_or_call_function: function(item, property){ - if(item[property] instanceof Function){ - return item[property](); - }else{ - return item[property]; - } - } -}; - -export default JS; diff --git a/priv/javascript/lib/kernel.js b/priv/javascript/lib/kernel.js deleted file mode 100644 index 2b5a3ae5..00000000 --- a/priv/javascript/lib/kernel.js +++ /dev/null @@ -1,154 +0,0 @@ -import SpecialForms from './kernel/special_forms'; -import Patterns from './patterns/patterns'; -import Tuple from './tuple'; -let Kernel = { - - SpecialForms: SpecialForms, - - tl: function(list){ - return SpecialForms.list(...list.slice(1)); - }, - - hd: function(list){ - return list[0]; - }, - - is_nil: function(x){ - return x == null; - }, - - is_atom: function(x){ - return typeof x === 'symbol'; - }, - - is_binary: function (x){ - return typeof x === 'string' || x instanceof String; - }, - - is_boolean: function (x){ - return typeof x === 'boolean' || x instanceof Boolean; - }, - - is_function: function(x, arity = -1){ - return typeof x === 'function' || x instanceof Function; - }, - - // from: http://stackoverflow.com/a/3885844 - is_float: function(x){ - return x === +x && x !== (x|0); - }, - - is_integer: function(x){ - return x === +x && x === (x|0); - }, - - is_list: function(x){ - return x instanceof Array; - }, - - is_map: function(x){ - return typeof x === 'object' || x instanceof Object; - }, - - is_number: function(x){ - return Kernel.is_integer(x) || Kernel.is_float(x); - }, - - is_tuple: function(x){ - return x instanceof Tuple; - }, - - length: function(x){ - return x.length; - }, - - is_pid: function(x){ - return false; - }, - - is_port: function(x){ - - }, - - is_reference: function(x){ - - }, - - is_bitstring: function(x){ - return Kernel.is_binary(x) || x instanceof SpecialForms.bitstring; - }, - - __in__: function(left, right){ - for(let x of right){ - if(Kernel.match__qmark__(left, x)){ - return true; - } - } - - return false; - }, - - abs: function(number){ - return Math.abs(number); - }, - - round: function(number){ - return Math.round(number); - }, - - elem: function(tuple, index){ - if(Kernel.is_list(tuple)){ - return tuple[index]; - } - - return tuple.get(index); - }, - - rem: function(left, right){ - return left % right; - }, - - div: function(left, right){ - return left / right; - }, - - and: function(left, right){ - return left && right; - }, - - or: function(left, right){ - return left || right; - }, - - not: function(arg){ - return !arg; - }, - - apply: function(module, func, args){ - if(arguments.length === 3){ - return module[func].apply(null, args); - }else{ - return module.apply(null, func); - } - }, - - to_string: function(arg){ - if(Kernel.is_tuple(arg)){ - return Tuple.to_string(arg); - } - - return arg.toString(); - }, - - throw: function(e){ - throw e; - }, - - match__qmark__: function(pattern, expr, guard = () => true){ - return Patterns.match_no_throw(pattern, expr, guard) != null; - } -}; - -export default Kernel; - - diff --git a/priv/javascript/lib/kernel/special_forms.js b/priv/javascript/lib/kernel/special_forms.js deleted file mode 100644 index 3e919d4e..00000000 --- a/priv/javascript/lib/kernel/special_forms.js +++ /dev/null @@ -1,184 +0,0 @@ -import BitString from '../bit_string'; -import Tuple from '../tuple'; -import Enum from '../enum'; -import * as Patterns from '../patterns/patterns'; - -let SpecialForms = { - - __DIR__: function(){ - if(__dirname){ - return __dirname; - } - - if(document.currentScript){ - return document.currentScript.src; - } - - return null; - }, - - atom: function(_value) { - return Symbol.for(_value); - }, - - list: function(...args){ - return Object.freeze(args); - }, - - bitstring: function(...args){ - return new BitString(...args); - }, - - bound: function(_var){ - return Patterns.bound(_var); - }, - - _case: function(condition, clauses){ - return Patterns.defmatch(...clauses)(condition); - }, - - cond: function(clauses){ - for(let clause of clauses){ - if(clause[0]){ - return clause[1](); - } - } - - throw new Error(); - }, - - fn: function(clauses){ - return Patterns.defmatch(clauses); - }, - - map: function(obj){ - return Object.freeze(obj); - }, - - map_update: function(map, values){ - let obj = Object.assign({}, map); - return Object.freeze(Object.assign(obj, values)); - }, - - _for: function(collections, fun, filter = () => true, into = [], previousValues = []){ - let pattern = collections[0][0]; - let collection = collections[0][1]; - - if(collections.length === 1){ - - for(let elem of collection){ - let r = Patterns.match_no_throw(pattern, elem); - let args = previousValues.concat(r); - - if(r && filter.apply(this, args)){ - into = Enum.into([fun.apply(this, args)], into); - } - } - - return into; - }else{ - let _into = [] - - for(let elem of collection){ - let r = Patterns.match_no_throw(pattern, elem); - if(r){ - _into = Enum.into(this._for(collections.slice(1), fun, filter, _into, previousValues.concat(r)), into); - } - } - - return _into; - } - }, - - receive: function(receive_fun, timeout_in_ms = null, timeout_fn = (time) => true){ - if (timeout_in_ms == null || timeout_in_ms === System.for('infinity')) { - while(true){ - if(self.mailbox.length !== 0){ - let message = self.mailbox[0]; - self.mailbox = self.mailbox.slice(1); - return receive_fun(message); - } - } - }else if(timeout_in_ms === 0){ - if(self.mailbox.length !== 0){ - let message = self.mailbox[0]; - self.mailbox = self.mailbox.slice(1); - return receive_fun(message); - }else{ - return null; - } - }else{ - let now = Date.now(); - while(Date.now() < (now + timeout_in_ms)){ - if(self.mailbox.length !== 0){ - let message = self.mailbox[0]; - self.mailbox = self.mailbox.slice(1); - return receive_fun(message); - } - } - - return timeout_fn(timeout_in_ms); - } - }, - - tuple: function(...args){ - return new Tuple(...args); - }, - - - _try: function(do_fun, rescue_function, catch_fun, else_function, after_function){ - let result = null; - - try{ - result = do_fun(); - }catch(e){ - let ex_result = null; - - if(rescue_function){ - try{ - ex_result = rescue_function(e); - return ex_result; - }catch(ex){ - if(ex instanceof Patterns.MatchError){ - throw ex; - } - } - } - - if(catch_fun){ - try{ - ex_result = catch_fun(e); - return ex_result; - }catch(ex){ - if(ex instanceof Patterns.MatchError){ - throw ex; - } - } - } - - throw e; - - }finally{ - if(after_function){ - after_function(); - } - } - - if(else_function){ - try{ - return else_function(result); - }catch(ex){ - if(ex instanceof Patterns.MatchError){ - throw new Error("No Match Found in Else"); - } - - throw ex; - } - }else{ - return result; - } - } - -}; - -export default SpecialForms; diff --git a/priv/javascript/lib/keyword.js b/priv/javascript/lib/keyword.js deleted file mode 100644 index 88532fa4..00000000 --- a/priv/javascript/lib/keyword.js +++ /dev/null @@ -1,25 +0,0 @@ -import Kernel from './kernel'; - -let Keyword = {}; - -Keyword.has_key__qm__ = function(keywords, key){ - for(let keyword of keywords){ - if(Kernel.elem(keyword, 0) == key){ - return true; - } - } - - return false; -} - -Keyword.get = function(keywords, key, the_default = null){ - for(let keyword of keywords){ - if(Kernel.elem(keyword, 0) == key){ - return Kernel.elem(keyword, 1); - } - } - - return the_default; -} - -export default Keyword; \ No newline at end of file diff --git a/priv/javascript/lib/list.js b/priv/javascript/lib/list.js deleted file mode 100644 index 5f69478b..00000000 --- a/priv/javascript/lib/list.js +++ /dev/null @@ -1,272 +0,0 @@ -import Kernel from './kernel'; - -let List = {}; - -List.delete = function(list, item){ - let new_value = []; - let value_found = false; - - for(let x of list){ - if(x === item && value_found !== false){ - new_value.push(x); - value_found = true; - }else if(x !== item){ - new_value.push(x); - } - } - - return Kernel.SpecialForms.list(...new_value); -}; - -List.delete_at = function(list, index){ - let new_value = []; - - for(let i = 0; i < list.length; i++){ - if(i !== index){ - new_value.push(list[i]); - } - } - - return Kernel.SpecialForms.list(...new_value); -}; - -List.duplicate = function(elem, n){ - let new_value = []; - - for (var i = 0; i < n; i++) { - new_value.push(elem); - } - - return Kernel.SpecialForms.list(...new_value); -}; - -List.first = function(list){ - return list[0]; -}; - -List.flatten = function(list, tail = Kernel.SpecialForms.list()){ - let new_value = []; - - for(let x of list){ - if(Kernel.is_list(x)){ - new_value = new_value.concat(List.flatten(x)); - }else{ - new_value.push(x); - } - } - - new_value = new_value.concat(tail); - - return Kernel.SpecialForms.list(...new_value); -}; - -List.foldl = function(list, acc, func){ - return list.reduce(func, acc); -}; - -List.foldr = function(list, acc, func){ - let new_acc = acc; - - for (var i = list.length - 1; i >= 0; i--) { - new_acc = func(list[i], new_acc); - } - - return new_acc; -}; - -List.insert_at = function(list, index, value){ - let new_value = []; - - for(let i = 0; i < list.length; i++){ - if(i === index){ - new_value.push(value); - new_value.push(list[i]); - }else{ - new_value.push(list[i]); - } - } - - return Kernel.SpecialForms.list(...new_value); -}; - -List.keydelete = function(list, key, position){ - let new_list = []; - - for(let i = 0; i < list.length; i++){ - if(!Kernel.match__qmark__(list[i][position], key)){ - new_list.push(list[i]); - } - } - - return Kernel.SpecialForms.list(...new_list); -}; - -List.keyfind = function(list, key, position, _default = null){ - - for(let i = 0; i < list.length; i++){ - if(Kernel.match__qmark__(list[i][position], key)){ - return list[i]; - } - } - - return _default; -}; - -List.keymember__qmark__ = function(list, key, position){ - - for(let i = 0; i < list.length; i++){ - if(Kernel.match__qmark__(list[i][position], key)){ - return true; - } - } - - return false; -}; - -List.keyreplace = function(list, key, position, new_tuple){ - let new_list = []; - - for(let i = 0; i < list.length; i++){ - if(!Kernel.match__qmark__(list[i][position], key)){ - new_list.push(list[i]); - }else{ - new_list.push(new_tuple); - } - } - - return Kernel.SpecialForms.list(...new_list); -}; - - -List.keysort = function(list, position){ - let new_list = list; - - new_list.sort(function(a, b){ - if(position === 0){ - if(a[position].value < b[position].value){ - return -1; - } - - if(a[position].value > b[position].value){ - return 1; - } - - return 0; - }else{ - if(a[position] < b[position]){ - return -1; - } - - if(a[position] > b[position]){ - return 1; - } - - return 0; - } - - }); - - return Kernel.SpecialForms.list(...new_list); -}; - -List.keystore = function(list, key, position, new_tuple){ - let new_list = []; - let replaced = false; - - for(let i = 0; i < list.length; i++){ - if(!Kernel.match__qmark__(list[i][position], key)){ - new_list.push(list[i]); - }else{ - new_list.push(new_tuple); - replaced = true; - } - } - - if(!replaced){ - new_list.push(new_tuple); - } - - return Kernel.SpecialForms.list(...new_list); -}; - -List.last = function(list){ - return list[list.length - 1]; -}; - -List.replace_at = function(list, index, value){ - let new_value = []; - - for(let i = 0; i < list.length; i++){ - if(i === index){ - new_value.push(value); - }else{ - new_value.push(list[i]); - } - } - - return Kernel.SpecialForms.list(...new_value); -}; - -List.update_at = function(list, index, fun){ - let new_value = []; - - for(let i = 0; i < list.count(); i++){ - if(i === index){ - new_value.push(fun(list.get(i))); - }else{ - new_value.push(list.get(i)); - } - } - - return new_value; -}; - -List.wrap = function(list){ - if(Kernel.is_list(list)){ - return list; - }else if(list == null){ - return Kernel.SpecialForms.list(); - }else{ - return Kernel.SpecialForms.list(list); - } -}; - -List.zip = function(list_of_lists){ - if(list_of_lists.length === 0){ - return Kernel.SpecialForms.list(); - } - - let new_value = []; - let smallest_length = list_of_lists[0]; - - for(let x of list_of_lists){ - if(x.length < smallest_length){ - smallest_length = x.length; - } - } - - for(let i = 0; i < smallest_length; i++){ - let current_value = []; - for(let j = 0; j < list_of_lists.length; j++){ - current_value.push(list_of_lists[j][i]); - } - - new_value.push(Kernel.SpecialForms.tuple(...current_value)); - } - - return Kernel.SpecialForms.list(...new_value); -}; - -List.to_tuple = function(list){ - return Kernel.SpecialForms.tuple.apply(null, list); -}; - -List.append = function(list, value){ - return Kernel.SpecialForms.list(...list.concat([value])); -}; - -List.concat = function(left, right){ - return left.concat(right); -}; - -export default List; diff --git a/priv/javascript/lib/patterns/patterns.js b/priv/javascript/lib/patterns/patterns.js deleted file mode 100644 index c586d86f..00000000 --- a/priv/javascript/lib/patterns/patterns.js +++ /dev/null @@ -1,14 +0,0 @@ -/* @flow */ - -import { defmatch, match, MatchError, match_no_throw, patternMap, Case, make_case } from "./patterns/defmatch"; -import { variable, wildcard, startsWith, capture, headTail, type, bound } from "./patterns/types"; - - -export default { - defmatch, match, MatchError, match_no_throw, patternMap, - variable, wildcard, startsWith, - capture, headTail, type, bound, Case, make_case -}; - - - diff --git a/priv/javascript/lib/patterns/patterns/checks.js b/priv/javascript/lib/patterns/patterns/checks.js deleted file mode 100644 index 72a96485..00000000 --- a/priv/javascript/lib/patterns/patterns/checks.js +++ /dev/null @@ -1,92 +0,0 @@ -/* @flow */ - -import { Variable, Wildcard, HeadTail, Capture, Type, StartsWith, Bound } from "./types"; -import Tuple from '../../tuple'; - -function is_number(value: any): boolean { - return typeof value === 'number'; -} - -function is_string(value: any): boolean{ - return typeof value === 'string'; -} - -function is_tuple(value: any): boolean{ - return value instanceof Tuple; -} - -function is_boolean(value: any): boolean { - return typeof value === 'boolean'; -} - -function is_symbol(value: any): boolean { - return typeof value === 'symbol'; -} - -function is_null(value: any): boolean { - return value === null; -} - -function is_undefined(value: any): boolean { - return typeof value === 'undefined'; -} - -function is_function(value: any): boolean { - return Object.prototype.toString.call(value) == '[object Function]'; -} - -function is_variable(value: any): boolean { - return value instanceof Variable; -} - -function is_wildcard(value: any): boolean { - return value instanceof Wildcard; -} - -function is_headTail(value: any): boolean { - return value instanceof HeadTail; -} - -function is_capture(value: any): boolean { - return value instanceof Capture; -} - -function is_type(value: any): boolean { - return value instanceof Type; -} - -function is_startsWith(value: any): boolean { - return value instanceof StartsWith; -} - -function is_bound(value: any): boolean { - return value instanceof Bound; -} - -function is_object(value: any): boolean { - return typeof value === 'object'; -} - -function is_array(value: any): boolean { - return Array.isArray(value); -} - -export default { - is_number, - is_string, - is_boolean, - is_symbol, - is_null, - is_undefined, - is_function, - is_variable, - is_wildcard, - is_headTail, - is_capture, - is_type, - is_startsWith, - is_bound, - is_object, - is_array, - is_tuple -}; diff --git a/priv/javascript/lib/patterns/patterns/defmatch.js b/priv/javascript/lib/patterns/patterns/defmatch.js deleted file mode 100644 index bd73c4a4..00000000 --- a/priv/javascript/lib/patterns/patterns/defmatch.js +++ /dev/null @@ -1,87 +0,0 @@ -/* @flow */ - -import { buildMatch } from "./match"; - -export class MatchError extends Error { - constructor(arg: any) { - super(); - - if(typeof arg === 'symbol'){ - this.message = 'No match for: ' + arg.toString(); - }else{ - this.message = 'No match for: ' + arg; - } - - this.stack = (new Error()).stack; - this.name = this.constructor.name; - } -} - - -export class Case { - pattern: Function; - fn: Function; - guard: Function; - - constructor(pattern: Array, fn: Function, guard: Function = () => true){ - this.pattern = buildMatch(pattern); - this.fn = fn; - this.guard = guard; - } -} - -export function make_case(pattern: Array, fn: Function, guard: Function = () => true): Case { - return new Case(pattern, fn, guard); -} - -export function defmatch(...cases: Array): Function { - return function(...args: Array): any { - for (let processedCase of cases) { - let result = []; - if (processedCase.pattern(args, result) && processedCase.guard.apply(this, result)) { - return processedCase.fn.apply(this, result); - } - } - - throw new MatchError(args); - }; -} - -export function match(pattern: any, expr: any, guard: Function = () => true): Array { - let result = []; - let processedPattern = buildMatch(pattern); - if (processedPattern(expr, result) && guard.apply(this, result)){ - return result; - }else{ - throw new MatchError(expr); - } -} - -export function match_no_throw(pattern: any, expr: any, guard: Function = () => true): ?Array { - try{ - return match(pattern, expr, guard); - }catch(e){ - if(e instanceof MatchError){ - return null; - } - - throw e; - } -} - -export function patternMap(collection: Array, pattern: any, fun: Function, guard: Function = () => true): Array { - let ret = []; - - for(let elem of collection){ - try{ - let result = fun.apply(this, match(pattern, elem, guard)); - ret = ret.concat(result); - }catch(e){ - if(!(e instanceof MatchError)){ - throw e; - } - } - } - - return ret; -} \ No newline at end of file diff --git a/priv/javascript/lib/patterns/patterns/match.js b/priv/javascript/lib/patterns/patterns/match.js deleted file mode 100644 index cb99bc25..00000000 --- a/priv/javascript/lib/patterns/patterns/match.js +++ /dev/null @@ -1,72 +0,0 @@ -/* @flow */ -import Checks from "./checks"; -import Resolvers from "./resolvers"; - -export function buildMatch(pattern: any): Function { - - if(Checks.is_tuple(pattern)){ - return Resolvers.resolveTuple(pattern); - } - - if(Checks.is_variable(pattern)){ - return Resolvers.resolveVariable(pattern); - } - - if(Checks.is_wildcard(pattern)){ - return Resolvers.resolveWildcard(pattern); - } - - if(Checks.is_undefined(pattern)){ - return Resolvers.resolveWildcard(pattern); - } - - if(Checks.is_headTail(pattern)){ - return Resolvers.resolveHeadTail(pattern); - } - - if(Checks.is_startsWith(pattern)){ - return Resolvers.resolveStartsWith(pattern); - } - - if(Checks.is_capture(pattern)){ - return Resolvers.resolveCapture(pattern); - } - - if(Checks.is_bound(pattern)){ - return Resolvers.resolveBound(pattern); - } - - if(Checks.is_type(pattern)){ - return Resolvers.resolveType(pattern); - } - - if(Checks.is_array(pattern)){ - return Resolvers.resolveArray(pattern); - } - - if(Checks.is_number(pattern)){ - return Resolvers.resolveNumber(pattern); - } - - if(Checks.is_string(pattern)){ - return Resolvers.resolveString(pattern); - } - - if(Checks.is_boolean(pattern)){ - return Resolvers.resolveBoolean(pattern); - } - - if(Checks.is_symbol(pattern)){ - return Resolvers.resolveSymbol(pattern); - } - - if(Checks.is_null(pattern)){ - return Resolvers.resolveNull(pattern); - } - - if(Checks.is_object(pattern)){ - return Resolvers.resolveObject(pattern); - } - - return Resolvers.resolveNoMatch(); -} \ No newline at end of file diff --git a/priv/javascript/lib/patterns/patterns/resolvers.js b/priv/javascript/lib/patterns/patterns/resolvers.js deleted file mode 100644 index 593bec92..00000000 --- a/priv/javascript/lib/patterns/patterns/resolvers.js +++ /dev/null @@ -1,200 +0,0 @@ -/* @flow */ - -import Checks from "./checks"; -import * as Types from "./types"; -import { buildMatch } from "./match"; -import Tuple from '../../tuple'; - -function resolveTuple(pattern: any): Function { - let matches = []; - - for(let elem of pattern){ - matches.push(buildMatch(elem)); - } - - return function(value: any, args: Array): boolean { - if(!Checks.is_tuple(value) || value.count() != pattern.count()){ - return false; - } - - - return value.values.every(function(v, i) { - return matches[i](value.get(i), args); - }); - }; -} - -function resolveSymbol(pattern: any): Function { - return function(value: any): boolean { - return Checks.is_symbol(value) && value === pattern; - }; -} - -function resolveString(pattern: any): Function { - return function(value: any): boolean { - return Checks.is_string(value) && value === pattern - }; -} - -function resolveNumber(pattern: any): Function { - return function(value: any): boolean { - return Checks.is_number(value) && value === pattern; - }; -} - -function resolveBoolean(pattern: any): Function { - return function(value: any): boolean { - return Checks.is_boolean(value) && value === pattern - }; -} - -function resolveFunction(pattern: any): Function { - return function(value: any): boolean { - return Checks.is_function(value) && value === pattern - }; -} - -function resolveNull(pattern: any): Function { - return function(value: any): boolean { - return Checks.is_null(value); - }; -} - -function resolveBound(pattern: Types.Bound): Function { - return function(value: any, args: Array): boolean { - if(typeof value === typeof pattern.value && value === pattern.value){ - args.push(value); - return true; - } - - return false; - }; -} - -function resolveWildcard(): Function { - return function(): boolean { - return true; - }; -} - -function resolveVariable(): Function { - return function(value: any, args: Array): boolean { - args.push(value); - return true; - }; -} - -function resolveHeadTail(): Function { - return function(value: any, args: Array): boolean { - if(!Checks.is_array(value) || value.length < 2){ - return false; - } - - const head = value[0]; - const tail = value.slice(1); - - args.push(head); - args.push(tail); - - return true; - }; -} - -function resolveCapture(pattern: Types.Capture): Function { - const matches = buildMatch(pattern.value); - - return function(value: any, args: Array): boolean { - if(matches(value, args)){ - args.push(value); - return true; - } - - return false; - }; -} - -function resolveStartsWith(pattern: Types.StartsWith): Function { - const prefix = pattern.prefix; - - return function(value: any, args: Array): boolean { - if(Checks.is_string(value) && value.startsWith(prefix)){ - args.push(value.substring(prefix.length)); - return true; - } - - return false; - }; -} - -function resolveType(pattern: Types.Type): Function { - return function(value: any, args: Array): boolean { - if(!value instanceof pattern.type){ - return false; - } - - const matches = buildMatch(pattern.objPattern); - return matches(value, args) && args.push(value) > 0; - }; -} - -function resolveArray(pattern: Array): Function { - const matches = pattern.map(x => buildMatch(x)); - - return function(value: any, args: Array): boolean { - if(!Checks.is_array(value) || value.length != pattern.length){ - return false; - } - - return value.every(function(v, i) { - return matches[i](value[i], args); - }); - }; -} - -function resolveObject(pattern: Object): Function { - let matches = {}; - - for(let key of Object.keys(pattern)){ - matches[key] = buildMatch(pattern[key]); - } - - return function(value: any, args: Array): boolean { - if(!Checks.is_object(value) || pattern.length > value.length){ - return false; - } - - for(let key of Object.keys(pattern)){ - if(!(key in value) || !matches[key](value[key], args) ){ - return false; - } - } - - return true; - }; -} - -function resolveNoMatch(): Function { - return function(): boolean { - return false; - } -} - -export default { - resolveBound, - resolveWildcard, - resolveVariable, - resolveHeadTail, - resolveCapture, - resolveStartsWith, - resolveType, - resolveArray, - resolveObject, - resolveNoMatch, - resolveSymbol, - resolveString, - resolveNumber, - resolveBoolean, - resolveFunction, - resolveNull, - resolveTuple -} \ No newline at end of file diff --git a/priv/javascript/lib/patterns/patterns/types.js b/priv/javascript/lib/patterns/patterns/types.js deleted file mode 100644 index dbd004a2..00000000 --- a/priv/javascript/lib/patterns/patterns/types.js +++ /dev/null @@ -1,80 +0,0 @@ -/* @flow */ -export class Variable { - name: ?string; - - constructor(name: ?string = null) { - this.name = name; - } -} - -export class Wildcard { - constructor() { - } -} - -export class StartsWith { - prefix: string; - - constructor(prefix: string) { - this.prefix = prefix; - } -} - -export class Capture { - value: any; - - constructor(value: any) { - this.value = value; - } -} - -export class HeadTail { - constructor() { - } -} - -export class Type { - type: any; - objPattern: Object; - - constructor(type: any, objPattern: Object = {}) { - this.type = type - this.objPattern = objPattern - } -} - -export class Bound { - value: any; - - constructor(value: any) { - this.value = value; - } -} - -export function variable(name: ?string = null): Variable { - return new Variable(name); -} - -export function wildcard(): Wildcard { - return new Wildcard(); -} - -export function startsWith(prefix: string): StartsWith { - return new StartsWith(prefix); -} - -export function capture(value: any): Capture { - return new Capture(value); -} - -export function headTail(): HeadTail { - return new HeadTail(); -} - -export function type(type: any, objPattern: Object = {}): Type { - return new Type(type, objPattern); -} - -export function bound(value: any): Bound { - return new Bound(value); -} diff --git a/priv/javascript/lib/post_office.js b/priv/javascript/lib/post_office.js deleted file mode 100644 index bd3f741e..00000000 --- a/priv/javascript/lib/post_office.js +++ /dev/null @@ -1,60 +0,0 @@ -/* @flow */ -function update(map: Map, key: Symbol, value: any): Map { - let m = new Map(map); - m.set(key, value); - return m; -} - -function remove(map: Map, key: Symbol): Map { - let m = new Map(map); - m.delete(key); - return m; -} - -class PostOffice { - mailboxes: Map; - subscribers: Map; - - constructor(){ - this.mailboxes = new Map(); - this.subscribers = new Map(); - } - - send(address: Symbol, message: any): void { - this.mailboxes = update(this.mailboxes, address, this.mailboxes.get(address).concat([message])); - - if(this.subscribers.get(address)){ - this.subscribers.get(address)(); - } - } - - receive(address: Symbol): any { - let result = this.mailboxes.get(address)[0]; - - this.mailboxes = update(this.mailboxes, address, this.mailboxes.get(address).slice(1)); - return result; - } - - peek(address: Symbol): any { - return this.mailboxes.get(address)[0]; - } - - add_mailbox(address: Symbol = Symbol()): Symbol { - this.mailboxes = update(this.mailboxes, address, []); - return address; - } - - remove_mailbox(address: Symbol): void { - this.mailboxes = remove(this.mailboxes, address); - } - - subscribe(address: Symbol, subscribtion_fn: Function): void { - this.subscribers = update(this.subscribers, address, subscribtion_fn); - } - - unsubscribe(address: Symbol): void { - this.subscribers = remove(this.subscribers, address); - } -} - -export default PostOffice \ No newline at end of file diff --git a/priv/javascript/lib/range.js b/priv/javascript/lib/range.js deleted file mode 100644 index 274eb95b..00000000 --- a/priv/javascript/lib/range.js +++ /dev/null @@ -1,44 +0,0 @@ -let Range = function(_first, _last){ - if (!(this instanceof Range)){ - return new Range(_first, _last); - } - - this.first = function(){ - return _first; - }; - - this.last = function(){ - return _last; - }; - - let _range = []; - - for(let i = _first; i <= _last; i++){ - _range.push(i); - } - - _range = Object.freeze(_range); - - this.value = function(){ - return _range; - }; - - this.length = function(){ - return _range.length; - }; - - return this; -}; - -Range.prototype[Symbol.iterator] = function(){ - return this.value()[Symbol.iterator](); -}; - -Range.new = function (first, last) { - return Range(first, last); -}; - -Range.range__qmark__ = function (range) { - return range instanceof Range; -}; -export default Range; diff --git a/priv/javascript/lib/tuple.js b/priv/javascript/lib/tuple.js deleted file mode 100644 index 08c4d17e..00000000 --- a/priv/javascript/lib/tuple.js +++ /dev/null @@ -1,90 +0,0 @@ -import Kernel from './kernel'; - -class Tuple { - - constructor(...args){ - this.values = Object.freeze(args); - } - - get(index) { - return this.values[index]; - } - - count() { - return this.values.length; - } - - [Symbol.iterator]() { - return this.values[Symbol.iterator](); - } - - toString() { - var i, s = ""; - for (i = 0; i < this.values.length; i++) { - if (s !== "") { - s += ", "; - } - s += this.values[i].toString(); - } - - return "{" + s + "}"; - } - - static to_string(tuple){ - return tuple.toString(); - }; - - static delete_at(tuple, index){ - let new_list = []; - - for (var i = 0; i < tuple.count(); i++) { - if(i !== index){ - new_list.push(tuple.get(i)); - } - } - - return Kernel.SpecialForms.tuple.apply(null, new_list); - }; - - static duplicate(data, size){ - let array = []; - - for (var i = size - 1; i >= 0; i--) { - array.push(data); - } - - return Kernel.SpecialForms.tuple.apply(null, array); - }; - - static insert_at(tuple, index, term){ - let new_tuple = []; - - for (var i = 0; i <= tuple.count(); i++) { - if(i === index){ - new_tuple.push(term); - i++; - new_tuple.push(tuple.get(i)); - }else{ - new_tuple.push(tuple.get(i)); - } - } - - return Kernel.SpecialForms.tuple.apply(null, new_tuple); - }; - - static from_list(list){ - return Kernel.SpecialForms.tuple.apply(null, list); - }; - - static to_list(tuple){ - let new_list = []; - - for (var i = 0; i < tuple.count(); i++) { - new_list.push(tuple.get(i)); - } - - return Kernel.SpecialForms.list(...new_list); - }; -} - -export default Tuple; \ No newline at end of file diff --git a/priv/javascript/tests/atom.spec.js b/priv/javascript/tests/atom.spec.js deleted file mode 100644 index c3c390cc..00000000 --- a/priv/javascript/tests/atom.spec.js +++ /dev/null @@ -1,26 +0,0 @@ -var Kernel = require('../lib/kernel'); -var Atom = require('../lib/atom'); -var expect = require('chai').expect; - -describe('Atom', function(){ - - describe('to_string', function(){ - it('must display correctly', function(){ - let atom = Kernel.SpecialForms.atom("test"); - expect(Atom.to_string(atom)).to.equal("test"); - }) - }) - - describe('to_char_list', function(){ - it('must return a list of characters', function(){ - let atom = Kernel.SpecialForms.atom("test"); - let char_list = Atom.to_char_list(atom); - expect(char_list.length).to.equal(4); - expect(char_list[0]).to.equal('t'); - expect(char_list[1]).to.equal('e'); - expect(char_list[2]).to.equal('s'); - expect(char_list[3]).to.equal('t'); - }) - }) -}) - diff --git a/priv/javascript/tests/bit_string.spec.js b/priv/javascript/tests/bit_string.spec.js deleted file mode 100644 index 9ba94106..00000000 --- a/priv/javascript/tests/bit_string.spec.js +++ /dev/null @@ -1,55 +0,0 @@ -var BitString = require('../lib/bit_string'); -var Kernel = require('../lib/kernel'); -var expect = require('chai').expect; - -describe('BitString', function(){ - - describe('creation', function(){ - it('create properly', function(){ - let bs = Kernel.SpecialForms.bitstring(BitString.integer(1)); - expect(Kernel.match__qmark__(bs.value, [1])).to.equal(true); - - bs = Kernel.SpecialForms.bitstring(BitString.binary("foo")); - expect(Kernel.match__qmark__(bs.value, [102, 111, 111])).to.equal(true); - - bs = Kernel.SpecialForms.bitstring(BitString.integer(0), BitString.binary("foo")); - expect(Kernel.match__qmark__(bs.value, [0, 102, 111, 111])).to.equal(true); - - bs = Kernel.SpecialForms.bitstring(BitString.float(3.14)); - expect(Kernel.match__qmark__(bs.value, [64, 9, 30, 184, 81, 235, 133, 31])).to.equal(true); - - bs = Kernel.SpecialForms.bitstring(BitString.signed(BitString.integer(-100))); - expect(Kernel.match__qmark__(bs.value, [156])).to.equal(true); - }); - }); - - describe('UTF conversions', function(){ - it('toUTF8Array', function(){ - let bs = BitString.toUTF8Array("fo≈"); - expect(Kernel.match__qmark__(bs, [102, 111, 226, 137, 136])).to.equal(true); - }); - - it('toUTF16Array', function(){ - let bs = BitString.toUTF16Array("fo≈"); - expect(Kernel.match__qmark__(bs, [0, 102, 0, 111, 34, 72])).to.equal(true); - }); - - it('toUTF32Array', function(){ - let bs = BitString.toUTF32Array("fo≈"); - expect(Kernel.match__qmark__(bs, [0, 0, 0, 102, 0, 0, 0, 111, 0, 0, 34, 72])).to.equal(true); - }); - }); - - describe('Float conversions', function(){ - it('float32ToBytes', function(){ - let bs = BitString.float32ToBytes(3.14); - expect(Kernel.match__qmark__(bs, [64, 72, 245, 195])).to.equal(true); - }); - - it('float64ToBytes', function(){ - let bs = BitString.float64ToBytes(3.14); - expect(Kernel.match__qmark__(bs, [64, 9, 30, 184, 81, 235, 133, 31])).to.equal(true); - }); - }); -}); - diff --git a/priv/javascript/tests/case.spec.js b/priv/javascript/tests/case.spec.js deleted file mode 100644 index 7a2d7bb4..00000000 --- a/priv/javascript/tests/case.spec.js +++ /dev/null @@ -1,27 +0,0 @@ -var Patterns = require("../lib/patterns/patterns"); -var Enum = require('../lib/enum'); -var Kernel = require('../lib/kernel'); -var expect = require('chai').expect; - - -describe('case', () => { - - it('case', () => { - let clauses = [ - Patterns.make_case( - [Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom("selector"), Patterns.variable(), Patterns.variable())], - function(i, value){ return value; }, - function(i){ return Kernel.is_integer(i); } - ), - Patterns.make_case( - [Patterns.variable()], - function(value){ return value; } - ) - ]; - - let result = Kernel.SpecialForms._case("thing", clauses); - - expect(result).to.equal("thing"); - }); - -}); \ No newline at end of file diff --git a/priv/javascript/tests/cond.spec.js b/priv/javascript/tests/cond.spec.js deleted file mode 100644 index 7f777756..00000000 --- a/priv/javascript/tests/cond.spec.js +++ /dev/null @@ -1,21 +0,0 @@ -var Patterns = require("../lib/patterns/patterns"); -var Enum = require('../lib/enum'); -var Kernel = require('../lib/kernel'); -var expect = require('chai').expect; - - -describe('cond', () => { - - it('cond', () => { - let clauses = [ - [ 1 + 1 == 1, () => "This will never match"], - [ 2 * 2 != 4, () => "Nor this"], - [ true, () => "This will"], - ]; - - let result = Kernel.SpecialForms.cond(clauses); - - expect(result).to.equal("This will"); - }); - -}); \ No newline at end of file diff --git a/priv/javascript/tests/enum.spec.js b/priv/javascript/tests/enum.spec.js deleted file mode 100644 index bbe29285..00000000 --- a/priv/javascript/tests/enum.spec.js +++ /dev/null @@ -1,8 +0,0 @@ -var Enum = require('../lib/enum'); -var List = require('../lib/list'); -var expect = require('chai').expect; - -describe('Enum', function(){ - -}) - diff --git a/priv/javascript/tests/for.spec.js b/priv/javascript/tests/for.spec.js deleted file mode 100644 index 7045a78c..00000000 --- a/priv/javascript/tests/for.spec.js +++ /dev/null @@ -1,55 +0,0 @@ -var Patterns = require("../lib/patterns/patterns"); -var Enum = require('../lib/enum'); -var Kernel = require('../lib/kernel'); -var expect = require('chai').expect; - -const $ = Patterns.variable(); - -describe('for', () => { - it('simple for', () => { - let collections = [ - [$, [1, 2, 3, 4]] - ]; - - let result = Kernel.SpecialForms._for(collections, (n) => n * 2); - - expect(result).to.eql([2, 4, 6, 8]); - }); - - it('for with multiple generators', () => { - //for x <- [1, 2], y <- [2, 3], do: x*y - let collections = [ - [$, [1, 2]], - [$, [2, 3]] - ]; - - let result = Kernel.SpecialForms._for(collections, (x, y) => x * y); - - expect(result).to.eql([2, 3, 4, 6]); - }); - - - it('for with filter', () => { - //for n <- [1, 2, 3, 4, 5, 6], rem(n, 2) == 0, do: n - let collections = [ - [$, [1, 2, 3, 4, 5, 6]] - ]; - - let result = Kernel.SpecialForms._for(collections, (n) => n, (n) => n % 2 == 0); - - expect(result).to.eql([2, 4, 6]); - }); - - it('for with pattern matching', () => { - //for {:user, name} <- [user: "john", admin: "john", user: "meg"], do - // String.upcase(name) - //end - let collections = [ - [[Symbol.for("user"), $], [[Symbol.for("user"), "john"], [Symbol.for("admin"), "john"], [Symbol.for("user"), "meg"]]] - ]; - - let result = Kernel.SpecialForms._for(collections, (name) => name.toUpperCase()); - - expect(result).to.eql(["JOHN", "MEG"]); - }); -}); \ No newline at end of file diff --git a/priv/javascript/tests/integer.spec.js b/priv/javascript/tests/integer.spec.js deleted file mode 100644 index ef78eb8f..00000000 --- a/priv/javascript/tests/integer.spec.js +++ /dev/null @@ -1,34 +0,0 @@ -var Integer = require('../lib/integer'); -var Kernel = require('../lib/kernel'); -var expect = require('chai').expect; - -describe('Integer', function(){ - it('parses integer string to integer', function(){ - let result = Integer.parse("34"); - expect(Kernel.elem(result, 0)).to.equal(34); - expect(Kernel.elem(result, 1)).to.equal(""); - }); - - it('parses float string to integer', function(){ - let result = Integer.parse("34.5"); - expect(Kernel.elem(result, 0)).to.equal(34); - expect(Kernel.elem(result, 1)).to.equal(".5"); - }); - - it('returns error when invalid', function(){ - let result = Integer.parse("three"); - expect(Kernel.match__qmark__(result, Kernel.SpecialForms.atom('error'))).to.equal(true); - }); - - it('converts base 10 integer to char_list', function(){ - let result = Integer.to_char_list(7); - expect(Kernel.match__qmark__(result, ["7"])).to.equal(true); - }); - - it('converts base 16 integer to char_list', function(){ - let result = Integer.to_char_list(1023, 16); - expect(Kernel.match__qmark__(result, ["3", "f", "f"])).to.equal(true); - }); - -}); - diff --git a/priv/javascript/tests/kernel.spec.js b/priv/javascript/tests/kernel.spec.js deleted file mode 100644 index 078caf0a..00000000 --- a/priv/javascript/tests/kernel.spec.js +++ /dev/null @@ -1,53 +0,0 @@ -var Kernel = require('../lib/kernel'); -var expect = require('chai').expect; - -describe('Kernel', function(){ - - describe('match?', function(){ - it('match numbers', function(){ - expect(Kernel.match__qmark__(1, 1)).to.equal(true); - expect(Kernel.match__qmark__(1.0, 1)).to.equal(true); - expect(Kernel.match__qmark__(2, 1)).to.equal(false); - expect(Kernel.match__qmark__(2, 1 + 1)).to.equal(true); - expect(Kernel.match__qmark__(undefined, 1 + 1)).to.equal(true); - expect(Kernel.match__qmark__(undefined, 1 + 1)).to.equal(true); - }); - - it('match strings', function(){ - expect(Kernel.match__qmark__("", "")).to.equal(true); - expect(Kernel.match__qmark__('', "")).to.equal(true); - expect(Kernel.match__qmark__("Hello", "Hell")).to.equal(false); - expect(Kernel.match__qmark__("Hello", "Hell" + "o")).to.equal(true); - }); - - it('match atoms', function(){ - expect(Kernel.match__qmark__(Kernel.SpecialForms.atom("test"), Kernel.SpecialForms.atom("test"))).to.equal(true); - expect(Kernel.match__qmark__(Kernel.SpecialForms.atom("test"), Kernel.SpecialForms.atom("notest"))).to.equal(false); - }); - - it('match tuples', function(){ - expect(Kernel.match__qmark__(Kernel.SpecialForms.tuple(1, 2, 3), Kernel.SpecialForms.tuple(1, 2, 3))).to.equal(true); - expect(Kernel.match__qmark__(Kernel.SpecialForms.tuple(1, undefined, 3), Kernel.SpecialForms.tuple(1, 2, 3))).to.equal(true); - expect(Kernel.match__qmark__(Kernel.SpecialForms.tuple(1, 2, 3), Kernel.SpecialForms.tuple(1, 2))).to.equal(false); - }); - - it('match list', function(){ - expect(Kernel.match__qmark__([1, 2, 3], [1, 2, 3])).to.equal(true); - expect(Kernel.match__qmark__([1, undefined, 3], [1, 2, 3])).to.equal(true); - expect(Kernel.match__qmark__([1, 2, 3], [1, 2])).to.equal(false); - }); - - it('match map', function(){ - expect(Kernel.match__qmark__({a: 1, b: 2}, {a: 1, b: 2})).to.equal(true); - expect(Kernel.match__qmark__({a: 1}, {a: 1, b: 2})).to.equal(true); - expect(Kernel.match__qmark__({a: undefined}, {a: 1, b: 2})).to.equal(true); - expect(Kernel.match__qmark__({c: 1}, {a: 1, b: 2})).to.equal(false); - expect(Kernel.match__qmark__({c: undefined}, {a: 1, b: 2})).to.equal(false); - }); - - it('match numbers with guards', function(){ - expect(Kernel.match__qmark__(1, 1, () => Kernel.is_number(1))).to.equal(true); - }); - }); -}); - diff --git a/priv/javascript/tests/list.spec.js b/priv/javascript/tests/list.spec.js deleted file mode 100644 index 8f49edc5..00000000 --- a/priv/javascript/tests/list.spec.js +++ /dev/null @@ -1,77 +0,0 @@ -var List = require('../lib/list'); -var Kernel = require('../lib/kernel'); -var expect = require('chai').expect; - -describe('List', function(){ - - describe('duplicate', function(){ - it('must make a tuple with the value duplicated the specified amount of times', function(){ - let t = List.duplicate("value", 3); - - expect(t.length).to.equal(3); - expect(t[0]).to.equal("value"); - expect(t[1]).to.equal("value"); - expect(t[2]).to.equal("value"); - - t = List.duplicate("value", 0); - expect(t.length).to.equal(0); - }) - }) - - describe('delete_at', function(){ - it('must delete first item', function(){ - let t = Kernel.SpecialForms.list(1, 2, 3); - t = List.delete_at(t, 0); - expect(t[0]).to.equal(2); - }) - }) - - describe('delete', function(){ - it('delete item in list', function(){ - let t = Kernel.SpecialForms.list(Kernel.SpecialForms.atom("a"), Kernel.SpecialForms.atom("b"), Kernel.SpecialForms.atom("c")); - t = List.delete(t, Kernel.SpecialForms.atom("b")); - expect(t[1]).to.equal(Kernel.SpecialForms.atom("c")); - }) - }) - - describe('flatten', function(){ - it('must flatten a list into one list', function(){ - let t = Kernel.SpecialForms.list(1, Kernel.SpecialForms.list(2), 3); - - t = List.flatten(t); - - expect(t[0]).to.equal(1); - expect(t[1]).to.equal(2); - expect(t[2]).to.equal(3); - }) - - it('must flatten a deeply nested list into one list', function(){ - let t = Kernel.SpecialForms.list(1, Kernel.SpecialForms.list(2, Kernel.SpecialForms.list(4)), 3); - - t = List.flatten(t); - - expect(t[0]).to.equal(1); - expect(t[1]).to.equal(2); - expect(t[2]).to.equal(4); - expect(t[3]).to.equal(3); - }) - }) - - describe('toString', function(){ - it('must display correctly', function(){ - let t = Kernel.SpecialForms.list(1, 2, 3); - expect(t.toString()).to.equal('1,2,3'); - }) - }) - - describe('destructuring', function(){ - it('destructure into an array', function(){ - let t = Kernel.SpecialForms.list(1, 2, 3); - let [a, b, c] = t; - expect(a).to.equal(1); - expect(b).to.equal(2); - expect(c).to.equal(3); - }) - }) -}) - diff --git a/priv/javascript/tests/patterns/defmatch.spec.js b/priv/javascript/tests/patterns/defmatch.spec.js deleted file mode 100644 index 2b28498f..00000000 --- a/priv/javascript/tests/patterns/defmatch.spec.js +++ /dev/null @@ -1,149 +0,0 @@ -var expect = require('chai').expect; -var Patterns = require("../../lib/patterns/patterns"); -var Tuple = require("../../lib/tuple"); - -const _ = Patterns.wildcard(); -const $ = Patterns.variable(); - -describe('example', () => { - it('must correctly evaluate example', () => { - - let fact = Patterns.defmatch( - Patterns.make_case([0], () => 1), - Patterns.make_case([$], (n) => n * fact(n - 1)) - ); - - let response = fact(0); - expect(response).to.equal(1); - - response = fact(10); - expect(response).to.equal(3628800); - }); -}); - -describe('defmatch', () => { - it('must throw error when no match is found', () => { - - let fn = Patterns.defmatch( - Patterns.make_case([0], () => 1) - ); - - expect(fn.bind(fn, 1)).to.throw("No match for: 1"); - }); - - it('must have wildcard except everything', () => { - - let fn = Patterns.defmatch( - Patterns.make_case([_], () => 1) - ); - - expect(fn(1)).to.equal(1); - expect(fn("1")).to.equal(1); - expect(fn("ABC")).to.equal(1); - expect(fn(() => 34)).to.equal(1); - }); - - it('must work symbols', () => { - - let fn = Patterns.defmatch( - Patterns.make_case([Symbol.for('infinity')], () => 1) - ); - - expect(fn(Symbol.for('infinity'))).to.equal(1); - expect(fn.bind(fn, Symbol('infinity'))).to.throw("No match for: Symbol(infinity)"); - }); - - it('must match on values in object', () => { - - let fn = Patterns.defmatch( - Patterns.make_case([{value: $}], (val) => 1 + val), - Patterns.make_case([{a: {b: {c: $} } }], (val) => 1 - val) - ); - - expect(fn({value: 20})).to.equal(21); - expect(fn({a: {b: {c: 20} } })).to.equal(-19); - }); - - it('must match on objects even when value has more keys', () => { - - let fn = Patterns.defmatch( - Patterns.make_case([{value: $}], (val) => 1 + val), - Patterns.make_case([{a: {b: {c: $} } }], (val) => 1 - val) - ); - - expect(fn({value: 20})).to.equal(21); - expect(fn({a: {b: {c: 20}, d: 10 } })).to.equal(-19); - }); - - it('must match on substrings', () => { - - let fn = Patterns.defmatch( - Patterns.make_case([Patterns.startsWith("Bearer ")], (token) => token) - ); - - expect(fn("Bearer 1234")).to.equal("1234"); - }); - - - it('must work with guards', () => { - - let fn = Patterns.defmatch( - Patterns.make_case([$], (number) => number, (number) => number > 0) - ); - - expect(fn(3)).to.equal(3); - expect(fn.bind(fn, -1)).to.throw("No match for: -1"); - }); - - it('must capture entire match as parameter', () => { - - let fn = Patterns.defmatch( - Patterns.make_case([Patterns.capture({a: {b: {c: $} } })], (val, bound_value) => bound_value["a"]["b"]["c"]) - ); - - expect(fn({a: {b: {c: 20} } })).to.equal(20); - - fn = Patterns.defmatch( - Patterns.make_case([Patterns.capture([1, $, 3, $])], (a, b, bound_value) => bound_value.length) - ); - - expect(fn([1, 2, 3, 4])).to.equal(4); - - fn = Patterns.defmatch( - Patterns.make_case( - [Patterns.capture([1, Patterns.capture({a: {b: {c: $} } }), 3, $])], - (c, two, four, arg) => two["a"]["b"]["c"] - ) - ); - - expect(fn([1, {a: {b: {c: 20} } }, 3, 4])).to.equal(20); - }); - - it('must produce a head and a tail', () => { - - let fn = Patterns.defmatch( - Patterns.make_case( - [Patterns.headTail()], - (head, tail) => tail - ) - ); - - expect(fn([3, 1, 2, 4]).length).to.equal(3); - }); - - it('must match on tuple', () => { - - let fn = Patterns.defmatch( - Patterns.make_case( - [new Tuple(1, 2, 3)], - () => 3 - ) - ); - - expect(fn(new Tuple(1, 2, 3))).to.equal(3); - expect(fn.bind(fn, new Tuple(1, 2, 4))).to.throw("No match for: {1, 2, 4}"); - }); - - - -}); \ No newline at end of file diff --git a/priv/javascript/tests/patterns/match.spec.js b/priv/javascript/tests/patterns/match.spec.js deleted file mode 100644 index 85f40631..00000000 --- a/priv/javascript/tests/patterns/match.spec.js +++ /dev/null @@ -1,48 +0,0 @@ -var expect = require('chai').expect; -var Patterns = require("../../lib/patterns/patterns"); - -const _ = Patterns.wildcard(); -const $ = Patterns.variable(); - -describe('match', () => { - it('must return value on parameter', () => { - let [a] = Patterns.match($, 1); - expect(a).to.equal(1); - }); - - it('must ignore value when wildcard given', () => { - let [a] = Patterns.match(_, 1); - expect(a).to.equal(undefined); - }); - - it('must match on multiple values when an array is given', () => { - let [a, ] = Patterns.match([$, 2, _, 4], [1, 2, 3, 4]); - expect(a).to.equal(1); - }); - - it('must throw an error when there is no match', () => { - expect(Patterns.match.bind(Patterns.match, [$, 2, _, 4], 1)).to.throw("No match for: 1"); - }); - - it('must match values in object', () => { - let [a] = Patterns.match({a: [1, $, 3]}, {a: [1, 2, 3]}); - expect(a).to.equal(2); - }); - - it('must match on capture variables', () => { - let a = 1; - - let [b] = Patterns.match(Patterns.capture(a), 1); - expect(b).to.equal(1); - - let c = {a: 1}; - - let [d] = Patterns.match(Patterns.capture(c), {a: 1}); - expect(d["a"]).to.equal(1); - }); - - it('must throw an error when capture value does not match', () => { - let a = 1; - expect(Patterns.match.bind(Patterns.match, Patterns.capture(a), 2)).to.throw("No match for: 2"); - }); -}); diff --git a/priv/javascript/tests/range.spec.js b/priv/javascript/tests/range.spec.js deleted file mode 100644 index 72201a4e..00000000 --- a/priv/javascript/tests/range.spec.js +++ /dev/null @@ -1,16 +0,0 @@ -var Range = require('../lib/range'); -var expect = require('chai').expect; - -describe('Range', function(){ - - describe('new', function(){ - it('must create a new Range', function(){ - let range = Range.new(0, 2); - - expect(range.first()).to.equal(0); - expect(range.last()).to.equal(2); - expect(range.length()).to.equal(3); - }) - }) -}) - diff --git a/priv/javascript/tests/try.spec.js b/priv/javascript/tests/try.spec.js deleted file mode 100644 index b0d1ba7d..00000000 --- a/priv/javascript/tests/try.spec.js +++ /dev/null @@ -1,37 +0,0 @@ -var Patterns = require("../lib/patterns/patterns"); -var Enum = require('../lib/enum'); -var Kernel = require('../lib/kernel'); -var expect = require('chai').expect; - - -describe('try', () => { - - it('try', () => { - /* - try do - 1 / x - else - y when y < 1 and y > -1 -> - :small - _ -> - :large - end - - */ - - let x = 1; - - let value = Kernel.SpecialForms._try(function() { - return 1 / x; - }, null, null, Patterns.defmatch(Patterns.make_case([Patterns.variable()], function(y) { - return Kernel.SpecialForms.atom('small'); - }, function(y) { - return (y < 1) && (y > -1); - }), Patterns.make_case([Patterns.wildcard()], function() { - return Kernel.SpecialForms.atom('large'); - })), null) - - expect(value).to.equal(Kernel.SpecialForms.atom('large')); - }); - -}); \ No newline at end of file diff --git a/priv/javascript/tests/tuple.spec.js b/priv/javascript/tests/tuple.spec.js deleted file mode 100644 index ade50062..00000000 --- a/priv/javascript/tests/tuple.spec.js +++ /dev/null @@ -1,54 +0,0 @@ -"use strict"; - -var Kernel = require('../lib/kernel'); -var Tuple = require('../lib/tuple'); -var expect = require('chai').expect; - -describe('Tuple', function(){ - - describe('duplicate', function(){ - it('must make a tuple with the value duplicated the specified amount of times', function(){ - let t = Tuple.duplicate("value", 3); - expect(t.count()).to.equal(3); - expect(Kernel.elem(t, 0)).to.equal("value"); - expect(Kernel.elem(t, 1)).to.equal("value"); - expect(Kernel.elem(t, 2)).to.equal("value"); - - t = Tuple.duplicate("value", 0); - expect(t.count()).to.equal(0); - }); - }); - - describe('delete_at', function(){ - it('must delete first item', function(){ - let t = Kernel.SpecialForms.tuple(1, 2, 3); - - t = Tuple.delete_at(t, 0); - - expect(t.get(0)).to.equal(2); - }); - }); - - describe('toString', function(){ - it('must display correctly', function(){ - let t = Kernel.SpecialForms.tuple(1, 2, 3); - expect(Kernel.to_string(t)).to.equal("{1, 2, 3}"); - }); - }); - - describe('from_list', function(){ - it('must create a tuple from a list', function(){ - let list = Kernel.SpecialForms.list(1, 2, 3); - let tuple = Tuple.from_list(list); - expect(Kernel.to_string(tuple)).to.equal("{1, 2, 3}"); - }); - }); - - describe('to_list', function(){ - it('must create a list from a tuple', function(){ - let t = Kernel.SpecialForms.tuple(1, 2, 3); - expect(Tuple.to_list(t).length).to.equal(3); - }); - }); -}); - diff --git a/priv/testrunner/colors.js b/priv/testrunner/colors.js new file mode 100644 index 00000000..b0b48173 --- /dev/null +++ b/priv/testrunner/colors.js @@ -0,0 +1,31 @@ +export default { + Reset: '\x1b[0m', + Bright: '\x1b[1m', + Dim: '\x1b[2m', + Underscore: '\x1b[4m', + Blink: '\x1b[5m', + Reverse: '\x1b[7m', + Hidden: '\x1b[8m', + fg: { + Black: '\x1b[30m', + Red: '\x1b[31m', + Green: '\x1b[32m', + Yellow: '\x1b[33m', + Blue: '\x1b[34m', + Magenta: '\x1b[35m', + Cyan: '\x1b[36m', + White: '\x1b[37m', + Crimson: '\x1b[38m', + }, + bg: { + Black: '\x1b[40m', + Red: '\x1b[41m', + Green: '\x1b[42m', + Yellow: '\x1b[43m', + Blue: '\x1b[44m', + Magenta: '\x1b[45m', + Cyan: '\x1b[46m', + White: '\x1b[47m', + Crimson: '\x1b[48m', + }, +}; diff --git a/priv/testrunner/esm/LICENSE b/priv/testrunner/esm/LICENSE new file mode 100644 index 00000000..2902552d --- /dev/null +++ b/priv/testrunner/esm/LICENSE @@ -0,0 +1,23 @@ +The MIT License (MIT) + +Copyright @std/esm contributors + +Based on reify, copyright Ben Newman + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/priv/testrunner/esm/README.md b/priv/testrunner/esm/README.md new file mode 100644 index 00000000..83c5c840 --- /dev/null +++ b/priv/testrunner/esm/README.md @@ -0,0 +1,131 @@ +# @std/esm + +This fast, small, zero dependency, package is all you need to enable +ES modules in Node 4+ today! + +:book: See the [release post](https://medium.com/web-on-the-edge/es-modules-in-node-today-32cff914e4b) +for all the details. + +Getting started +--- + + 1. Run `npm i --save @std/esm` in your app or package directory. + 2. Add `.esm-cache` to your `.gitignore`. + 3. Create the ESM loader to import your main ES module: + + **index.js** + ```js + require = require("@std/esm")(module) + module.exports = require("./main.mjs").default + ``` + + By default, `@std/esm` **only** processes files of packages that opt-in + with a `@std/esm` options object or `@std/esm` as a dependency, dev + dependency, or peer dependency in their package.json. However, you can + enable processing **all** files with specific options by passing an options + object as the second argument or passing `true` to use the options from + your package.json. + + ```js + const loader1 = require("@std/esm")(module, { cjs: true, esm: "js" }) + const loader2 = require("@std/esm")(module, true) + ``` + +Enable ESM in the Node CLI by loading `@std/esm` with the [`-r` option](https://nodejs.org/api/cli.html#cli_r_require_module): + +```shell +node -r @std/esm file.mjs +``` + +Enable ESM in the Node REPL by loading `@std/esm` upon entering: + +```shell +$ node +> require("@std/esm") +@std/esm enabled +> import p from "path" +undefined +> p.join("hello", "world") +'hello/world' +``` + +*Note: The `"cjs"` and `"gz"` options are [unlocked](#unlockables) in the Node REPL.* + +Standard Features +--- + +The `@std/esm` loader is as spec-compliant +as possible and follows [Node’s rules](https://github.com/nodejs/node-eps/blob/master/002-es-modules.md). + +:point_right: This means, by default, ESM requires the use of the `.mjs` file +extension.
+:unlock: You can [unlock](#unlockables) ESM with the `.js` file extension using +the `"js"` ESM mode. + +Out of the box `@std/esm` just works, no configuration necessary, and supports: + +* [`import`](https://ponyfoo.com/articles/es6-modules-in-depth#import) / [`export`](https://ponyfoo.com/articles/es6-modules-in-depth#export) +* [Dynamic `import()`](https://github.com/tc39/proposal-dynamic-import) +* [Live bindings](https://ponyfoo.com/articles/es6-modules-in-depth#bindings-not-values) +* [Loading `.mjs` files as ESM](https://github.com/nodejs/node-eps/blob/master/002-es-modules.md#32-determining-if-source-is-an-es-module) +* [The file URI scheme](https://en.wikipedia.org/wiki/File_URI_scheme) +* Node 4+ support + +Unlockables +--- + +Unlock extra features with `"@std/esm":options` or +`"@std":{"esm":options}` in your package.json. + +*Note: All options are **off** by default and may be specified as either an object or ESM mode string.* + + + + + + + + + + + + + + + + + + + + + + + + +
+
{
+  "@std/esm": {
+
"esm": +

A string ESM mode

+
    +
  • "mjs" files as ESM (default)
  • +
  • "all" files as ESM
  • +
  • "js" files with import, export, or "use module" as ESM
  • +
+
"cjs": +

A boolean for CJS features in ESM

+ +
"await":

A boolean for top-level await in the main ES module

"gz": +

A boolean for gzipped module support (i.e. .js.gz, .mjs.gz)

+ +
+
  }
+}
+
diff --git a/priv/testrunner/esm/esm.js.gz b/priv/testrunner/esm/esm.js.gz new file mode 100644 index 00000000..d199117d Binary files /dev/null and b/priv/testrunner/esm/esm.js.gz differ diff --git a/priv/testrunner/esm/index.js b/priv/testrunner/esm/index.js new file mode 100644 index 00000000..5eb27ed1 --- /dev/null +++ b/priv/testrunner/esm/index.js @@ -0,0 +1,48 @@ +/* eslint strict: off, node/no-unsupported-features: ["error", { version: 4 }] */ + +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const util = require('util'); +const vm = require('vm'); +const zlib = require('zlib'); + +const esmPath = path.resolve(__dirname, 'esm.js.gz'); +const inspectKey = util.inspect.custom || 'inspect'; + +const descriptor = Object.create(null); +descriptor.value = () => '@std/esm enabled'; + +const mod = new module.constructor(module.id); +mod.filename = __filename; +mod.parent = module.parent; + +const scriptOptions = Object.create(null); +scriptOptions.filename = __filename; + +const content = + `(function(require,module,__filename){${ + zlib.gunzipSync(fs.readFileSync(esmPath)).toString() + }\n})`; + +const compiled = vm.runInThisContext(content, scriptOptions); + +function makeLoaderFunction() { + compiled(require, mod, __filename); + return mod.exports; +} + +const loader = makeLoaderFunction(); + +module.exports = (mod, options) => { + const type = typeof options; + + if (options === true || type === 'function' || (type === 'object' && options !== null)) { + return makeLoaderFunction()(mod, options); + } + + return loader(mod, options); +}; + +Object.freeze(Object.defineProperty(module.exports, inspectKey, descriptor)); diff --git a/priv/testrunner/esm/package.json b/priv/testrunner/esm/package.json new file mode 100644 index 00000000..fa5bb5c5 --- /dev/null +++ b/priv/testrunner/esm/package.json @@ -0,0 +1,20 @@ +{ + "name": "@std/esm", + "version": "0.8.3", + "description": "Enable ES modules in Node today!", + "keywords": "commonjs, ecmascript, export, import, modules, node, require", + "repository": "standard-things/esm", + "license": "MIT", + "author": "John-David Dalton ", + "main": "index.js", + "engines": { + "node": ">=4" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "files": [ + "index.js", + "esm.js.gz" + ] +} diff --git a/priv/testrunner/index.js b/priv/testrunner/index.js new file mode 100644 index 00000000..9dddc392 --- /dev/null +++ b/priv/testrunner/index.js @@ -0,0 +1,28 @@ +require = require('./esm/index.js')(module, { cjs: true, esm: 'js' }); +const runner = require('./testRunner.js').default; +const Colors = require('./colors.js').default; + +const testFiles = process.argv.slice(2); +console.time('Finished in'); +runner + .start(testFiles) + .then((results) => { + const testsFailed = results.failed > 0; + + process.stdout.write('\n\n'); + console.timeEnd('Finished in'); + console.log( + testsFailed ? Colors.fg.Red : Colors.fg.Green, + `${results.tests} tests, ${results.success} succeeded, ${results.failed} failed\n`, + Colors.Reset, + ); + + if (testsFailed) { + process.exit(1); + } else { + process.exit(0); + } + }) + .catch((e) => { + console.log(e); + }); diff --git a/priv/testrunner/testRunner.js b/priv/testrunner/testRunner.js new file mode 100644 index 00000000..3bdd9a7c --- /dev/null +++ b/priv/testrunner/testRunner.js @@ -0,0 +1,134 @@ +import Colors from './colors.js'; +import Vendor from './vendor.build.js'; + +async function start(files) { + const results = { + tests: 0, + success: 0, + failed: 0, + }; + + for (const file of files) { + const mod = await import(file); + if (mod.default.__elixirscript_test_module__) { + runTests(mod, results); + } + } + + return results; +} + +function runSetup(mod, name, incomingContext = new Map()) { + if (mod.default[name]) { + const result = mod.default[name](incomingContext); + + return resolveContext(result, incomingContext); + } + + return incomingContext; +} + +function runTeardown(mod, name, incomingContext) { + if (mod.default[name]) { + const result = mod.default[name](incomingContext); + } +} + +function resolveContext(context, parentContext) { + if (context === Symbol.for('ok')) { + return parentContext; + } else if (context instanceof Vendor.ErlangTypes.Tuple && context.get(0) === Symbol.for('ok')) { + return resolveContext(context.get(1), parentContext); + } else if (context instanceof Map) { + return new Map([...parentContext, ...context]); + } else if (Array.isArray(context)) { + return mergeContextKeywordList(context, parentContext); + } + + throw new Error('Invalid context'); +} + +function mergeContextKeywordList(context, parentContext) { + const newContext = new Map([...parentContext]); + + for (const entry of context) { + newContext.set(entry.get(0), entry.get(1)); + } + + return newContext; +} + +function runTests(mod, results) { + const contextSetupAll = runSetup(mod, '__elixirscript_test_setup_all'); + + for (const key of Object.keys(mod.default)) { + if (key.startsWith('__elixirscript_test_case')) { + results.tests++; + const test = mod.default[key](); + const result = runTest(mod, test, contextSetupAll, results); + + if (result) { + results.success++; + } else { + results.failed++; + } + } + } + + runTeardown(mod, '__elixirscript_test_teardown_all', contextSetupAll); +} + +function runTest(mod, test, incomingContext, results) { + const context = runSetup(mod, '__elixirscript_test_setup', incomingContext); + let testPassed = true; + try { + test.get(Symbol.for('test'))(context); + process.stdout.write(Colors.fg.Green + '.' + Colors.Reset); + } catch (e) { + process.stdout.write('\n'); + handleError(e, test, results, mod); + testPassed = false; + } + + runTeardown(mod, '__elixirscript_test_teardown', context); + return testPassed; +} + +function handleError(e, test, results, mod) { + if (e.__reason) { + if (e.__reason instanceof Map && e.__reason.get(Symbol.for('message'))) { + const errorMessage = e.__reason.get(Symbol.for('message')); + const expr = e.__reason.get(Symbol.for('expr')); + const left = e.__reason.get(Symbol.for('left')); + const right = e.__reason.get(Symbol.for('right')); + const file = e.__reason.get(Symbol.for('file')); + const line = e.__reason.get(Symbol.for('line')); + const moduleName = Symbol.keyFor(mod.default.__MODULE__).replace('Elixir.', ''); + let testMessage = test.get(Symbol.for('message')); + testMessage = `${results.failed}) ${testMessage} (${moduleName})`; + + printErrorLine(testMessage); + console.log(Colors.fg.Red, errorMessage, Colors.Reset); + printErrorLine(left, 'left'); + printErrorLine(right, 'right'); + printErrorLine(file, 'file'); + printErrorLine(line, 'line'); + } + } else { + console.log(e); + } +} + +function printErrorLine(value, label = null) { + if (value && value !== Symbol.for('ex_unit_no_meaningful_value')) { + if (label) { + console.log(Colors.fg.Cyan, `${label}:`, Colors.Reset, `${value}`); + } else { + console.log(`${value}`, Colors.Reset); + } + } +} + +export default { + start, +}; diff --git a/priv/testrunner/vendor.build.js b/priv/testrunner/vendor.build.js new file mode 100644 index 00000000..f83931bf --- /dev/null +++ b/priv/testrunner/vendor.build.js @@ -0,0 +1,2 @@ +function unwrapExports(a){return a&&a.__esModule&&Object.prototype.hasOwnProperty.call(a,"default")?a["default"]:a}function createCommonjsModule(a,b){return b={exports:{}},a(b,b.exports),b.exports}var tuple=createCommonjsModule(function(a,b){Object.defineProperty(b,"__esModule",{value:!0});class Tuple{constructor(...a){this.values=Object.freeze(a),this.length=this.values.length}get(a){return this.values[a]}count(){return this.values.length}[Symbol.iterator](){return this.values[Symbol.iterator]()}toString(){let a,b="";for(a=0;a"}}});unwrapExports(pid);var pid_1=pid.PID,reference=createCommonjsModule(function(a,b){Object.defineProperty(b,"__esModule",{value:!0});let c=-1;b.Reference=class Reference{constructor(){++c,this.id=c,this.ref=Symbol()}toString(){return"Ref#<0.0.0."+this.id+">"}}});unwrapExports(reference);var reference_1=reference.Reference,bit_string=createCommonjsModule(function(a,b){Object.defineProperty(b,"__esModule",{value:!0});class BitString{constructor(...a){this.value=Object.freeze(this.process(a)),this.length=this.value.length,this.bit_size=8*this.length,this.byte_size=this.length}get(a){return this.value[a]}count(){return this.value.length}slice(a,b=void 0){let c=this.value.slice(a,b),d=c.map(a=>BitString.integer(a));return new BitString(...d)}[Symbol.iterator](){return this.value[Symbol.iterator]()}toString(){var a,b="";for(a=0;a>"}process(a){let b=[];var c;for(c=0;cb?c.push(b):2048>b?c.push(192|b>>6,128|63&b):55296>b||57344<=b?c.push(224|b>>12,128|63&b>>6,128|63&b):(d++,b=65536+((1023&b)<<10|1023&a.charCodeAt(d)),c.push(240|b>>18,128|63&b>>12,128|63&b>>6,128|63&b));return c}static toUTF16Array(a){for(var b,c=[],d=0;d=b?(c.push(0),c.push(b)):(c.push(255&b>>8),c.push(255&b)));return c}static toUTF32Array(a){for(var b,c=[],d=0;d=b?(c.push(0),c.push(0),c.push(0),c.push(b)):(c.push(0),c.push(0),c.push(255&b>>8),c.push(255&b)));return c}//http://stackoverflow.com/questions/2003493/javascript-float-from-to-bits +static float32ToBytes(a){var b=[],c=new ArrayBuffer(4);new Float32Array(c)[0]=a;let d=new Uint32Array(c)[0];return b.push(255&d>>24),b.push(255&d>>16),b.push(255&d>>8),b.push(255&d),b}static float64ToBytes(a){var b=[],c=new ArrayBuffer(8);new Float64Array(c)[0]=a;var d=new Uint32Array(c)[0],e=new Uint32Array(c)[1];return b.push(255&e>>24),b.push(255&e>>16),b.push(255&e>>8),b.push(255&e),b.push(255&d>>24),b.push(255&d>>16),b.push(255&d>>8),b.push(255&d),b}}b.BitString=BitString});unwrapExports(bit_string);var bit_string_1=bit_string.BitString,lib=createCommonjsModule(function(a,b){Object.defineProperty(b,"__esModule",{value:!0}),b.Tuple=tuple.Tuple,b.PID=pid.PID,b.Reference=reference.Reference,b.BitString=bit_string.BitString}),ErlangTypes=unwrapExports(lib),lib_1=lib.Tuple,lib_2=lib.PID,lib_3=lib.Reference,lib_4=lib.BitString,vendor={ErlangTypes};export default vendor; diff --git a/priv/testrunner/vendor.js b/priv/testrunner/vendor.js new file mode 100644 index 00000000..1d79bd80 --- /dev/null +++ b/priv/testrunner/vendor.js @@ -0,0 +1,5 @@ +import ErlangTypes from 'erlang-types'; + +export default { + ErlangTypes, +}; diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 00000000..dbdce767 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,52 @@ +const rollup = require('rollup') +const babel = require('rollup-plugin-babel') +const nodeResolve = require('rollup-plugin-node-resolve') +const commonjs = require('rollup-plugin-commonjs') +const minify = require('rollup-plugin-babel-minify') + +const plugins = [ + nodeResolve({ + mainFields: ['jsnext', 'main'], + }), + commonjs(), + babel({ + babelrc: false, + }), + minify({ + keepFnName: true, + keepClassName: true, + }), +] + +rollup + .rollup({ + input: 'src/javascript/elixir.js', + output: { + file: 'priv/build/es/ElixirScript.Core.js', + format: 'es', + }, + plugins, + }) + .then(bundle => { + bundle.write({ + format: 'es', + file: 'priv/build/es/ElixirScript.Core.js', + sourcemap: 'inline', + }) + }) + +rollup + .rollup({ + input: 'priv/testrunner/vendor.js', + output: { + file: 'priv/testrunner/vendor.build.js', + format: 'es', + }, + plugins, + }) + .then(bundle => { + bundle.write({ + format: 'es', + file: 'priv/testrunner/vendor.build.js', + }) + }) diff --git a/src/javascript/elixir.js b/src/javascript/elixir.js new file mode 100644 index 00000000..3f58d0a3 --- /dev/null +++ b/src/javascript/elixir.js @@ -0,0 +1,5 @@ +import Core from './lib/core'; + +export default { + Core, +}; diff --git a/src/javascript/lib/core.js b/src/javascript/lib/core.js new file mode 100644 index 00000000..7f87d1da --- /dev/null +++ b/src/javascript/lib/core.js @@ -0,0 +1,80 @@ +import Patterns from 'tailored'; +import ErlangTypes from 'erlang-types'; +import Functions from './core/functions'; +import SpecialForms from './core/special_forms'; +import erlang from './core/erlang_compat/erlang'; +import maps from './core/erlang_compat/maps'; +import lists from './core/erlang_compat/lists'; +import elixir_errors from './core/erlang_compat/elixir_errors'; +import elixir_config from './core/erlang_compat/elixir_config'; +import io from './core/erlang_compat/io'; +import filename from './core/erlang_compat/filename'; +import binary from './core/erlang_compat/binary'; +import unicode from './core/erlang_compat/unicode'; +import Store from './core/store'; +import math from './core/erlang_compat/math'; +import proplists from './core/erlang_compat/proplists'; + +class Integer {} +class Float {} + +function get_global() { + if (typeof self !== 'undefined') { + return self; + } else if (typeof window !== 'undefined') { + return window; + } else if (typeof global !== 'undefined') { + return global; + } + + /* As long as the window check precedes this, it won't display in a browser, + unless the ground cracks open and swallows JavaScript whole. */ + /* eslint-disable no-console */ + console.warn('No global state found'); + /* eslint-enable no-console */ + + return null; +} + +function initApp() { + const Elixir = {}; + + Elixir.__table__ = {}; + Elixir.start = (app, args) => { + app.__load(Elixir).start(Symbol.for('normal'), args); + }; + Elixir.load = module => module.__load(Elixir); + + return Elixir; +} + +const globalState = get_global(); + +globalState.__elixirscript_store__ = new Map(); +globalState.__elixirscript_names__ = new Map(); + +export default { + Tuple: ErlangTypes.Tuple, + PID: ErlangTypes.PID, + BitString: ErlangTypes.BitString, + Reference: ErlangTypes.Reference, + Patterns, + Integer, + Float, + Functions, + SpecialForms, + Store, + global: globalState, + erlang, + maps, + lists, + elixir_errors, + io, + filename, + binary, + unicode, + elixir_config, + math, + proplists, + initApp, +}; diff --git a/src/javascript/lib/core/erlang_compat/binary.js b/src/javascript/lib/core/erlang_compat/binary.js new file mode 100644 index 00000000..20a7b629 --- /dev/null +++ b/src/javascript/lib/core/erlang_compat/binary.js @@ -0,0 +1,69 @@ +import erlang from './erlang'; +import proplists from './proplists'; + +function at(subject, pos) { + return subject.charAt(pos); +} + +function copy(subject, n = 1) { + return subject.repeat(n); +} + +function first(subject) { + if (subject.length === 0) { + throw new Error('Binary is of length 0'); + } + return at(subject, 0); +} + +function last(subject) { + if (subject.length === 0) { + throw new Error('Binary is of length 0'); + } + return subject.slice(-1); +} + +function list_to_bin(bytelist) { + return erlang.list_to_binary(bytelist); +} + +function part(subject, posOrTuple, len = null) { + if (len === null) { + const [pos, theLen] = posOrTuple.values; + return subject.substr(pos, theLen); + } + + return subject.substr(posOrTuple, len); +} + +// TODO: Support more options +// TODO: pattern cannot be list of strings +function replace(subject, pattern, replacement, options = []) { + const opt_global = proplists.get_value(Symbol.for('global'), options); + + let regex; + if (opt_global !== Symbol.for('undefined')) { + regex = new RegExp(pattern, 'g'); + } else { + regex = new RegExp(pattern, ''); + } + + return subject.replace(regex, replacement); +} + +// TODO: Support more options, global is implied +// TODO: pattern cannot be list of strings +function split(subject, pattern, options = []) { + return subject.split(pattern); +} + +export default { + at, + copy, + first, + last, + list_to_bin, + part, + replace, + split, +}; diff --git a/src/javascript/lib/core/erlang_compat/elixir_config.js b/src/javascript/lib/core/erlang_compat/elixir_config.js new file mode 100644 index 00000000..dba52d2e --- /dev/null +++ b/src/javascript/lib/core/erlang_compat/elixir_config.js @@ -0,0 +1,43 @@ +const MODULE = Symbol.for('elixir_config'); +const ets = new Map(); + +function _new(opts) { + ets.set(MODULE, new Map()); + ets.get(MODULE).set(MODULE, opts); + return MODULE; +} + +function _delete(module) { + ets.delete(module); + return true; +} + +function put(key, value) { + ets.get(MODULE).set(key, value); + return Symbol.for('ok'); +} + +function get(key) { + return ets.get(MODULE).get(key); +} + +function update(key, fun) { + const value = fun(ets.get(MODULE).get(key)); + put(key, value); + return value; +} + +function get_and_put(key, value) { + const oldValue = get(key); + put(key, value); + return oldValue; +} + +export default { + new: _new, + delete: _delete, + put, + get, + update, + get_and_put, +}; diff --git a/src/javascript/lib/core/erlang_compat/elixir_errors.js b/src/javascript/lib/core/erlang_compat/elixir_errors.js new file mode 100644 index 00000000..c6b5d22b --- /dev/null +++ b/src/javascript/lib/core/erlang_compat/elixir_errors.js @@ -0,0 +1,14 @@ +/* It is far too "meta" to warn about including a warning in a warning */ +/* eslint-disable no-console */ + +function warn(message) { + const messageString = message.join(''); + console.warn(`warning: ${messageString}`); + + + return Symbol.for('ok'); +} + +export default { + warn, +}; diff --git a/src/javascript/lib/core/erlang_compat/erlang.js b/src/javascript/lib/core/erlang_compat/erlang.js new file mode 100644 index 00000000..ddece09d --- /dev/null +++ b/src/javascript/lib/core/erlang_compat/erlang.js @@ -0,0 +1,602 @@ +// http://erlang.org/doc/man/erlang.html +import ErlangTypes from 'erlang-types'; +import lists from './lists'; + +const selfPID = new ErlangTypes.PID(); + +function is_boolean(value) { + return typeof value === 'boolean' || value instanceof Boolean; +} + +function atom_to_binary(atom, encoding = Symbol.for('utf8')) { + if (encoding !== Symbol.for('utf8')) { + throw new Error(`unsupported encoding ${encoding}`); + } + + if (atom === null) { + return 'nil'; + } else if (is_boolean(atom)) { + return atom.toString(); + } else if (atom.__MODULE__) { + return Symbol.keyFor(atom.__MODULE__); + } + + return Symbol.keyFor(atom); +} + +function atom_to_list(atom) { + return atom_to_binary(atom); +} + +function binary_to_atom(binary, encoding = Symbol.for('utf8')) { + if (encoding !== Symbol.for('utf8')) { + throw new Error(`unsupported encoding ${encoding}`); + } + + if (binary === 'nil') { + return null; + } else if (binary === 'true') { + return true; + } else if (binary === 'false') { + return false; + } + + return Symbol.for(binary); +} + +function binary_to_existing_atom(binary, encoding = Symbol.for('utf8')) { + return binary_to_atom(binary, encoding); +} + +function list_concatenation(list1, list2) { + return list1.concat(list2); +} + +function list_subtraction(list1, list2) { + const list = [...list1]; + + for (const item of list2) { + const index = list.indexOf(item); + + if (index > -1) { + list.splice(index, 1); + } + } + + return list; +} + +function arrayEquals(left, right) { + if (!Array.isArray(right)) { + return false; + } + + if (left.length !== right.length) { + return false; + } + + for (let i = 0; i < left.length; i++) { + if (equals(left[i], right[i]) === false) { + return false; + } + } + + return true; +} + +function tupleEquals(left, right) { + if (right instanceof ErlangTypes.Tuple === false) { + return false; + } + + if (left.length !== right.length) { + return false; + } + + return arrayEquals(left.values, right.values); +} + +function bitstringEquals(left, right) { + if (right instanceof ErlangTypes.BitString === false) { + return false; + } + + if (left.length !== right.length) { + return false; + } + + return arrayEquals(left.value, right.value); +} + +function pidEquals(left, right) { + if (right instanceof ErlangTypes.PID === false) { + return false; + } + + return left.id === right.id; +} + +function referenceEquals(left, right) { + if (right instanceof ErlangTypes.Reference === false) { + return false; + } + + return left.id === right.id; +} + +function mapEquals(left, right) { + if (right instanceof Map === false) { + return false; + } + + const leftEntries = Array.from(left.entries()); + const rightEntries = Array.from(right.entries()); + + return arrayEquals(leftEntries, rightEntries); +} + +function equals(left, right) { + if (Array.isArray(left)) { + return arrayEquals(left, right); + } + + if (left instanceof ErlangTypes.Tuple) { + return tupleEquals(left, right); + } + + if (left instanceof ErlangTypes.PID) { + return pidEquals(left, right); + } + + if (left instanceof ErlangTypes.BitString) { + return bitstringEquals(left, right); + } + + if (left instanceof ErlangTypes.Reference) { + return referenceEquals(left, right); + } + + if (left instanceof Map) { + return mapEquals(left, right); + } + + return left === right; +} + +function div(left, right) { + return left / right; +} + +function not(x) { + return !x; +} + +function rem(left, right) { + return left % right; +} + +function band(left, right) { + return left & right; +} + +function bor(left, right) { + return left | right; +} + +function bnot(x) { + return ~x; +} + +function bsl(left, right) { + return left << right; +} + +function bsr(left, right) { + return left >> right; +} + +function bxor(left, right) { + return left ^ right; +} + +function is_atom(value) { + if (value === null) { + return true; + } else if (is_boolean(value)) { + return true; + } + + return typeof value === 'symbol' || value instanceof Symbol || value.__MODULE__ != null; +} + +function is_bitstring(value) { + return value instanceof ErlangTypes.BitString; +} + +function is_number(value) { + return typeof value === 'number' || value instanceof Number; +} + +function is_float(value) { + return is_number(value) && !Number.isInteger(value); +} + +function is_function(value) { + return typeof value === 'function' || value instanceof Function; +} + +function is_integer(value) { + return Number.isInteger(value); +} + +function is_list(value) { + return Array.isArray(value); +} + +function is_map(value) { + return value instanceof Map; +} + +function is_pid(value) { + return value instanceof ErlangTypes.PID; +} + +function is_port() { + return false; +} + +function is_reference(value) { + return value instanceof ErlangTypes.Reference; +} + +function is_tuple(value) { + return value instanceof ErlangTypes.Tuple; +} + +function is_binary(value) { + return typeof value === 'string' || value instanceof String; +} + +function element(n, tuple) { + return tuple.get(n - 1); +} + +function setelement(index, tuple1, value) { + const tupleData = [...tuple1.values]; + + tupleData[index - 1] = value; + + return new ErlangTypes.Tuple(...tupleData); +} + +function make_tuple(arity, initialValue) { + const list = []; + + for (let i = 0; i < arity; i++) { + list.push(initialValue); + } + + return new ErlangTypes.Tuple(...list); +} + +function insert_element(index, tuple, term) { + const list = [...tuple.values]; + list.splice(index - 1, 0, term); + + return new ErlangTypes.Tuple(...list); +} + +function append_element(tuple, term) { + const list = [...tuple.values]; + list.push(term); + + return new ErlangTypes.Tuple(...list); +} + +function delete_element(index, tuple) { + const list = [...tuple.values]; + list.splice(index - 1, 1); + + return new ErlangTypes.Tuple(...list); +} + +function tuple_to_list(tuple) { + const list = [...tuple.values]; + return list; +} + +function abs(number) { + return Math.abs(number); +} + +function apply(...args) { + if (args.length === 2) { + return args[0].apply(this, ...args[1]); + } + + return args[0][atom_to_binary(args[1])].apply(this, ...args[2]); +} + +function binary_part(binary, start, _length) { + return binary.substring(start, start + _length); +} + +function bit_size(bitstring) { + return bitstring.bit_size; +} + +function byte_size(bitstring) { + if (typeof bitstring === 'string' || bitstring instanceof String) { + return bitstring.length; + } + return bitstring.byte_size; +} + +function hd(list) { + return list[0]; +} + +function length(list) { + return list.length; +} + +function make_ref() { + return new ErlangTypes.Reference(); +} + +function map_size(map) { + return map.size; +} + +function max(first, second) { + return Math.max(first, second); +} + +function min(first, second) { + return Math.min(first, second); +} + +function round(number) { + return Math.round(number); +} + +function tl(list) { + return list.slice(1); +} + +function trunc(number) { + return Math.trunc(number); +} + +function tuple_size(tuple) { + return tuple.length; +} + +function binary_to_float(str) { + return parseFloat(str); +} + +function binary_to_integer(str, base = 10) { + return parseInt(str, base); +} + +function process_info(pid, item) { + if (item) { + if (item === Symbol.for('current_stacktrace')) { + return new ErlangTypes.Tuple(item, []); + } + + return new ErlangTypes.Tuple(item, null); + } + + return []; +} + +function list_to_binary(iolist) { + const iolistFlattened = lists.flatten(iolist); + + const value = iolistFlattened.reduce((acc, current) => { + if (current === null) { + return acc; + } else if (is_integer(current)) { + return acc + String.fromCodePoint(current); + } else if (is_bitstring(current)) { + return acc + String.fromCodePoint(...current.value); + } + + return acc + current; + }, ''); + + return value; +} + +function iolist_to_binary(ioListOrBinary) { + if (ioListOrBinary === null) { + return ''; + } + + if (is_binary(ioListOrBinary)) { + return ioListOrBinary; + } + + if (is_bitstring(ioListOrBinary)) { + return String.fromCodePoint(...ioListOrBinary.value); + } + + if (is_number(ioListOrBinary)) { + return String.fromCodePoint(ioListOrBinary); + } + + const iolistFlattened = lists.flatten(ioListOrBinary); + + const value = iolistFlattened.reduce((acc, current) => { + if (current === null) { + return acc; + } else if (is_integer(current)) { + return acc + String.fromCodePoint(current); + } else if (is_bitstring(current)) { + return acc + String.fromCodePoint(...current.value); + } + + return acc + iolist_to_binary(current); + }, ''); + + return value; +} + +function io_size(ioListOrBinary) { + return iolist_to_binary(ioListOrBinary).length; +} + +function integer_to_binary(integer, base = 10) { + return integer.toString(base); +} + +function node() { + return Symbol.for('nonode@nohost'); +} + +function nodes(arg = []) { + const nodeTypes = Array.isArray(arg) ? arg : [arg]; + const nodesFound = []; + + for (const nodeType of nodeTypes) { + if (nodeType === Symbol.for('this')) { + nodesFound.push(Symbol.for('nonode@nohost')); + console.log(nodesFound); + } + } + return nodesFound; +} + +function self() { + return selfPID; +} + +function _throw(term) { + throw term; +} + +function error(reason) { + let theError = null; + + if (reason instanceof Map && reason.has(Symbol.for('__exception__'))) { + let name = Symbol.keyFor(reason.get(Symbol.for('__struct__')).__MODULE__); + name = name + .split('.') + .slice(1) + .join('.'); + const message = reason.get(Symbol.for('message')); + theError = new Error(`** (${name}) ${message}`); + } else if (is_binary(reason)) { + theError = new Error(`** (RuntimeError) ${reason}`); + } else { + theError = new Error(`** (ErlangError) Erlang Error ${reason.toString()}`); + } + + theError.__reason = reason; + + throw theError; +} + +function exit(...args) { + if (args.length === 2) { + throw args[1]; + } else { + throw args[0]; + } +} + +function raise(_class, reason) { + if (_class === Symbol.for('throw')) { + _throw(reason); + } else if (_class === Symbol.for('error')) { + error(reason); + } else { + exit(reason); + } +} + +function function_exported(module, _function) { + return module[_function] != null; +} + +function lessThanEqualTo(one, two) { + return one <= two; +} + +function add(one, two) { + return one + two; +} + +export default { + atom_to_binary, + binary_to_atom, + binary_to_existing_atom, + list_concatenation, + list_subtraction, + div, + not, + rem, + band, + bor, + bsl, + bsr, + bxor, + bnot, + is_bitstring, + is_boolean, + is_float, + is_function, + is_integer, + is_list, + is_map, + is_number, + is_pid, + is_port, + is_reference, + is_tuple, + is_atom, + is_binary, + element, + setelement, + make_tuple, + insert_element, + append_element, + delete_element, + tuple_to_list, + abs, + apply, + binary_part, + bit_size, + byte_size, + hd, + length, + make_ref, + map_size, + max, + min, + round, + tl, + trunc, + tuple_size, + binary_to_float, + binary_to_integer, + process_info, + iolist_to_binary, + io_size, + integer_to_binary, + atom_to_list, + node, + self, + throw: _throw, + error, + exit, + raise, + list_to_binary, + nodes, + function_exported, + equals, + lessThanEqualTo, + add, +}; diff --git a/src/javascript/lib/core/erlang_compat/filename.js b/src/javascript/lib/core/erlang_compat/filename.js new file mode 100644 index 00000000..1d2fe0af --- /dev/null +++ b/src/javascript/lib/core/erlang_compat/filename.js @@ -0,0 +1,25 @@ +function join(arg, extra = null) { + const components = Array.isArray(arg) ? arg : [arg, extra]; + let names = []; + for (let i = components.length - 1; i >= 0; i--) { + const name = components[i]; + const normalized_name = name.replace(/\/+/g, '/').replace(/^\/|\/$/g, ''); + names.push(normalized_name); + if (name[0] === '/') { + names.push(''); + break; + } + } + return names.reverse().join('/'); +} + +function dirname(arg) { + const path = join([arg]); + const index = path.lastIndexOf('/'); + return index === -1 ? '.' : (path.substr(0, index) || '/'); +} + +export default { + join, + dirname, +}; diff --git a/src/javascript/lib/core/erlang_compat/io.js b/src/javascript/lib/core/erlang_compat/io.js new file mode 100644 index 00000000..abff3f57 --- /dev/null +++ b/src/javascript/lib/core/erlang_compat/io.js @@ -0,0 +1,19 @@ +/* The purpose of this module is, in fact, to do IO. */ +/* eslint-disable no-console */ +import erlang from './erlang'; + +function put_chars(ioDevice, charData) { + const dataToWrite = erlang.iolist_to_binary(charData); + + if (ioDevice === Symbol.for('stderr')) { + console.error(dataToWrite); + } else { + console.log(dataToWrite); + } + + return Symbol.for('ok'); +} + +export default { + put_chars, +}; diff --git a/src/javascript/lib/core/erlang_compat/lists.js b/src/javascript/lib/core/erlang_compat/lists.js new file mode 100644 index 00000000..066f903a --- /dev/null +++ b/src/javascript/lib/core/erlang_compat/lists.js @@ -0,0 +1,261 @@ +// http://erlang.org/doc/man/lists.html +import ErlangTypes from 'erlang-types'; + +function reverse(list) { + return [...list].reverse(); +} + +function foreach(fun, list) { + list.forEach(x => fun(x)); + + return Symbol.for('ok'); +} + +function duplicate(n, elem) { + const list = []; + + while (list.length < n) { + list.push(elem); + } + + return list; +} + +function flatten(deepList, tail = []) { + const val = deepList.reduce((acc, value) => { + if (Array.isArray(value)) { + return acc.concat(flatten(value)); + } + + return acc.concat(value); + }, []); + + return val.concat(tail); +} + +function foldl(fun, acc0, list) { + return list.reduce((acc, value) => fun(value, acc), acc0); +} + +function foldr(fun, acc0, list) { + return foldl(fun, acc0, reverse(list)); +} + +function keyfind(key, n, list) { + for (const ele of list) { + if (ele instanceof ErlangTypes.Tuple && ele.get(n - 1) === key) { + return ele; + } + } + + return false; +} + +function keymember(key, n, tupleList) { + if (keyfind(key, n, tupleList) === false) { + return false; + } + + return true; +} + +function keyreplace(key, n, tupleList, newTuple) { + const newTupleList = [...tupleList]; + + for (let index = 0; index < newTupleList.length; index++) { + if (newTupleList[index].get(n - 1) === key) { + newTupleList[index] = newTuple; + return newTupleList; + } + } + + return newTupleList; +} + +function keysort(n, tupleList) { + const newTupleList = [...tupleList]; + + return newTupleList.sort((a, b) => { + if (a.get(n - 1) < b.get(n - 1)) { + return -1; + } else if (a.get(n - 1) > b.get(n - 1)) { + return 1; + } + + return 0; + }); +} + +function keystore(key, n, tupleList, newTuple) { + const newTupleList = [...tupleList]; + + for (let index = 0; index < newTupleList.length; index++) { + if (newTupleList[index].get(n - 1) === key) { + newTupleList[index] = newTuple; + return newTupleList; + } + } + + return newTupleList.concat(newTuple); +} + +function keydelete(key, n, tupleList) { + const newTupleList = []; + let deleted = false; + + for (let index = 0; index < tupleList.length; index++) { + if (deleted === false && tupleList[index].get(n - 1) === key) { + deleted = true; + } else { + newTupleList.push(tupleList[index]); + } + } + + return newTupleList; +} + +function keytake(key, n, tupleList) { + const result = keyfind(key, n, tupleList); + + if (result !== false) { + return new ErlangTypes.Tuple(result.get(n - 1), result, keydelete(key, n, tupleList)); + } + + return false; +} + +function mapfoldl(fun, acc0, list1) { + const listResult = []; + let accResult = acc0; + + for (const item of list1) { + const tuple = fun(item, accResult); + listResult.push(tuple.get(0)); + accResult = tuple.get(1); + } + + return new ErlangTypes.Tuple(listResult, accResult); +} + +function concat(things) { + return things.map(v => v.toString()).join(); +} + +function map(fun, list) { + return list.map(value => fun(value)); +} + +function filter(pred, list1) { + return list1.filter(x => pred(x)); +} + +function filtermap(fun, list1) { + const list2 = []; + + for (const item of list1) { + const value = fun(item); + + if (value === true) { + list2.push(item); + } else if (value instanceof ErlangTypes.Tuple && value.get(0) === true) { + list2.push(value.get(1)); + } + } + + return list2; +} + +function member(elem, list) { + for (const item of list) { + if (item === elem) { + return true; + } + } + + return false; +} + +function all(pred, list) { + for (const item of list) { + if (pred(item) === false) { + return false; + } + } + + return true; +} + +function any(pred, list) { + for (const item of list) { + if (pred(item) === true) { + return true; + } + } + + return false; +} + +function splitwith(pred, list) { + let switchToList2 = false; + const list1 = []; + const list2 = []; + + for (const item of list) { + if (switchToList2 === true) { + list2.push(item); + } else if (pred(item) === true) { + list1.push(item); + } else { + switchToList2 = true; + list2.push(item); + } + } + + return new ErlangTypes.Tuple(list1, list2); +} + +function sort(...args) { + if (args.length === 1) { + const list2 = [...args[0]]; + return list2.sort(); + } + + const fun = args[0]; + const list2 = [...args[1]]; + + return list2.sort((a, b) => { + const result = fun(a, b); + + if (result === true) { + return -1; + } + + return 1; + }); +} + +export default { + reverse, + foreach, + duplicate, + flatten, + foldl, + foldr, + keydelete, + keyfind, + keymember, + keyreplace, + keysort, + keystore, + keytake, + mapfoldl, + concat, + map, + filter, + filtermap, + member, + all, + any, + splitwith, + sort, +}; diff --git a/src/javascript/lib/core/erlang_compat/maps.js b/src/javascript/lib/core/erlang_compat/maps.js new file mode 100644 index 00000000..ec8b4e79 --- /dev/null +++ b/src/javascript/lib/core/erlang_compat/maps.js @@ -0,0 +1,238 @@ +// http://erlang.org/doc/man/maps.html +import ErlangTypes from 'erlang-types'; +import erlang from './erlang'; + +const OK = Symbol.for('ok'); +const ERROR = Symbol.for('error'); +const BADMAP = Symbol.for('badmap'); +const BADKEY = Symbol.for('badkey'); + +function is_non_primitive(key) { + return ( + erlang.is_list(key) || + erlang.is_map(key) || + erlang.is_pid(key) || + erlang.is_reference(key) || + erlang.is_bitstring(key) || + erlang.is_tuple(key) + ); +} + +function __put(map, key, value) { + const map2 = new Map(map); + + if (is_non_primitive(key)) { + for (const map_key of map.keys()) { + if (erlang.equals(map_key, key)) { + map2.set(map_key, value); + return map2; + } + } + } + + map2.set(key, value); + return map2; +} + +function __has(map, key) { + if (is_non_primitive(key)) { + for (const map_key of map.keys()) { + if (erlang.equals(map_key, key)) { + return true; + } + } + + return false; + } + + return map.has(key); +} + +function __get(map, key) { + if (is_non_primitive(key)) { + for (const map_key of map.keys()) { + if (erlang.equals(map_key, key)) { + return map.get(map_key); + } + } + + return null; + } + + return map.get(key); +} + +function __delete(map, key) { + if (is_non_primitive(key)) { + for (const map_key of map.keys()) { + if (erlang.equals(map_key, key)) { + map.delete(map_key); + } + } + } else { + map.delete(key); + } +} + +function find(key, map) { + if (erlang.is_map(map) === false) { + return new ErlangTypes.Tuple(BADMAP, map); + } + + const value = __get(map, key); + + if (typeof value !== 'undefined') { + return new ErlangTypes.Tuple(OK, value); + } + + return ERROR; +} + +function fold(fun, init, map) { + let acc = init; + + for (const [key, value] of map.entries()) { + acc = fun(key, value, acc); + } + + return acc; +} + +function remove(key, map1) { + if (erlang.is_map(map1) === false) { + return new ErlangTypes.Tuple(BADMAP, map1); + } + + const map2 = new Map(map1); + + __delete(map2, key); + + return map2; +} + +function to_list(map) { + if (erlang.is_map(map) === false) { + return new ErlangTypes.Tuple(BADMAP, map); + } + + const list = []; + + for (const [key, value] of map.entries()) { + list.push(new ErlangTypes.Tuple(key, value)); + } + + return list; +} + +function from_list(list) { + return list.reduce((acc, item) => { + const [key, value] = item; + acc.set(key, value); + + return acc; + }, new Map()); +} + +function keys(map) { + if (erlang.is_map(map) === false) { + return new ErlangTypes.Tuple(BADMAP, map); + } + + return Array.from(map.keys()); +} + +function values(map) { + if (erlang.is_map(map) === false) { + return new ErlangTypes.Tuple(BADMAP, map); + } + + return Array.from(map.values()); +} + +function is_key(key, map) { + return __has(map, key); +} + +function put(key, value, map1) { + if (erlang.is_map(map1) === false) { + return new ErlangTypes.Tuple(BADMAP, map1); + } + + return __put(map1, key, value); +} + +function merge(map1, map2) { + if (erlang.is_map(map1) === false) { + return new ErlangTypes.Tuple(BADMAP, map1); + } + + if (erlang.is_map(map2) === false) { + return new ErlangTypes.Tuple(BADMAP, map2); + } + + return new Map([...map1, ...map2]); +} + +function update(key, value, map1) { + if (erlang.is_map(map1) === false) { + return new ErlangTypes.Tuple(BADMAP, map1); + } + + if (is_key(key, map1) === false) { + return new ErlangTypes.Tuple(BADKEY, key); + } + + return new Map([...map1, [key, value]]); +} + +function get(...args) { + const key = args[0]; + const map = args[1]; + + if (erlang.is_map(map) === false) { + return new ErlangTypes.Tuple(BADMAP, map); + } + + if (is_key(key, map)) { + return __get(map, key); + } + + if (args.length === 3) { + return args[2]; + } + + return new ErlangTypes.Tuple(BADKEY, key); +} + +function take(key, map1) { + if (erlang.is_map(map1) === false) { + return new ErlangTypes.Tuple(BADMAP, map1); + } + + if (!is_key(key, map1)) { + return ERROR; + } + + const value = __get(map1, key); + const map2 = new Map(map1); + __delete(map2, key); + + return new ErlangTypes.Tuple(value, map2); +} + +export default { + find, + fold, + remove, + to_list, + from_list, + keys, + values, + is_key, + put, + merge, + update, + get, + take, + __has, +}; diff --git a/src/javascript/lib/core/erlang_compat/math.js b/src/javascript/lib/core/erlang_compat/math.js new file mode 100644 index 00000000..57a8275b --- /dev/null +++ b/src/javascript/lib/core/erlang_compat/math.js @@ -0,0 +1,7 @@ +function log2(x) { + return Math.log2(x); +} + +export default { + log2, +}; diff --git a/src/javascript/lib/core/erlang_compat/proplists.js b/src/javascript/lib/core/erlang_compat/proplists.js new file mode 100644 index 00000000..a9673774 --- /dev/null +++ b/src/javascript/lib/core/erlang_compat/proplists.js @@ -0,0 +1,25 @@ +import lists from './lists'; + +function get_value(key, list, defaultv = Symbol.for('undefined')) { + const tuple = lists.keyfind(key, 1, list); + if (tuple) { + const [, value] = tuple.values; + return value; + } else if (lists.member(key, list)) { + return true; + } + return defaultv; +} + +function is_defined(key, list) { + const tuple = lists.keyfind(key, 1, list); + if (tuple) { + return true; + } + return false; +} + +export default { + get_value, + is_defined, +}; diff --git a/src/javascript/lib/core/erlang_compat/unicode.js b/src/javascript/lib/core/erlang_compat/unicode.js new file mode 100644 index 00000000..f4f4083e --- /dev/null +++ b/src/javascript/lib/core/erlang_compat/unicode.js @@ -0,0 +1,33 @@ +import erlang from './erlang'; +import lists from './lists'; + +function characters_to_list(characters, inEncoding = Symbol.for('unicode')) { + let values = characters; + + if (Array.isArray(characters)) { + values = lists.flatten(characters); + } + + if (erlang.is_binary(values)) { + return values.split('').map(c => c.codePointAt(0)); + } + + return values.reduce((acc, c) => { + if (erlang.is_integer(c)) { + return acc.concat(c); + } + + return acc.concat(characters_to_list(c, inEncoding)); + }, []); +} + +function characters_to_binary(characters) { + const values = characters_to_list(characters); + + return String.fromCodePoint(...values); +} + +export default { + characters_to_list, + characters_to_binary, +}; diff --git a/src/javascript/lib/core/functions.js b/src/javascript/lib/core/functions.js new file mode 100644 index 00000000..91bf31e7 --- /dev/null +++ b/src/javascript/lib/core/functions.js @@ -0,0 +1,223 @@ +import GraphemeSplitter from 'grapheme-splitter'; +import Protocol from './protocol'; +import Core from '../core'; +import proplists from './erlang_compat/proplists'; +import erlang from './erlang_compat/erlang'; + +function call_property(item, property) { + if (!property) { + if (item instanceof Function || typeof item === 'function') { + return item(); + } + + return item; + } + + if (item instanceof Map) { + let prop = null; + + if (item.has(property)) { + prop = property; + } else if (item.has(Symbol.for(property))) { + prop = Symbol.for(property); + } + + if (prop === null) { + throw new Error(`Property ${property} not found in ${item}`); + } + + return item.get(prop); + } + + let prop = null; + + if ( + typeof item === 'number' || + typeof item === 'symbol' || + typeof item === 'boolean' || + typeof item === 'string' + ) { + if (item[property] !== undefined) { + prop = property; + } else if (item[Symbol.for(property)] !== undefined) { + prop = Symbol.for(property); + } + } else if (property in item) { + prop = property; + } else if (Symbol.for(property) in item) { + prop = Symbol.for(property); + } + + if (prop === null) { + throw new Error(`Property ${property} not found in ${item}`); + } + + if (item[prop] instanceof Function || typeof item[prop] === 'function') { + return item[prop](); + } + return item[prop]; +} + +function defprotocol(spec) { + return new Protocol(spec); +} + +function defimpl(protocol, type, impl) { + protocol.implementation(type, impl); +} + +function build_namespace(ns, ns_string) { + let parts = ns_string.split('.'); + const root = ns; + let parent = ns; + + if (parts[0] === 'Elixir') { + parts = parts.slice(1); + } + + for (const part of parts) { + if (typeof parent[part] === 'undefined') { + parent[part] = {}; + } + + parent = parent[part]; + } + + root.__table__ = ns.__table__ || {}; + root.__table__[Symbol.for(ns_string)] = parent; + + return parent; +} + +function map_to_object(map, options = []) { + const opt_keys = proplists.get_value(Symbol.for('keys'), options); + const opt_symbols = proplists.get_value(Symbol.for('symbols'), options); + + const object = {}; + + for (const entry of map.entries()) { + let key = entry[0]; + const value = entry[1]; + + if (opt_keys === Symbol.for('string') && typeof key === 'number') { + key = key.toString(); + } else if ( + (opt_keys === Symbol.for('string') || opt_symbols !== Symbol.for('undefined')) && + typeof key === 'symbol' + ) { + key = erlang.atom_to_binary(key); + } + + if (value instanceof Map) { + object[key] = map_to_object(value, options); + } else if (opt_symbols !== Symbol.for('undefined') && typeof value === 'symbol') { + object[key] = erlang.atom_to_binary(value); + } else { + object[key] = value; + } + } + + return object; +} + +function object_to_map(object, options = []) { + const opt_atom_keys = proplists.get_value(Symbol.for('keys'), options) === Symbol.for('atom'); + const opt_recurse_array = proplists.get_value(Symbol.for('recurse_array'), options) === true; + + if (object.constructor === Object) { + const map = new Map(); + Reflect.ownKeys(object).forEach((key) => { + let key2 = key; + let value = object[key]; + if (opt_atom_keys && typeof key === 'string') { + key2 = Symbol.for(key); + } + + if ( + value !== null && + (value.constructor === Object || (value instanceof Array && opt_recurse_array)) + ) { + value = object_to_map(value, options); + } + map.set(key2, value); + }); + return map; + } else if (object instanceof Array && opt_recurse_array) { + return object.map((ele) => { + if (ele !== null && (ele.constructor === Object || ele instanceof Array)) { + return object_to_map(ele, options); + } + return ele; + }); + } + throw new Error(`Object ${object} is not an native object or array`); +} + +class Recurse { + constructor(func) { + this.func = func; + } +} + +function trampoline(f) { + let currentValue = f; + + while (currentValue && currentValue instanceof Recurse) { + currentValue = currentValue.func(); + } + + return currentValue; +} + +function split_at(value, position) { + const splitter = new GraphemeSplitter(); + const splitValues = splitter.splitGraphemes(value); + + if (position < 0) { + const newPosition = splitValues.length + position; + if (newPosition < 0) { + return new Core.Tuple('', value); + } + + return split_at(value, newPosition); + } + + let first = ''; + let second = ''; + let index = 0; + + for (const character of splitValues) { + if (index < position) { + first += character; + } else { + second += character; + } + + index += 1; + } + + return new Core.Tuple(first, second); +} + +function graphemes(str) { + const splitter = new GraphemeSplitter(); + return splitter.splitGraphemes(str); +} + +function concat(head, tail) { + return [head].concat(tail); +} + +export default { + call_property, + defprotocol, + defimpl, + build_namespace, + map_to_object, + object_to_map, + trampoline, + Recurse, + split_at, + graphemes, + concat, +}; diff --git a/src/javascript/lib/core/protocol.js b/src/javascript/lib/core/protocol.js new file mode 100644 index 00000000..15466ed8 --- /dev/null +++ b/src/javascript/lib/core/protocol.js @@ -0,0 +1,72 @@ +import Core from '../core'; + +// https://github.com/airportyh/protomorphism +class Protocol { + constructor(spec) { + this.registry = new Map(); + this.fallback = null; + + function createFun(funName) { + return function (...args) { + const thing = args[0]; + let fun = null; + + if (thing === null && this.hasImplementation(Symbol('null'))) { + fun = this.registry.get(Symbol)[funName]; + } else if (Number.isInteger(thing) && this.hasImplementation(Core.Integer)) { + fun = this.registry.get(Core.Integer)[funName]; + } else if ( + typeof thing === 'number' && + !Number.isInteger(thing) && + this.hasImplementation(Core.Float) + ) { + fun = this.registry.get(Core.Float)[funName]; + } else if (typeof thing === 'string' && this.hasImplementation(Core.BitString)) { + fun = this.registry.get(Core.BitString)[funName]; + } else if ( + thing && + thing instanceof Map && + thing.has(Symbol.for('__struct__')) && + this.hasImplementation(thing) + ) { + fun = this.registry.get(thing.get(Symbol.for('__struct__')).__MODULE__)[funName]; + } else if (thing !== null && this.hasImplementation(thing)) { + fun = this.registry.get(thing.constructor)[funName]; + } else if (this.fallback) { + fun = this.fallback[funName]; + } + + if (fun != null) { + const retval = fun.apply(this, args); + return retval; + } + + throw new Error(`No implementation found for ${thing}`); + }; + } + + for (const funName in spec) { + this[funName] = createFun(funName).bind(this); + } + } + + implementation(type, implementation) { + if (type === null) { + this.fallback = implementation; + } else { + this.registry.set(type, implementation); + } + } + + hasImplementation(thing) { + if (thing === Core.Integer || thing === Core.Float || thing === Core.BitString) { + return this.registry.has(thing); + } else if (thing && thing instanceof Map && thing.has(Symbol.for('__struct__'))) { + return this.registry.has(thing.get(Symbol.for('__struct__')).__MODULE__); + } + + return this.registry.has(thing.constructor); + } +} + +export default Protocol; diff --git a/src/javascript/lib/core/special_forms.js b/src/javascript/lib/core/special_forms.js new file mode 100644 index 00000000..7721612b --- /dev/null +++ b/src/javascript/lib/core/special_forms.js @@ -0,0 +1,175 @@ +import Core from '../core'; + +function _case(condition, clauses) { + return Core.Patterns.defmatch(...clauses)(condition); +} + +function cond(...clauses) { + for (const clause of clauses) { + if (clause[0]) { + return clause[1](); + } + } + + throw new Error(); +} + +function run_list_generators(generator, generators) { + if (generators.length === 0) { + return generator.map((x) => { + if (Array.isArray(x)) { + return x; + } + return [x]; + }); + } + const list = generators.pop(); + + const next_gen = []; + for (const j of list()) { + for (const i of generator) { + next_gen.push([j].concat(i)); + } + } + + return run_list_generators(next_gen, generators); +} + +function _for(expression, generators, collectable_protocol, into = []) { + const [result, fun] = collectable_protocol.into(into); + let accumulatingResult = result; + + const generatedValues = run_list_generators(generators.pop()(), generators); + + for (const value of generatedValues) { + if (expression.guard.apply(this, value)) { + accumulatingResult = fun( + accumulatingResult, + new Core.Tuple(Symbol.for('cont'), expression.fn.apply(this, value)), + ); + } + } + + return fun(accumulatingResult, Symbol.for('done')); +} + +function _try(do_fun, rescue_function, catch_fun, else_function, after_function) { + let result = null; + + try { + result = do_fun(); + } catch (e) { + let ex_result = null; + if (rescue_function) { + try { + let value = e; + if (e.__reason) { + value = e.__reason; + value.set('__reason', e.__reason); + } + + ex_result = rescue_function(value); + return ex_result; + } catch (ex) { + throw ex; + } + } + + if (catch_fun) { + try { + ex_result = catch_fun(e); + return ex_result; + } catch (ex) { + throw ex; + } + } + + throw e; + } finally { + if (after_function) { + after_function(); + } + } + + if (else_function) { + try { + return else_function(result); + } catch (ex) { + if (ex instanceof Core.Patterns.MatchError) { + throw new Error('No Match Found in Else'); + } + + throw ex; + } + } else { + return result; + } +} + +function _with(...args) { + let argsToPass = []; + let successFunction = null; + let elseFunction = null; + + if (typeof args[args.length - 2] === 'function') { + [successFunction, elseFunction] = args.splice(-2); + } else { + successFunction = args.pop(); + } + + for (let i = 0; i < args.length; i++) { + const [pattern, func] = args[i]; + + const result = func(...argsToPass); + + const patternResult = Core.Patterns.match_or_default(pattern, result); + + if (patternResult == null) { + if (elseFunction) { + return elseFunction.call(null, result); + } + return result; + } + + argsToPass = argsToPass.concat(patternResult); + } + + return successFunction(...argsToPass); +} + +function receive(clauses, timeout = 0, timeoutFn = () => true) { + /* It's more important to warn developers than follow style guides */ + /* eslint-disable no-console */ + console.warn('Receive not supported'); + /* eslint-enable no-console */ + + const messages = []; // this.mailbox.get(); + const NOMATCH = Symbol('NOMATCH'); + + for (let i = 0; i < messages.length; i++) { + for (const clause of clauses) { + const value = Core.Patterns.match_or_default( + clause.pattern, + messages[i], + clause.guard, + NOMATCH, + ); + + if (value !== NOMATCH) { + this.mailbox.removeAt(i); + return clause.fn.apply(null, value); + } + } + } + + return null; +} + +export default { + _case, + cond, + _for, + _try, + _with, + receive, +}; diff --git a/src/javascript/lib/core/store.js b/src/javascript/lib/core/store.js new file mode 100644 index 00000000..231334eb --- /dev/null +++ b/src/javascript/lib/core/store.js @@ -0,0 +1,47 @@ +import Core from '../core'; + +function get_key(key) { + let real_key = key; + + if (Core.global.__elixirscript_names__.has(key)) { + real_key = Core.global.__elixirscript_names__.get(key); + } + + if (Core.global.__elixirscript_store__.has(real_key)) { + return real_key; + } + + throw new Error(`Key ${real_key} not found`); +} + +function create(value, name = null) { + const key = new Core.PID(); + + if (name !== null) { + Core.global.__elixirscript_names__.set(name, key); + } + + return Core.global.__elixirscript_store__.set(key, value); +} + +function update(key, value) { + const real_key = get_key(key); + return Core.global.__elixirscript_store__.set(real_key, value); +} + +function read(key) { + const real_key = get_key(key); + return Core.global.__elixirscript_store__.get(real_key); +} + +function remove(key) { + const real_key = get_key(key); + return Core.global.__elixirscript_store__.delete(real_key); +} + +export default { + create, + update, + read, + remove, +}; diff --git a/src/javascript/tests/case.spec.js b/src/javascript/tests/case.spec.js new file mode 100644 index 00000000..8715ae11 --- /dev/null +++ b/src/javascript/tests/case.spec.js @@ -0,0 +1,21 @@ +import test from 'ava'; +import Core from '../lib/core'; + +const Patterns = Core.Patterns; +const SpecialForms = Core.SpecialForms; +const Tuple = Core.Tuple; + +test('case', (t) => { + const clauses = [ + Patterns.clause( + [new Tuple(Symbol.for('selector'), Patterns.variable(), Patterns.variable())], + (i, value) => value, + i => Kernel.is_integer(i), + ), + Patterns.clause([Patterns.variable()], value => value), + ]; + + const result = SpecialForms._case('thing', clauses); + + t.is(result, 'thing'); +}); diff --git a/src/javascript/tests/cond.spec.js b/src/javascript/tests/cond.spec.js new file mode 100644 index 00000000..f1a09736 --- /dev/null +++ b/src/javascript/tests/cond.spec.js @@ -0,0 +1,16 @@ +import test from 'ava'; +import Core from '../lib/core'; + +const SpecialForms = Core.SpecialForms; + +test('cond', (t) => { + const clauses = [ + [1 + 1 === 1, () => 'This will never match'], + [2 * 2 !== 4, () => 'Nor this'], + [true, () => 'This will'], + ]; + + const result = SpecialForms.cond(...clauses); + + t.is(result, 'This will'); +}); diff --git a/src/javascript/tests/core/erlang_compat/binary_spec.js b/src/javascript/tests/core/erlang_compat/binary_spec.js new file mode 100644 index 00000000..2bef01af --- /dev/null +++ b/src/javascript/tests/core/erlang_compat/binary_spec.js @@ -0,0 +1,67 @@ +import test from 'ava'; +import Core from '../../../lib/core'; + +test('at/1', (t) => { + const result = Core.binary.at('abc', 0); + t.deepEqual(result, 'a'); +}); + +test('copy/1', (t) => { + const result = Core.binary.copy('h'); + t.deepEqual(result, 'h'); +}); + +test('copy/2', (t) => { + const result = Core.binary.copy('h', 3); + t.deepEqual(result, 'hhh'); +}); + +test('first/1', (t) => { + const result = Core.binary.first('abc'); + t.deepEqual(result, 'a'); +}); + +test('last/1', (t) => { + const result = Core.binary.last('abc'); + t.deepEqual(result, 'c'); +}); + +test('list_to_bin/1', (t) => { + const result = Core.binary.list_to_bin([104, 101, 108, 108, 111]); + t.deepEqual(result, 'hello'); +}); + +test('part/2', (t) => { + let posLen = new Core.Tuple(1, 1); + let result = Core.binary.part('abcde', posLen); + t.deepEqual(result, 'b'); + + posLen = new Core.Tuple(1, 3); + result = Core.binary.part('abcde', posLen); + t.deepEqual(result, 'bcd'); +}); + +test('part/3', (t) => { + let result = Core.binary.part('abcde', 1, 1); + t.deepEqual(result, 'b'); + + result = Core.binary.part('abcde', 1, 3); + t.deepEqual(result, 'bcd'); +}); + +test('replace/3', (t) => { + const result = Core.binary.replace('abcb', 'b', 'c'); + t.deepEqual(result, 'accb'); +}); + +test('replace/4', (t) => { + const result = Core.binary.replace('abcb', 'b', 'c', [ + new Core.Tuple(Symbol.for('global'), true), + ]); + t.deepEqual(result, 'accc'); +}); + +test('split/2', (t) => { + const result = Core.binary.split('abcd', 'b'); + t.deepEqual(result, ['a', 'cd']); +}); diff --git a/src/javascript/tests/core/erlang_compat/elixir_errors_spec.js b/src/javascript/tests/core/erlang_compat/elixir_errors_spec.js new file mode 100644 index 00000000..c4cc10cd --- /dev/null +++ b/src/javascript/tests/core/erlang_compat/elixir_errors_spec.js @@ -0,0 +1,7 @@ +import test from 'ava'; +import Core from '../../../lib/core'; + +test('warn', (t) => { + const result = Core.elixir_errors.warn(['this is a warning']); + t.deepEqual(result, Symbol.for('ok')); +}); diff --git a/src/javascript/tests/core/erlang_compat/erlang_spec.js b/src/javascript/tests/core/erlang_compat/erlang_spec.js new file mode 100644 index 00000000..6e41e6ea --- /dev/null +++ b/src/javascript/tests/core/erlang_compat/erlang_spec.js @@ -0,0 +1,165 @@ +import test from 'ava'; +import Core from '../../../lib/core'; + +test('is_atom', (t) => { + t.is(Core.erlang.is_atom(null), true); + t.is(Core.erlang.is_atom(true), true); + t.is(Core.erlang.is_atom(false), true); + t.is(Core.erlang.is_atom(Symbol.for('error')), true); + t.is(Core.erlang.is_atom('Hello'), false); +}); + +test('is_boolean', (t) => { + t.is(Core.erlang.is_boolean(null), false); + t.is(Core.erlang.is_boolean(true), true); + t.is(Core.erlang.is_boolean(false), true); + t.is(Core.erlang.is_boolean(Symbol.for('error')), false); + t.is(Core.erlang.is_boolean('Hello'), false); +}); + +test('is_number', (t) => { + t.is(Core.erlang.is_number(null), false); + t.is(Core.erlang.is_number(1), true); + t.is(Core.erlang.is_number(2.3), true); + t.is(Core.erlang.is_number(Symbol.for('error')), false); + t.is(Core.erlang.is_number('Hello'), false); +}); + +test('is_float', (t) => { + t.is(Core.erlang.is_float(null), false); + t.is(Core.erlang.is_float(1), false); + t.is(Core.erlang.is_float(2.3), true); + t.is(Core.erlang.is_float(Symbol.for('error')), false); + t.is(Core.erlang.is_float('Hello'), false); +}); + +test('is_integer', (t) => { + t.is(Core.erlang.is_integer(null), false); + t.is(Core.erlang.is_integer(1), true); + t.is(Core.erlang.is_integer(2.3), false); + t.is(Core.erlang.is_integer(Symbol.for('error')), false); + t.is(Core.erlang.is_integer('Hello'), false); +}); + +test('is_function', (t) => { + t.is(Core.erlang.is_function(null), false); + t.is(Core.erlang.is_function(Math.max), true); + t.is(Core.erlang.is_function(2.3), false); + t.is(Core.erlang.is_function(Symbol.for('error')), false); + t.is(Core.erlang.is_function(() => 'hello'), true); +}); + +test('is_list', (t) => { + t.is(Core.erlang.is_list(null), false); + t.is(Core.erlang.is_list([]), true); + t.is(Core.erlang.is_list(2.3), false); + t.is(Core.erlang.is_list(Symbol.for('error')), false); + t.is(Core.erlang.is_list(() => 'hello'), false); +}); + +test('atom_to_binary', (t) => { + t.is(Core.erlang.atom_to_binary(Symbol.for('error')), 'error'); + t.is(Core.erlang.atom_to_binary(Symbol.for('error'), Symbol.for('utf8')), 'error'); + + t.is(Core.erlang.atom_to_binary(null, Symbol.for('utf8')), 'nil'); + + t.is(Core.erlang.atom_to_binary(true, Symbol.for('utf8')), 'true'); + t.is(Core.erlang.atom_to_binary(false, Symbol.for('utf8')), 'false'); + + t.throws(() => Core.erlang.atom_to_binary(Symbol.for('error'), Symbol.for('utf16')), Error); +}); + +test('list_concatenation', (t) => { + t.deepEqual(Core.erlang.list_concatenation([], []), []); + t.deepEqual(Core.erlang.list_concatenation([1], []), [1]); + t.deepEqual(Core.erlang.list_concatenation([1, 2, 3], [4, 5, 6]), [1, 2, 3, 4, 5, 6]); +}); + +test('list_subtraction', (t) => { + t.deepEqual(Core.erlang.list_subtraction([], []), []); + t.deepEqual(Core.erlang.list_subtraction([1], []), [1]); + t.deepEqual(Core.erlang.list_subtraction([1, 2, 3], [4, 5, 6]), [1, 2, 3]); + t.deepEqual(Core.erlang.list_subtraction([1, 2, 3], [1, 2, 3]), []); + t.deepEqual(Core.erlang.list_subtraction([1, 2, 3], [1, 2]), [3]); +}); + +test('node', (t) => { + t.deepEqual(Core.erlang.node(), Symbol.for('nonode@nohost')); +}); + +test('nodes/0', (t) => { + t.deepEqual(Core.erlang.nodes(), []); +}); + +test('nodes/1', (t) => { + t.deepEqual(Core.erlang.nodes(Symbol.for('this')), [Symbol.for('nonode@nohost')]); + + t.deepEqual(Core.erlang.nodes([Symbol.for('this')]), [Symbol.for('nonode@nohost')]); + + t.deepEqual(Core.erlang.nodes([Symbol.for('connected')]), []); +}); + +test('equals', (t) => { + t.is(Core.erlang.equals(1, 1), true); + t.is(Core.erlang.equals(1, 'a'), false); + t.is(Core.erlang.equals('a', 'a'), true); + t.is(Core.erlang.equals('a', 'b'), false); + t.is(Core.erlang.equals(Symbol.for('this'), Symbol.for('this')), true); + t.is(Core.erlang.equals([], []), true); + t.is(Core.erlang.equals([1], []), false); + t.is( + Core.erlang.equals( + new Map([[Symbol.for('nest1'), 'valuenest1']]), + new Map([[Symbol.for('nest2'), 'valuenest2']]), + ), + false, + ); + t.is( + Core.erlang.equals( + new Map([[Symbol.for('nest1'), 'valuenest1']]), + new Map([[Symbol.for('nest1'), 'valuenest1']]), + ), + true, + ); + t.is(Core.erlang.equals(new Core.Tuple('abc'), new Core.Tuple('abc')), true); + t.is(Core.erlang.equals(new Core.Tuple('abc'), new Core.Tuple('abc', 's')), false); + + const pid = new Core.PID(); + t.is(Core.erlang.equals(pid, pid), true); + t.is(Core.erlang.equals(pid, new Core.PID()), false); + + const ref = new Core.Reference(); + t.is(Core.erlang.equals(ref, ref), true); + t.is(Core.erlang.equals(ref, new Core.Reference()), false); +}); + +test('error/1', (t) => { + let error = t.throws(() => { + Core.erlang.error( + new Map([ + [Symbol.for('__exception__'), true], + [Symbol.for('message'), 'hi'], + [ + Symbol.for('__struct__'), + { + __MODULE__: Symbol.for('Elixir.ArgumentError'), + }, + ], + ]), + ); + }, Error); + + t.is(error.message, '** (ArgumentError) hi'); + + error = t.throws(() => { + Core.erlang.error('hi'); + }, Error); + + t.is(error.message, '** (RuntimeError) hi'); + + error = t.throws(() => { + Core.erlang.error(new Core.Tuple('abc', 's')); + }, Error); + + t.is(error.message, '** (ErlangError) Erlang Error {abc, s}'); +}); diff --git a/src/javascript/tests/core/erlang_compat/filename_spec.js b/src/javascript/tests/core/erlang_compat/filename_spec.js new file mode 100644 index 00000000..98bc1ab3 --- /dev/null +++ b/src/javascript/tests/core/erlang_compat/filename_spec.js @@ -0,0 +1,32 @@ +import test from 'ava'; +import Core from '../../../lib/core'; + +test('dirname/1', (t) => { + let result = Core.filename.dirname('/usr/src/kalle.erl'); + t.is(result, '/usr/src'); + + result = Core.filename.dirname('usr/kalle.erl'); + t.is(result, 'usr'); + + result = Core.filename.dirname('kalle.erl'); + t.is(result, '.'); + + result = Core.filename.dirname('/kalle.erl'); + t.is(result, '/'); +}); + +test('join/1', (t) => { + let result = Core.filename.join(['/usr', 'local', 'bin']); + t.is(result, '/usr/local/bin'); + + result = Core.filename.join(['a', '///b/', 'c/']); + t.is(result, '/b/c'); + + result = Core.filename.join(['a/b///c/']); + t.is(result, 'a/b/c'); +}); + +test('join/2', (t) => { + const result = Core.filename.join('/usr', 'bin'); + t.is(result, '/usr/bin'); +}); diff --git a/src/javascript/tests/core/erlang_compat/io_spec.js b/src/javascript/tests/core/erlang_compat/io_spec.js new file mode 100644 index 00000000..def6eab5 --- /dev/null +++ b/src/javascript/tests/core/erlang_compat/io_spec.js @@ -0,0 +1,10 @@ +import test from 'ava'; +import Core from '../../../lib/core'; + +test('put_chars', (t) => { + let result = Core.io.put_chars(Symbol.for('stdio'), 'Hello'); + t.deepEqual(result, Symbol.for('ok')); + + result = Core.io.put_chars(Symbol.for('stderr'), 'Hello'); + t.deepEqual(result, Symbol.for('ok')); +}); diff --git a/src/javascript/tests/core/erlang_compat/lists_spec.js b/src/javascript/tests/core/erlang_compat/lists_spec.js new file mode 100644 index 00000000..7e943b73 --- /dev/null +++ b/src/javascript/tests/core/erlang_compat/lists_spec.js @@ -0,0 +1,41 @@ +import test from 'ava'; +import Core from '../../../lib/core'; + +test('reverse', (t) => { + t.deepEqual(Core.lists.reverse([1, 2, 3]), [3, 2, 1]); +}); + +test('duplicate', (t) => { + t.deepEqual(Core.lists.duplicate(0, 1), []); + t.deepEqual(Core.lists.duplicate(1, 1), [1]); + t.deepEqual(Core.lists.duplicate(2, 1), [1, 1]); +}); + +test('flatten', (t) => { + t.deepEqual(Core.lists.flatten([1, 2, 3]), [1, 2, 3]); + t.deepEqual(Core.lists.flatten([1, [[2], 3]]), [1, 2, 3]); +}); + +test('foldl', (t) => { + t.deepEqual(Core.lists.foldl((v, acc) => acc + v, 0, [1, 2, 3]), 6); +}); + +test('foldr', (t) => { + t.deepEqual(Core.lists.foldr((v, acc) => acc + v.toString(), '', [1, 2, 3]), '321'); +}); + +test('member/2', (t) => { + let result = Core.lists.member('abc', ['abc']); + t.deepEqual(result, true); + + result = Core.lists.member('abc', ['abcd']); + t.deepEqual(result, false); +}); + +test('keyfind/3', (t) => { + let result = Core.lists.keyfind('abc', 1, ['abc']); + t.deepEqual(result, false); + + result = Core.lists.keyfind('abc', 1, [new Core.Tuple('abc')]); + t.deepEqual(result, new Core.Tuple('abc')); +}); diff --git a/src/javascript/tests/core/erlang_compat/maps_spec.js b/src/javascript/tests/core/erlang_compat/maps_spec.js new file mode 100644 index 00000000..31a20dcd --- /dev/null +++ b/src/javascript/tests/core/erlang_compat/maps_spec.js @@ -0,0 +1,83 @@ +import test from 'ava'; +import Core from '../../../lib/core'; + +test('find', (t) => { + let myMap = new Map(); + let result = Core.maps.find('t', myMap); + t.is(result, Symbol.for('error')); + + myMap = 'Hello'; + result = Core.maps.find('t', myMap); + t.deepEqual(result.values, [Symbol.for('badmap'), myMap]); + + myMap = new Map([['t', 'b']]); + result = Core.maps.find('t', myMap); + t.deepEqual(result.values, [Symbol.for('ok'), 'b']); + + myMap = new Map([[[1], 'b']]); + result = Core.maps.find([1], myMap); + t.deepEqual(result.values, [Symbol.for('ok'), 'b']); + + myMap = new Map([[new Map(), 'b']]); + result = Core.maps.find(new Map(), myMap); + t.deepEqual(result.values, [Symbol.for('ok'), 'b']); +}); + +test('fold', (t) => { + const myMap = new Map([['a', 1], ['b', 2]]); + const result = Core.maps.fold((k, v, acc) => acc + v, 0, myMap); + t.is(result, 3); +}); + +test('is_key', (t) => { + const myMap = new Map([['a', 1], ['b', 2]]); + let result = Core.maps.is_key('a', myMap); + t.is(result, true); + + result = Core.maps.is_key('c', myMap); + t.is(result, false); +}); + +test('get/2', (t) => { + const myMap = new Map([['a', 1], ['b', 2]]); + const result = Core.maps.get('a', myMap); + t.is(result, 1); +}); + +test('get/3', (t) => { + let myMap = new Map([['a', 1], ['b', 2]]); + let result = Core.maps.get('a', myMap); + t.is(result, 1); + + myMap = new Map([['a', 1], ['b', 2]]); + result = Core.maps.get('c', myMap, 'undefined'); + t.is(result, 'undefined'); +}); + +test('put/3', (t) => { + const keyMap = new Map([['a', 5]]); + + let myMap = new Map([]); + myMap = Core.maps.put(keyMap, 5, myMap); + myMap = Core.maps.put(new Map([['a', 5]]), 6, myMap); + + const result = Core.maps.get(new Map([['a', 5]]), myMap); + t.is(result, 6); +}); + +test('take/2', (t) => { + const myMap = new Map([['a', 1], ['b', 2]]); + const [a, result] = Core.maps.take('a', myMap); + t.is(a, 1); + t.is(result.has('a'), false); +}); + +test('remove', (t) => { + let myMap = new Map([['a', 1], ['b', 2]]); + let result = Core.maps.remove('a', myMap); + t.is(result.has('a'), false); + + myMap = new Map([[[1], 1], ['b', 2]]); + result = Core.maps.remove([1], myMap); + t.is(Core.maps.__has(result, [1]), false); +}); diff --git a/src/javascript/tests/core/erlang_compat/math_spec.js b/src/javascript/tests/core/erlang_compat/math_spec.js new file mode 100644 index 00000000..ea22fa04 --- /dev/null +++ b/src/javascript/tests/core/erlang_compat/math_spec.js @@ -0,0 +1,13 @@ +import test from 'ava'; +import Core from '../../../lib/core'; + +test('log2/1', (t) => { + let result = Core.math.log2(1); + t.is(result, 0); + + result = Core.math.log2(2); + t.is(result, 1); + + result = Core.math.log2(4); + t.is(result, 2); +}); diff --git a/src/javascript/tests/core/erlang_compat/proplists_spec.js b/src/javascript/tests/core/erlang_compat/proplists_spec.js new file mode 100644 index 00000000..b38e5f98 --- /dev/null +++ b/src/javascript/tests/core/erlang_compat/proplists_spec.js @@ -0,0 +1,26 @@ +import test from 'ava'; +import Core from '../../../lib/core'; + +test('get_value/2', (t) => { + let result = Core.proplists.get_value('abc', [new Core.Tuple('abc', '123')]); + t.deepEqual(result, '123'); + + result = Core.proplists.get_value('abc', ['abc']); + t.deepEqual(result, true); + + result = Core.proplists.get_value('abcd', ['abc']); + t.deepEqual(result, Symbol.for('undefined')); +}); + +test('get_value/3', (t) => { + let result = Core.proplists.get_value('abc', [new Core.Tuple('abc', '123')], 'xyz'); + t.deepEqual(result, '123'); + + result = Core.proplists.get_value('abcd', [new Core.Tuple('abc', '123')], 'xyz'); + t.deepEqual(result, 'xyz'); +}); + +test('is_defined/2', (t) => { + const result = Core.binary.at('abc', 0); + t.deepEqual(result, 'a'); +}); diff --git a/src/javascript/tests/core/erlang_compat/unicode_spec.js b/src/javascript/tests/core/erlang_compat/unicode_spec.js new file mode 100644 index 00000000..0781284f --- /dev/null +++ b/src/javascript/tests/core/erlang_compat/unicode_spec.js @@ -0,0 +1,30 @@ +import test from 'ava'; +import Core from '../../../lib/core'; + +test('characters_to_list', (t) => { + let result = Core.unicode.characters_to_list('hello'); + t.deepEqual(result, [104, 101, 108, 108, 111]); + + result = Core.unicode.characters_to_list(['hello', 'fg']); + t.deepEqual(result, [104, 101, 108, 108, 111, 102, 103]); + + result = Core.unicode.characters_to_list(['hello', 'fg', 34]); + t.deepEqual(result, [104, 101, 108, 108, 111, 102, 103, 34]); + + result = Core.unicode.characters_to_list(['hello', 'fg', 34, ['s']]); + t.deepEqual(result, [104, 101, 108, 108, 111, 102, 103, 34, 115]); +}); + +test('characters_to_binary', (t) => { + let result = Core.unicode.characters_to_binary('hello'); + t.deepEqual(result, 'hello'); + + result = Core.unicode.characters_to_binary(['hello', 'fg']); + t.deepEqual(result, 'hellofg'); + + result = Core.unicode.characters_to_binary(['hello', 'fg', 34]); + t.deepEqual(result, 'hellofg"'); + + result = Core.unicode.characters_to_binary(['hello', 'fg', 34, ['s']]); + t.deepEqual(result, 'hellofg"s'); +}); diff --git a/src/javascript/tests/core/functions.spec.js b/src/javascript/tests/core/functions.spec.js new file mode 100644 index 00000000..d3ed14e1 --- /dev/null +++ b/src/javascript/tests/core/functions.spec.js @@ -0,0 +1,86 @@ +import test from 'ava'; +import Core from '../../lib/core'; + +const Functions = Core.Functions; + +test('call_property', (t) => { + t.is(Functions.call_property(1, 'toString'), '1'); + t.is(Functions.call_property([], 'toString'), ''); + t.is(Functions.call_property([], 'length'), 0); + t.is(Functions.call_property('', 'toString'), ''); + t.is(Functions.call_property('', 'length'), 0); + t.is(Functions.call_property(Symbol('test'), 'toString'), 'Symbol(test)'); + t.is(Functions.call_property({ completed: false }, 'completed'), false); + t.is(Functions.call_property({ id: 0 }, 'id'), 0); +}); + +test('split_at', (t) => { + t.deepEqual(Functions.split_at('sweetelixir', 5).values, ['sweet', 'elixir']); + t.deepEqual(Functions.split_at('sweetelixir', -6).values, ['sweet', 'elixir']); + t.deepEqual(Functions.split_at('abc', 0).values, ['', 'abc']); + t.deepEqual(Functions.split_at('abc', 1000).values, ['abc', '']); + t.deepEqual(Functions.split_at('abc', -1000).values, ['', 'abc']); + t.deepEqual(Functions.split_at('😀abélkm', 4).values, ['😀abé', 'lkm']); + t.deepEqual(Functions.split_at('👨‍👩‍👦‍👦abélkm', 4).values, ['👨‍👩‍👦‍👦abé', 'lkm']); +}); + +test('map_to_object/1', (t) => { + const map = new Map([[Symbol.for('key'), 'value'], [Symbol.for('anotherKey'), 'value2']]); + const result = Functions.map_to_object(map); + const obj = {}; + obj[Symbol.for('key')] = 'value'; + obj[Symbol.for('anotherKey')] = 'value2'; + t.deepEqual(result, obj); +}); + +test('map_to_object/2', (t) => { + const map = new Map([[Symbol.for('key'), 'value'], [Symbol.for('anotherKey'), 'value2']]); + const options = [new Core.Tuple(Symbol.for('keys'), Symbol.for('string'))]; + const result = Functions.map_to_object(map, options); + + t.deepEqual(result, { key: 'value', anotherKey: 'value2' }); +}); + +test('object_to_map/1', (t) => { + let obj = {}; + let result = Functions.object_to_map(obj); + t.deepEqual(result, new Map()); + + obj = { key: 'value', key2: null }; + result = Functions.object_to_map(obj); + t.deepEqual(result, new Map([['key', 'value'], ['key2', null]])); + + obj = {}; + obj[Symbol.for('key')] = 'value'; + result = Functions.object_to_map(obj); + t.deepEqual(result, new Map([[Symbol.for('key'), 'value']])); +}); + +test('object_to_map/2', (t) => { + let obj = {}; + let result = Functions.object_to_map(obj, []); + t.deepEqual(result, new Map()); + + obj = { key: 'value' }; + result = Functions.object_to_map(obj, [new Core.Tuple(Symbol.for('keys'), Symbol.for('atom'))]); + t.deepEqual(result, new Map([[Symbol.for('key'), 'value']])); + + obj = {}; + obj[Symbol.for('key')] = [{ nest1: 'valuenest1' }, { nest2: 'valuenest2' }]; + result = Functions.object_to_map(obj, [ + new Core.Tuple(Symbol.for('keys'), Symbol.for('atom')), + new Core.Tuple(Symbol.for('recurse_array'), true), + ]); + t.deepEqual( + result, + new Map([ + [ + Symbol.for('key'), + [ + new Map([[Symbol.for('nest1'), 'valuenest1']]), + new Map([[Symbol.for('nest2'), 'valuenest2']]), + ], + ], + ]), + ); +}); diff --git a/src/javascript/tests/for.spec.js b/src/javascript/tests/for.spec.js new file mode 100644 index 00000000..9bcd1894 --- /dev/null +++ b/src/javascript/tests/for.spec.js @@ -0,0 +1,126 @@ +import test from 'ava'; +import Core from '../lib/core'; + +const Patterns = Core.Patterns; +const SpecialForms = Core.SpecialForms; +const Tuple = Core.Tuple; +const BitString = Core.BitString; + +const $ = Patterns.variable(); + +const collectable = { + into(original) { + const fun = Patterns.defmatch( + Patterns.clause( + [ + $, + Patterns.type(Tuple, { + values: [Symbol.for('cont'), Patterns.variable()], + }), + ], + (list, x) => list.concat([x]), + ), + Patterns.clause([$, Symbol.for('done')], list => list), + ); + + return new Tuple([], fun); + }, +}; + +test('simple for', (t) => { + const gen = Patterns.list_generator($, [1, 2, 3, 4]); + const result = SpecialForms._for(Patterns.clause([$], x => x * 2), [gen], collectable); + + t.deepEqual(result, [2, 4, 6, 8]); +}); + +test('for with multiple generators', (t) => { + // for x <- [1, 2], y <- [2, 3], do: x*y + + const gen = Patterns.list_generator($, [1, 2]); + const gen2 = Patterns.list_generator($, [2, 3]); + const result = SpecialForms._for( + Patterns.clause([$, $], (x, y) => x * y), + [gen, gen2], + collectable, + ); + + t.deepEqual(result, [2, 3, 4, 6]); +}); + +test('for with filter', (t) => { + // for n <- [1, 2, 3, 4, 5, 6], rem(n, 2) == 0, do: n + const gen = Patterns.list_generator($, [1, 2, 3, 4, 5, 6]); + const result = SpecialForms._for( + Patterns.clause([$], x => x, x => x % 2 === 0), + [gen], + collectable, + ); + + t.deepEqual(result, [2, 4, 6]); +}); + +test('for with pattern matching', (t) => { + // for {:user, name} <- [user: "john", admin: "john", user: "meg"], do + // String.upcase(name) + // end + + const gen = Patterns.list_generator( + [Symbol.for('user'), $], + [[Symbol.for('user'), 'john'], [Symbol.for('admin'), 'john'], [Symbol.for('user'), 'meg']], + ); + + const result = SpecialForms._for( + Patterns.clause([[Symbol.for('user'), $]], name => name.toUpperCase()), + [gen], + collectable, + ); + + t.deepEqual(result, ['JOHN', 'MEG']); +}); + +test('for with bitstring', (t) => { + // for <> >>, do: {r, g, b} + + const gen = Patterns.bitstring_generator( + Patterns.bitStringMatch( + BitString.integer({ value: $ }), + BitString.integer({ value: $ }), + BitString.integer({ value: $ }), + ), + new BitString( + BitString.integer(213), + BitString.integer(45), + BitString.integer(132), + BitString.integer(64), + BitString.integer(76), + BitString.integer(32), + BitString.integer(76), + BitString.integer(0), + BitString.integer(0), + BitString.integer(234), + BitString.integer(32), + BitString.integer(15), + ), + ); + + const expression = Patterns.clause( + [ + Patterns.bitStringMatch( + BitString.integer({ value: $ }), + BitString.integer({ value: $ }), + BitString.integer({ value: $ }), + ), + ], + (r, g, b) => new Tuple(r, g, b), + ); + + const result = SpecialForms._for(expression, [gen], collectable); + + t.deepEqual(result, [ + new Tuple(213, 45, 132), + new Tuple(64, 76, 32), + new Tuple(76, 0, 0), + new Tuple(234, 32, 15), + ]); +}); diff --git a/src/javascript/tests/try.spec.js b/src/javascript/tests/try.spec.js new file mode 100644 index 00000000..fdcbaefd --- /dev/null +++ b/src/javascript/tests/try.spec.js @@ -0,0 +1,33 @@ +import test from 'ava'; +import Core from '../lib/core'; + +const { Patterns, SpecialForms } = Core; + +test('try', (t) => { + /* + try do + 1 / x + else + y when y < 1 and y > -1 -> + :small + _ -> + :large + end + + */ + + const x = 1; + + const value = SpecialForms._try( + () => 1 / x, + null, + null, + Patterns.defmatch( + Patterns.clause([Patterns.variable()], () => Symbol.for('small'), y => y < 1 && y > -1), + Patterns.clause([Patterns.wildcard()], () => Symbol.for('large')), + ), + null, + ); + + t.is(value, Symbol.for('large')); +}); diff --git a/src/javascript/tests/with.spec.js b/src/javascript/tests/with.spec.js new file mode 100644 index 00000000..31fb2c49 --- /dev/null +++ b/src/javascript/tests/with.spec.js @@ -0,0 +1,148 @@ +import test from 'ava'; +import Core from '../lib/core'; + +const Patterns = Core.Patterns; +const SpecialForms = Core.SpecialForms; +const Tuple = Core.Tuple; +const MatchError = Core.Patterns.MatchError; + +const $ = Patterns.variable(); + +function map_fetch(map, key) { + if (key in map) { + return new Tuple(Symbol.for('ok'), map[key]); + } + + return Symbol.for('error'); +} + +test('with', (t) => { + /* + opts = %{width: 10, height: 15} + + with {:ok, width} <- Map.fetch(opts, :width), + {:ok, height} <- Map.fetch(opts, :height), + do: {:ok, width * height} + + {:ok, 150} + */ + + const opts = { width: 10, height: 15 }; + + const value = SpecialForms._with( + [new Tuple(Symbol.for('ok'), $), () => map_fetch(opts, 'width')], + [new Tuple(Symbol.for('ok'), $), () => map_fetch(opts, 'height')], + (width, height) => new Tuple(Symbol.for('ok'), width * height), + ); + + t.deepEqual(value, new Tuple(Symbol.for('ok'), 150)); +}); + +test('with without match', (t) => { + /* + opts = %{width: 10} + + with {:ok, width} <- Map.fetch(opts, :width), + {:ok, height} <- Map.fetch(opts, :height), + do: {:ok, width * height} + + :error + */ + + const opts = { width: 10 }; + + const value = SpecialForms._with( + [new Tuple(Symbol.for('ok'), $), () => map_fetch(opts, 'width')], + [new Tuple(Symbol.for('ok'), $), () => map_fetch(opts, 'height')], + (width, height) => new Tuple(Symbol.for('ok'), width * height), + ); + + t.deepEqual(value, Symbol.for('error')); +}); + +test('with bare expression', (t) => { + /* + opts = %{width: 10} + + with {:ok, width} <- Map.fetch(opts, :width), + double_width = width * 2, + {:ok, height} <- Map.fetch(opts, :height), + do: {:ok, double_width * height} + + {:ok, 300} + */ + + const opts = { width: 10, height: 15 }; + + const value = SpecialForms._with( + [new Tuple(Symbol.for('ok'), $), () => map_fetch(opts, 'width')], + [$, width => width * 2], + [new Tuple(Symbol.for('ok'), $), () => map_fetch(opts, 'height')], + (width, double_width, height) => new Tuple(Symbol.for('ok'), double_width * height), + ); + + t.deepEqual(value, new Tuple(Symbol.for('ok'), 300)); +}); + +test('with else', (t) => { + /* + opts = %{width: 10} + + with {:ok, width} <- Map.fetch(opts, :width), + {:ok, height} <- Map.fetch(opts, :height) do + {:ok, width * height} + else + :error -> {:error, :wrong_data} + end + + {:error, :wrong_data} + */ + + const opts = { width: 10 }; + + const value = SpecialForms._with( + [new Tuple(Symbol.for('ok'), $), () => map_fetch(opts, 'width')], + [new Tuple(Symbol.for('ok'), $), () => map_fetch(opts, 'height')], + (width, height) => new Tuple(Symbol.for('ok'), width * height), + Patterns.defmatch( + Patterns.clause( + [Symbol.for('error')], + () => new Tuple(Symbol.for('error'), Symbol.for('wrong_data')), + ), + ), + ); + + t.deepEqual(value, new Tuple(Symbol.for('error'), Symbol.for('wrong_data'))); +}); + +test('with else that don`t match', (t) => { + /* + opts = %{width: 10} + + with {:ok, width} <- Map.fetch(opts, :width), + {:ok, height} <- Map.fetch(opts, :height) do + {:ok, width * height} + else + :fail -> {:error, :wrong_data} + end + + {:error, :wrong_data} + */ + + const opts = { width: 10 }; + + const withFunction = SpecialForms._with.bind( + null, + [new Tuple(Symbol.for('ok'), $), () => map_fetch(opts, 'width')], + [new Tuple(Symbol.for('ok'), $), () => map_fetch(opts, 'height')], + (width, height) => new Tuple(Symbol.for('ok'), width * height), + Patterns.defmatch( + Patterns.clause( + [Symbol.for('fail')], + () => new Tuple(Symbol.for('error'), Symbol.for('wrong_data')), + ), + ), + ); + + t.throws(withFunction, MatchError); +}); diff --git a/test/beam_test.exs b/test/beam_test.exs new file mode 100644 index 00000000..66369a94 --- /dev/null +++ b/test/beam_test.exs @@ -0,0 +1,15 @@ +defmodule ElixirScript.Beam.Test do + use ExUnit.Case + + test "can get ast from beam" do + assert {:ok, _} = ElixirScript.Beam.debug_info(Atom) + end + + test "errors when not found" do + assert {:error, _} = ElixirScript.Beam.debug_info(Some.Module) + end + + test "can get ast from beam that is protocol" do + assert {:ok, Enumerable, _, _} = ElixirScript.Beam.debug_info(Enumerable) + end +end diff --git a/test/cli_test.exs b/test/cli_test.exs new file mode 100644 index 00000000..0a61dbf2 --- /dev/null +++ b/test/cli_test.exs @@ -0,0 +1,33 @@ +defmodule ElixirScript.CLI.Test do + use ExUnit.Case + import ExUnit.CaptureIO + + test "parse_args" do + {_, args} = ElixirScript.CLI.parse_args(["Atom", "--output", "build"]) + assert args == [output: "build"] + end + + test "process help" do + assert capture_io(fn -> + ElixirScript.CLI.process(:help) + end) =~ "usage: elixirscript [options]" + end + + test "process version" do + assert capture_io(fn -> + ElixirScript.CLI.process(:version) + end) =~ Mix.Project.config()[:version] + end + + test "process unknown" do + assert capture_io(fn -> + ElixirScript.CLI.process({"", [unknown: ""]}) + end) =~ "usage: elixirscript [options]" + end + + test "process input" do + assert capture_io(fn -> + ElixirScript.CLI.process({["Atom"], []}) + end) =~ "export default" + end +end diff --git a/test/compiler_test.exs b/test/compiler_test.exs new file mode 100644 index 00000000..b7b05dd3 --- /dev/null +++ b/test/compiler_test.exs @@ -0,0 +1,40 @@ +defmodule ElixirScript.Compiler.Test do + use ExUnit.Case + + test "Can compile one entry module" do + result = ElixirScript.Compiler.compile(Version) + assert result |> Map.to_list() |> hd |> elem(1) |> Map.get(:js_code) |> is_binary + end + + test "Can compile multiple entry modules" do + result = ElixirScript.Compiler.compile([Atom, String, Agent]) + assert result |> Map.to_list() |> hd |> elem(1) |> Map.get(:js_code) |> is_binary + end + + test "Output" do + result = ElixirScript.Compiler.compile(Atom, []) + assert result |> Map.to_list() |> hd |> elem(1) |> Map.get(:js_code) =~ "export default" + end + + test "compile file" do + path = System.tmp_dir() + path = Path.join([path, "Elixir.ElixirScript.Beam.Test.js"]) + + input_path = Path.join([File.cwd!(), "test", "beam_test.exs"]) + + ElixirScript.Compiler.compile(input_path, output: path) + assert File.exists?(path) + assert String.contains?(File.read!(path), "export default") + end + + test "compile wildcard" do + path = System.tmp_dir() + file = Path.join([path, "Elixir.ElixirScript.FFI.Test.js"]) + + input_path = Path.join([File.cwd!(), "test", "*fi_test.exs"]) + + ElixirScript.Compiler.compile(input_path, output: path) + assert File.exists?(file) + assert String.contains?(File.read!(file), "export default") + end +end diff --git a/test/elixir_script_test.exs b/test/elixir_script_test.exs deleted file mode 100644 index 4849e865..00000000 --- a/test/elixir_script_test.exs +++ /dev/null @@ -1,122 +0,0 @@ -defmodule ElixirScript.Test do - use ShouldI - import ElixirScript.TestHelper - - should "chain methods" do - js_code = ElixirScript.compile(""" - JQuery.("
").text(html) - """) - - assert hd(js_code) == "JQuery('
').text(html)" - end - - should "turn javascript ast into javascript code strings" do - js_code = ElixirScript.compile(":atom") - assert hd(js_code) == "Kernel.SpecialForms.atom('atom')" - end - - should "parse one module correctly" do - js_code = ElixirScript.compile(""" - - defmodule Elephant do - @ul JQuery.("#todo-list") - - def something() do - @ul - end - - defp something_else() do - end - end - """) - - assert_js_matches """ - import { Patterns, Kernel, Atom, Enum, Integer, JS, List, Range, Tuple, Agent, Keyword, BitString } from 'elixir'; - const __MODULE__ = Kernel.SpecialForms.atom('Elephant'); - let something_else = Patterns.defmatch(Patterns.make_case([],function() { - return null; - })); - let something = Patterns.defmatch(Patterns.make_case([],function() { - return ul; - })); - const ul = JQuery('#todo-list'); - export { - something - }; - """, hd(js_code) - end - - should "parse multiple modules correctly" do - - js_code = ElixirScript.compile(""" - defmodule Animals do - - defmodule Elephant do - defstruct trunk: true - end - - - def something() do - %Elephant{} - end - - end - """, env: make_custom_env) - - assert_js_matches """ - import { Patterns, Kernel, Atom, Enum, Integer, JS, List, Range, Tuple, Agent, Keyword, BitString } from 'elixir'; - import * as Elephant from 'animals/elephant'; - - const __MODULE__ = Kernel.SpecialForms.atom('Animals'); - let something = Patterns.defmatch(Patterns.make_case([],function() { - return Elephant.defstruct(); - })); - export { - something - }; - """, hd(js_code) - - assert_js_matches """ - import { Patterns, Kernel, Atom, Enum, Integer, JS, List, Range, Tuple, Agent, Keyword, BitString } from 'elixir'; - const __MODULE__ = Kernel.SpecialForms.atom('Elephant'); - function defstruct(trunk = true) { - return Kernel.SpecialForms.map({ - [Kernel.SpecialForms.atom('__struct__')]: __MODULE__, [Kernel.SpecialForms.atom('trunk')]: trunk - }); - } - export { - defstruct - }; - """, List.last(js_code) - end - - - should "parse macros" do - - js_code = ElixirScript.compile(""" - defmodule Animals do - use ElixirScript.Using - - defp something_else() do - ElixirScript.Math.squared(1) - end - - end - """, env: make_custom_env) - - assert_js_matches """ - import { Patterns, Kernel, Atom, Enum, Integer, JS, List, Range, Tuple, Agent, Keyword, BitString } from 'elixir'; - const __MODULE__ = Kernel.SpecialForms.atom('Animals'); - let something_else = Patterns.defmatch(Patterns.make_case([],function() { - return 1 * 1; - })); - let sandwich = Patterns.defmatch(Patterns.make_case([],function() { - return null; - })); - export { - sandwich - }; - """, hd(js_code) - end - -end diff --git a/test/ffi_test.exs b/test/ffi_test.exs new file mode 100644 index 00000000..5b593d52 --- /dev/null +++ b/test/ffi_test.exs @@ -0,0 +1,17 @@ +defmodule ElixirScript.FFI.Test do + use ExUnit.Case + + defmodule MyTestModule do + use ElixirScript.FFI + + defexternal my_test_function(arg1, arg2) + end + + test "FFI module has __foreign_info__ attribute" do + assert Keyword.has_key?(MyTestModule.__info__(:attributes), :__foreign_info__) + end + + test "FFI module makes foreign function" do + assert Keyword.has_key?(MyTestModule.__info__(:functions), :my_test_function) + end +end diff --git a/test/lib/js_test.exs b/test/lib/js_test.exs deleted file mode 100644 index 09ef6379..00000000 --- a/test/lib/js_test.exs +++ /dev/null @@ -1,38 +0,0 @@ -defmodule ElixirScript.Lib.JS.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate new" do - ex_ast = quote do - JS.new A.B, [1, 2, 3] - end - - js_code = """ - new A.B(1, 2, 3) - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - JS.new A, [1, 2, 3] - end - - js_code = """ - new A(1, 2, 3) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate update" do - ex_ast = quote do - JS.update A, "b", [1, 2, 3] - end - - js_code = """ - A['b'] = Kernel.SpecialForms.list(1, 2, 3) - """ - - assert_translation(ex_ast, js_code) - end -end \ No newline at end of file diff --git a/test/lib/kernel_test.exs b/test/lib/kernel_test.exs deleted file mode 100644 index 7cdaccb2..00000000 --- a/test/lib/kernel_test.exs +++ /dev/null @@ -1,16 +0,0 @@ -defmodule ElixirScript.Lib.Kernel.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate range" do - ex_ast = quote do - 1..4 - end - - js_code = """ - Range(1,4) - """ - - assert_translation(ex_ast, js_code) - end -end \ No newline at end of file diff --git a/test/manifest_test.exs b/test/manifest_test.exs new file mode 100644 index 00000000..52efd044 --- /dev/null +++ b/test/manifest_test.exs @@ -0,0 +1,12 @@ +defmodule ElixirScript.Manifest.Test do + use ExUnit.Case + alias ElixirScript.Manifest + + test "write manifest" do + result = ElixirScript.Compiler.compile(Atom) + path = Path.join([System.tmp_dir(), "write_manifest_test", ".compile.elixir_script"]) + Manifest.write_manifest(path, result) + assert File.exists?(path) + end + +end diff --git a/test/passes/translate/form_test.exs b/test/passes/translate/form_test.exs new file mode 100644 index 00000000..07d1b902 --- /dev/null +++ b/test/passes/translate/form_test.exs @@ -0,0 +1,219 @@ +defmodule ElixirScript.Translate.Forms.Test do + use ExUnit.Case + alias ElixirScript.Translate.Form + alias ElixirScript.Translate.Helpers + alias ESTree.Tools.Builder, as: J + use ExUnitProperties + + setup_all do + {:ok, pid} = ElixirScript.State.start_link(%{}) + + state = %{ + pid: pid, + vars: %{} + } + + [state: state] + end + + property "integers, floats, binaries, and booleans translates to a literal JavaScript AST node", + %{state: state} do + check all value <- + StreamData.one_of([ + StreamData.integer(), + StreamData.boolean(), + StreamData.binary(), + StreamData.float() + ]) do + {js_ast, _} = Form.compile(value, state) + assert js_ast == J.literal(value) + end + end + + property "atom translates to Symbol.for call", %{state: state} do + check all atom <- StreamData.atom(:alphanumeric) do + {js_ast, _} = Form.compile(atom, state) + + assert js_ast == + J.call_expression( + J.member_expression( + J.identifier("Symbol"), + J.identifier("for") + ), + [J.literal(atom)] + ) + end + end + + property "tuple translates to new Tuple object", %{state: state} do + check all tuple <- StreamData.tuple({StreamData.integer(), StreamData.binary()}) do + {js_ast, _} = Form.compile(tuple, state) + + assert js_ast == + J.new_expression( + J.member_expression( + J.member_expression( + J.identifier("ElixirScript"), + J.identifier("Core") + ), + J.identifier("Tuple") + ), + [J.literal(elem(tuple, 0)), J.literal(elem(tuple, 1))] + ) + end + end + + property "list translates to a JavaScript Array", %{state: state} do + check all list <- StreamData.list_of(StreamData.binary()) do + {js_ast, _} = Form.compile(list, state) + assert js_ast.type == "ArrayExpression" + assert length(js_ast.elements) == length(list) + + Enum.zip(js_ast.elements, list) + |> Enum.each(fn {ast, original} -> + assert ast == J.literal(original) + end) + end + end + + property "local function call translates to local JavaScript function call", %{state: state} do + check all func <- StreamData.filter(StreamData.atom(:alphanumeric), fn x -> x not in [:fn] end), + params <- StreamData.list_of(StreamData.binary()) do + ast = {func, [], params} + + str_func = + if func in ElixirScript.Translate.Identifier.js_reserved_words() do + "__#{to_string(func)}__" + else + to_string(func) + end + + {js_ast, _} = Form.compile(ast, state) + assert js_ast.type == "CallExpression" + assert length(js_ast.arguments) == length(params) + assert js_ast.callee.type == "Identifier" + assert js_ast.callee.name == str_func + + Enum.zip(js_ast.arguments, params) + |> Enum.each(fn {ast, original} -> + assert ast == J.literal(original) + end) + end + end + + property "super function call translates to local JavaScript function call" do + check all func <- StreamData.atom(:alphanumeric), + params <- StreamData.list_of(StreamData.binary()) do + ast = {:super, [], [{:def, func}] ++ params} + state = %{function: {func, nil}, vars: %{}} + + str_func = + if func in ElixirScript.Translate.Identifier.js_reserved_words() do + "__#{to_string(func)}__" + else + to_string(func) + end + + {js_ast, _} = Form.compile(ast, state) + assert js_ast.type == "CallExpression" + assert length(js_ast.arguments) == length(params) + assert js_ast.callee.type == "Identifier" + assert js_ast.callee.name == str_func + + Enum.zip(js_ast.arguments, params) + |> Enum.each(fn {ast, original} -> + assert ast == J.literal(original) + end) + end + end + + test "module", %{state: state} do + ast = IO + + ElixirScript.State.put_module(state.pid, IO, %{}) + + {js_ast, _} = Form.compile(ast, state) + assert js_ast == %ESTree.Identifier{loc: nil, name: "$IO$", type: "Identifier"} + end + + test "unknown module", %{state: state} do + ast = Enum + + {js_ast, _} = Form.compile(ast, state) + + assert js_ast == %ESTree.ObjectExpression{ + loc: nil, + properties: [], + type: "ObjectExpression" + } + end + + test "function returning an array" do + ast = {:fn, [], [{:foo, [], [], [1, 2, 3]}]} + state = %{function: {:something, nil}} + + {js_ast, _} = Form.compile(ast, state) + + return_statement = Enum.at(Enum.at(hd(js_ast.body.body).body.body, 1).consequent.body, 1) + + assert return_statement.argument == + J.array_expression([ + J.literal(1), + J.literal(2), + J.literal(3) + ]) + end + + test "calling field on field" do + ast = + { + {:., [line: 16], [ + {{:., [line: 16], [{:map, [line: 16], nil}, :token_count]}, [line: 16], []}, + :toLocaleString + ]}, + [line: 16], + [] + } + + state = %{function: {:something, nil}, vars: %{}} + + {js_ast, _} = Form.compile(ast, state) + + assert js_ast == + Helpers.call(ElixirScript.Translate.Forms.JS.call_property(), [ + Helpers.call(ElixirScript.Translate.Forms.JS.call_property(), [ + J.identifier("map"), + J.literal("token_count") + ]), + J.literal("toLocaleString") + ]) + end + + test "make sure counter used in guard", %{state: state} do + state = + Map.merge(state, %{ + anonymous_fn: false, + function: {:filter_names_in_guards, nil}, + in_guard: true, + module: Integration, + vars: %{"has__qmark__" => 0} + }) + + ast = {{:., [], [:erlang, :==]}, [line: 29], [{:has?, [line: 29], nil}, 5]} + + {js_ast, _} = Form.compile(ast, state) + assert hd(js_ast.arguments).name === "has__qmark__0" + end + + test "multi bind", %{state: state} do + ast = + {:=, [line: 35], [ + [{:|, [line: 35], [{:a, [line: 35], nil}, {:_, [line: 35], nil}]}], + {:=, [line: 35], [{:b, [line: 35], nil}, [1, 2, 3, 4, 5]]} + ]} + + {js_ast, _} = Form.compile(ast, state) + + assert length(js_ast) > 1 + end +end diff --git a/test/passes/translate/forms/bitstring_test.exs b/test/passes/translate/forms/bitstring_test.exs new file mode 100644 index 00000000..1d62517d --- /dev/null +++ b/test/passes/translate/forms/bitstring_test.exs @@ -0,0 +1,30 @@ +defmodule ElixirScript.Translate.Forms.Bitstring.Test do + use ExUnit.Case + alias ElixirScript.Translate.Form + alias ESTree.Tools.Builder, as: J + + setup_all do + {:ok, pid} = ElixirScript.State.start_link(%{}) + + state = %{ + pid: pid, + vars: %{} + } + + [state: state] + end + + test "string interpolation", %{state: state} do + ast = {:<<>>, [line: 5], + [{:::, [], ["Hello, ", {:binary, [], []}]}, + {:::, [line: 5], + [{{:., [line: 5], [String.Chars, :to_string]}, [line: 5], + [{:name, [line: 5], nil}]}, {:binary, [], []}]}]} + + {js_ast, _} = Form.compile(ast, state) + assert js_ast.type == "BinaryExpression" + assert js_ast.left == J.literal("Hello, ") + assert js_ast.right.type == "CallExpression" + end + +end diff --git a/test/passes/translate/forms/js_test.exs b/test/passes/translate/forms/js_test.exs new file mode 100644 index 00000000..d3989cb9 --- /dev/null +++ b/test/passes/translate/forms/js_test.exs @@ -0,0 +1,99 @@ +defmodule ElixirScript.Translate.Forms.JS.Test do + use ExUnit.Case + alias ElixirScript.Translate.{Form, Helpers} + alias ESTree.Tools.Builder, as: J + + setup_all do + {:ok, pid} = ElixirScript.State.start_link(%{}) + + state = %{ + pid: pid, + vars: %{} + } + + [state: state] + end + + test "debugger" do + ast = {{:., [], [ElixirScript.JS, :debugger]}, [], []} + state = %{function: {:each, nil}, module: Enum, vars: %{:_ => 0, "entry" => 0, "enumerable" => 0, "fun" => 0}} + + {js_ast, _} = Form.compile(ast, state) + assert js_ast == J.debugger_statement() + end + + test "this" do + ast = {{:., [], [ElixirScript.JS, :this]}, [], []} + state = %{function: {:each, nil}, module: Enum, vars: %{:_ => 0, "entry" => 0, "enumerable" => 0, "fun" => 0}} + + {js_ast, _} = Form.compile(ast, state) + assert js_ast == J.this_expression() + end + + test "new" do + ast = {{:., [], [ElixirScript.JS, :new]}, [], [BLT, ["bacon", "lettuce", "tomato"]]} + state = %{function: {:each, nil}, module: Enum, vars: %{:_ => 0, "entry" => 0, "enumerable" => 0, "fun" => 0}} + + {js_ast, _} = Form.compile(ast, state) + assert js_ast == J.new_expression( + J.identifier("BLT"), + [ + J.literal("bacon"), + J.literal("lettuce"), + J.literal("tomato") + ] + ) + end + + + test "throw" do + ast = {{:., [], [ElixirScript.JS, :throw]}, [], [1]} + state = %{function: {:each, nil}, module: Enum, vars: %{:_ => 0, "entry" => 0, "enumerable" => 0, "fun" => 0}} + + {js_ast, _} = Form.compile(ast, state) + assert js_ast == J.throw_statement(J.literal(1)) + end + + test "import" do + ast = {{:., [], [ElixirScript.JS, :import]}, [], ["react"]} + state = %{function: {:each, nil}, module: Enum, vars: %{:_ => 0, "entry" => 0, "enumerable" => 0, "fun" => 0}} + + {js_ast, _} = Form.compile(ast, state) + assert js_ast == J.call_expression( + J.identifier("import"), + [J.literal("react")] + ) + end + + test "mutate/3" do + ast = {{:., [], [ElixirScript.JS, :mutate]}, [], [{:entry, [], nil}, "a", 2]} + state = %{function: {:each, nil}, module: Enum, vars: %{:_ => 0, "entry" => 0, "enumerable" => 0, "fun" => 0}} + + {js_ast, _} = Form.compile(ast, state) + assert js_ast == J.assignment_expression( + :=, + J.member_expression( + J.identifier("entry0"), + J.literal("a"), + true + ), + J.literal(2) + ) + end + + test "map_to_object/1" do + ast = {{:., [], [ElixirScript.JS, :map_to_object]}, [], [{:entry, [], nil}]} + state = %{function: {:each, nil}, module: Enum, vars: %{:_ => 0, "entry" => 0, "enumerable" => 0, "fun" => 0}} + + {js_ast, _} = Form.compile(ast, state) + assert js_ast == J.call_expression( + J.member_expression( + Helpers.functions(), + J.identifier("map_to_object") + ), + [ + J.identifier("entry0") + ] + ) + end +end diff --git a/test/passes/translate/forms/map_test.exs b/test/passes/translate/forms/map_test.exs new file mode 100644 index 00000000..22eaa6b1 --- /dev/null +++ b/test/passes/translate/forms/map_test.exs @@ -0,0 +1,84 @@ +defmodule ElixirScript.Translate.Forms.Map.Test do + use ExUnit.Case + alias ElixirScript.Translate.Form + alias ESTree.Tools.Builder, as: J + use ExUnitProperties + + setup_all do + {:ok, pid} = ElixirScript.State.start_link(%{}) + + state = %{ + pid: pid + } + + [state: state] + end + + property "maps convert to Map objects", %{state: state} do + check all tuple <- StreamData.tuple({ + StreamData.one_of([ + StreamData.integer(), + StreamData.boolean(), + StreamData.binary(), + StreamData.float() + ]), + StreamData.binary() + }) do + + properties = [tuple] + ast = {:%{}, [], properties} + + {js_ast, _} = Form.compile(ast, state) + assert js_ast == J.new_expression( + J.identifier("Map"), + [ + J.array_expression([ + J.array_expression([ + J.literal(elem(tuple, 0)), + J.literal(elem(tuple, 1)), + ]) + ]) + ] + ) + end + end + + property "maps update converts to new Map objects using old version", %{state: state} do + check all key <- StreamData.binary(), + old_value <- StreamData.integer(), + new_value <- StreamData.integer() do + + properties = [{key, old_value}] + map_ast = {:%{}, [], properties} + new_values = [{key, new_value}] + + ast = {:%{}, [], [{:|, [], [map_ast, new_values]}]} + + map_js_ast = J.new_expression( + J.identifier("Map"), + [ + J.array_expression([ + J.array_expression([ + J.literal(key), + J.literal(old_value), + ]) + ]) + ] + ) + + {js_ast, _} = Form.compile(ast, state) + assert js_ast == J.new_expression( + J.identifier("Map"), + [ + J.array_expression( + [J.spread_element(map_js_ast)] ++ [J.array_expression([ + J.literal(key), + J.literal(new_value) + ])] + ) + ] + ) + end + end + +end diff --git a/test/passes/translate/forms/receive_test.exs b/test/passes/translate/forms/receive_test.exs new file mode 100644 index 00000000..9f8496a7 --- /dev/null +++ b/test/passes/translate/forms/receive_test.exs @@ -0,0 +1,34 @@ +defmodule ElixirScript.Translate.Forms.Receive.Test do + use ExUnit.Case + alias ElixirScript.Translate.{Form, Helpers} + alias ESTree.Tools.Builder, as: J + + setup_all do + {:ok, pid} = ElixirScript.State.start_link(%{}) + + state = %{ + pid: pid, + module: __MODULE__ + } + + [state: state] + end + + test "receive translation", %{state: state} do + clause = {:->, [line: 644], [[], [{:__block__, [], [1]}]]} + ast = {:receive, [line: 644], [[do: [clause], after: nil]]} + + state = + state + |> Map.put(:function, {:each, nil}) + |> Map.put(:anonymous_fn, false) + + {js_ast, _} = Form.compile(ast, state) + + assert js_ast.callee == + J.member_expression( + Helpers.special_forms(), + J.identifier("receive") + ) + end +end diff --git a/test/passes/translate/forms/remote_test.exs b/test/passes/translate/forms/remote_test.exs new file mode 100644 index 00000000..41d15bf4 --- /dev/null +++ b/test/passes/translate/forms/remote_test.exs @@ -0,0 +1,14 @@ +defmodule ElixirScript.Translate.Forms.Remote.Test do + use ExUnit.Case + alias ElixirScript.Translate.Form + alias ESTree.Tools.Builder, as: J + + test "variable counter" do + ast = {:., [line: 644], [{:fun, [line: 644], nil}]} + state = %{function: {:each, nil}, module: Enum, vars: %{:_ => 0, "entry" => 0, "enumerable" => 0, "fun" => 0}} + + {js_ast, _} = Form.compile(ast, state) + assert js_ast == J.identifier("fun0") + end + +end diff --git a/test/pattern_matching/match_test.exs b/test/pattern_matching/match_test.exs deleted file mode 100644 index 453623a0..00000000 --- a/test/pattern_matching/match_test.exs +++ /dev/null @@ -1,223 +0,0 @@ -defmodule ElixirScript.PatternMatching.Match.Test do - use ShouldI - alias ElixirScript.Translator - alias ElixirScript.Translator.Primitive - alias ElixirScript.PatternMatching.Match - alias ElixirScript.Translator.Map - alias ESTree.Tools.Builder, as: JS - - should "match wildcard" do - params = [{:_, [], Test}] - result = Match.build_match(params, __ENV__) - expected_result = { [Match.wildcard], [JS.identifier(:undefined)] } - - assert result == expected_result - end - - should "match one identifier param" do - params = [{:a, [], Test}] - result = Match.build_match(params, __ENV__) - expected_result = {[Match.parameter], [JS.identifier("a")]} - - assert result == expected_result - end - - should "match multiple identifier params" do - params = [{:a, [], Test}, {:b, [], Test}, {:c, [], Test}] - result = Match.build_match(params, __ENV__) - expected_result = { - List.duplicate(Match.parameter, 3), - [JS.identifier("a"), JS.identifier("b"), JS.identifier("c")] - } - - assert result == expected_result - end - - should "match head and tail param" do - params = [[{:|, [], [{:head, [], Elixir}, {:tail, [], Elixir}]}]] - result = Match.build_match(params, __ENV__) - expected_result = { - [Match.head_tail], - [JS.identifier("head"), JS.identifier("tail")] - } - - assert result == expected_result - end - - should "match prefix param" do - params = [{:<>, [context: Elixir, import: Kernel], ["Bearer ", {:token, [], Elixir}]}] - result = Match.build_match(params, __ENV__) - expected_result = { - [Match.starts_with("Bearer ")], - [JS.identifier("token")] - } - - assert result == expected_result - end - - should "match list" do - params = [[{:a, [], Elixir}, {:b, [], Elixir}, {:c, [], Elixir}]] - result = Match.build_match(params, __ENV__) - expected_result = { - [Primitive.make_list_no_translate(List.duplicate(Match.parameter, 3))], - [JS.identifier("a"), JS.identifier("b"), JS.identifier("c")] - } - - assert result == expected_result - end - - should "match list with a literal" do - params = [[1, {:b, [], Elixir}, {:c, [], Elixir}]] - result = Match.build_match(params, __ENV__) - expected_result = { - [Primitive.make_list_no_translate([JS.literal(1), Match.parameter, Match.parameter])], - [JS.identifier("b"), JS.identifier("c")] - } - - assert result == expected_result - end - - should "match number" do - params = [1] - result = Match.build_match(params, __ENV__) - expected_result = { - [JS.literal(1)], - [] - } - - assert result == expected_result - end - - should "match struct pattern" do - params = [{:%, [], [{:__aliases__, [alias: false], [:Hello]}, {:%{}, [], []}]}] - result = Match.build_match(params, __ENV__) - expected_result = { - [JS.object_expression([ - Map.make_property(Translator.translate(:__struct__, __ENV__), Translator.translate(:Hello, __ENV__)) - ])], - [] - } - - assert result == expected_result - end - - should "match struct pattern with property" do - params = [{:%, [], [{:__aliases__, [alias: false], [:Hello]}, {:%{}, [], [key: 1]}]}] - result = Match.build_match(params, __ENV__) - expected_result = { - [JS.object_expression([ - Map.make_property(Translator.translate(:__struct__, __ENV__), Translator.translate(:Hello, __ENV__)), - Map.make_property(Translator.translate(:key, __ENV__), Translator.translate(1, __ENV__)) - ])], - [] - } - - assert result == expected_result - end - - should "match struct pattern with property param" do - params = [{:%, [], [{:__aliases__, [alias: false], [:Hello]}, {:%{}, [], [key: {:key, [], Elixir }]}]}] - result = Match.build_match(params, __ENV__) - expected_result = { - [JS.object_expression([ - Map.make_property(Translator.translate(:__struct__, __ENV__), Translator.translate(:Hello, __ENV__)), - Map.make_property(Translator.translate(:key, __ENV__), Match.parameter) - ])], - [JS.identifier("key")] - } - - assert result == expected_result - end - - should "capture parameter when assigning it" do - params = [{:=, [], [1, {:a, [], Elixir}]}] - result = Match.build_match(params, __ENV__) - expected_result = { - [Match.capture(JS.literal(1))], - [JS.identifier("a")] - } - - assert result == expected_result - - - params = [{:=, [], [{:a, [], Elixir}, 1]}] - result = Match.build_match(params, __ENV__) - expected_result = { - [Match.capture(JS.literal(1))], - [JS.identifier("a")] - } - - assert result == expected_result - - - params = [{:=, [], [{:%, [], [{:__aliases__, [alias: false], [:AStruct]}, {:%{}, [], []}]}, {:a, [], ElixirScript.Translator.Function.Test}]}] - result = Match.build_match(params, __ENV__) - expected_result = { - [Match.capture(JS.object_expression([ - Map.make_property(Translator.translate(:__struct__, __ENV__), Translator.translate(:AStruct, __ENV__)), - ]))], - [JS.identifier("a")] - } - - assert result == expected_result - end - - should "match and assign list" do - params = [{:=, [], [[{:a, [], Elixir}, {:b, [], Elixir}, {:c, [], Elixir}], {:d, [], Elixir}]}] - result = Match.build_match(params, __ENV__) - expected_result = { - [Match.capture(Primitive.make_list_no_translate([Match.parameter, Match.parameter, Match.parameter]))], - [JS.identifier("a"), JS.identifier("b"), JS.identifier("c"), JS.identifier("d")] - } - - assert result == expected_result - end - - should "match on tuple" do - params = [{:{}, [], [1, {:b, [], Elixir}, 3]}] - result = Match.build_match(params, __ENV__) - expected_result = { - [Primitive.make_tuple_no_translate([JS.literal(1), Match.parameter, JS.literal(3)])], - [JS.identifier("b")] - } - - assert result == expected_result - - params = [{1, {:b, [], Elixir}}] - result = Match.build_match(params, __ENV__) - expected_result = { - [Primitive.make_tuple_no_translate([JS.literal(1), Match.parameter])], - [JS.identifier("b")] - } - - assert result == expected_result - end - - should "match on map" do - params = [{:%{}, [], [which: 13]}] - result = Match.build_match(params, __ENV__) - - expected_result = { - [JS.object_expression([ - Map.make_property(Translator.translate(:which, __ENV__), JS.literal(13)) - ])], - [] - } - - assert result == expected_result - end - - - should "match on bound value" do - params = [{:^, [], [{:a, [], Elixir}]}] - result = Match.build_match(params, __ENV__) - - expected_result = { - [Match.bound(JS.identifier("a"))], - [nil] - } - - assert result == expected_result - end - -end \ No newline at end of file diff --git a/test/test_helper.exs b/test/test_helper.exs index a9a3cf35..30fac768 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,72 +1,3 @@ exclude = if Node.alive?, do: [], else: [skip: true] -ExUnit.start(exclude: exclude, formatters: [ShouldI.CLIFormatter]) - -defmodule ElixirScript.Math do - defmacro squared(x) do - quote do - unquote(x) * unquote(x) - end - end -end - -defmodule ElixirScript.Using do - defmacro __using__(_) do - quote do - def sandwich() do - end - end - end -end - -defmodule ElixirScript.TestHelper do - use ShouldI - require Logger - - def make_custom_env do - require Logger - require ElixirScript.Math - require ElixirScript.Using - - __ENV__ - end - - def ex_ast_to_js(ex_ast) do - ElixirScript.compile_quoted(ex_ast, [env: make_custom_env, import_standard_libs: false]) - end - - def strip_spaces(js) do - js |> strip_new_lines |> String.replace(" ", "") - end - - def strip_new_lines(js) do - js |> String.replace("\n", "") - end - - - def assert_translation(ex_ast, js_code) do - converted_code = ex_ast_to_js(ex_ast) |> Enum.join("\n\n") - - assert converted_code |> strip_spaces == strip_spaces(js_code), """ - **Code Does Not Match ** - - ***Expected*** - #{js_code} - - ***Actual*** - #{converted_code} - """ - end - - def assert_js_matches(expected_js_code, actual_js_code) do - assert strip_spaces(expected_js_code) == strip_spaces(actual_js_code), """ - **Code Does Not Match ** - - ***Expected*** - #{expected_js_code} - - ***Actual*** - #{actual_js_code} - """ - end -end +ExUnit.start(exclude: exclude) diff --git a/test/translator/access_test.exs b/test/translator/access_test.exs deleted file mode 100644 index 3b0eb2aa..00000000 --- a/test/translator/access_test.exs +++ /dev/null @@ -1,16 +0,0 @@ -defmodule ElixirScript.Translator.Access.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate access" do - ex_ast = quote do: a[:b] - js_code = "a[Kernel.SpecialForms.atom('b')]" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do: a["b"] - js_code = "a['b']" - - assert_translation(ex_ast, js_code) - end -end \ No newline at end of file diff --git a/test/translator/alias_test.exs b/test/translator/alias_test.exs deleted file mode 100644 index 613af750..00000000 --- a/test/translator/alias_test.exs +++ /dev/null @@ -1,41 +0,0 @@ -defmodule ElixirScript.Translator.Alias.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate alias without as" do - ex_ast = quote do - alias Hello.World - end - - js_code = """ - import * as World from 'hello/world'; - """ - - assert_translation(ex_ast, js_code) - end - - should "translate alias with as" do - ex_ast = quote do - alias Hello.World, as: Test - end - - js_code = """ - import * as Test from 'hello/world'; - """ - - assert_translation(ex_ast, js_code) - end - - should "translate default alias with as" do - ex_ast = quote do - alias Hello.World, [as: Test, default: true] - end - - js_code = """ - import { default as Test } from 'hello/world'; - """ - - assert_translation(ex_ast, js_code) - end - -end diff --git a/test/translator/assignment_test.exs b/test/translator/assignment_test.exs deleted file mode 100644 index bd10d8ce..00000000 --- a/test/translator/assignment_test.exs +++ /dev/null @@ -1,74 +0,0 @@ -defmodule ElixirScript.Translator.Assignment.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate simple assignment" do - ex_ast = quote do: a = 1 - js_code = "let [a] = Patterns.match(Patterns.variable(), 1);" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do: a = :atom - js_code = "let [a] = Patterns.match(Patterns.variable(), Kernel.SpecialForms.atom('atom'));" - - assert_translation(ex_ast, js_code) - end - - should "translate tuple assignment" do - ex_ast = quote do - {a, b} = {1, 2} - end - js_code = """ - let [a, b] = Patterns.match(Kernel.SpecialForms.tuple(Patterns.variable(), Patterns.variable()), Kernel.SpecialForms.tuple(1, 2)); - let _ref = Kernel.SpecialForms.tuple(a, b); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do: {a, _, c} = {1, 2, 3} - js_code = """ - let [a, undefined, c] = Patterns.match(Kernel.SpecialForms.tuple(Patterns.variable(), Patterns.wildcard(), Patterns.variable()), Kernel.SpecialForms.tuple(1, 2, 3)); - let _ref = Kernel.SpecialForms.tuple(a, undefined, c); - """ - - assert_translation(ex_ast, js_code) - - - ex_ast = quote do: {^a, _, c} = {1, 2, 3} - js_code = """ - let [,undefined,c] = Patterns.match(Kernel.SpecialForms.tuple(Patterns.bound(a),Patterns.wildcard(),Patterns.variable()),Kernel.SpecialForms.tuple(1,2,3)); - let _ref = Kernel.SpecialForms.tuple(undefined,undefined,c); - """ - - assert_translation(ex_ast, js_code) - end - - should "translate bound assignment" do - ex_ast = quote do: ^a = 1 - js_code = """ - let [] = Patterns.match(Patterns.bound(a),1); - """ - - assert_translation(ex_ast, js_code) - end - - should "translate list assignment" do - ex_ast = quote do: [a, b] = [1, 2] - js_code = """ - let [a, b] = Patterns.match(Kernel.SpecialForms.list(Patterns.variable(), Patterns.variable()), Kernel.SpecialForms.list(1, 2)); - let _ref = Kernel.SpecialForms.list(a, b); - """ - - assert_translation(ex_ast, js_code) - end - - should "translate head/tail assignment" do - ex_ast = quote do: [a | b] = [1, 2, 3, 4] - js_code = """ - let [a,b] = Patterns.match(Patterns.headTail(),Kernel.SpecialForms.list(1,2,3,4)); - let _ref = Kernel.SpecialForms.list(a,b); - """ - - assert_translation(ex_ast, js_code) - end -end \ No newline at end of file diff --git a/test/translator/atom_test.exs b/test/translator/atom_test.exs deleted file mode 100644 index 5c439a6b..00000000 --- a/test/translator/atom_test.exs +++ /dev/null @@ -1,9 +0,0 @@ -defmodule ElixirScript.Translator.Atom.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate atom" do - ex_ast = quote do: :atom - assert_translation(ex_ast, "Kernel.SpecialForms.atom('atom')") - end -end \ No newline at end of file diff --git a/test/translator/bitstring_test.exs b/test/translator/bitstring_test.exs deleted file mode 100644 index b6461910..00000000 --- a/test/translator/bitstring_test.exs +++ /dev/null @@ -1,42 +0,0 @@ -defmodule ElixirScript.Translator.Bitstring.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate bitstring" do - ex_ast = quote do: <<1, 2, 3>> - assert_translation(ex_ast, "Kernel.SpecialForms.bitstring(BitString.integer(1), BitString.integer(2), BitString.integer(3))") - - ex_ast = quote do: <<1, "foo">> - assert_translation(ex_ast, "Kernel.SpecialForms.bitstring(BitString.integer(1), BitString.binary('foo'))") - - ex_ast = quote do: <<1, "foo" :: binary>> - assert_translation(ex_ast, "Kernel.SpecialForms.bitstring(BitString.integer(1), BitString.binary('foo'))") - - ex_ast = quote do: <<1, "foo" :: utf8, "bar" :: utf32>> - assert_translation(ex_ast, "Kernel.SpecialForms.bitstring(BitString.integer(1), BitString.utf8('foo'), BitString.utf32('bar'))") - - ex_ast = quote do: <<102 :: integer-native, rest :: binary>> - assert_translation(ex_ast, "Kernel.SpecialForms.bitstring(BitString.native(BitString.integer(102)), BitString.binary(rest))") - - ex_ast = quote do: <<102 :: unsigned-big-integer, rest :: binary>> - assert_translation(ex_ast, "Kernel.SpecialForms.bitstring(BitString.integer(BitString.big(BitString.unsigned(102))), BitString.binary(rest))") - - ex_ast = quote do: <<102, _rest :: size(16)>> - assert_translation(ex_ast, "Kernel.SpecialForms.bitstring(BitString.integer(102), BitString.size(_rest, 16))") - - ex_ast = quote do: <<102, _rest :: size(16)-unit(4)>> - assert_translation(ex_ast, "Kernel.SpecialForms.bitstring(BitString.integer(102), BitString.unit(BitString.size(_rest, 16), 4))") - - ex_ast = quote do: <<102, _rest :: 16 * 4>> - assert_translation(ex_ast, "Kernel.SpecialForms.bitstring(BitString.integer(102), BitString.unit(BitString.size(_rest, 16), 4))") - - ex_ast = quote do: <<102, _rest :: _ * 4>> - assert_translation(ex_ast, "Kernel.SpecialForms.bitstring(BitString.integer(102), BitString.unit(BitString.size(_rest, undefined), 4))") - - ex_ast = quote do: <<102, _rest :: 16>> - assert_translation(ex_ast, "Kernel.SpecialForms.bitstring(BitString.integer(102), BitString.size(_rest, 16))") - - ex_ast = quote do: << 1, <<2>> >> - assert_translation(ex_ast, "Kernel.SpecialForms.bitstring(BitString.integer(1), Kernel.SpecialForms.bitstring(BitString.integer(2)))") - end -end \ No newline at end of file diff --git a/test/translator/bug_test.exs b/test/translator/bug_test.exs deleted file mode 100644 index 58442828..00000000 --- a/test/translator/bug_test.exs +++ /dev/null @@ -1,126 +0,0 @@ -defmodule ElixirScript.Translator.Bug.Test do - use ShouldI - import ElixirScript.TestHelper - - should "Translate react element" do - ex_ast = quote do - React.createElement( - React.Text, - %{"style" => styles().welcome}, - "Welcome to React Native!" - ) - end - - js_code = """ - React.createElement(React.Text,Kernel.SpecialForms.map({ - style: JS.get_property_or_call_function(styles,'welcome') - }),'Welcome to React Native!') - """ - - assert_translation(ex_ast, js_code) - - end - - should "correctly not create 2 imports" do - ex_ast = quote do - defmodule App.Todo do - JS.import JQuery, "jquery" - JQuery.(e.target) - end - end - - js_code = """ - import { default as JQuery } from 'jquery'; - const __MODULE__ = Kernel.SpecialForms.atom('Todo'); - - JQuery(JS.get_property_or_call_function(e, 'target')); - export {}; - """ - - assert_translation(ex_ast, js_code) - end - - should "correctly translate module names when used" do - ex_ast = quote do - @graphic_store App.Stores.GraphicStore.create_store() - end - - js_code = """ - const graphic_store = JS.get_property_or_call_function(App.Stores.GraphicStore, 'create_store'); - - """ - - assert_translation(ex_ast, js_code) - end - - should "replace !" do - ex_ast = quote do - Enum.fetch!(data, i) - end - - js_code = """ - Enum.fetch__emark__(data, i) - """ - - assert_translation(ex_ast, js_code) - end - - should "chain calls correctly" do - ex_ast = quote do - :this.getRawCanvas().getContext("2d") - end - - js_code = """ - JS.get_property_or_call_function(this, 'getRawCanvas').getContext('2d') - """ - - assert_translation(ex_ast, js_code) - - - ex_ast = quote do - :this.getRawCanvas(one).get("fg").getContext("2d") - end - - js_code = """ - this.getRawCanvas(one).get('fg').getContext('2d') - """ - - assert_translation(ex_ast, js_code) - end - - should "correctly call multi-module functions" do - ex_ast = quote do - def getDispatcher() do - DeLorean.Flux.createDispatcher(%{ - startPainting: fn() -> this.dispatch("startPainting") end, - stopPainting: fn() -> this.dispatch("stopPainting") end, - addPoint: fn(data) -> this.dispatch("addPoint", data) end, - getStores: fn() -> %{ graphic: GraphicStore } end - }) - end - end - - - js_code = """ - let getDispatcher = Patterns.defmatch(Patterns.make_case([],function() { - return DeLorean.Flux.createDispatcher(Kernel.SpecialForms.map({ - [Kernel.SpecialForms.atom('startPainting')]: Patterns.defmatch(Patterns.make_case([],function() { - return this.dispatch('startPainting'); - })), [Kernel.SpecialForms.atom('stopPainting')]: Patterns.defmatch(Patterns.make_case([],function() { - return this.dispatch('stopPainting'); - })), [Kernel.SpecialForms.atom('addPoint')]: Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(data) { - return this.dispatch('addPoint',data); - })), [Kernel.SpecialForms.atom('getStores')]: Patterns.defmatch(Patterns.make_case([],function() { - return Kernel.SpecialForms.map({ - [Kernel.SpecialForms.atom('graphic')]: GraphicStore - }); - })) - })); - })); - """ - - assert_translation(ex_ast, js_code) - end - - -end diff --git a/test/translator/capture_test.exs b/test/translator/capture_test.exs deleted file mode 100644 index a21d3f07..00000000 --- a/test/translator/capture_test.exs +++ /dev/null @@ -1,125 +0,0 @@ -defmodule ElixirScript.Translator.Capture.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate capture operator with Module, function, and arity" do - ex_ast = quote do - fun = &Kernel.is_atom/1 - end - - js_code = """ - let [fun] = Patterns.match(Patterns.variable(),Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(__1) { - return Kernel.is_atom(__1); - }))); - """ - - assert_translation(ex_ast, js_code) - - end - - should "translate capture operator with function, and parameters" do - - ex_ast = quote do - fun = &is_atom(&1) - end - - js_code = """ - let [fun] = Patterns.match(Patterns.variable(),Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(__1) { - return Kernel.is_atom(__1); - }))); - """ - - assert_translation(ex_ast, js_code) - - - end - - should "translate capture operator with function, and arity" do - - ex_ast = quote do - fun = &local_function/1 - end - - js_code = """ - let [fun] = Patterns.match(Patterns.variable(),Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(__1) { - return local_function(__1); - }))); - """ - - assert_translation(ex_ast, js_code) - - end - - should "translate capture operator with anonymous function" do - - ex_ast = quote do - fun = &(&1 * 2) - end - - js_code = """ - let [fun] = Patterns.match(Patterns.variable(),Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(__1) { - return __1 * 2; - }))); - """ - - assert_translation(ex_ast, js_code) - - end - - should "translate capture operator with anonymous function tuple" do - - ex_ast = quote do - fun = &{&1, &2} - end - - js_code = """ - let [fun] = Patterns.match(Patterns.variable(),Patterns.defmatch(Patterns.make_case([Patterns.variable(), Patterns.variable()],function(__1,__2) { - return Kernel.SpecialForms.tuple(__1,__2); - }))); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - fun = &{&1, &2, &3} - end - - js_code = """ - let [fun] = Patterns.match(Patterns.variable(),Patterns.defmatch(Patterns.make_case([Patterns.variable(), Patterns.variable(), Patterns.variable()],function(__1,__2,__3) { - return Kernel.SpecialForms.tuple(__1,__2,__3); - }))); - """ - - assert_translation(ex_ast, js_code) - - - end - - should "translate capture operator with anonymous functions as parameters" do - - ex_ast = quote do - Enum.map(items, &process(&1)) - end - - js_code = """ - Enum.map(items,Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(__1) { - return process(__1); - }))) - """ - - assert_translation(ex_ast, js_code) - - - ex_ast = quote do - elem.keypress(&process_event(&1)) - end - - js_code = """ - elem.keypress(Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(__1) { - return process_event(__1); - }))) - """ - - assert_translation(ex_ast, js_code) - end -end \ No newline at end of file diff --git a/test/translator/case_test.exs b/test/translator/case_test.exs deleted file mode 100644 index 55cb93ff..00000000 --- a/test/translator/case_test.exs +++ /dev/null @@ -1,221 +0,0 @@ -defmodule ElixirScript.Translator.Case.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate case" do - - ex_ast = quote do - case data do - :ok -> value - :error -> nil - end - end - - js_code = """ - Patterns.defmatch(Patterns.make_case([Kernel.SpecialForms.atom('ok')],function() { - return value; - }),Patterns.make_case([Kernel.SpecialForms.atom('error')],function() { - return null; - })).call(this,data) - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - case data do - false -> value = 13 - true -> true - end - end - - js_code = """ - Patterns.defmatch(Patterns.make_case([false],function() { - let [value0] = Patterns.match(Patterns.variable(),13); - return value0; - }),Patterns.make_case([true],function() { - return true; - })).call(this,data) - """ - - assert_translation(ex_ast, js_code) - - - - ex_ast = quote do - case data do - false -> value = 13 - _ -> true - end - end - - js_code = """ - Patterns.defmatch(Patterns.make_case([false],function() { - let [value0] = Patterns.match(Patterns.variable(),13); - return value0; - }),Patterns.make_case([Patterns.wildcard()],function() { - return true; - })).call(this,data) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate case with guard" do - ex_ast = quote do - case data do - number when number in [1,2,3,4] -> - value = 13 - _ -> - true - end - end - - js_code = """ - Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(number) { - let [value0] = Patterns.match(Patterns.variable(),13); - return value0; - },function(number) { - return Kernel.__in__(number,Kernel.SpecialForms.list(1,2,3,4)); - }),Patterns.make_case([Patterns.wildcard()],function() { - return true; - })).call(this,data) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate case with multiple statements in body" do - ex_ast = quote do - case data do - :ok -> - Logger.info("info") - Todo.add(data) - :error -> - nil - end - end - - js_code = """ - Patterns.defmatch(Patterns.make_case([Kernel.SpecialForms.atom('ok')],function() { - console.info('info'); - return Todo.add(data); - }),Patterns.make_case([Kernel.SpecialForms.atom('error')],function() { - return null; - })).call(this,data) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate case with destructing" do - ex_ast = quote do - case data do - { one, two } -> - Logger.info(one) - :error -> - nil - end - end - - js_code = """ - Patterns.defmatch(Patterns.make_case([Kernel.SpecialForms.tuple(Patterns.variable(),Patterns.variable())],function(one,two) { - return console.info(one); - }),Patterns.make_case([Kernel.SpecialForms.atom('error')],function() { - return null; - })).call(this,data) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate case with nested destructing" do - ex_ast = quote do - case data do - { {one, two} , three } -> - Logger.info(one) - :error -> - nil - end - end - - js_code = """ - Patterns.defmatch(Patterns.make_case([Kernel.SpecialForms.tuple(Kernel.SpecialForms.tuple(Patterns.variable(),Patterns.variable()),Patterns.variable())],function(one,two,three) { - return console.info(one); - }),Patterns.make_case([Kernel.SpecialForms.atom('error')],function() { - return null; - })).call(this,data) - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - case data do - { one, {two, three} } -> - Logger.info(one) - :error -> - nil - end - end - - js_code = """ - Patterns.defmatch(Patterns.make_case([Kernel.SpecialForms.tuple(Patterns.variable(),Kernel.SpecialForms.tuple(Patterns.variable(),Patterns.variable()))],function(one,two,three) { - return console.info(one); - }),Patterns.make_case([Kernel.SpecialForms.atom('error')],function() { - return null; - })).call(this,data) - - """ - - assert_translation(ex_ast, js_code) - - - ex_ast = quote do - case data do - %AStruct{key: %BStruct{ key2: value }} -> - Logger.info(value) - :error -> - nil - end - end - - js_code = """ - Patterns.defmatch(Patterns.make_case([{ - [Kernel.SpecialForms.atom('__struct__')]: Kernel.SpecialForms.atom('AStruct'), [Kernel.SpecialForms.atom('key')]: { - [Kernel.SpecialForms.atom('__struct__')]: Kernel.SpecialForms.atom('BStruct'), [Kernel.SpecialForms.atom('key2')]: Patterns.variable() - } - }],function(value) { - return console.info(value); - }),Patterns.make_case([Kernel.SpecialForms.atom('error')],function() { - return null; - })).call(this,data) - """ - - assert_translation(ex_ast, js_code) - - - ex_ast = quote do - case data do - %AStruct{key: %BStruct{ key2: value, key3: %CStruct{ key4: value2 } }} -> - Logger.info(value) - :error -> - nil - end - end - - js_code = """ - Patterns.defmatch(Patterns.make_case([{ - [Kernel.SpecialForms.atom('__struct__')]: Kernel.SpecialForms.atom('AStruct'), [Kernel.SpecialForms.atom('key')]: { - [Kernel.SpecialForms.atom('__struct__')]: Kernel.SpecialForms.atom('BStruct'), [Kernel.SpecialForms.atom('key2')]: Patterns.variable(), [Kernel.SpecialForms.atom('key3')]: { - [Kernel.SpecialForms.atom('__struct__')]: Kernel.SpecialForms.atom('CStruct'), [Kernel.SpecialForms.atom('key4')]: Patterns.variable() - } - } - }],function(value,value2) { - return console.info(value); - }),Patterns.make_case([Kernel.SpecialForms.atom('error')],function() { - return null; - })).call(this,data) - """ - - assert_translation(ex_ast, js_code) - end -end \ No newline at end of file diff --git a/test/translator/cond_test.exs b/test/translator/cond_test.exs deleted file mode 100644 index b95791f1..00000000 --- a/test/translator/cond_test.exs +++ /dev/null @@ -1,58 +0,0 @@ -defmodule ElixirScript.Translator.Cond.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate cond" do - ex_ast = quote do - cond do - 1 + 1 == 1 -> - "This will never match" - 2 * 2 != 4 -> - "Nor this" - true -> - "This will" - end - end - - js_code = """ - Kernel.SpecialForms.cond(Kernel.SpecialForms.list(1 + 1 == 1,function() { - return 'This will never match'; - }),Kernel.SpecialForms.list(2 * 2 != 4,function() { - return 'Nor this'; - }),Kernel.SpecialForms.list(true,function() { - return 'This will'; - })) - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - cond do - 1 + 1 == 1 -> - a = 1 - "This will never match" - 2 * 2 != 4 -> - a = 2 - "Nor this" - true -> - a = 3 - "This will" - end - end - - js_code = """ - Kernel.SpecialForms.cond(Kernel.SpecialForms.list(1 + 1 == 1,function() { - let [a] = Patterns.match(Patterns.variable(),1); - return 'This will never match'; - }),Kernel.SpecialForms.list(2 * 2 != 4,function() { - let [a] = Patterns.match(Patterns.variable(),2); - return 'Nor this'; - }),Kernel.SpecialForms.list(true,function() { - let [a] = Patterns.match(Patterns.variable(),3); - return 'This will'; - })) - """ - - assert_translation(ex_ast, js_code) - end -end \ No newline at end of file diff --git a/test/translator/defmodule_test.exs b/test/translator/defmodule_test.exs deleted file mode 100644 index 27b8a2bc..00000000 --- a/test/translator/defmodule_test.exs +++ /dev/null @@ -1,322 +0,0 @@ -defmodule ElixirScript.Translator.Defmodule.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate empty module" do - ex_ast = quote do - defmodule Elephant do - end - end - - js_code = """ - const __MODULE__ = Kernel.SpecialForms.atom('Elephant'); - export {}; - """ - - assert_translation(ex_ast, js_code) - end - - should "translate defmodules" do - ex_ast = quote do - defmodule Elephant do - @ul JQuery.("#todo-list") - - def something() do - @ul - end - - defp something_else() do - end - end - end - - js_code = """ - const __MODULE__ = Kernel.SpecialForms.atom('Elephant'); - let something_else = Patterns.defmatch(Patterns.make_case([],function() { - return null; - })); - let something = Patterns.defmatch(Patterns.make_case([],function() { - return ul; - })); - const ul = JQuery('#todo-list'); - export { - something - }; - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - defmodule Elephant do - alias Icabod.Crane - - def something() do - end - - defp something_else() do - end - end - end - - js_code = """ - import * as Crane from 'icabod/crane'; - const __MODULE__ = Kernel.SpecialForms.atom('Elephant'); - let something_else = Patterns.defmatch(Patterns.make_case([],function() { - return null; - })); - let something = Patterns.defmatch(Patterns.make_case([],function() { - return null; - })); - export { - something - }; - """ - - assert_translation(ex_ast, js_code) - end - - should "translate modules with inner modules" do - ex_ast = quote do - defmodule Animals do - - defmodule Elephant do - defstruct trunk: true - end - - - def something() do - %Elephant{} - end - - defp something_else() do - end - - end - end - - js_code = """ - const __MODULE__ = Kernel.SpecialForms.atom('Elephant'); - function defstruct(trunk = true) { - return Kernel.SpecialForms.map({ - [Kernel.SpecialForms.atom('__struct__')]: __MODULE__, [Kernel.SpecialForms.atom('trunk')]: trunk - }); - } - export { - defstruct - }; - - import * as Elephant from 'animals/elephant'; - const __MODULE__ = Kernel.SpecialForms.atom('Animals'); - let something_else = Patterns.defmatch(Patterns.make_case([],function() { - return null; - })); - let something = Patterns.defmatch(Patterns.make_case([],function() { - return Elephant.defstruct(); - })); - export { - something - }; - - """ - - assert_translation(ex_ast, js_code) - end - - - should "translate modules with inner module that has inner module" do - ex_ast = quote do - defmodule Animals do - - defmodule Elephant do - defstruct trunk: true - - defmodule Bear do - defstruct trunk: true - end - end - - - def something() do - %Elephant{} - end - - defp something_else() do - end - - end - end - - js_code = """ - const __MODULE__ = Kernel.SpecialForms.atom('Bear'); - function defstruct(trunk = true) { - return Kernel.SpecialForms.map({ - [Kernel.SpecialForms.atom('__struct__')]: __MODULE__, [Kernel.SpecialForms.atom('trunk')]: trunk - }); - } - export { - defstruct - }; - - import * as Bear from 'elephant/bear'; - const __MODULE__ = Kernel.SpecialForms.atom('Elephant'); - function defstruct(trunk = true) { - return Kernel.SpecialForms.map({ - [Kernel.SpecialForms.atom('__struct__')]: __MODULE__, [Kernel.SpecialForms.atom('trunk')]: trunk - }); - } - export { - defstruct - }; - - import * as Elephant from 'animals/elephant'; - const __MODULE__ = Kernel.SpecialForms.atom('Animals'); - let something_else = Patterns.defmatch(Patterns.make_case([],function() { - return null; - })); - let something = Patterns.defmatch(Patterns.make_case([],function() { - return Elephant.defstruct(); - })); - export { - something - }; - """ - - assert_translation(ex_ast, js_code) - end - - should "Pull out module references and make them into imports if modules listed" do - ex_ast = quote do - defmodule Animals do - Lions.Tigers.oh_my() - end - - defmodule Lions.Tigers do - Lions.Tigers.Bears.oh_my() - end - end - - js_code = """ - import * as Tigers from 'lions/tigers'; - const __MODULE__ = Kernel.SpecialForms.atom('Animals'); - JS.get_property_or_call_function(Tigers,'oh_my'); - export {}; - - const __MODULE__ = Kernel.SpecialForms.atom('Tigers'); - JS.get_property_or_call_function(Lions.Tigers.Bears,'oh_my'); - export {}; - """ - - assert_translation(ex_ast, js_code) - end - - should "ignore aliases already added" do - ex_ast = quote do - defmodule Animals do - alias Lions.Tigers - - Tigers.oh_my() - end - - defmodule Lions.Tigers do - Lions.Tigers.Bears.oh_my() - - def oh_my() do - end - end - end - - js_code = """ - const __MODULE__ = Kernel.SpecialForms.atom('Tigers'); - let oh_my = Patterns.defmatch(Patterns.make_case([],function() { - return null; - })); - JS.get_property_or_call_function(Lions.Tigers.Bears,'oh_my'); - export { - oh_my - }; - - import * as Tigers from 'lions/tigers'; - const __MODULE__ = Kernel.SpecialForms.atom('Animals'); - JS.get_property_or_call_function(Tigers,'oh_my'); - export {}; - """ - - assert_translation(ex_ast, js_code) - end - - should "import only" do - ex_ast = quote do - defmodule Lions.Tigers do - def oh_my() do - end - - def oh_my2() do - end - end - - defmodule Animals do - import Lions.Tigers, only: [oh_my: 1] - - oh_my() - end - end - - js_code = """ - import { oh_my } from 'lions/tigers'; - const __MODULE__ = Kernel.SpecialForms.atom('Animals'); - oh_my(); - export {}; - - const __MODULE__ = Kernel.SpecialForms.atom('Tigers'); - let oh_my2 = Patterns.defmatch(Patterns.make_case([],function() { - return null; - })); - let oh_my = Patterns.defmatch(Patterns.make_case([],function() { - return null; - })); - export { - oh_my2, oh_my - }; - """ - - assert_translation(ex_ast, js_code) - end - - should "import except" do - ex_ast = quote do - defmodule Lions.Tigers do - def oh_my() do - end - - def oh_my2() do - end - end - - defmodule Animals do - import Lions.Tigers, except: [oh_my: 1] - - oh_my2() - end - end - - js_code = """ - const __MODULE__ = Kernel.SpecialForms.atom('Tigers'); - let oh_my2 = Patterns.defmatch(Patterns.make_case([],function() { - return null; - })); - let oh_my = Patterns.defmatch(Patterns.make_case([],function() { - return null; - })); - export { - oh_my2, oh_my - }; - - import { oh_my2 } from 'lions/tigers'; - const __MODULE__ = Kernel.SpecialForms.atom('Animals'); - oh_my2(); - export {}; - """ - - assert_translation(ex_ast, js_code) - end -end diff --git a/test/translator/for_test.exs b/test/translator/for_test.exs deleted file mode 100644 index 1f5c6d88..00000000 --- a/test/translator/for_test.exs +++ /dev/null @@ -1,129 +0,0 @@ -defmodule ElixirScript.Translator.For.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate simple for" do - ex_ast = quote do - for n <- [1, 2, 3, 4], do: n * 2 - end - - js_code = """ - Kernel.SpecialForms._for(Kernel.SpecialForms.list(Kernel.SpecialForms.list(Patterns.variable(),Kernel.SpecialForms.list(1,2,3,4))),function(n) { - return n * 2; - },function() { - return true; - },Kernel.SpecialForms.list()) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate simple for with into" do - ex_ast = quote do - for n <- [1, 2, 3, 4], into: [], do: n * 2 - end - - js_code = """ - Kernel.SpecialForms._for(Kernel.SpecialForms.list(Kernel.SpecialForms.list(Patterns.variable(),Kernel.SpecialForms.list(1,2,3,4))),function(n) { - return n * 2; - },function() { - return true; - },Kernel.SpecialForms.list()) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate for with string" do - ex_ast = quote do - for n <- "Opera", do: n - end - - js_code = """ - Kernel.SpecialForms._for(Kernel.SpecialForms.list(Kernel.SpecialForms.list(Patterns.variable(),'Opera')),function(n) { - return n; - },function() { - return true; - },Kernel.SpecialForms.list()) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate multiple generator for" do - ex_ast = quote do - for x <- [1, 2], y <- [2, 3], do: x*y - end - - js_code = """ - Kernel.SpecialForms._for(Kernel.SpecialForms.list( - Kernel.SpecialForms.list(Patterns.variable(), Kernel.SpecialForms.list(1,2)), - Kernel.SpecialForms.list(Patterns.variable(), Kernel.SpecialForms.list(2,3))), function(x,y) { - return x * y; - },function() { - return true; - },Kernel.SpecialForms.list()) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate multiple generator for, assignment, and do block" do - ex_ast = quote do - r = for x <- [1, 2], y <- [2, 3] do - x*y - end - end - - js_code = """ - let [r] = Patterns.match(Patterns.variable(),Kernel.SpecialForms._for(Kernel.SpecialForms.list(Kernel.SpecialForms.list(Patterns.variable(),Kernel.SpecialForms.list(1,2)),Kernel.SpecialForms.list(Patterns.variable(),Kernel.SpecialForms.list(2,3))),function(x,y) { - return x * y; - },function() { - return true; - },Kernel.SpecialForms.list())); - """ - - assert_translation(ex_ast, js_code) - end - - should "translate for with filter" do - ex_ast = quote do - for n <- [1, 2, 3, 4, 5, 6], rem(n, 2) == 0, do: n - end - - js_code = """ - Kernel.SpecialForms._for(Kernel.SpecialForms.list(Kernel.SpecialForms.list(Patterns.variable(),Kernel.SpecialForms.list(1,2,3,4,5,6))),function(n) { - return n; - },function(n) { - return n % 2 == 0; - },Kernel.SpecialForms.list()) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate for with pattern matched input" do - ex_ast = quote do - for {:user, name} <- [user: "john", admin: "john", user: "meg"] do - String.upcase(name) - end - end - - js_code = """ - Kernel.SpecialForms._for(Kernel.SpecialForms.list( - Kernel.SpecialForms.list( - Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('user'),Patterns.variable()), - Kernel.SpecialForms.list( - Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('user'),'john'), - Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('admin'),'john'), - Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('user'),'meg')))),function(name) { - return String.upcase(name); - },function() { - return true; - },Kernel.SpecialForms.list() - ) - """ - - assert_translation(ex_ast, js_code) - end -end \ No newline at end of file diff --git a/test/translator/function_test.exs b/test/translator/function_test.exs deleted file mode 100644 index ebbf42b7..00000000 --- a/test/translator/function_test.exs +++ /dev/null @@ -1,752 +0,0 @@ -defmodule ElixirScript.Translator.Function.Test do - use ShouldI - import ElixirScript.TestHelper - - should "call fun" do - ex_ast = quote do - fun.(:atom) - end - - js_code = """ - fun(Kernel.SpecialForms.atom('atom')) - """ - - assert_translation(ex_ast, js_code) - - end - - - should "translate function with a macro" do - ex_ast = quote do - def test1() do - ElixirScript.Math.squared(1) - end - end - - js_code = """ - let test1 = Patterns.defmatch(Patterns.make_case([],function() { - return 1 * 1; - })); - """ - - assert_translation(ex_ast, js_code) - end - - should "translate functions" do - ex_ast = quote do - def test1() do - end - end - - js_code = """ - let test1 = Patterns.defmatch(Patterns.make_case([],function() { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - def test1(alpha, beta) do - end - end - - js_code = """ - let test1 = Patterns.defmatch(Patterns.make_case([Patterns.variable(), Patterns.variable()],function(alpha,beta) { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - def test1(alpha, beta) do - a = alpha - end - end - - js_code = """ - let test1 = Patterns.defmatch(Patterns.make_case([Patterns.variable(), Patterns.variable()],function(alpha,beta) { - let [a0] = Patterns.match(Patterns.variable(),alpha); - return a0; - })); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - def test1(alpha, beta) do - if 1 == 1 do - 1 - else - 2 - end - end - end - - js_code = """ - let test1 = Patterns.defmatch(Patterns.make_case([Patterns.variable(), Patterns.variable()],function(alpha,beta) { - return Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(x) { - return 2; - },function(x) { - return Kernel.__in__(x,Kernel.SpecialForms.list(false,null)); - }),Patterns.make_case([Patterns.wildcard()],function() { - return 1; - })).call(this,1 == 1); - })); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - def test1(alpha, beta) do - if 1 == 1 do - if 2 == 2 do - 4 - else - a = 1 - end - else - 2 - end - end - end - - js_code = """ - let test1 = Patterns.defmatch(Patterns.make_case([Patterns.variable(), Patterns.variable()],function(alpha,beta) { - return Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(x) { - return 2; - },function(x) { - return Kernel.__in__(x,Kernel.SpecialForms.list(false,null)); - }),Patterns.make_case([Patterns.wildcard()],function() { - return Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(x) { - let [a000] = Patterns.match(Patterns.variable(),1); - return a000; - },function(x) { - return Kernel.__in__(x,Kernel.SpecialForms.list(false,null)); - }),Patterns.make_case([Patterns.wildcard()],function() { - return 4; - })).call(this,2 == 2); - })).call(this,1 == 1); - })); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - def test1(alpha, beta) do - {a, b} = {1, 2} - end - end - - js_code = """ - let test1 = Patterns.defmatch(Patterns.make_case([Patterns.variable(), Patterns.variable()],function(alpha,beta) { - let [a0,b0] = Patterns.match(Kernel.SpecialForms.tuple(Patterns.variable(),Patterns.variable()),Kernel.SpecialForms.tuple(1,2)); - let _ref = Kernel.SpecialForms.tuple(a0,b0); - return _ref; - })); - """ - - assert_translation(ex_ast, js_code) - end - - should "translate function calls" do - ex_ast = quote do - test1() - end - - js_code = "test1()" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - test?() - end - - js_code = "test__qmark__()" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - test1(3, 2) - end - - js_code = "test1(3,2)" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - Taco.test1() - end - - js_code = "JS.get_property_or_call_function(Taco, 'test1')" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - Taco.test1(3, 2) - end - - js_code = "Taco.test1(3,2)" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - Taco.test1(Taco.test2(1), 2) - end - - js_code = "Taco.test1(Taco.test2(1),2)" - - assert_translation(ex_ast, js_code) - end - - - should "translate anonymous functions" do - ex_ast = quote do - Enum.map(list, fn(x) -> x * 2 end) - end - - js_code = """ - Enum.map(list,Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(x) { - return x * 2; - }))) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate function arity" do - ex_ast = quote do - defmodule Example do - - defp example() do - end - - defp example(oneArg) do - end - - defp example(oneArg, twoArg) do - end - - defp example(oneArg, twoArg, redArg) do - end - - defp example(oneArg, twoArg, redArg, blueArg) do - end - end - end - - js_code = """ - const __MODULE__ = Kernel.SpecialForms.atom('Example'); - let example = Patterns.defmatch(Patterns.make_case([],function() { - return null; - }),Patterns.make_case([Patterns.variable()],function(oneArg) { - return null; - }),Patterns.make_case([Patterns.variable(), Patterns.variable()],function(oneArg,twoArg) { - return null; - }),Patterns.make_case([Patterns.variable(), Patterns.variable(), Patterns.variable()],function(oneArg,twoArg,redArg) { - return null; - }),Patterns.make_case([Patterns.variable(), Patterns.variable(), Patterns.variable(), Patterns.variable()],function(oneArg,twoArg,redArg,blueArg) { - return null; - })); - export {}; - """ - assert_translation(ex_ast, js_code) - - - ex_ast = quote do - defmodule Example do - def example() do - end - - def example(oneArg) do - end - - def example(oneArg, twoArg) do - end - - def example(oneArg, twoArg, redArg) do - end - - def example(oneArg, twoArg, redArg, blueArg) do - end - end - end - - js_code = """ - const __MODULE__ = Kernel.SpecialForms.atom('Example'); - let example = Patterns.defmatch(Patterns.make_case([],function() { - return null; - }),Patterns.make_case([Patterns.variable()],function(oneArg) { - return null; - }),Patterns.make_case([Patterns.variable(), Patterns.variable()],function(oneArg,twoArg) { - return null; - }),Patterns.make_case([Patterns.variable(), Patterns.variable(), Patterns.variable()],function(oneArg,twoArg,redArg) { - return null; - }),Patterns.make_case([Patterns.variable(), Patterns.variable(), Patterns.variable(), Patterns.variable()],function(oneArg,twoArg,redArg,blueArg) { - return null; - })); - export { - example - }; - """ - assert_translation(ex_ast, js_code) - - - ex_ast = quote do - defmodule Example do - def example(oneArg) do - end - end - end - - js_code = """ - const __MODULE__ = Kernel.SpecialForms.atom('Example'); - let example = Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(oneArg) { - return null; - })); - export { - example - }; - """ - assert_translation(ex_ast, js_code) - - end - - should "test |> operator" do - ex_ast = quote do - 1 |> Taco.test - end - - js_code = "Taco.test(1)" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - 1 |> Taco.test |> Home.hello("hi") - end - - js_code = "Home.hello(Taco.test(1), 'hi')" - - assert_translation(ex_ast, js_code) - end - - - should "test Kernel function" do - ex_ast = quote do - is_atom(:atom) - end - - js_code = "Kernel.is_atom(Kernel.SpecialForms.atom('atom'))" - - assert_translation(ex_ast, js_code) - end - - should "guards" do - ex_ast = quote do - def something(one) when is_number(one) do - end - end - - - js_code = """ - let something = Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(one) { - return null; - },function(one) { - return Kernel.is_number(one); - })); - """ - - assert_translation(ex_ast, js_code) - - - ex_ast = quote do - def something(one) when is_number(one) or is_atom(one) do - end - end - - - js_code = """ - let something = Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(one) { - return null; - },function(one) { - return Kernel.is_number(one) || Kernel.is_atom(one); - })); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - defp something(one) when is_number(one) or is_atom(one) do - end - end - - - js_code = """ - let something = Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(one) { - return null; - },function(one) { - return Kernel.is_number(one) || Kernel.is_atom(one); - })); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - defp something(one, two) when one in [1, 2, 3] do - end - end - - - js_code = """ - let something = Patterns.defmatch(Patterns.make_case([Patterns.variable(), Patterns.variable()],function(one,two) { - return null; - },function(one,two) { - return Kernel.__in__(one,Kernel.SpecialForms.list(1,2,3)); - })); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - defmodule Example do - def something(one) when one in [1, 2, 3] do - end - - def something(one) when is_number(one) or is_atom(one) do - end - end - end - - js_code = """ - const __MODULE__ = Kernel.SpecialForms.atom('Example'); - let something = Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(one) { - return null; - },function(one) { - return Kernel.__in__(one,Kernel.SpecialForms.list(1,2,3)); - }),Patterns.make_case([Patterns.variable()],function(one) { - return null; - },function(one) { - return Kernel.is_number(one) || Kernel.is_atom(one); - })); - export { - something - }; - """ - assert_translation(ex_ast, js_code) - - end - - should "pattern match function with literal" do - ex_ast = quote do - def something(1) do - end - end - - - js_code = """ - let something = Patterns.defmatch(Patterns.make_case([1],function() { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - end - - should "pattern match function with list" do - ex_ast = quote do - def something([apple | fruits]) do - end - end - - - js_code = """ - let something = Patterns.defmatch(Patterns.make_case([Patterns.headTail()],function(apple,fruits) { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - end - - should "pattern match function with multiple items in list" do - ex_ast = quote do - def something([apple, pear, banana]) do - end - end - - - js_code = """ - let something = Patterns.defmatch(Patterns.make_case([Kernel.SpecialForms.list(Patterns.variable(),Patterns.variable(),Patterns.variable())],function(apple,pear,banana) { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - end - - should "pattern match function with tuple" do - ex_ast = quote do - def something({ apple , fruits }) do - end - end - - - js_code = """ - let something = Patterns.defmatch(Patterns.make_case([Kernel.SpecialForms.tuple(Patterns.variable(),Patterns.variable())],function(apple,fruits) { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - end - - should "pattern match function with struct" do - ex_ast = quote do - def something(%AStruct{}) do - end - end - - - js_code = """ - let something = Patterns.defmatch(Patterns.make_case([{ - [Kernel.SpecialForms.atom('__struct__')]: Kernel.SpecialForms.atom('AStruct') - }],function() { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - end - - should "pattern match function with struct reference" do - ex_ast = quote do - def something(%AStruct{} = a) do - end - end - - js_code = """ - let something = Patterns.defmatch(Patterns.make_case([Patterns.capture({ - [Kernel.SpecialForms.atom('__struct__')]: Kernel.SpecialForms.atom('AStruct') - })],function(a) { - return null; - })); - """ - assert_translation(ex_ast, js_code) - end - - should "pattern match function with map reference" do - ex_ast = quote do - def something(%{ which: 13 } = a) do - end - end - - js_code = """ - let something = Patterns.defmatch(Patterns.make_case([Patterns.capture({ - [Kernel.SpecialForms.atom('which')]: 13 - })],function(a) { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - end - - should "pattern match function with struct decontructed" do - ex_ast = quote do - def something(%AStruct{key: value, key1: 2}) do - end - end - - - js_code = """ - let something = Patterns.defmatch(Patterns.make_case([{ - [Kernel.SpecialForms.atom('__struct__')]: Kernel.SpecialForms.atom('AStruct'), [Kernel.SpecialForms.atom('key')]: Patterns.variable(), [Kernel.SpecialForms.atom('key1')]: 2 - }],function(value) { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - def something(%AStruct{key: value, key1: 2}) when is_number(value) do - end - end - - - js_code = """ - let something = Patterns.defmatch(Patterns.make_case([{ - [Kernel.SpecialForms.atom('__struct__')]: Kernel.SpecialForms.atom('AStruct'), [Kernel.SpecialForms.atom('key')]: Patterns.variable(), [Kernel.SpecialForms.atom('key1')]: 2 - }],function(value) { - return null; - },function(value) { - return Kernel.is_number(value); - })); - """ - - assert_translation(ex_ast, js_code) - end - - should "pattern match function with binary part" do - ex_ast = quote do - def something("Bearer " <> token) do - end - end - - - js_code = """ - let something = Patterns.defmatch(Patterns.make_case([Patterns.startsWith('Bearer ')],function(token) { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - def something("Bearer " <> token, hotel) do - end - end - - - js_code = """ - let something = Patterns.defmatch(Patterns.make_case([Patterns.startsWith('Bearer '), Patterns.variable()],function(token,hotel) { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - def something("Bearer " <> token, hotel, 1) do - end - end - - - js_code = """ - let something = Patterns.defmatch(Patterns.make_case([Patterns.startsWith('Bearer '), Patterns.variable(), 1],function(token,hotel) { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - end - - should "combine pattern matched functions of same arity" do - ex_ast = quote do - defmodule Example do - def something(1) do - end - - def something(2) do - end - - def something(one) when is_binary(one) do - end - - def something(one) do - end - end - - end - - - js_code = """ - const __MODULE__ = Kernel.SpecialForms.atom('Example'); - let something = Patterns.defmatch(Patterns.make_case([1],function() { - return null; - }),Patterns.make_case([2],function() { - return null; - }),Patterns.make_case([Patterns.variable()],function(one) { - return null; - },function(one) { - return Kernel.is_binary(one); - }),Patterns.make_case([Patterns.variable()],function(one) { - return null; - })); - export { - something - }; - """ - - assert_translation(ex_ast, js_code) - - end - - should "translate varible declaration correctly" do - ex_ast = quote do - def test1(alpha, beta) do - a = 1 - a = 2 - end - end - - js_code = """ - let test1 = Patterns.defmatch(Patterns.make_case([Patterns.variable(), Patterns.variable()],function(alpha,beta) { - let [a0] = Patterns.match(Patterns.variable(),1); - let [a1] = Patterns.match(Patterns.variable(),2); - return a1; - })); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - def test1(alpha, beta) do - a = 1 - a = a - a = 2 - end - end - - js_code = """ - let test1 = Patterns.defmatch(Patterns.make_case([Patterns.variable(), Patterns.variable()],function(alpha,beta) { - let [a0] = Patterns.match(Patterns.variable(),1); - let [a1] = Patterns.match(Patterns.variable(),a0); - let [a2] = Patterns.match(Patterns.variable(),2); - return a2; - })); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - def test1(alpha, beta) do - a = 1 - [a, b, c] = [a, 2, 3] - end - end - - js_code = """ - let test1 = Patterns.defmatch(Patterns.make_case([Patterns.variable(), Patterns.variable()],function(alpha,beta) { - let [a0] = Patterns.match(Patterns.variable(),1); - let [a1,b0,c0] = Patterns.match(Kernel.SpecialForms.list(Patterns.variable(),Patterns.variable(),Patterns.variable()),Kernel.SpecialForms.list(a0,2,3)); - let _ref = Kernel.SpecialForms.list(a1,b0,c0); - return _ref; - })); - """ - - assert_translation(ex_ast, js_code) - end - - - should "translate function variables with ? or !" do - ex_ast = quote do - def test1(alpha?, beta!) do - a? = 1 - b! = 2 - end - end - - js_code = """ - let test1 = Patterns.defmatch(Patterns.make_case([Patterns.variable(), Patterns.variable()],function(alpha__qmark__,beta__emark__) { - let [a__qmark__0] = Patterns.match(Patterns.variable(),1); - let [b__emark__0] = Patterns.match(Patterns.variable(),2); - return b__emark__0; - })); - """ - - assert_translation(ex_ast, js_code) - end - -end diff --git a/test/translator/kernel_test.exs b/test/translator/kernel_test.exs deleted file mode 100644 index d23e206a..00000000 --- a/test/translator/kernel_test.exs +++ /dev/null @@ -1,69 +0,0 @@ -defmodule ElixirScript.Translator.Kernel.Test do - use ShouldI - import ElixirScript.TestHelper - - should "max" do - ex_ast = quote do - max(1, 2) - end - - js_code = """ - Math.max(1, 2) - """ - - assert_translation(ex_ast, js_code) - - end - - should "apply/2" do - ex_ast = quote do - apply(fun, [1, 2, 3]) - end - - js_code = """ - fun(1,2,3) - """ - - assert_translation(ex_ast, js_code) - - end - - should "apply/3" do - ex_ast = quote do - apply(Enum, :reverse, [1, 2, 3]) - end - - js_code = """ - Enum.reverse(1,2,3) - """ - - assert_translation(ex_ast, js_code) - - end - - should "hd" do - ex_ast = quote do - hd([1, 2, 3]) - end - - js_code = """ - Kernel.SpecialForms.list(1,2,3)[0] - """ - - assert_translation(ex_ast, js_code) - - end - - should "tl" do - ex_ast = quote do - tl([1, 2, 3]) - end - - js_code = """ - Kernel.SpecialForms.list(1,2,3).slice(1) - """ - - assert_translation(ex_ast, js_code) - - end -end \ No newline at end of file diff --git a/test/translator/list_test.exs b/test/translator/list_test.exs deleted file mode 100644 index d30db067..00000000 --- a/test/translator/list_test.exs +++ /dev/null @@ -1,38 +0,0 @@ -defmodule ElixirScript.Translator.List.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate list" do - ex_ast = quote do: [1, 2, 3] - js_code = "Kernel.SpecialForms.list(1, 2, 3)" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do: ["a", "b", "c"] - js_code = "Kernel.SpecialForms.list('a', 'b', 'c')" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do: [:a, :b, :c] - js_code = "Kernel.SpecialForms.list(Kernel.SpecialForms.atom('a'), Kernel.SpecialForms.atom('b'), Kernel.SpecialForms.atom('c'))" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do: [:a, 2, "c"] - js_code = "Kernel.SpecialForms.list(Kernel.SpecialForms.atom('a'), 2, 'c')" - - assert_translation(ex_ast, js_code) - end - - should "concatenate lists" do - ex_ast = quote do: [1, 2, 3] ++ [4, 5, 6] - js_code = "Kernel.SpecialForms.list(1,2,3).concat(Kernel.SpecialForms.list(4,5,6))" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do: this.list ++ [4, 5, 6] - js_code = "JS.get_property_or_call_function(this,'list').concat(Kernel.SpecialForms.list(4,5,6))" - - assert_translation(ex_ast, js_code) - end -end \ No newline at end of file diff --git a/test/translator/map_test.exs b/test/translator/map_test.exs deleted file mode 100644 index 618ddafd..00000000 --- a/test/translator/map_test.exs +++ /dev/null @@ -1,55 +0,0 @@ -defmodule ElixirScript.Translator.Map.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate empty map" do - ex_ast = quote do: %{} - js_code = "Kernel.SpecialForms.map({})" - - assert_translation(ex_ast, js_code) - end - - should "translate map with elements" do - ex_ast = quote do: %{one: "one", two: "two"} - js_code = "Kernel.SpecialForms.map({[Kernel.SpecialForms.atom('one')]: 'one', [Kernel.SpecialForms.atom('two')]: 'two'})" - - assert_translation(ex_ast, js_code) - end - - should "translate map within map" do - ex_ast = quote do: %{one: "one", two: %{three: "three"}} - js_code = """ - Kernel.SpecialForms.map({ - [Kernel.SpecialForms.atom('one')]: 'one', - [Kernel.SpecialForms.atom('two')]: Kernel.SpecialForms.map({ - [Kernel.SpecialForms.atom('three')]: 'three' - }) - }) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate map with string keys" do - ex_ast = quote do: %{"one" => "one", "two" => "two"} - js_code = """ - Kernel.SpecialForms.map({ - one: 'one', two: 'two' - }) - """ - - assert_translation(ex_ast, js_code) - end - - - should "translate map update" do - ex_ast = quote do: %{ map | value: 1 } - js_code = """ - Kernel.SpecialForms.map_update(map,{ - [Kernel.SpecialForms.atom('value')]: 1 - }) - """ - - assert_translation(ex_ast, js_code) - end -end diff --git a/test/translator/nil_test.exs b/test/translator/nil_test.exs deleted file mode 100644 index 04e130e9..00000000 --- a/test/translator/nil_test.exs +++ /dev/null @@ -1,9 +0,0 @@ -defmodule ElixirScript.Translator.Nil.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate nil" do - ex_ast = quote do: nil - assert_translation(ex_ast, "null") - end -end \ No newline at end of file diff --git a/test/translator/number_test.exs b/test/translator/number_test.exs deleted file mode 100644 index 0719659d..00000000 --- a/test/translator/number_test.exs +++ /dev/null @@ -1,18 +0,0 @@ -defmodule ElixirScript.Translator.Number.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate numbers" do - ex_ast = quote do: 1 - assert_translation(ex_ast, "1") - - ex_ast = quote do: 1_000 - assert_translation(ex_ast, "1000") - - ex_ast = quote do: 1.1 - assert_translation(ex_ast, "1.1") - - ex_ast = quote do: -1.1 - assert_translation(ex_ast, "-1.1") - end -end \ No newline at end of file diff --git a/test/translator/quote_test.exs b/test/translator/quote_test.exs deleted file mode 100644 index 588fa8fb..00000000 --- a/test/translator/quote_test.exs +++ /dev/null @@ -1,116 +0,0 @@ -defmodule ElixirScript.Translator.Quote.Test do - use ShouldI - import ElixirScript.TestHelper - - test "quote number" do - ex_ast = quote do - quote do: 1 - end - - js_code = "1" - - assert_translation(ex_ast, js_code) - end - - test "quote atom" do - ex_ast = quote do - quote do: :time - end - - js_code = "Kernel.SpecialForms.atom('time')" - - assert_translation(ex_ast, js_code) - end - - - test "quote 2 element tuple" do - ex_ast = quote do - quote do: {1, 2} - end - - js_code = "Kernel.SpecialForms.tuple(1, 2)" - - assert_translation(ex_ast, js_code) - end - - - test "quote 3 element tuple" do - ex_ast = quote do - quote do: {1, 2, 3} - end - - js_code = "Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('{}'), Kernel.SpecialForms.list(), Kernel.SpecialForms.list(1, 2, 3))" - - assert_translation(ex_ast, js_code) - end - - - test "quote function call" do - ex_ast = quote do - quote do: test(1) - end - - js_code = "Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('test'), - Kernel.SpecialForms.list(Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('context'),Kernel.SpecialForms.atom('Elixir.ElixirScript.Translator.Quote.Test')),Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('import'),Kernel.SpecialForms.atom('Elixir.ExUnit.Case'))), - Kernel.SpecialForms.list(1))" - - assert_translation(ex_ast, js_code) - end - - - test "quote function with variable" do - ex_ast = quote do - quote do: test(x) - end - - js_code = "Kernel.SpecialForms.tuple( - Kernel.SpecialForms.atom('test'), - Kernel.SpecialForms.list(Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('context'),Kernel.SpecialForms.atom('Elixir.ElixirScript.Translator.Quote.Test')),Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('import'),Kernel.SpecialForms.atom('Elixir.ExUnit.Case'))), - Kernel.SpecialForms.list(Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('x'),Kernel.SpecialForms.list(),Kernel.SpecialForms.atom('Elixir.ElixirScript.Translator.Quote.Test'))) - )" - - assert_translation(ex_ast, js_code) - end - - - test "quote function call with unquote" do - ex_ast = quote do - quote do: test(unquote(x)) - end - - js_code = "Kernel.SpecialForms.tuple( - Kernel.SpecialForms.atom('test'), - Kernel.SpecialForms.list(Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('context'),Kernel.SpecialForms.atom('Elixir.ElixirScript.Translator.Quote.Test')),Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('import'),Kernel.SpecialForms.atom('Elixir.ExUnit.Case'))), - Kernel.SpecialForms.list(x) - )" - - assert_translation(ex_ast, js_code) - end - - - test "quote function call with unquote_slicing" do - ex_ast = quote do - quote do: sum(1, unquote_splicing(values), 5) - end - - js_code = "Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('sum'), Kernel.SpecialForms.list(), Enum.concat(Kernel.SpecialForms.list(1), values, Kernel.SpecialForms.list(5)))" - - assert_translation(ex_ast, js_code) - end - - test "bind_quoted" do - ex_ast = quote do - quote bind_quoted: [x: x] do - x * x - end - end - - js_code = "Kernel.SpecialForms.tuple( - Kernel.SpecialForms.atom('*'), - Kernel.SpecialForms.list(Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('context'),Kernel.SpecialForms.atom('Elixir.ElixirScript.Translator.Quote.Test')),Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('import'),Kernel.SpecialForms.atom('Elixir.Kernel'))), - Kernel.SpecialForms.list(x, x) - )" - - assert_translation(ex_ast, js_code) - end -end \ No newline at end of file diff --git a/test/translator/receive_test.exs b/test/translator/receive_test.exs deleted file mode 100644 index 7155492e..00000000 --- a/test/translator/receive_test.exs +++ /dev/null @@ -1,63 +0,0 @@ -defmodule ElixirScript.Translator.Receive.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate receive without after" do - ex_ast = quote do - receive do - :ok -> - value - :error -> - value - _ -> - IO.puts "Unexpected message received" - end - end - - js_code = """ - Kernel.SpecialForms.receive(function(message) { - return Patterns.defmatch(Patterns.make_case([Kernel.SpecialForms.atom('ok')],function() { - return value; - }),Patterns.make_case([Kernel.SpecialForms.atom('error')],function() { - return value; - }),Patterns.make_case([Patterns.wildcard()],function() { - return IO.puts('Unexpected message received'); - })).call(this,message); - }) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate receive with after" do - ex_ast = quote do - receive do - :ok -> - value - :error -> - value - _ -> - IO.puts "Unexpected message received" - after - 5000 -> - IO.puts "No message in 5 seconds" - end - end - - js_code = """ - Kernel.SpecialForms.receive(function(message) { - return Patterns.defmatch(Patterns.make_case([Kernel.SpecialForms.atom('ok')],function() { - return value; - }),Patterns.make_case([Kernel.SpecialForms.atom('error')],function() { - return value; - }),Patterns.make_case([Patterns.wildcard()],function() { - return IO.puts('Unexpected message received'); - })).call(this,message); - },5000,Patterns.defmatch(Patterns.make_case([5000],function() { - return IO.puts('No message in 5 seconds'); - }))) - """ - - assert_translation(ex_ast, js_code) - end -end \ No newline at end of file diff --git a/test/translator/require_test.exs b/test/translator/require_test.exs deleted file mode 100644 index a3c975b9..00000000 --- a/test/translator/require_test.exs +++ /dev/null @@ -1,28 +0,0 @@ -defmodule ElixirScript.Translator.Require.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate require without as" do - ex_ast = quote do - require Hello.World - end - - js_code = """ - import * as World from 'hello/world'; - """ - - assert_translation(ex_ast, js_code) - end - - should "translate require with as" do - ex_ast = quote do - require Hello.World, as: Test - end - - js_code = """ - import * as Test from 'hello/world'; - """ - - assert_translation(ex_ast, js_code) - end -end diff --git a/test/translator/string_test.exs b/test/translator/string_test.exs deleted file mode 100644 index b2539a5d..00000000 --- a/test/translator/string_test.exs +++ /dev/null @@ -1,43 +0,0 @@ -defmodule ElixirScript.Translator.String.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate string" do - ex_ast = quote do: "Hello" - assert_translation(ex_ast, "'Hello'") - end - - should "translate multiline string" do - ex_ast = quote do: """ - Hello - This is another line - """ - assert_translation(ex_ast, "'Hello\\nThis is another line\\n'") - end - - should "translate string concatenation" do - ex_ast = quote do: "Hello" <> "World" - assert_translation(ex_ast, "'Hello' + 'World'") - end - - should "translate string interpolation" do - ex_ast = quote do: "Hello #{"world"}" - assert_translation(ex_ast, "'Hello ' + 'world'") - - ex_ast = quote do: "Hello #{length([])}" - assert_translation(ex_ast, "'Hello ' + Kernel.to_string(Kernel.SpecialForms.list().length)") - end - - should "translate multiline string interpolation" do - ex_ast = quote do: """ - Hello #{length([])} - """ - assert_translation(ex_ast, "'Hello ' + (Kernel.to_string(Kernel.SpecialForms.list().length) + '\\n')") - - ex_ast = quote do: """ - Hello #{length([])} - How are you, #{length([])}? - """ - assert_translation(ex_ast, "'Hello ' + (Kernel.to_string(Kernel.SpecialForms.list().length) + ('\\nHow are you, ' + (Kernel.to_string(Kernel.SpecialForms.list().length) + '?\\n')))") - end -end \ No newline at end of file diff --git a/test/translator/struct_test.exs b/test/translator/struct_test.exs deleted file mode 100644 index 0a2dee52..00000000 --- a/test/translator/struct_test.exs +++ /dev/null @@ -1,168 +0,0 @@ -defmodule ElixirScript.Translator.Struct.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate struct with default values" do - ex_ast = quote do - defmodule User do - defstruct name: "john", age: 27 - end - end - - js_code = """ - const __MODULE__ = Kernel.SpecialForms.atom('User'); - function defstruct(name = 'john',age = 27) { - return Kernel.SpecialForms.map({ - [Kernel.SpecialForms.atom('__struct__')]: __MODULE__, [Kernel.SpecialForms.atom('name')]: name, [Kernel.SpecialForms.atom('age')]: age - }); - } - export { - defstruct - }; - """ - - assert_translation(ex_ast, js_code) - end - - should "translate struct without default values" do - - ex_ast = quote do - defmodule User do - defstruct :name, :age - end - end - - js_code = """ - const __MODULE__ = Kernel.SpecialForms.atom('User'); - function defstruct(name,age) { - return Kernel.SpecialForms.map({ - [Kernel.SpecialForms.atom('__struct__')]: __MODULE__, [Kernel.SpecialForms.atom('name')]: name, [Kernel.SpecialForms.atom('age')]: age - }); - } - export { - defstruct - }; - """ - - assert_translation(ex_ast, js_code) - - end - - should "translate struct creation" do - ex_ast = quote do - user = %User{} - end - - js_code = """ - let [user] = Patterns.match(Patterns.variable(),User.defstruct()); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - user = %User{name: "John"} - end - - js_code = """ - let [user] = Patterns.match(Patterns.variable(),User.defstruct(name = 'John')); - """ - - assert_translation(ex_ast, js_code) - end - - should "translate struct update" do - ex_ast = quote do - user = %{ map | key: value } - end - - js_code = """ - let [user] = Patterns.match(Patterns.variable(),Kernel.SpecialForms.map_update(map,{ - [Kernel.SpecialForms.atom('key')]: value - })); - """ - - assert_translation(ex_ast, js_code) - - - ex_ast = quote do - user = %{ map | key: value, key1: value1 } - end - - js_code = """ - let [user] = Patterns.match(Patterns.variable(),Kernel.SpecialForms.map_update(map,{ - [Kernel.SpecialForms.atom('key')]: value, [Kernel.SpecialForms.atom('key1')]: value1 - })); - """ - - assert_translation(ex_ast, js_code) - end - - should "translate defexception" do - ex_ast = quote do - defmodule MyAppError do - defexception message: "This is a message" - end - end - - js_code = """ - const __MODULE__ = Kernel.SpecialForms.atom('MyAppError'); - function defexception(message = 'This is a message') { - return Kernel.SpecialForms.map({ - [Kernel.SpecialForms.atom('__struct__')]: __MODULE__, [Kernel.SpecialForms.atom('message')]: message - }); - } - export { - defexception - }; - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - defmodule MyAppError do - defexception [:message] - end - end - - js_code = """ - const __MODULE__ = Kernel.SpecialForms.atom('MyAppError'); - function defexception(message = null) { - return Kernel.SpecialForms.map({ - [Kernel.SpecialForms.atom('__struct__')]: __MODULE__, [Kernel.SpecialForms.atom('message')]: message - }); - } - export { - defexception - }; - """ - - assert_translation(ex_ast, js_code) - - end - - should "translate raise exception" do - ex_ast = quote do - raise MyAppError, message: "did not get what was expected" - end - - js_code = """ - throw MyAppError.defexception(message = 'did not get what was expected'); - """ - - assert_translation(ex_ast, js_code) - - - ex_ast = quote do - raise "did not get what was expected" - end - - js_code = """ - throw { - [Kernel.SpecialForms.atom('__struct__')]: Kernel.SpecialForms.atom('RuntimeError'), [Kernel.SpecialForms.atom('message')]: 'did not get what was expected' - }; - """ - - assert_translation(ex_ast, js_code) - - end -end diff --git a/test/translator/try_test.exs b/test/translator/try_test.exs deleted file mode 100644 index 3ae54cc2..00000000 --- a/test/translator/try_test.exs +++ /dev/null @@ -1,224 +0,0 @@ -defmodule ElixirScript.Translator.Try.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate with a rescue with one match" do - ex_ast = quote do - try do - do_something_that_may_fail(some_arg) - rescue - ArgumentError -> - IO.puts "Invalid argument given" - end - end - - js_code = """ - Kernel.SpecialForms._try(function() { - return do_something_that_may_fail(some_arg); - },Patterns.defmatch(Patterns.make_case([{ - [Kernel.SpecialForms.atom('__struct__')]: Kernel.SpecialForms.atom('ArgumentError') - }],function() { - return IO.puts('Invalid argument given'); - })),null,null,null) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate with a rescue with a list match" do - ex_ast = quote do - try do - do_something_that_may_fail(some_arg) - rescue - [ArgumentError] -> - IO.puts "Invalid argument given" - end - end - - js_code = """ - Kernel.SpecialForms._try(function() { - return do_something_that_may_fail(some_arg); - },Patterns.defmatch(Patterns.make_case([{ - [Kernel.SpecialForms.atom('__struct__')]: Kernel.SpecialForms.atom('ArgumentError') - }],function() { - return IO.puts('Invalid argument given'); - })),null,null,null) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate with a rescue with an in guard" do - ex_ast = quote do - try do - do_something_that_may_fail(some_arg) - rescue - x in [ArgumentError] -> - IO.puts "Invalid argument given" - end - end - - js_code = """ - Kernel.SpecialForms._try(function() { - return do_something_that_may_fail(some_arg); - },Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(x) { - return IO.puts('Invalid argument given'); - },function(x) { - return Kernel.__in__(x,Kernel.SpecialForms.list(ArgumentError.defstruct())); - })),null,null,null) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate with a rescue with an identifier" do - ex_ast = quote do - try do - do_something_that_may_fail(some_arg) - rescue - x -> - IO.puts "Invalid argument given" - end - end - - js_code = """ - Kernel.SpecialForms._try(function() { - return do_something_that_may_fail(some_arg); - },Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(x) { - return IO.puts('Invalid argument given'); - })),null,null,null) - """ - - assert_translation(ex_ast, js_code) - end - - - should "translate with a rescue with multiple patterns" do - ex_ast = quote do - try do - do_something_that_may_fail(some_arg) - rescue - [ArgumentError] -> - IO.puts "ArgumentError" - x -> - IO.puts "x" - end - end - - js_code = """ - Kernel.SpecialForms._try(function() { - return do_something_that_may_fail(some_arg); - },Patterns.defmatch(Patterns.make_case([{ - [Kernel.SpecialForms.atom('__struct__')]: Kernel.SpecialForms.atom('ArgumentError') - }],function() { - return IO.puts('ArgumentError'); - }),Patterns.make_case([Patterns.variable()],function(x) { - return IO.puts('x'); - })),null,null,null) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate with a rescue and after clause" do - ex_ast = quote do - try do - do_something_that_may_fail(some_arg) - rescue - ArgumentError -> - IO.puts "Invalid argument given" - after - IO.puts "This is printed regardless if it failed or succeed" - end - end - - js_code = """ - Kernel.SpecialForms._try(function() { - return do_something_that_may_fail(some_arg); - },Patterns.defmatch(Patterns.make_case([{ - [Kernel.SpecialForms.atom('__struct__')]: Kernel.SpecialForms.atom('ArgumentError') - }],function() { - return IO.puts('Invalid argument given'); - })),null,null,function() { - return IO.puts('This is printed regardless if it failed or succeed'); - }) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate with an after clause" do - ex_ast = quote do - try do - do_something_that_may_fail(some_arg) - after - IO.puts "This is printed regardless if it failed or succeed" - end - end - - js_code = """ - Kernel.SpecialForms._try(function() { - return do_something_that_may_fail(some_arg); - },null,null,null,function() { - return IO.puts('This is printed regardless if it failed or succeed'); - }) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate else" do - ex_ast = quote do - try do - 1 / x - else - y when y < 1 and y > -1 -> - :small - _ -> - :large - end - end - - js_code = """ - Kernel.SpecialForms._try(function() { - return 1 / x; - },null,null,Patterns.defmatch(Patterns.make_case([Patterns.variable()],function(y) { - return Kernel.SpecialForms.atom('small'); - },function(y) { - return (y < 1) && (y > -1); - }),Patterns.make_case([Patterns.wildcard()],function() { - return Kernel.SpecialForms.atom('large'); - })),null) - """ - - assert_translation(ex_ast, js_code) - end - - should "translate catch" do - ex_ast = quote do - try do - do_something_that_may_fail(some_arg) - rescue - ArgumentError -> - IO.puts "Invalid argument given" - catch - :throw, :Error -> - IO.puts "caught error" - end - end - - js_code = """ - Kernel.SpecialForms._try(function() { - return do_something_that_may_fail(some_arg); - },Patterns.defmatch(Patterns.make_case([{ - [Kernel.SpecialForms.atom('__struct__')]: Kernel.SpecialForms.atom('ArgumentError') - }],function() { - return IO.puts('Invalid argument given'); - })),Patterns.defmatch(Patterns.make_case([Kernel.SpecialForms.atom('throw'), Kernel.SpecialForms.atom('Error')],function() { - return IO.puts('caught error'); - })),null,null) - """ - - assert_translation(ex_ast, js_code) - end -end \ No newline at end of file diff --git a/test/translator/tuple_test.exs b/test/translator/tuple_test.exs deleted file mode 100644 index d1903e75..00000000 --- a/test/translator/tuple_test.exs +++ /dev/null @@ -1,35 +0,0 @@ -defmodule ElixirScript.Translator.Tuple.Test do - use ShouldI - import ElixirScript.TestHelper - - should "translate 2 item tuple" do - ex_ast = quote do: {1, 2} - js_code = "Kernel.SpecialForms.tuple(1, 2)" - - assert_translation(ex_ast, js_code) - end - - should "translate multiple item tuple" do - ex_ast = quote do: {1, 2, 3, 4, 5} - js_code = "Kernel.SpecialForms.tuple(1, 2, 3, 4, 5)" - - assert_translation(ex_ast, js_code) - end - - should "translate tuples of different typed items" do - ex_ast = quote do: {"a", "b", "c"} - js_code = "Kernel.SpecialForms.tuple('a', 'b', 'c')" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do: {:a, :b, :c} - js_code = "Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('a'), Kernel.SpecialForms.atom('b'), Kernel.SpecialForms.atom('c'))" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do: {:a, 2, "c"} - js_code = "Kernel.SpecialForms.tuple(Kernel.SpecialForms.atom('a'), 2, 'c')" - - assert_translation(ex_ast, js_code) - end -end \ No newline at end of file diff --git a/test_elixir_script/integration_test.exs b/test_elixir_script/integration_test.exs new file mode 100644 index 00000000..ca30430f --- /dev/null +++ b/test_elixir_script/integration_test.exs @@ -0,0 +1,66 @@ +defmodule ElixirScript.Integration.Test do + use ElixirScript.Test + + test "Something" do + assert {:ok, _} = {:ok, 1} + end + + test "Atom.to_string" do + val = Atom.to_string(:atom) + assert val == "atom" + end + + test "String interpolation with number" do + val = "#{5}" + assert val == "5" + end + + test "shorthand failure" do + orders = [%{email: "test@hotmail.com"}, %{email: "test2@hotmail.com"}] + + val = Enum.reduce(orders, [], + &(&2 ++ [ [:option, %{value: &1.email}, &1.email] ])) + + assert val == [ + [:option, %{value: "test@hotmail.com"}, "test@hotmail.com"], + [:option, %{value: "test2@hotmail.com"}, "test2@hotmail.com"] + ] + end + + test "map equals" do + map1 = %{test: "map"} + map2 = %{test: "map"} + + assert map1 == map2 + end + + test "multi-remote call" do + map = %{token_count: 5_000_000} + val = map.token_count.toLocaleString() + + assert val == "5,000,000" + end + + test "filter names in guards" do + has? = 5 + + val = case 5 do + _ when has? == 5 -> + true + end + + assert val == true + end + + test "tuple_get" do + map = %{{1} => 5} + val = Map.get(map, {1}) + + assert val == 5 + end + + test "multi_bind" do + [_a | _] = val = [1, 2, 3, 4, 5] + assert val == [1, 2, 3, 4, 5] + end +end diff --git a/test_elixir_script/try_test.exs b/test_elixir_script/try_test.exs new file mode 100644 index 00000000..0e73cd8b --- /dev/null +++ b/test_elixir_script/try_test.exs @@ -0,0 +1,48 @@ +defmodule ElixirScript.Try.Test do + use ElixirScript.Test + + test "returns value in try if no error" do + value = try do + 1 + 1 + rescue + _ -> + 3 + end + + assert value == 2 + end + + test "returns rescue value on error" do + value = try do + raise ArithmeticError + rescue + _ -> + 3 + end + + assert value == 3 + end + + test "returns rescue value from matching error" do + value = try do + raise ArithmeticError + rescue + ArithmeticError -> + 3 + end + + assert value == 3 + end + + test "returns rescue value from matching errors" do + value = try do + raise ArithmeticError + rescue + _ in [ArithmeticError, ArgumentError] -> + 3 + end + + assert value == 3 + end + +end