diff --git a/.babelrc b/.babelrc index 9a615718..0c2b576e 100644 --- a/.babelrc +++ b/.babelrc @@ -1,3 +1,12 @@ { - "plugins": ["transform-es2015-modules-commonjs"] -} \ No newline at end of file + "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 3221dd47..00000000 --- a/.eslintrc +++ /dev/null @@ -1,26 +0,0 @@ -{ - "rules": { - "indent": [ - 2, - 4 - ], - "quotes": [ - 2, - "single" - ], - "linebreak-style": [ - 2, - "unix" - ], - "semi": [ - 2, - "always" - ] - }, - "env": { - "es6": true, - "browser": true, - "node": true - }, - "extends": "eslint:recommended" -} \ 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 e9c2abd6..1a813296 100644 --- a/.gitignore +++ b/.gitignore @@ -10,11 +10,18 @@ deploy.sh .DS_Store sample/dest fprof.trace -index.js /doc /bench/snapshots .tern-port test/std_lib_compile_test.exs src/elixirscript -priv/**/*.js 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 f43c7343..43916696 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,27 @@ sudo: false language: elixir elixir: - - 1.3.0 + - 1.9 otp_release: - - 18.0 -env: - - TRAVIS_NODE_VERSION="6" + - 22.0 +cache: + directories: + - _build + - deps + - node_modules install: - - rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION + - nvm install 12.8.1 && nvm use 12.8.1 - npm install -script: - mix local.hex --force - mix local.rebar --force - mix deps.get - - mix do std_lib, clean, compile - - mix test - - npm test \ No newline at end of file +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 6519eb1a..89d3cd1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,37 +1,259 @@ # 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 +* `@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. +* 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 + +* 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 + +* [`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" @@ -41,351 +263,438 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ``` ### 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 + +* 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() - ``` + +* [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` + +* `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 + +* 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 + +* 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` + +* 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` + +* 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 + +* `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 + +* elixir_script mix compiler ### Removed -- `Html`, `View`, and `VDom` modules have been 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 + +* 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 + +* `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 + +* `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 + +* 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. + +* `--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" }` +* 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. + +* `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 + +* Optional parameters should now work as expected ## [0.15.2] - 2016-02-21 + ### Addded -- Support for variables as map keys + +* Support for variables as map keys ### Fixed -- Protocol implementations for Integer and Float which where not recognized -- Calling properties on non-objects + +* 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 + +* 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 + +* 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 + +* `__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` + +* `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 + +* .DS_Store and LICENSE from output ## [0.14.0] - 2015-12-06 + ### Added -- Can now implement protocols using JavaScript types + +* 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 +* 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 + +* 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: +* 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 +* 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 + +* `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 + +* 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 + +* 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. + +* 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` + +* 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 + +* 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 + +* 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 + +* Updated `Kernel` module to translate some functions to it's JavaScript equivalent ### Fixed -- Fixed `case` implementation to add `this` to call + +* 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 + +* 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 + +* 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; + +* 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 + +* 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 + +* 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 + +* 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 + +* 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 + +* 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 + +* 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 + +* 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) + +* 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 + +* Fixed multi arity implementation ## [0.3.0] - 2015-04-23 + ### Added -- function and case guards -- function and case pattern matching + +* function and case guards +* function and case pattern matching ### Changed -- Can now use ^ on a variable during assignment + +* Can now use ^ on a variable during assignment ## [0.2.1] - 2015-04-14 + ### Changed -- Renamed project to ElixirScript -- Reduced escript file size + +* 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 + +* 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 + +* 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 + +* 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 index b83cdf14..56fcb028 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,9 +6,9 @@ This contributing guide is based on the contributing for Elixir with changes sui Use the issues tracker for: -* [bug reports](#bug-reports) -* [submitting pull requests](#pull-requests) -* [feature requests](#feature-requests) +- [bug reports](#bug-reports) +- [submitting pull requests](#pull-requests) +- [feature requests](#feature-requests) ## Bug reports @@ -18,7 +18,7 @@ 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/bryanjos/elixirscript/search?type=Issues). + 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. @@ -51,7 +51,7 @@ Example: ## 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* +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. @@ -63,29 +63,22 @@ found in Elixir in ElixirScript. ElixirScript is broken up into the following parts: -* The compiler, written in Elixir -* The standard library modules, mostly written in Elixir -* The javascript core +- The compiler, written in Elixir +- The JavaScript core -The ElixirScript compiler is in the `lib` folder of the project. +The ElixirScript compiler is in the `lib` folder of the project. Here is where Elixir code is converted into JavaScript. -The standard library modules are in the `lib/elixir_script/prelude` folder. +The JavaScript code is in the `src/javascript`. +This is where the special forms and the Erlang Compatibility Layer are defined -The JavaScript code is in the `src/javascript`. -This is where features such as pattern matching and the standard library are implemented. +The ElixirScript tests are ran using `mix test` -The ElixirScript tests can be run using `mix test` - -The JavaScript tests can be run using `npm 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. -Contributing to the JavaScript code may be the easiest and most rewarding changes. -Don't see a feature, module or function from Elixir in ElixirScript yet? You can implement it -in the JavaScript code. - ## Pull requests Good pull requests - patches, improvements, new features - are a fantastic @@ -111,72 +104,72 @@ 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: + 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/bryanjos/elixirscript - ``` +```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 - ``` +```sh +git checkout master +git pull upstream master +``` 3. Create a new topic branch (off of `master`) to contain your feature, change, - or fix. + 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. + **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 - ``` +```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. + 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 - ``` +```sh +mix test +npm test +``` - This is needed to ensure your changes can - pass all the tests. +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 - ``` +```sh +git push origin +``` 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) - with a clear title and description. + 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 - ``` + 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/FAQ.md b/FAQ.md deleted file mode 100644 index 50a7dcc8..00000000 --- a/FAQ.md +++ /dev/null @@ -1,98 +0,0 @@ -# FAQ - -# Q. How do I get started? - -### A. Check out the [Getting Started](GettingStarted.html) Guide for more info. - -# Q. How much is implemented? - -### A. Most of Kernel.SpecialForms as well as some modules in the Standard Library - -The compiler to this point has been focused on translating Kernel.SpecialForms and Kernel. Below is a list of what is complete, incomplete, as well as missing - -#### Kernel.SpecialForms - -* Complete - * `__DIR__` - * `__MODULE__` - * `^var` - * `&expr` - * `for` - * `%{}` - * `{args}` - * `<>` - * `fn [clauses] end` - * `cond(clauses)` - * `__block__` - * `__aliases__` - * `unquote` - * `unquote_splicing` - * `%` - * `left.right` - * `quote` - * `import` - * `case` - * `left = right` - * `require` - * `left :: right` - * `alias` - * `__CALLER__` - * `__ENV__` - -* Missing - * `super(args)` - -* Caveats - * `quote` - ignores `context` options - * `left = right` does not support full unification yet. - ```elixir - a = 1 # works as expected - - 1 = a # may not work currently, but in some cases it will - ``` - * `send` and `receive` only work on functions defined with `defgen` or `defgenp` - - -#### Completed Modules - - * Tuple - * List - * Atom - * Range - * Logger - * Map - * MapSet - -#### Incomplete Modules - - * Kernel - * Enum - * Agent - * Integer - * Keyword - * Base - * String - * Bitwise - * Set - -#### Missing Modules - * Everything else - - -## Q. Can I use it today? - -### A. Yes, but realize this is not at 1.0 yet. - -You **can** use ElixirScript on your front ends and have it work and interoperate with JavaScript modules. The problem is since most of the standard library is incomplete. - -## Q. Can I use pattern matching? - -### A. Yes - -## Q. Can I use processes? - -### A. Yes, with some caveats as mentioned above. Only current works with functions defined with `defgen` or `defgenp` - -## Q. What about OTP? - -### A. Not yet. Maybe never. diff --git a/GettingStarted.md b/GettingStarted.md deleted file mode 100644 index 0c2c9c92..00000000 --- a/GettingStarted.md +++ /dev/null @@ -1,176 +0,0 @@ -# Getting Started with ElixirScript - - -The intent of this guide is to get you started with ElixirScript. It will give you instructions on using ElixirScript. I will go over the three ways you can use ElixirScript: - -* As an escript -* As a mix task -* As a library in your application - -### Escript - -**macOS**: Elixirscript can be installed via homebrew `brew install elixirscript`. For everyone else, plase read below - -* Step 1: Get escript - - You can download the elixirscript escript from the [releases page on github](https://github.com/bryanjos/elixirscript/releases). It is a tar file named elixirscript.tar.gz. - -* Step 2: Untar - - Next, untar elixirscript.tar.gz - - ```bash - tar -xvzf elixirscript.tar.gz - ``` - - You will want to put the bin folder from the uncompressed folder into your path. This should allow you to use the elixirscript escript. - -* Step 3: Use - - This is the help output of elixirscript - - ```bash - usage: elixirscript [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 - --std-lib [path] outputs the elixirscript standard library JavaScript files to the specified path - --full-build informs the compiler to do a full build instead of an incremental one - only used when output is specified - --core-path es6 import path to the elixirscript standard lib - only used with the [output] option. When used, Elixir.js is not exported - -v --version the current version number - -h --help this message - ``` - - the `` is the elixir code string or file path you want to convert from elixir to javascript. Below is an example of using a code string and turning it into JavaScript - - ```bash - $ elixirscript ":atom" -ex - Symbol.for('atom') - ``` - - The elixirscript escript changed the elixir code, `:atom` into the JavaScript code `Symbol.for('atom')`. The `-ex` parameter lets the script know that the input is an Elixir code string instead of a file. - - elixirscript also takes a path to your `.ex` and `.exjs` files as well: - - ```bash - $ elixirscript "src" -o "dist" - ``` - - If you look in the dist folder, you should see 2 folders. One, `app`, contains your code and the other, `elixir` contains the elixirscript standard library files. - -### mix elixirscript - -* Step 1: Get dependency - - The first step is getting the dependency. In your mix.exs file for your elixir project, add elixir_script to your deps. - - ```elixir - {:elixir_script, "~> 0.23"} - ``` - -* Step 2: Now download the dep - - ```bash - $ mix deps.get - ``` - - Now you should have the mix task, elixirscript. - -* Step 3: Use - ```bash - $ mix elixirscript "src" -o "dist" - ``` - - What you will notice is that the parameters are exactly the same as the escript. - -### ElixirScript module -* Step 1: Get dependency - - The first step is getting the dependency. In your mix.exs file for your elixir project, add elixir_script to your deps. - - ```elixir - {:elixir_script, "~> 0.23"} - ``` - -* Step 2: Now download the dep - - ```bash - $ mix deps.get - ``` - -* Step 3: Use - Now you will be able to use the ElixirScript module within your code. - - ```elixir - ElixirScript.compile(":atom") - ``` - - The is also compile_path/2 and compile_quoted/2. Each of the functions take an options keyword list. - - -### Macros -Macros can be used in Elixirscript just like in Elixir. The only exception is that `defmacrop` is unsupported - - -### JavaScript Interop - -Elixirscript has a couple of ways of interacting with JavaScript. - -#### Globally scoped functions - -To call functions in JavaScript in the global scope, such as those defined on `window`, use the erlang module syntax - -```elixir -# Calling alert -:window.alert("hi") - -# console -:console.log("hello") - -# document -:document.getElementById("main") -``` - -#### Globally scoped modules - -To call globally scoped modules defined in JavaScript, you can call them just like you would an Elixir module - -```elixir -Date.now() -``` - -Only works if module begins with a captial letter - -#### Importing ES Modules - -To import ES modules, first you must require the `JS` module. Then import the module using `JS.import` - -```elixir -defmodule MyModule do - require JS - JS.import React, "react" - - def func() do - React.render(my_component) - end -end - -``` - -#### The JS module - -The JS module has a number of other functions and macros. For more information, check out the docs. - -#### Frontend Project Boilerplate - -There is an [elixirscript frontend boilerplate project](https://github.com/bryanjos/elixirscript-project-boilerplate). This setup uses gulp and webpack to build and bundle assets. - - -#### ElixirScript-Brunch - -There is an Brunch plugin, [ElixirScript-Brunch](https://www.npmjs.com/package/elixirscript-brunch). -There are instructions there on how to use it with Phoenix. 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 9213a465..6d464386 100644 --- a/README.md +++ b/README.md @@ -1,75 +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) [![Deps Status](https://beta.hexfaktor.org/badge/all/github/bryanjos/elixirscript.svg)](https://beta.hexfaktor.org/github/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 (only for development) +[Documentation for current release](http://hexdocs.pm/elixir_script/) -Usage -======== +# Requirements -Please check the [Getting Started Guide](GettingStarted.md) for usage +* 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 -FAQ, Limitations -======== +Add dependency to your deps in mix.exs: -Please check the [FAQ](FAQ.md) +```elixir +{:elixir_script, "~> x.x"} +``` +Add `elixir_script` to list of mix compilers in mix.exs +Also add `elixir_script` configuration -Development -=========== +```elixir + 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 +``` -Clone the repo +Run `mix compile` - git clone git@github.com:bryanjos/elixirscript.git +# Examples -Get dependencies +### Application - mix deps.get - npm install +[ElixirScript Todo Example](https://github.com/elixirscript/todo-elixirscript) -Compile +### Library - mix do std_lib, clean, compile +[ElixirScript React](https://github.com/elixirscript/elixirscript_react) -Test +### Starter kit - mix test - npm test +[Elixirscript Starter Kit](https://github.com/harlantwood/elixirscript-starter-kit) +# Development -Build -============= - MIX_ENV=prod mix do clean, compile, std_lib, dist +```bash +# Clone the repo +git clone git@github.com:bryanjos/elixirscript.git -This will build a tarball in the dist folder. -By default the escript built will look into the folder above it for the -core JavaScript files needed for ElixirScript. To change the location, -update the `lib_path` config variable in the `:elixir_script` config block -to the path to look in and then do a clean build. +#Get dependencies +make deps -Communication -======== +# Compile +make -[#elixirscript](https://elixir-lang.slack.com/messages/elixirscript/) on the elixir-lang Slack - -Contributing -======== - -Please check the [CONTRIBUTING.md](CONTRIBUTING.md) +# Test +make test +``` +# Communication -### Example projects -* [hello](https://github.com/bryanjos/hello) Shows using Phoenix + Elixirscript with file watching -* [elixirscript frontend boilerplate](https://github.com/bryanjos/elixirscript-project-boilerplate) A boilerplate project for elixirscript frontends -* [elixirscript react example](https://github.com/bryanjos/elixirscript_react) An example of using with React +[#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 -#### 1.0 Roadmap -There is a [1.0.0 Milestone](https://github.com/bryanjos/elixirscript/milestones/1.0.0) defined which includes issues that are needed to be cleared before 1.0 can be reached. +Please check the [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index 8b68b656..00000000 --- a/gulpfile.js +++ /dev/null @@ -1,13 +0,0 @@ -var gulp = require('gulp'); -var babel = require('gulp-babel'); -var sourcemaps = require('gulp-sourcemaps'); - -var path = './src/javascript'; - -gulp.task('build', function() { - return gulp.src([path + '/**/*.js', '!' + path + '/dist_build/**/*.js', '!' + path + '/tests/**/*.js']) - .pipe(sourcemaps.init()) - .pipe(babel({ presets: ["react", "stage-0"], babelrc: false })) - .pipe(sourcemaps.write()) - .pipe(gulp.dest('./src/elixirscript')); -}); diff --git a/lib/elixir_script.ex b/lib/elixir_script.ex index 49b71830..cfb1b9a6 100644 --- a/lib/elixir_script.ex +++ b/lib/elixir_script.ex @@ -1,225 +1,78 @@ defmodule ElixirScript do - 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 - * `:core_path` - The es6 import path used to import the elixirscript core. - When using this option, the Elixir.js file is not exported - * `:full_build` - For compile_path, tells the compiler to perform a full build instead of incremental one - * `:output` - option to tell compiler how to output data - * `nil`: Return as list - * `:stdout`: Write to standard out - * `path (string)`: Write to specified path - """ - defmacro __using__(_) do - quote do - import Kernel, except: [ - if: 2, unless: 2, abs: 1, apply: 2, apply: 3, binary_part: 3, hd: 1, - tl: 1, is_atom: 1, is_binary: 1, is_bitstring: 1, is_boolean: 1, is_float: 1, - is_function: 1, is_function: 2, is_integer: 1, is_list: 1, is_number: 1, - is_pid: 1, is_tuple: 1, is_map: 1, is_port: 1, is_reference: 1, length: 1, - map_size: 1, max: 2, min: 2, round: 1, trunc: 1, tuple_size: 1, elem: 2, is_nil: 1, - make_ref: 1, spawn: 1, spawn: 3, spawn_link: 1, spawn_link: 3, spawn_monitor: 1, - spawn_monitor: 3, send: 2, self: 0, match?: 2, to_string: 1, "|>": 2, in: 2, "..": 2 - ] - import ElixirScript.Kernel - require JS - end - end - - # This is the serialized state of the ElixirScript.State module containing references to the standard library - @external_resource stdlib_state_path = Path.join([__DIR__, "elixir_script", "translator", "stdlib_state.bin"]) - @stdlib_state File.read(stdlib_state_path) - @lib_path Application.get_env(:elixir_script, :lib_path) - @version Mix.Project.config[:version] - - @doc """ - Compiles the given Elixir code string - """ - @spec compile(binary, Map.t) :: [binary | {binary, binary} | :ok] - 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, Map.t) :: [binary | {binary, binary} | :ok] - def compile_quoted(quoted, opts \\ %{}) do - - opts = build_compiler_options(Map.merge(opts, %{import_standard_libs: false})) - - data = quoted - |> get_modules_from_quoted - |> Enum.map(fn(x) -> %{ast: x, app: :app} end) - - result = %{ data: data } - |> ElixirScript.Passes.Init.execute(opts) - |> ElixirScript.Passes.FindModules.execute(opts) - |> ElixirScript.Passes.FindLoadOnly.execute(opts) - |> ElixirScript.Passes.FindFunctions.execute(opts) - |> ElixirScript.Passes.AddStdLib.execute(opts) - |> ElixirScript.Passes.JavaScriptAST.execute(opts) - |> ElixirScript.Passes.ConsolidateProtocols.execute(opts) - |> ElixirScript.Passes.JavaScriptCode.execute(opts) - |> ElixirScript.Passes.JavaScriptName.execute(opts) - |> ElixirScript.Passes.HandleOutput.execute(opts) - - result - end - - defp get_modules_from_quoted(quoted) do - results = case quoted do - { :__block__, _, list } -> - {modules, not_modules} = Enum.partition(list, - fn - {type, _, _ } when type in [:defprotocol, :defimpl, :defmodule] -> - true - _ -> - false - end) - - temp_module = case not_modules do - [] -> - [] - _ -> - [{:defmodule, [], [{:__aliases__, [], [:ElixirScript, :Temp]}, [do: { :__block__, [], not_modules }]]}] - end - - modules ++ temp_module - - {type, _, _ } = x when type in [:defprotocol, :defimpl, :defmodule] -> - x - x -> - {:defmodule, [], [{:__aliases__, [], [:ElixirScript, :Temp]}, [do: { :__block__, [], [x] }]]} - end - - List.wrap(results) - end - - @doc """ - Compiles the elixir files found at the given path - """ - @spec compile_path(binary, Map.t) :: [binary | {binary, binary} | :ok] - def compile_path(path, opts \\ %{}) do - - opts = build_compiler_options(opts) - - result = %{ path: path } - |> ElixirScript.Passes.Init.execute(opts) - |> ElixirScript.Passes.DepsPaths.execute(opts) - |> ElixirScript.Passes.ASTFromFile.execute(opts) - |> ElixirScript.Passes.LoadModules.execute(opts) - |> ElixirScript.Passes.FindModules.execute(opts) - |> ElixirScript.Passes.FindLoadOnly.execute(opts) - |> ElixirScript.Passes.FindChangedFiles.execute(opts) - |> ElixirScript.Passes.FindFunctions.execute(opts) - |> ElixirScript.Passes.AddStdLib.execute(opts) - |> ElixirScript.Passes.JavaScriptAST.execute(opts) - |> ElixirScript.Passes.ConsolidateProtocols.execute(opts) - |> ElixirScript.Passes.JavaScriptCode.execute(opts) - |> ElixirScript.Passes.JavaScriptName.execute(opts) - |> ElixirScript.Passes.WriteCache.execute(opts) - |> ElixirScript.Passes.HandleOutput.execute(opts) - - result - end - - @doc false - def get_stdlib_state() do - case @stdlib_state do - {:ok, data} -> - data - {:error, _} -> - raise RuntimeError, message: "Standard Library state not found. Please run `mix std_lib`" - end - end - - @doc false - def version(), do: @version - - @doc false - def compile_std_lib() do - compile_std_lib(Path.join([File.cwd!, "priv"])) - end - - @doc false - def compile_std_lib(output_path) do - opts = build_compiler_options(%{std_lib: true, include_path: true, output: output_path, app: :elixir}) - libs_path = Path.join([__DIR__, "elixir_script", "prelude"]) - - result = %{ path: libs_path } - |> ElixirScript.Passes.Init.execute(opts) - |> ElixirScript.Passes.DepsPaths.execute(opts) - |> ElixirScript.Passes.ASTFromFile.execute(opts) - |> ElixirScript.Passes.FindModules.execute(opts) - |> ElixirScript.Passes.FindLoadOnly.execute(opts) - |> ElixirScript.Passes.FindFunctions.execute(opts) - |> ElixirScript.Passes.JavaScriptAST.execute(opts) - |> ElixirScript.Passes.ConsolidateProtocols.execute(opts) - |> ElixirScript.Passes.JavaScriptCode.execute(opts) - |> ElixirScript.Passes.JavaScriptName.execute(opts) - |> ElixirScript.Passes.HandleOutput.execute(opts) - - result - end - - defp build_compiler_options(opts) do - default_options = Map.new - |> Map.put(:include_path, false) - |> Map.put(:root, nil) - |> Map.put(:env, __ENV__) - |> Map.put(:import_standard_libs, true) - |> Map.put(:core_path, "Elixir.Bootstrap") - |> Map.put(:full_build, false) - |> Map.put(:output, nil) - |> Map.put(:app, :app) - - Map.merge(default_options, opts) - end - - @doc """ - Copies the javascript that makes up the ElixirScript stdlib - to the specified location - """ - def copy_stdlib_to_destination(destination) do - Enum.each(Path.wildcard(Path.join([operating_path, "elixir", "*.js"])), fn(path) -> - base = Path.basename(path) - File.mkdir_p!(Path.join([destination, "elixir"])) - File.cp!(path, Path.join([destination, "elixir", base])) - end) - end - - #Gets path to js files whether the mix project is available - #or when used as an escript - defp operating_path do - case @lib_path do - nil -> - if Code.ensure_loaded?(Mix.Project) do - Mix.Project.build_path <> "/lib/elixir_script/priv" - else - split_path = Path.split(Application.app_dir(:elixirscript)) - replaced_path = List.delete_at(split_path, length(split_path) - 1) - replaced_path = List.delete_at(replaced_path, length(replaced_path) - 1) - Path.join(replaced_path) - end - lib_path -> - lib_path - end - end + * `input` (required): The entry module(s) for your application or library + + * `output`: The path of the generated JavaScript files. (defaults to `priv/elixir_script/build`) + + This should be a directory. If given a file, it will dump JavaScript files into the same directory as the given file path + + * `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. + 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" + + ```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 af2efbfc..1eedfc37 100644 --- a/lib/elixir_script/cli.ex +++ b/lib/elixir_script/cli.ex @@ -4,92 +4,82 @@ defmodule ElixirScript.CLI do @app_version Mix.Project.config()[:version] @switches [ - output: :binary, elixir: :boolean, - help: :boolean, core_path: :binary, - full_build: :boolean, version: :boolean, - watch: :boolean + output: :string, + help: :boolean, + version: :boolean, + root: :string ] @aliases [ - o: :output, ex: :elixir, h: :help, v: :version + o: :output, + h: :help, + v: :version ] - def main(argv) do - argv - |> parse_args - |> process - end - def parse_args(args) do - parse = OptionParser.parse(args, switches: @switches, aliases: @aliases) + {options, input, errors} = OptionParser.parse(args, switches: @switches, aliases: @aliases) - case parse do - { [help: true] , _ , _ } -> :help - { [version: true] , _ , _ } -> :version - { options , [input], _ } -> { input, options } - _ -> :help - end + cond do + length(errors) > 0 -> + :help + + Keyword.get(options, :help, false) -> + :help + Keyword.get(options, :version, false) -> + :version + + length(input) == 0 -> + :help + + true -> + {input, options} + end end - def help_message() do - """ - usage: elixirscript [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 - --full-build informs the compiler to do a full build instead of an incremental one - only used when output is specified - --core-path es6 import path to the elixirscript standard lib - only used with the [output] option. When used, Elixir.js is not exported - -v --version the current version number - -h --help this message - """ + 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 help_message + IO.write(help_message()) end def process(:version) do - IO.write @app_version + IO.write(@app_version) end - def process({ input, options }) do + 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 - {watch, options} = Keyword.pop(options, :watch, false) - - compile_opts = %{ - include_path: true, - core_path: Keyword.get(options, :core_path, "Elixir.Bootstrap"), - full_build: Keyword.get(options, :full_build, false), - output: Keyword.get(options, :output, :stdout) - } + defp do_process(input, options) do + compile_opts = [ + output: Keyword.get(options, :output, :stdout), + root: Keyword.get(options, :root, ".") + ] - case options[:elixir] do - true -> - ElixirScript.compile(input, compile_opts) - _ -> - ElixirScript.compile_path(input, compile_opts) - - if watch do - ElixirScript.Watcher.start_link(input, compile_opts) - :timer.sleep :infinity - end - 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 @@ -97,4 +87,11 @@ defmodule ElixirScript.CLI do end end) end + + 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/compiler/cache.ex b/lib/elixir_script/compiler/cache.ex deleted file mode 100644 index e17fb95c..00000000 --- a/lib/elixir_script/compiler/cache.ex +++ /dev/null @@ -1,156 +0,0 @@ -defmodule ElixirScript.Compiler.Cache do - @moduledoc false - require Logger - - def delete(path) do - case get_tmp_file(path) do - nil -> - nil - tmp_file -> - if !File.exists?(tmp_file) do - File.rm(tmp_file) - end - end - end - - def get(path) do - case get_tmp_file(path) do - nil -> - nil - tmp_file -> - if File.exists?(tmp_file) do - case File.read(tmp_file) do - {:ok, data} -> - :erlang.binary_to_term(data) - {:error, reason} -> - Logger.info("Unable to read compiler cache") - Logger.info(reason) - nil - end - else - nil - end - end - end - - def write(path, stats) do - case get_tmp_file(path) do - nil -> - { :error, nil } - tmp_file -> - case File.write(tmp_file, :erlang.term_to_binary(stats)) do - :ok -> - :ok - {:error, reason} -> - Logger.info("Unable to write compiler cache") - Logger.info(reason) - { :error, reason } - end - end - end - - def get_changed_files(old_file_stats, new_file_stats) do - if(length(old_file_stats) != length(new_file_stats)) do - new_file_stats - else - old_file_stats = Enum.sort(old_file_stats, fn {file1, _}, {file2, _} -> file1 < file2 end) - new_file_stats = Enum.sort(new_file_stats, fn {file1, _}, {file2, _} -> file1 < file2 end) - zipped = Enum.zip(old_file_stats, new_file_stats) - - if(Enum.any?(zipped, fn({ {old_file, _} , {new_file, _ } }) -> old_file != new_file end)) do - new_file_stats - else - Enum.reduce(zipped, [], fn({ {_, old_stat} , {_, new_stat } = new }, state) -> - cond do - old_stat.mtime != new_stat.mtime -> - state ++ [new] - true -> - state - end - end) - end - end - end - - def build_file_stats(path) do - Enum.map(path, fn(file) -> - { file, File.stat!(file) } - end) - end - - def new(state) do - %{ - input_files: [], - state: state, - full_build?: true, - version: ElixirScript.version() - } - end - - defp get_tmp_file(path) do - case System.tmp_dir do - nil -> - nil - tmp -> - tmp_dir = Path.join([tmp, "elixirscript"]) - if !File.exists?(tmp_dir) do - case File.mkdir_p(tmp_dir) do - :ok -> - encoded_path = Path.absname(path) |> Path.dirname |> Base.encode64 - Path.join([tmp_dir, encoded_path]) - {:error, reason} -> - Logger.info("Unable to write compiler cache") - Logger.info(reason) - nil - end - else - encoded_path = Path.absname(path) |> Path.dirname |> Base.encode64 - Path.join([tmp_dir, encoded_path]) - end - end - end - - def get_compiler_cache(path, opts) do - refresh_cache = cond do - Map.get(opts, :full_build) -> - true - empty?(opts.output) -> - true - old_version?(opts) -> - true - get(path) == nil -> - true - true -> - false - end - - if refresh_cache do - delete(path) - new(ElixirScript.get_stdlib_state()) - else - %{ get(path) | full_build?: false } - end - end - - defp empty?(path) when is_binary(path) do - case File.ls(path) do - {:ok, []} -> - true - {:error, _} -> - true - _ -> - false - end - end - - defp empty?(_) do - true - end - - defp old_version?(opts) do - cache_version = Map.get(opts, :version, nil) - cache_version == ElixirScript.version() - end - - -end 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 new file mode 100644 index 00000000..a8713880 --- /dev/null +++ b/lib/elixir_script/lib/js.ex @@ -0,0 +1,94 @@ +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. + + ```elixir + ElixirScript.JS.new User, ["first_name", "last_name"] + ``` + """ + defexternal new(module, params) + + @doc """ + Returns the type of the given value + """ + defexternal typeof(value) + + @doc """ + Determines if value is an instance of type. + """ + defexternal instanceof(value, type) + + @doc """ + Throws the term given + """ + defexternal throw(term) + + @doc """ + Creates a breakpoint for JavaScript debuggers to stop at + """ + defexternal debugger() + + @doc """ + The current JavaScript context + """ + defexternal this() + + @doc """ + Mutates an existing JavaScript object. + + ```elixir + ElixirScript.JS.mutate elem, "width", 100 + ``` + """ + defexternal mutate(object, key, value) + + @doc """ + 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"}) + ``` + """ + defexternal map_to_object(map) + + @doc """ + 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) + ``` + """ + defexternal map_to_object(map, options) + + @doc """ + Takes the given object and returns a map + Options include [{:keys, :atom}, {:recurse_array, true}] + + ```elixir + ElixirScript.JS.object_to_object({my: "object"}) + ``` + """ + defexternal object_to_map(object) + + @doc """ + Takes the given object and returns a map + Options include [{:keys, :atom}, {:recurse_array, true}] + + ```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/prelude/string.ex b/lib/elixir_script/lib/string.ex similarity index 80% rename from lib/elixir_script/prelude/string.ex rename to lib/elixir_script/lib/string.ex index e76d72a6..f5227fc5 100644 --- a/lib/elixir_script/prelude/string.ex +++ b/lib/elixir_script/lib/string.ex @@ -1,13 +1,13 @@ defmodule ElixirScript.String do - @moduledoc false + @moduledoc false import Kernel, except: [length: 1] def to_atom(str) do - Symbol.for(str) + :erlang.binary_to_atom(str, :utf8) end def to_existing_atom(str) do - Symbol.for(str) + :erlang.binary_to_existing_atom(str, :utf8) end def to_char_list(str) do @@ -15,15 +15,15 @@ defmodule ElixirScript.String do end def to_float(str) do - Elixir.Core.get_global().parseFloat(str) + :erlang.binary_to_float(str) end def to_integer(str) do - Elixir.Core.get_global().parseInt(str, 10) + :erlang.binary_to_integer(str) end def to_integer(str, base) do - Elixir.Core.get_global().parseInt(str, base) + :erlang.binary_to_integer(str, base) end def upcase(str) do @@ -54,6 +54,19 @@ defmodule ElixirScript.String 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 @@ -73,11 +86,11 @@ defmodule ElixirScript.String do end def graphemes(str) do - str.split('') + ElixirScript.Core.Functions.graphemes(str) end def length(str) do - str.length() + graphemes(str).length() end def match?(str, regex) do @@ -123,7 +136,6 @@ defmodule ElixirScript.String do end end - def ends_with?(str, suffix) when is_binary(suffix) do str.endsWith(suffix) end @@ -149,7 +161,6 @@ defmodule ElixirScript.String do str.repeat(n) end - def contains?(str, s) when is_binary(s) do str.indexOf(s) > -1 end @@ -171,7 +182,6 @@ defmodule ElixirScript.String do end end - def codepoints(str) do do_codepoints(str, []) end @@ -184,7 +194,11 @@ defmodule ElixirScript.String do do_codepoints(str.substr(1), codepoint_list ++ [first(str).codePointAt(0)]) end - def valid_character?(codepoint) do - Elixir.Core.Functions.is_valid_character(codepoint) + 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_systems.ex b/lib/elixir_script/module_systems.ex deleted file mode 100644 index c25968f9..00000000 --- a/lib/elixir_script/module_systems.ex +++ /dev/null @@ -1,23 +0,0 @@ -defmodule ElixirScript.ModuleSystems do - @moduledoc false - defp module_system() do - ElixirScript.ModuleSystems.ES6 - end - - def import_namespace_module(module_name, from, env) do - module_system.import_namespace_module(module_name, from, env) - end - - def import_module(module_name, from, env) do - module_system.import_module(module_name, from, env) - end - - def import_module(import_name, from) do - module_system.import_module(import_name, from) - end - - def export_module(exported_object) do - module_system.export_module(exported_object) - end - -end diff --git a/lib/elixir_script/module_systems/common.ex b/lib/elixir_script/module_systems/common.ex deleted file mode 100644 index 46798e29..00000000 --- a/lib/elixir_script/module_systems/common.ex +++ /dev/null @@ -1,44 +0,0 @@ -defmodule ElixirScript.ModuleSystems.Common do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - - def import_namespace_module(module_name, from, env) do - do_import_module(Translator.translate!(module_name, env), from) - end - - def import_module(module_name, from, env) do - do_import_module(Translator.translate!(module_name, env), from) - end - - def import_module(import_name, from) do - do_import_module(JS.identifier(import_name), from) - end - - defp do_import_module(ref, file_path) do - - ref_declarator = JS.variable_declarator( - ref, - JS.call_expression( - JS.identifier("require"), - [JS.literal(file_path)] - ) - ) - - JS.variable_declaration([ref_declarator], :const) - - end - - def export_module(exported_object) do - JS.assignment_expression( - :=, - JS.member_expression( - JS.identifier("module"), - JS.identifier("exports") - ), - exported_object - ) - end - - -end 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/es6.ex b/lib/elixir_script/module_systems/es6.ex deleted file mode 100644 index 1398d1a5..00000000 --- a/lib/elixir_script/module_systems/es6.ex +++ /dev/null @@ -1,43 +0,0 @@ -defmodule ElixirScript.ModuleSystems.ES6 do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - - def import_namespace_module(module_name, from, env) do - import_specifier = JS.import_namespace_specifier( - Translator.translate!(module_name, env), - Translator.translate!(module_name, env) - ) - - do_import_module([import_specifier], from) - end - - def import_module(module_name, from, env) do - import_specifier = JS.import_default_specifier( - Translator.translate!(module_name, env), - Translator.translate!(module_name, env) - ) - - do_import_module([import_specifier], from) - end - - def import_module(import_name, from) do - import_specifier = JS.import_default_specifier( - JS.identifier(import_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 - - def 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/add_std_lib.ex b/lib/elixir_script/passes/add_std_lib.ex deleted file mode 100644 index 2cb7a127..00000000 --- a/lib/elixir_script/passes/add_std_lib.ex +++ /dev/null @@ -1,10 +0,0 @@ -defmodule ElixirScript.Passes.AddStdLib do - @moduledoc false - alias ElixirScript.Translator.State - - def execute(compiler_data, _) do - State.deserialize(ElixirScript.get_stdlib_state, []) - compiler_data - end - -end diff --git a/lib/elixir_script/passes/ast_from_file.ex b/lib/elixir_script/passes/ast_from_file.ex deleted file mode 100644 index 24e656bf..00000000 --- a/lib/elixir_script/passes/ast_from_file.ex +++ /dev/null @@ -1,26 +0,0 @@ -defmodule ElixirScript.Passes.ASTFromFile do - @moduledoc false - - def execute(compiler_data, opts) do - data = Enum.reduce(compiler_data.data, [], fn({dep, paths}, list) -> - - file_paths = paths - |> Enum.flat_map(fn(path) -> Path.join(path, "**/*.{ex,exs,exjs}") |> Path.wildcard end) - |> Enum.reduce([], fn(path, list) -> - quoted = path - |> File.read! - |> Code.string_to_quoted! - - stat = File.stat!(path) - - list ++ [%{ path: path, app: dep, stat: stat, ast: quoted }] - end) - - - list ++ file_paths - end) - - Map.put(compiler_data, :data, data) - end - -end diff --git a/lib/elixir_script/passes/consolidate_protocols.ex b/lib/elixir_script/passes/consolidate_protocols.ex deleted file mode 100644 index 60c1e33e..00000000 --- a/lib/elixir_script/passes/consolidate_protocols.ex +++ /dev/null @@ -1,101 +0,0 @@ -defmodule ElixirScript.Passes.ConsolidateProtocols do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator.Utils - alias ElixirScript.ModuleSystems - alias ElixirScript.Translator.State - require Logger - - def execute(compiler_data, opts) do - State.set_module_data(compiler_data.data) - data = State.get_module_data() - - only_protocols_and_impls = Enum.filter(data, fn - ({_, %{type: :module}}) -> - false - ({_, %{type: :consolidated}}) -> - false - _ -> - true - end) - - grouped = group_protocol_data(only_protocols_and_impls) - consolidated_protocols = update_protocols(grouped, opts) - - data = Enum.reduce(consolidated_protocols, data, fn({ key, value }, d) -> Keyword.put(d, key, value) end) - - %{ compiler_data | data: data } - end - - - defp group_protocol_data(data) do - Enum.reduce(data, %{}, fn({module_name, module_data} = dat, state) -> - if module_data.type == :protocol do - existing = Map.get(state, module_name, %{}) - existing = Map.put(existing, :protocol, dat) - Map.put(state, module_name, existing) - else - existing = Map.get(state, module_data.implements, %{}) - existing_protocol_data = Map.get(existing, :impls, []) - existing_protocol_data = existing_protocol_data ++ [dat] - existing = Map.put(existing, :impls, existing_protocol_data) - Map.put(state, module_data.implements, existing) - end - end) - end - - defp update_protocols(grouped_protocol_data, opts) do - Enum.map(grouped_protocol_data, fn - ({ protocol_name, %{ protocol: protocol, impls: impls } }) -> - make_defimpl(protocol_name, protocol, Enum.uniq(impls), opts) - - ({ protocol_name, %{ protocol: protocol } }) -> - make_defimpl(protocol_name, protocol, [], opts) - end) - end - - defp make_defimpl(name, { _, protocol }, implementations, compiler_opts) do - imports = [ModuleSystems.import_module(:Elixir, Utils.make_local_file_path(:elixir, compiler_opts.core_path, compiler_opts.root))] - - declarator = JS.variable_declarator( - JS.identifier("impls"), - JS.array_expression([]) - ) - - declaration = JS.variable_declaration([declarator], :let) - - default = JS.export_default_declaration(JS.identifier("impls")) - - protocol_name = Atom.to_string(name) - - app_name = protocol.app - - body = Enum.flat_map(implementations, fn({_, impl_data}) -> - x = Atom.to_string(Utils.quoted_to_name(impl_data.for)) - x = String.to_atom(protocol_name <> ".DefImpl." <> x) - name = Utils.name_to_js_name(x) - imports = ModuleSystems.import_module(name, Utils.make_local_file_path(impl_data.app, Utils.name_to_js_file_name(x), compiler_opts.root)) - call = JS.call_expression( - JS.member_expression( - JS.identifier("impls"), - JS.identifier("push") - ), - [JS.identifier(name)] - ) - - [imports, call] - end) - - module_name = String.to_atom(protocol_name <> ".DefImpl") - module_data = %{ - module: String.to_atom(protocol_name <> ".DefImpl"), - javascript_ast: imports ++ [declaration] ++ body ++ [default], - app: app_name, - type: :consolidated, - protocol: name - } - - { module_name, module_data } - end - -end diff --git a/lib/elixir_script/passes/deps_paths.ex b/lib/elixir_script/passes/deps_paths.ex deleted file mode 100644 index b0dac9b1..00000000 --- a/lib/elixir_script/passes/deps_paths.ex +++ /dev/null @@ -1,8 +0,0 @@ -defmodule ElixirScript.Passes.DepsPaths do - @moduledoc false - def execute(compiler_data, opts) do - data = [{opts[:app], List.wrap(compiler_data.path)}] - Map.put(compiler_data, :data, data) - end - -end diff --git a/lib/elixir_script/passes/find_changed_files.ex b/lib/elixir_script/passes/find_changed_files.ex deleted file mode 100644 index cacff587..00000000 --- a/lib/elixir_script/passes/find_changed_files.ex +++ /dev/null @@ -1,20 +0,0 @@ -defmodule ElixirScript.Passes.FindChangedFiles do - @moduledoc false - alias ElixirScript.Compiler.Cache - alias ElixirScript.Translator.State - @version Mix.Project.config[:version] - - def execute(compiler_data, opts) do - compiler_cache = Cache.get_compiler_cache(compiler_data.path, opts) - new_file_stats = Enum.map(compiler_data.data, fn({_, data}) -> { data.path, data.stat } end) |> Enum.uniq - - changed_files = Cache.get_changed_files(compiler_cache.input_files, new_file_stats) - |> Enum.map(fn {file, _} -> file end) - - - State.deserialize(compiler_cache.state, compiler_data.loaded_modules) - - - Map.put(compiler_data, :changed_files, changed_files) - end -end diff --git a/lib/elixir_script/passes/find_deps.ex b/lib/elixir_script/passes/find_deps.ex deleted file mode 100644 index c3f379aa..00000000 --- a/lib/elixir_script/passes/find_deps.ex +++ /dev/null @@ -1,85 +0,0 @@ -defmodule ElixirScript.Passes.FindDeps do - @moduledoc false - alias ElixirScript.Translator.Utils - - def execute(compiler_data, _) do - data = Enum.map(compiler_data.data, &do_find_dependencies(&1)) - Map.put(compiler_data, :data, data) - end - - defp do_find_dependencies({ module, module_data }) do - {_, deps} = Macro.prewalk(module_data.ast, [], &collect_references(&1, &2)) - { module, Map.put(module_data, :deps, [ElixirScript.Kernel] ++ deps) } - end - - defp collect_references({:import, _, [{{:., _, [{:__aliases__, _, head_import_name}, :{}]}, _, tail_imports }]}, state) do - deps = Enum.map(tail_imports, fn({:__aliases__, context, name}) -> - full_module_name = { :__aliases__, context, head_import_name ++ name } - Utils.quoted_to_name(full_module_name) - end) - - state ++ deps - end - - defp collect_references({:import, _, [{:__aliases__, _, _} = module_name]} = ast, state) do - module_name = Utils.quoted_to_name(module_name) - { ast, state ++ [module_name] } - end - - defp collect_references({:import, _, [{:__aliases__, _, _} = module_name, _]} = ast, state) do - module_name = Utils.quoted_to_name(module_name) - {ast, state ++ [module_name] } - end - - defp collect_references({:alias, _, [{{:., _, [{:__aliases__, _, head_alias_name}, :{}]}, _, tail_aliases }]} = ast, state) do - deps = Enum.map(tail_aliases, fn({:__aliases__, context, name}) -> - full_module_name = { :__aliases__, context, head_alias_name ++ name } - Utils.quoted_to_name(full_module_name) - end) - - { ast, state ++ deps } - end - - defp collect_references({:alias, _, [{:__aliases__, _, _} = module_name] } = ast, state) do - module_name = Utils.quoted_to_name(module_name) - { ast, state ++ [module_name] } - end - - defp collect_references({:alias, _, [{:__aliases__, _, _} = module_name, _]} = ast, state) do - module_name = Utils.quoted_to_name(module_name) - {ast, state ++ [module_name] } - end - - defp collect_references({:require, _, [{{:., _, [{:__aliases__, _, head_alias_name}, :{}]}, _, tail_aliases }]} = ast, state) do - deps = Enum.map(tail_aliases, fn({:__aliases__, context, name}) -> - full_module_name = { :__aliases__, context, head_alias_name ++ name } - Utils.quoted_to_name(full_module_name) - end) - - { ast, state ++ deps } - end - - defp collect_references({:require, _, [{:__aliases__, _, _} = module_name] } = ast, state) do - module_name = Utils.quoted_to_name(module_name) - { ast, state ++ [module_name] } - end - - defp collect_references({:require, _, [{:__aliases__, _, _} = module_name, _]} = ast, state) do - module_name = Utils.quoted_to_name(module_name) - { ast, state ++ [module_name] } - end - - defp collect_references({:., _, [{:__aliases__, _, _} = module_name, _]} = ast, state) do - module_name = Utils.quoted_to_name(module_name) - { ast, state ++ [module_name] } - end - - defp collect_references({{:., _, [{:__aliases__, _, _} = module_name, _]}, _, _ } = ast, state) do - module_name = Utils.quoted_to_name(module_name) - { ast, state ++ [module_name] } - end - - defp collect_references(ast, state) do - { ast, state } - end -end diff --git a/lib/elixir_script/passes/find_functions.ex b/lib/elixir_script/passes/find_functions.ex deleted file mode 100644 index d4ca8581..00000000 --- a/lib/elixir_script/passes/find_functions.ex +++ /dev/null @@ -1,75 +0,0 @@ -defmodule ElixirScript.Passes.FindFunctions do - @moduledoc false - @function_types [:def, :defp, :defgen, :defgenp, :defmacro, :defmacrop] - - def execute(data, _) do - new_data = Enum.map(data.data, fn { module_name, module_data } -> - - %{def: functions, defp: private_functions, defgen: generators, defgenp: private_generators, defmacro: macros, defmacrop: private_macros } = get_functions_from_module(module_data.ast) - - module_data = Map.put(module_data, :functions, functions ++ generators) - |> Map.put(:private_functions, private_functions ++ private_generators) - |> Map.put(:macros, macros) - |> Map.put(:private_macros, private_macros) - - {module_name, module_data} - end) - - %{data | data: new_data} - end - - defp get_functions_from_module({:__block__, _, list}) do - Enum.reduce(list, new_function_map(), fn - ({type, _, [{:when, _, [{name, _, params} | _guards] }, _] }, state) when type in @function_types and is_atom(params) -> - arity = 0 - - add_function_to_map(state, type, name, arity) - - ({type, _, [{:when, _, [{name, _, params} | _guards] }, _] }, state) when type in @function_types -> - arity = if is_nil(params), do: 0, else: length(params) - - add_function_to_map(state, type, name, arity) - - ({type, _, [{name, _, params}, _]}, state) when type in @function_types and is_atom(params) -> - arity = 0 - - add_function_to_map(state, type, name, arity) - - ({type, _, [{name, _, params}, _]}, state) when type in @function_types -> - arity = if is_nil(params), do: 0, else: length(params) - add_function_to_map(state, type, name, arity) - - ({type, _, [{name, _, params}]}, state) when is_atom(params) and type in @function_types -> - arity = 0 - add_function_to_map(state, type, name, arity) - - ({type, _, [{name, _, params}]}, state) when type in @function_types -> - arity = length(params) - add_function_to_map(state, type, name, arity) - - _, state -> - state - - end) - end - - defp new_function_map() do - %{ def: Keyword.new, defp: Keyword.new, defgen: Keyword.new, defgenp: Keyword.new, defmacro: Keyword.new, defmacrop: Keyword.new } - end - - defp get_functions_from_module(_) do - new_function_map() - end - - defp add_function_to_map(map, type, name, arity) do - list = Map.get(map, type) - - if {name, arity} in list do - map - else - Map.put(map, type, list ++ [{ name, arity }]) - end - end - - -end diff --git a/lib/elixir_script/passes/find_load_only.ex b/lib/elixir_script/passes/find_load_only.ex deleted file mode 100644 index 86fb0754..00000000 --- a/lib/elixir_script/passes/find_load_only.ex +++ /dev/null @@ -1,33 +0,0 @@ -defmodule ElixirScript.Passes.FindLoadOnly do - @moduledoc false - - def execute(compiler_data, opts) do - data = compiler_data.data - |> Enum.map(fn({module_name, module_data}) -> - - {_, load_only} = Macro.prewalk(module_data.ast, false, fn - ({:@, _, [{:load_only, _, [true]}]} = ast, state) -> - {ast, true} - - ({:@, _, [{:load_only, _, [false]}]} = ast, state) -> - {ast, false} - - ({:@, _, [{:load_only, _, []}]} = ast, state) -> - {ast, true} - - (ast, state) -> - {ast, state} - end) - - { module_name, Map.put(module_data, :load_only, load_only) } - - end) - - - - %{ compiler_data | data: data } - end - - - -end diff --git a/lib/elixir_script/passes/find_modules.ex b/lib/elixir_script/passes/find_modules.ex deleted file mode 100644 index fc3faee9..00000000 --- a/lib/elixir_script/passes/find_modules.ex +++ /dev/null @@ -1,177 +0,0 @@ -defmodule ElixirScript.Passes.FindModules do - @moduledoc false - alias ElixirScript.Translator.Utils - alias ElixirScript.Translator.State - - def execute(compiler_data, opts) do - data = Enum.reduce(compiler_data.data, [], fn(data, list) -> - quoted = update_quoted(data.ast) - { _, modules } = Macro.postwalk(quoted, [], &get_defmodules(&1, &2, opts)) - - modules = Enum.map(modules, fn(x) -> { x.name, Map.merge(data, x) } end) - list ++ modules - end) - - Map.put(compiler_data, :data, data) - end - - defp get_defmodules({:defprotocol, _, [{:__aliases__, _, _} = the_alias, [do: {:__block__, _, _} = block]]} = ast, state, _) do - s = %{ name: Utils.quoted_to_name(the_alias), type: :protocol, ast: block } - { ast, state ++ [s] } - end - - defp get_defmodules({:defprotocol, _, [{:__aliases__, _, _} = the_alias, [do: spec]]} = ast, state, _) do - s = %{ name: Utils.quoted_to_name(the_alias), type: :protocol, ast: {:__block__, [], [spec]} } - { ast, state ++ [s] } - end - - defp get_defmodules({:defimpl, _, [ the_alias, [for: {:__aliases__, _, type_name} = type], [do: {:__block__, context, spec}] ]} = ast, state, _) do - {:__aliases__, _, original_name} = Utils.name_to_quoted(State.get_module_name(the_alias)) - name = original_name ++ [DefImpl] ++ [Elixir] ++ type_name - s = %{name: Utils.quoted_to_name({:__aliases__, [], name}), type: :impl, for: type, ast: {:__block__, context, spec}, implements: Utils.quoted_to_name({:__aliases__, [], original_name}) } - { ast, state ++ [s] } - end - - defp get_defmodules({:defimpl, _, [ the_alias, [for: {:__aliases__, _, type_name} = type], [do: spec] ]} = ast, state, _) do - {:__aliases__, _, original_name} = Utils.name_to_quoted(State.get_module_name(the_alias)) - name = original_name ++ [DefImpl] ++ [Elixir] ++ type_name - s = %{name: Utils.quoted_to_name({:__aliases__, [], name}), type: :impl, for: type, ast: {:__block__, [], [spec]}, implements: Utils.quoted_to_name({:__aliases__, [], original_name}) } - { ast, state ++ [s] } - end - - defp get_defmodules({:defmodule, _, [{:__aliases__, _, [:ElixirScript, :Temp]}, [do: body]]} = ast, state, opts) do - s = %{name: ElixirScript.Temp, type: :module, ast: body } - { ast, state ++ [s] } - end - - - defp get_defmodules({:defmodule, _, [{:__aliases__, _, _}, [do: _]]} = ast, state, opts) do - { ast, do_module_processing(ast, state, opts) } - end - - defp get_defmodules(ast, state, _) do - { ast, state } - end - - defp do_module_processing({:defmodule, context1, [{:__aliases__, _, name} = the_alias, [do: body]]}, state, opts) do - { body, inner_modules } = make_inner_module_aliases(name, body) - - aliases = Enum.map(inner_modules, fn - ({:defmodule, _, [{:__aliases__, _, inner_module_name}, [do: inner_module_body]]}) -> - { :alias, [], [{:__aliases__, [alias: false], name ++ inner_module_name}, [as: {:__aliases__, [alias: false], inner_module_name }] ] } - end) - - state = Enum.reduce(inner_modules, state, fn - ({:defmodule, context1, [{:__aliases__, context2, inner_module_name}, [do: inner_module_body]]}, state) -> - - module_name = Utils.quoted_to_name({:__aliases__, [], tl(name) ++ inner_module_name}) - state = Enum.reject(state, fn(x) -> x.name == module_name end) - - this_module_aliases = aliases -- [{ :alias, [], [{:__aliases__, [alias: false], name ++ inner_module_name}, [as: {:__aliases__, [alias: false], inner_module_name }] ] }] - - do_module_processing( - {:defmodule, context1, [{:__aliases__, context2, name ++ inner_module_name}, [do: add_aliases_to_body(inner_module_body, this_module_aliases)]]}, - state, opts) - end) - - body = case body do - {:__block__, context, list } -> - list = Enum.map(list, fn - {:use, _, [module, _] } = using -> - {:use, handle_use_expression(using, module, opts) } - {:use, _, [module] } = using -> - {:use, handle_use_expression(using, module, opts) } - ast -> - {:expanded, ast} - end) - |> Enum.reduce([], fn - {:use, ast}, state -> - case ast do - {:__block__, _, list} -> - state ++ list - _ -> - state ++ [ast] - end - - {:expanded, ast}, state -> - state ++ [ast] - end) - - {:__block__, context, list} - - _ -> - body - end - - body = add_aliases_to_body(body, aliases) - - [%{name: Utils.quoted_to_name(the_alias), type: :module, ast: body }] ++ state - end - - defp add_aliases_to_body(body, aliases) do - case body do - { :__block__, context, body } -> - { :__block__, context, aliases ++ List.wrap(body) } - _ -> - { :__block__, [], aliases ++ List.wrap(body) } - end - end - - defp make_inner_module_aliases(name, body) do - case body do - nil -> - { { :__block__, [], [] }, [] } - - {:__block__, context, list2 } -> - { list2, inner_modules } = Enum.partition(list2, fn(x) -> - case x do - {:defmodule, _, [{:__aliases__, _, inner_module_name}, [do: inner_module_body]]} -> - false - _ -> - true - end - end) - - { {:__block__, context, list2}, inner_modules } - {:defmodule, _, [{:__aliases__, context, inner_module_name}, [do: inner_module_body]]} = mod -> - { {:__block__, context, [] }, [mod] } - _ -> - { body, [] } - end - end - - defp handle_use_expression(using_ast, module, opts) do - module = Utils.quoted_to_name(module) - - eval = """ - require #{inspect module} - __ENV__ - """ - {env, _} = Code.eval_string(eval, [], opts.env) - - - case Macro.expand(using_ast, env) do - {:__block__, _, - [{:__block__, _, - [{:require, _, _}, - {{:., _, [_, :__using__]}, _, _} = ast]}]} -> - Macro.expand_once(ast, env) - end - end - - defp update_quoted(quoted) do - Macro.prewalk(quoted, fn - ({name, context, parms}) -> - context = if context[:import] == Kernel do - context = Keyword.update!(context, :import, fn(_) -> ElixirScript.Kernel end) - else - context - end - - {name, context, parms} - (x) -> - x - end) - 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/handle_output.ex b/lib/elixir_script/passes/handle_output.ex deleted file mode 100644 index c9f640fb..00000000 --- a/lib/elixir_script/passes/handle_output.ex +++ /dev/null @@ -1,81 +0,0 @@ -defmodule ElixirScript.Passes.HandleOutput do - @moduledoc false - alias ElixirScript.Translator.State - - def execute(compiler_data, opts) do - - if Map.get(opts, :std_lib, false) do - State.set_module_data(compiler_data.data) - new_std_state = State.serialize() - stdlib_state_path = Path.join([File.cwd!(), "lib", "elixir_script", "translator", "stdlib_state.bin"]) - File.write!(stdlib_state_path, new_std_state) - State.stop() - else - State.stop() - out(compiler_data, opts) - end - end - - defp out(compiler_output, %{import_standard_libs: false} = compiler_opts) do - data = Enum.filter(compiler_output.data, fn({m, d}) -> d.app != :elixir end) - - out(%{ compiler_output | data: data }, Map.delete(compiler_opts, :import_standard_libs)) - end - - defp out(compiler_output, %{output: nil} = compiler_opts) do - compiler_output - |> remove_load_only - |> process_include_path(compiler_opts) - end - - defp out(compiler_output, %{output: :stdout} = compiler_opts) do - compiler_output - |> remove_load_only - |> process_include_path(compiler_opts) - |> Enum.each(fn - {_, code, _} -> IO.write(code) - code -> IO.write(code) - end) - end - - defp out(compiler_output, %{output: output_path, core_path: _} = compiler_opts) do - if Map.get(compiler_opts, :std_lib, false) == false do - ElixirScript.copy_stdlib_to_destination(output_path) - end - - compiler_output = remove_load_only(compiler_output) - - compiler_output.data - |> Enum.each(fn({_, x}) -> - write_to_file(x, output_path) - end) - end - - defp write_to_file(module_data, destination) do - file_name = Path.join([destination, to_string(module_data.app), module_data.javascript_name]) - - if !File.exists?(Path.dirname(file_name)) do - File.mkdir_p!(Path.dirname(file_name)) - end - - File.write!(file_name, module_data.javascript_code) - end - - defp process_include_path(compiler_output, compiler_opts) do - compiler_output.data - |> Enum.map(fn - {_, module_data} -> - case compiler_opts.include_path do - true -> - { module_data.javascript_name, module_data.javascript_code, module_data.app } - false -> - module_data.javascript_code - end - end) - end - - defp remove_load_only(compiler_output) do - data = Enum.filter(compiler_output.data, fn({m, d}) -> Map.get(d, :load_only, false) == false end) - %{ compiler_output | data: data } - end -end diff --git a/lib/elixir_script/passes/init.ex b/lib/elixir_script/passes/init.ex deleted file mode 100644 index 7f30662b..00000000 --- a/lib/elixir_script/passes/init.ex +++ /dev/null @@ -1,10 +0,0 @@ -defmodule ElixirScript.Passes.Init do - @moduledoc false - alias ElixirScript.Translator.State - - def execute(compiler_data, opts) do - State.start_link(opts, []) - compiler_data - end - -end diff --git a/lib/elixir_script/passes/java_script_ast.ex b/lib/elixir_script/passes/java_script_ast.ex deleted file mode 100644 index 920dfb97..00000000 --- a/lib/elixir_script/passes/java_script_ast.ex +++ /dev/null @@ -1,52 +0,0 @@ -defmodule ElixirScript.Passes.JavaScriptAST do - @moduledoc false - alias ElixirScript.Translator.Utils - alias ElixirScript.Translator.State - - def execute(compiler_data, opts) do - - State.set_module_data(compiler_data.data) - State.set_loaded_modules(Map.get(compiler_data, :loaded_modules, [])) - - parent = self - - data = State.get_module_data() - |> Enum.map(fn({module_name, module_data}) -> - - spawn_link fn -> - module_data = compile(module_data, opts) - result = {module_name, module_data} - send parent, {self, result } - end - - end) - |> Enum.map(fn pid -> - receive do - {^pid, result} -> - result - end - end) - - %{ compiler_data | data: data } - end - - defp compile(%{load_only: true} = module_data, opts) do - module_data - end - - defp compile(module_data, opts) do - - env = ElixirScript.Translator.LexicalScope.module_scope(module_data.name, Utils.name_to_js_file_name(module_data.name) <> ".js", opts.env) - - module = case module_data.type do - :module -> - ElixirScript.Translator.Defmodule.make_module(module_data.name, module_data.ast, env) - :protocol -> - ElixirScript.Translator.Defprotocol.make(module_data.name, module_data.functions, env) - :impl -> - ElixirScript.Translator.Defimpl.make(module_data.name, module_data.for, module_data.ast, env) - end - - Map.put(module_data, :javascript_ast, module.body) - end -end diff --git a/lib/elixir_script/passes/java_script_code.ex b/lib/elixir_script/passes/java_script_code.ex deleted file mode 100644 index 8d8e00de..00000000 --- a/lib/elixir_script/passes/java_script_code.ex +++ /dev/null @@ -1,56 +0,0 @@ -defmodule ElixirScript.Passes.JavaScriptCode do - @moduledoc false - alias ESTree.Tools.{ Builder, Generator } - - def execute(compiler_data, _) do - parent = self - - data = Enum.map(compiler_data.data, fn({module_name, module_data}) -> - - spawn_link fn -> - module_data = compile(module_data) - result = {module_name, module_data} - send parent, {self, result } - end - - end) - |> Enum.map(fn pid -> - receive do - {^pid, result} -> - result - end - end) - - %{ compiler_data | data: data } - end - - defp compile(%{load_only: true} = module_data) do - module_data - end - - - defp compile(module_data) do - js_ast = Builder.program(module_data.javascript_ast) - - js_code = js_ast - |> prepare_js_ast - |> Generator.generate - - Map.put(module_data, :javascript_code, js_code) - end - - defp prepare_js_ast(js_ast) do - case js_ast do - modules when is_list(modules) -> - modules - |> Enum.reduce([], &(&2 ++ &1.body)) - |> Builder.program - %ElixirScript.Translator.Group{ body: body } -> - Builder.program(body) - %ElixirScript.Translator.Empty{ } -> - Builder.program([]) - _ -> - js_ast - end - end -end diff --git a/lib/elixir_script/passes/java_script_name.ex b/lib/elixir_script/passes/java_script_name.ex deleted file mode 100644 index 07a873ff..00000000 --- a/lib/elixir_script/passes/java_script_name.ex +++ /dev/null @@ -1,14 +0,0 @@ -defmodule ElixirScript.Passes.JavaScriptName do - @moduledoc false - alias ElixirScript.Translator.Utils - - def execute(compiler_data, _) do - data = Enum.map(compiler_data.data, fn({module_name, module_data}) -> - js_name = Utils.name_to_js_file_name(module_name) <> ".js" - module_data = Map.put(module_data, :javascript_name, js_name) - {module_name, module_data} - end) - - %{ compiler_data | data: data } - end -end diff --git a/lib/elixir_script/passes/load_modules.ex b/lib/elixir_script/passes/load_modules.ex deleted file mode 100644 index fe1e9704..00000000 --- a/lib/elixir_script/passes/load_modules.ex +++ /dev/null @@ -1,20 +0,0 @@ -defmodule ElixirScript.Passes.LoadModules do - @moduledoc false - def execute(compiler_data, _) do - - ex_files = Enum.map(compiler_data.data, fn - { _, %{path: path} } -> path - %{path: path} -> path - end) - - loaded_modules = case Enum.reverse(ex_files) do - [] -> - [] - files -> - Kernel.ParallelCompiler.files(files) - end - - Map.put(compiler_data, :loaded_modules, loaded_modules) - 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/remove_unused.ex b/lib/elixir_script/passes/remove_unused.ex deleted file mode 100644 index 5182adcc..00000000 --- a/lib/elixir_script/passes/remove_unused.ex +++ /dev/null @@ -1,19 +0,0 @@ -defmodule ElixirScript.Passes.RemoveUnused do - @moduledoc false - def execute(compiler_data, opts) do - data = Enum.filter(compiler_data.data, fn({module_name, _}) -> - module_found_in_deps(module_name, compiler_data.data, [opts[:app]]) == true - end) - - Map.put(compiler_data, :data, data) - end - - defp module_found_in_deps(module_name, module_filepath_map, apps_not_to_touch) do - Enum.any?(module_filepath_map, fn - ({_, %{deps: deps, app: app, type: :module}}) -> - Enum.member?(apps_not_to_touch, app) or Enum.member?(deps, module_name) - _ -> - true - end) - 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/passes/write_cache.ex b/lib/elixir_script/passes/write_cache.ex deleted file mode 100644 index d52a6d20..00000000 --- a/lib/elixir_script/passes/write_cache.ex +++ /dev/null @@ -1,23 +0,0 @@ -defmodule ElixirScript.Passes.WriteCache do - @moduledoc false - alias ElixirScript.Compiler.Cache - alias ElixirScript.Translator.State - - def execute(compiler_data, opts) do - compiler_cache = Cache.get_compiler_cache(compiler_data.path, opts) - - State.set_module_data(compiler_data.data) - new_state = State.serialize() - new_file_stats = Enum.filter(compiler_data.data, fn({ _, data }) -> Map.has_key?(data, :path) end) - |> Enum.map(fn({_, data}) -> { data.path, data.stat } end) - |> Enum.uniq - - compiler_cache = %{compiler_cache | input_files: new_file_stats, state: new_state } - - Cache.write(compiler_data.path, compiler_cache) - - compiler_data - end - - -end diff --git a/lib/elixir_script/prelude/agent.ex b/lib/elixir_script/prelude/agent.ex deleted file mode 100644 index f1750335..00000000 --- a/lib/elixir_script/prelude/agent.ex +++ /dev/null @@ -1,54 +0,0 @@ -defmodule ElixirScript.Agent do - @moduledoc false - require JS - - def start(fun, options \\ []) do - pid = JS.new Elixir.Core.PID, [] - - name = if Elixir.Keyword.has_key?(options, :name) do - Elixir.Keyword.get(options, :name) - else - nil - end - - Elixir.Core.Store.create(pid, fun.(), name) - { :ok, pid } - end - - def start_link(fun, options \\ []) do - pid = JS.new Elixir.Core.PID, [] - - name = if Elixir.Keyword.has_key?(options, :name) do - Elixir.Keyword.get(options, :name) - else - nil - end - - Elixir.Core.Store.create(pid, fun.(), name) - { :ok, pid } - end - - def stop(agent) do - Elixir.Core.Store.remove(agent) - :ok - end - - def update(agent, fun) do - current_state = Elixir.Core.Store.read(agent) - Elixir.Core.Store.update(agent, fun.(current_state)) - :ok - end - - def get(agent, fun) do - current_state = Elixir.Core.Store.read(agent) - fun.(current_state) - end - - def get_and_update(agent, fun) do - current_state = Elixir.Core.Store.read(agent) - {val, new_state} = fun.(current_state) - Elixir.Core.Store.update(agent, new_state) - val - end - -end diff --git a/lib/elixir_script/prelude/atom.ex b/lib/elixir_script/prelude/atom.ex deleted file mode 100644 index f932c1d2..00000000 --- a/lib/elixir_script/prelude/atom.ex +++ /dev/null @@ -1,13 +0,0 @@ -defmodule ElixirScript.Atom do - @moduledoc false - import Kernel, except: [to_string: 1] - - def to_char_list(atom) do - to_string(atom).split("") - end - - def to_string(atom) do - Symbol.keyFor(atom) - end - -end diff --git a/lib/elixir_script/prelude/base.ex b/lib/elixir_script/prelude/base.ex deleted file mode 100644 index a3851f33..00000000 --- a/lib/elixir_script/prelude/base.ex +++ /dev/null @@ -1,20 +0,0 @@ -defmodule ElixirScript.Base do - @moduledoc false - - def encode64(data) do - Elixir.Core.b64EncodeUnicode(data) - end - - def decode64(data) do - if Elixir.Core.can_decode64(data) do - {:ok, decode64!(data) } - else - :error - end - end - - def decode64!(data) do - Elixir.Core.get_global().atob(data) - end - -end diff --git a/lib/elixir_script/prelude/bitwise.ex b/lib/elixir_script/prelude/bitwise.ex deleted file mode 100644 index 9775f36b..00000000 --- a/lib/elixir_script/prelude/bitwise.ex +++ /dev/null @@ -1,74 +0,0 @@ -defmodule ElixirScript.Bitwise do - @moduledoc false - defmacro bnot(expr) do - quote do - Elixir.Core.bnot(unquote(expr)) - end - end - - defmacro ~~~(expr) do - quote do - Elixir.Core.bnot(unquote(expr)) - end - end - - defmacro band(left, right) do - quote do - Elixir.Core.band(unquote(left), unquote(right)) - end - end - - defmacro left &&& right do - quote do - Elixir.Core.band(unquote(left), unquote(right)) - end - end - - defmacro bor(left, right) do - quote do - Elixir.Core.bor(unquote(left), unquote(right)) - end - end - - defmacro left ||| right do - quote do - Elixir.Core.bor(unquote(left), unquote(right)) - end - end - - defmacro bxor(left, right) do - quote do - Elixir.Core.bxor(unquote(left), unquote(right)) - end - end - - defmacro left ^^^ right do - quote do - Elixir.Core.bxor(unquote(left), unquote(right)) - end - end - - defmacro bsl(left, right) do - quote do - Elixir.Core.bsl(unquote(left), unquote(right)) - end - end - - defmacro left <<< right do - quote do - Elixir.Core.bsl(unquote(left), unquote(right)) - end - end - - defmacro bsr(left, right) do - quote do - Elixir.Core.bsr(unquote(left), unquote(right)) - end - end - - defmacro left >>> right do - quote do - Elixir.Core.bsr(unquote(left), unquote(right)) - end - end -end diff --git a/lib/elixir_script/prelude/collectable.ex b/lib/elixir_script/prelude/collectable.ex deleted file mode 100644 index 339b17ce..00000000 --- a/lib/elixir_script/prelude/collectable.ex +++ /dev/null @@ -1,4 +0,0 @@ -defprotocol ElixirScript.Collectable do - @moduledoc false - def into(collectable) -end diff --git a/lib/elixir_script/prelude/enumerable.ex b/lib/elixir_script/prelude/enumerable.ex deleted file mode 100644 index 3fbba469..00000000 --- a/lib/elixir_script/prelude/enumerable.ex +++ /dev/null @@ -1,6 +0,0 @@ -defprotocol ElixirScript.Enumerable do - @moduledoc false - def reduce(enumerable, acc, fun) - def member?(enumerable, element) - def count(enumerable) -end diff --git a/lib/elixir_script/prelude/integer.ex b/lib/elixir_script/prelude/integer.ex deleted file mode 100644 index 5ef4415e..00000000 --- a/lib/elixir_script/prelude/integer.ex +++ /dev/null @@ -1,33 +0,0 @@ -defmodule ElixirScript.Integer do - @moduledoc false - def is_even(number) do - rem(number, 2) == 0 - end - - def is_odd(number) do - rem(number, 2) != 0 - end - - def to_char_list(number) do - to_char_list(number, 10) - end - - def to_char_list(number, base) do - number.toString(base).split('') - end - - def parse(bin) do - result = Elixir.Core.Functions.get_global().parseInt(bin) - - if Elixir.Core.Functions.get_global().isNaN(result) do - :error - else - case bin.indexOf(".") do - index_of_dot when index_of_dot < 0 -> - {result, ""} - index_of_dot -> - {result, bin.substring(index_of_dot)} - end - end - end -end diff --git a/lib/elixir_script/prelude/js.ex b/lib/elixir_script/prelude/js.ex deleted file mode 100644 index 35c9508a..00000000 --- a/lib/elixir_script/prelude/js.ex +++ /dev/null @@ -1,129 +0,0 @@ -defmodule 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, or updating - an existing one. - """ - - @doc """ - Creates new JavaScript objects. - - ex: - JS.new User, ["first_name", "last_name"] - """ - defmacro new(module, params) do - end - - - @doc """ - Updates an existing JavaScript object. - - ex: - JS.update elem, %{"width" => 100} - """ - defmacro update(object, map) do - end - - - @doc """ - Imports a JavaScript module. - - Elixir modules can use the normal `import`, `alias` and `require`, - but JavaScript modules work differently and have to be imported - using this. - - If `default` is set to true then it is treated as a default import. - Otherwise it is treated as a namespace import. - - ex: - JS.import A, "a" #translates to "import A from 'a'" - - JS.import A, "a", default: false #translates to "import * as A from 'a'" - """ - defmacro import(module, from, opts \\ [default: true]) do - end - - @doc """ - Returns the type of the given value - """ - defmacro typeof(value) do - end - - @doc """ - Determines if value is an instance of type. - """ - defmacro instanceof(value, type) do - end - - @doc """ - Throws the term given - """ - defmacro throw(term) do - end - - - @doc """ - Returns a reference to the global JavaScript object. - - In browsers this would be window or self. - In node this would be the global object. - """ - def global() do - Elixir.Core.Functions.get_global() - end - - @doc """ - Defines a generator. This is compiled into a generator function in JavaScript. - defgen and defgenp are currently the only ways to use process in Elixirscript right now. - """ - defmacro defgen(call, expr \\ nil) do - quote do - def unquote(call), unquote(expr) - end - end - - @doc """ - Defines a private generator. This is compiled into a generator function in JavaScript. - """ - defmacro defgenp(call, expr \\ nil) do - quote do - defp unquote(call), unquote(expr) - end - end - - @doc """ - Determines if term is a generator - """ - def is_generator(term) do - term.constructor.name === "GeneratorFunction" - end - - @doc """ - Yields the current generator function - """ - defmacro yield() do - end - - @doc """ - Yields the current generator function with the given term - """ - defmacro yield(term) do - end - - @doc """ - Yields control to the given generator - """ - defmacro yield_to(gen) do - end - - @doc """ - Provides a convenient way to create a string-based map. - - 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"}` - """ - defmacro object(args) do - end -end diff --git a/lib/elixir_script/prelude/kernel.ex b/lib/elixir_script/prelude/kernel.ex deleted file mode 100644 index 60128d88..00000000 --- a/lib/elixir_script/prelude/kernel.ex +++ /dev/null @@ -1,233 +0,0 @@ -defmodule ElixirScript.Kernel do - @moduledoc false - import Kernel, only: [defmodule: 2, def: 1, def: 2, defp: 2, - defmacro: 1, defmacro: 2, defmacrop: 2, ||: 2, !: 1, ++: 2, in: 2, &&: 2, ===: 2, @: 1] - require JS - - - defmacro if(condition, clauses) do - build_if(condition, clauses) - end - - defp build_if(condition, do: do_clause) do - build_if(condition, do: do_clause, else: nil) - end - - defp build_if(condition, do: do_clause, else: else_clause) do - quote do - case unquote(condition) do - x when x in [false, nil] -> - unquote(else_clause) - _ -> - unquote(do_clause) - end - end - end - - defmacro unless(condition, clauses) do - build_unless(condition, clauses) - end - - defp build_unless(condition, do: do_clause) do - build_unless(condition, do: do_clause, else: nil) - end - - defp build_unless(condition, do: do_clause, else: else_clause) do - quote do - if(unquote(condition), do: unquote(else_clause), else: unquote(do_clause)) - end - end - - def abs(number) do - Math.abs(number) - end - - def apply(fun, args) do - Elixir.Core.Functions.apply(fun, args) - end - - def apply(module, fun, args) do - Elixir.Core.Functions.apply(module, Atom.to_string(fun), args) - end - - def binary_part(binary, start, len) do - binary.substring(start, len) - end - - def hd(list) do - list[0] - end - - def tl(list) do - list.slice(1) - end - - def is_atom(term) do - JS.typeof(term) === "symbol" - end - - def is_binary(term) do - JS.typeof(term) === "string" - end - - def is_bitstring(term) do - is_binary(term) || JS.instanceof(term, Elixir.Core.BitString) - end - - def is_boolean(term) do - JS.typeof(term) === "boolean" || JS.instanceof(term, Boolean) - end - - def is_float(term) do - is_number(term) && !Number.isInteger(term) - end - - def is_function(term) do - is_function(term, 0) - end - - def is_function(term, _) do - JS.typeof(term) === "function" || JS.instanceof(term, Function) - end - - def is_integer(term) do - Number.isInteger(term) - end - - def is_list(term) do - Array.isArray(term) - end - - def is_number(term) do - JS.typeof(term) === "number" || JS.instanceof(term, Number) - end - - def is_pid(term) do - JS.instanceof(term, Elixir.Core.PID) - end - - def is_tuple(term) do - JS.instanceof(term, Elixir.Core.Tuple) - end - - def is_map(term) do - JS.typeof(term) === "object" || JS.instanceof(term, Object) - end - - def is_port(_) do - false - end - - def is_reference(_) do - false - end - - def length(term) do - term.length - end - - def map_size(term) do - Object.keys(term).length - end - - def max(first, second) do - Math.max(first, second) - end - - def min(first, second) do - Math.min(first, second) - end - - def round(number) do - Math.round(number) - end - - def trunc(number) do - Math.floor(number) - end - - def tuple_size(tuple) do - Elixir.Core.Functions.size(tuple) - end - - def elem(tuple, index) do - Elixir.Core.Functions.apply(tuple, "get", [index]) - end - - def is_nil(term) do - term === nil - end - - def make_ref() do - Elixir.Core.processes.make_ref() - end - - def spawn(gen) do - Elixir.Core.processes.spawn(gen) - end - - def spawn(module, fun, args) do - Elixir.Core.processes.spawn(module, Atom.to_string(fun), args) - end - - def spawn_link(gen) do - Elixir.Core.processes.spawn_link(gen) - end - - def spawn_link(module, fun, args) do - Elixir.Core.processes.spawn_link(module, Atom.to_string(fun), args) - end - - def spawn_monitor(gen) do - Elixir.Core.processes.spawn_monitor(gen) - end - - def spawn_monitor(module, fun, args) do - Elixir.Core.processes.spawn_monitor(module, Atom.to_string(fun), args) - end - - def send(pid, message) do - Elixir.Core.processes.send(pid, message) - end - - def self() do - Elixir.Core.processes.pid() - end - - defmacro match?(left, right) do - quote do - case unquote(right) do - unquote(left) -> - true - _ -> - false - end - end - end - - defmacro to_string(arg) when Kernel.is_binary(arg) do - arg - end - - defmacro to_string(arg) do - quote do - String.Chars.to_string(unquote(arg)) - end - end - - defmacro left |> {fun, context, params} do - {fun, context, [left] ++ params } - end - - defmacro left in right do - quote do - Elixir.Core.Functions.contains(unquote(left), unquote(right)) - end - end - - defmacro first .. last do - quote do - %ElixirScript.Range{ first: unquote(first), last: unquote(last) } - end - end -end diff --git a/lib/elixir_script/prelude/keyword.ex b/lib/elixir_script/prelude/keyword.ex deleted file mode 100644 index c08c71b1..00000000 --- a/lib/elixir_script/prelude/keyword.ex +++ /dev/null @@ -1,42 +0,0 @@ -defmodule ElixirScript.Keyword do - @moduledoc false - - def has_key?(kw, key) do - do_has_key?(kw, key) - end - - defp do_has_key?([], _) do - false - end - - defp do_has_key?(kw, key) do - case hd(kw) do - {the_key, _} when the_key == key -> - true - _ -> - do_has_key?(tl(kw), key) - end - end - - def get(kw, key) do - get(kw, key, nil) - end - - def get(kw, key, default_value) do - case has_key?(kw, key) do - true -> - do_get(kw, key) - false -> - default_value - end - end - - defp do_get(kw, key) do - case hd(kw) do - { kw_key, value } when kw_key == key -> - value - _ -> - do_get(tl(kw), key) - end - end -end diff --git a/lib/elixir_script/prelude/list.ex b/lib/elixir_script/prelude/list.ex deleted file mode 100644 index 962c224c..00000000 --- a/lib/elixir_script/prelude/list.ex +++ /dev/null @@ -1,252 +0,0 @@ -defmodule ElixirScript.List do - @moduledoc false - require JS - - def duplicate(data, size) do - do_duplicate(data, size, []) - end - - defp do_duplicate(_, 0, list) do - list - end - - defp do_duplicate(data, size, list) do - do_duplicate(data, size - 1, list ++ [data]) - end - - def to_tuple(list) do - JS.new(Elixir.Core.Tuple, list) - end - - def wrap(list) when is_list(list), do: list - def wrap(nil), do: [] - def wrap(term), do: [term] - - def append(list, term) do - concat(list, [term]) - end - - def prepend(list, term) do - concat([term], list) - end - - def concat(list_a, list_b) do - list_a.concat(list_b) - end - - def first(list) do - list[0] - end - - def last(list) do - list[length(list) - 1] - end - - def delete(list, item) do - do_delete(list, item, 0, []) - end - - defp do_delete(list, item, current_index, new_list) do - if current_index == length(list) do - new_list - else - new_list = case list[current_index] do - ^item -> - new_list - _ -> - new_list ++ [list[current_index]] - end - - do_delete(list, item, current_index + 1, new_list) - end - end - - def delete_at(list, index) do - do_delete_at(list, index, 0, []) - end - - defp do_delete_at(list, index, current_index, new_list) do - if current_index == length(list) do - new_list - else - new_list = case current_index == index do - true -> - new_list - _ -> - new_list ++ [list[current_index]] - end - - do_delete_at(list, index, current_index + 1, new_list) - end - end - - def insert_at(list, index, value) do - do_insert_at(list, index, value, 0, []) - end - - defp do_insert_at(list, index, value, current_index, new_list) do - if current_index == length(list) do - new_list - else - new_list = case current_index == index do - true -> - new_list ++ [value, list[current_index]] - _ -> - new_list ++ [list[current_index]] - end - - do_insert_at(list, index, value, current_index + 1, new_list) - end - end - - def replace_at(list, index, value) do - do_replace_at(list, index, value, 0, []) - end - - defp do_replace_at(list, index, value, current_index, new_list) do - if current_index == length(list) do - new_list - else - new_list = case current_index == index do - true -> - new_list ++ [value] - _ -> - new_list ++ [list[current_index]] - end - - do_replace_at(list, index, value, current_index + 1, new_list) - end - end - - - def update_at(list, index, func) do - do_update_at(list, index, func, 0, []) - end - - defp do_update_at(list, index, func, current_index, new_list) do - if current_index == length(list) do - new_list - else - new_list = case current_index == index do - true -> - new_list ++ [func.(list[current_index])] - _ -> - new_list ++ [list[current_index]] - end - - do_update_at(list, index, func, current_index + 1, new_list) - end - end - - - def foldl(list, acc, func) do - do_foldl(list, acc, func, []) - end - - def foldr(list, acc, func) do - do_foldl(list.concat([]).reverse(), acc, func, []) - end - - defp do_foldl([], acc, _, new_list) do - { acc, new_list } - end - - defp do_foldl(list, acc, func, new_list) do - { acc, value } = func.(hd(list), acc) - do_foldl(tl(list), acc, func, new_list ++ [value]) - end - - def flatten(list) do - do_flatten(list, []) - end - - def flatten(list, tail) do - do_flatten(list, []) ++ tail - end - - defp do_flatten([], flattened_list) do - flattened_list - end - - defp do_flatten(list, flattened_list) do - flattened_list = case hd(list) do - l when is_list(l) -> - flattened_list ++ do_flatten(l, []) - item -> - flattened_list ++ [item] - end - - do_flatten(tl(list), flattened_list) - end - - - def keydelete(list, key, position) do - do_keydelete(list, key, position, []) - end - - defp do_keydelete([], _, _, new_list) do - new_list - end - - defp do_keydelete(list, key, position, new_list) do - current_value = hd(list) - - new_list = if elem(current_value, position) == key do - new_list - else - new_list ++ [current_value] - end - - do_keydelete(tl(list), key, position, new_list) - end - - def keyfind(list, key, position) do - do_keyfind(list, key, position, nil) - end - - def keyfind(list, key, position, default) do - do_keyfind(list, key, position, default) - end - - defp do_keyfind([], _, _, default) do - default - end - - defp do_keyfind(list, key, position, default) do - current_value = hd(list) - - if elem(current_value, position) == key do - current_value - else - do_keyfind(tl(list), key, position, default) - end - end - - def keymember?(list, key, position) do - keyfind(list, key, position) != nil - end - - def keyreplace(list, key, position, new_tuple) do - do_keyreplace(list, key, position, [], new_tuple) - end - - defp do_keyreplace([], _, _, new_list, _) do - new_list - end - - defp do_keyreplace(list, key, position, new_list, new_tuple) do - current_value = hd(list) - - new_list = if elem(current_value, position) == key do - new_list ++ [new_tuple] - else - new_list ++ [current_value] - end - - do_keyreplace(tl(list), key, position, new_list, new_tuple) - end - - def zip(list_of_lists) do - Elixir.Core.Functions.zip(list_of_lists) - end -end diff --git a/lib/elixir_script/prelude/macro/env.ex b/lib/elixir_script/prelude/macro/env.ex deleted file mode 100644 index 1528e2c8..00000000 --- a/lib/elixir_script/prelude/macro/env.ex +++ /dev/null @@ -1,37 +0,0 @@ -defmodule ElixirScript.Macro.Env do - @moduledoc false - - @type t :: %ElixirScript.Macro.Env{ - module: atom, - file: binary, - line: non_neg_integer, - function: { atom, non_neg_integer } | nil, - context: :match | :guard | :generator | nil, - aliases: [{atom, atom}], - requires: [atom], - functions: [{atom, [{ atom, non_neg_integer }]}], - macros: [{atom, [{ atom, non_neg_integer }]}], - macro_aliases: [{atom, {integer, atom}}], - context_modules: [atom], - vars: [{atom, atom | non_neg_integer}], - export_vars: [{atom, atom | non_neg_integer}] | nil, - lexical_tracker: nil - } - - defstruct [ - module: nil, - file: nil, - line: 0, - function: nil, - context: nil, - aliases: [], - requires: [], - functions: [], - macros: [], - macro_aliases: [], - context_modules: [], - vars: [], - export_vars: nil, - lexical_tracker: nil - ] -end diff --git a/lib/elixir_script/prelude/map.ex b/lib/elixir_script/prelude/map.ex deleted file mode 100644 index 6edcf85c..00000000 --- a/lib/elixir_script/prelude/map.ex +++ /dev/null @@ -1,203 +0,0 @@ -defmodule ElixirScript.Map do - @moduledoc false - - def new() do - %{} - end - - def keys(map) do - Elixir.Core.Functions.get_object_keys(map) - end - - def size(map) do - keys(map).length - end - - def to_list(map) do - do_to_list(map, []) - end - - def do_to_list(map, list) do - case size(map) do - 0 -> - list - _ -> - key = hd(keys(map)) - value = map[key] - do_to_list(Map.delete(map, key), list ++ [{key, value}]) - end - end - - def values(map) do - Object.values(map) - end - - def from_struct(struct) do - struct - |> Elixir.Core.Functions.class_to_obj - |> delete(:__struct__) - end - - def delete(map, key) do - map - |> Elixir.Core.Functions.delete_property_from_map(key) - end - - def equal?(map1, map2) do - map1 === map2 - end - - def fetch!(map, key) do - case key in keys(map) do - true -> - map[key] - false -> - raise "#{key} not found in map" - end - end - - def fetch(map, key) do - case key in keys(map) do - true -> - { :ok, map[key] } - false -> - :error - end - end - - def has_key?(map, key) do - key in keys(map) - end - - def merge(map1, map2) do - Elixir.Core.SpecialForms.map_update(map1, map2) - end - - def split(map, keys) do - do_split(map, keys, { %{}, %{} }) - end - - defp do_split(_, [], split_tuple) do - split_tuple - end - - defp do_split(map, keys, { key_map, non_key_map }) do - key = hd(keys) - - new_split_tuple = case key in keys(map) do - true -> - { Map.put(key_map, key, map[key]), non_key_map } - false -> - { key_map, Map.put(non_key_map, key, map[key]) } - end - - do_split(map, tl(keys), new_split_tuple) - end - - def take(map, keys) do - {key_map, _} = split(map, keys) - key_map - end - - def drop(map, keys) do - {_, non_key_map} = split(map, keys) - non_key_map - end - - def put_new(map, key, value) do - case key in keys(map) do - true -> - map - false -> - Map.put(map, key, value) - end - end - - def put_new_lazy(map, key, func) do - case key in keys(map) do - true -> - map - false -> - Map.put(map, key, func.()) - end - end - - def put(map, key, value) do - Elixir.Core.Functions.add_property_to_map(map, key, value) - end - - def get(map, key) do - get(map, key, nil) - end - - def get(map, key, default_value) do - case key in keys(map) do - true -> - map[key] - false -> - default_value - end - end - - def get_lazy(map, key, func) do - case key in keys(map) do - true -> - func.(map[key]) - false -> - func.() - end - end - - def get_and_update(map, key, func) do - case key in keys(map) do - true -> - { nil, map } - false -> - new_value = func.(map[key]) - { new_value, Map.put(map, key, new_value) } - end - end - - def pop(map, key) do - pop(map, key, nil) - end - - def pop(map, key, default_value) do - case key in keys(map) do - true -> - { map[key], Map.delete(map, key) } - false -> - { default_value, map } - end - end - - def pop_lazy(map, key, func) do - case key in keys(map) do - true -> - { func.(map[key]), Map.delete(map, key) } - false -> - { func.(), map } - end - end - - - def update!(map, key, func) do - case key in keys(map) do - true -> - Map.put(map, key, func.(map[key])) - false -> - raise "#{key} not found in map" - end - end - - - def update(map, key, initial, func) do - case key in keys(map) do - true -> - Map.put(map, key, func.(map[key])) - false -> - Map.put(map, key, initial) - end - end - -end diff --git a/lib/elixir_script/prelude/map_set.ex b/lib/elixir_script/prelude/map_set.ex deleted file mode 100644 index a22fe57b..00000000 --- a/lib/elixir_script/prelude/map_set.ex +++ /dev/null @@ -1,100 +0,0 @@ -defmodule ElixirScript.MapSet do - @moduledoc false - defstruct set: [] - - def new() do - %MapSet{} - end - - def size(set) do - length(set.set) - end - - def to_list(set) do - set.set - end - - def delete(set, term) do - %{ set | set: Elixir.List.remove(set.set, term) } - end - - def put(set, term) do - case member?(set, term) do - false -> - %{ set | set: set.set ++ term } - true -> - set - end - end - - def member?(set, term) do - set.set.indexOf(term) >= 0 - end - - def equal?(set1, set2) do - set1 === set2 - end - - def difference(set1, set2) do - do_difference(to_list(set1), set2, new()) - end - - def do_difference([], _, difference_set) do - difference_set - end - - def do_difference(set1_list, set2, difference_set) do - term = hd(set1_list) - case member?(set2, term) do - true -> - do_difference(tl(set1_list), set2, difference_set) - false -> - do_difference(tl(set1_list), set2, %{ difference_set | set: difference_set.set ++ [term]}) - end - end - - def intersection(set1, set2) do - do_intersection(to_list(set1), set2, new()) - end - - def do_intersection([], _, intersection_set) do - intersection_set - end - - def do_intersection(set1_list, set2, intersection_set) do - term = hd(set1_list) - case member?(set2, term) do - false -> - do_intersection(tl(set1_list), set2, intersection_set) - true -> - do_intersection(tl(set1_list), set2, %{ intersection_set | set: intersection_set.set ++ [term]}) - end - end - - def union(set1, set2) do - %{ set1 | set: set1.set ++ set2.set} - end - - def disjoint?(set1, set2) do - size(intersection(set1, set2)) == 0 - end - - def subset?(set1, set2) do - do_subset?(to_list(set1), set2) - end - - def do_subset?([], _) do - true - end - - def do_subset?(set1_list, set2) do - term = hd(set1_list) - case member?(set2, term) do - false -> - false - true -> - do_subset?(tl(set1_list), set2) - end - end - -end diff --git a/lib/elixir_script/prelude/module.ex b/lib/elixir_script/prelude/module.ex deleted file mode 100644 index 44c1acc4..00000000 --- a/lib/elixir_script/prelude/module.ex +++ /dev/null @@ -1,9 +0,0 @@ -defmodule ElixirScript.Module do - @moduledoc false - - defstruct name: nil, - functions: Keyword.new, private_functions: Keyword.new, - body: nil, js_imports: [], module_refs: [], type: :module, - impls: HashDict.new, impl_type: nil, app_name: nil - -end diff --git a/lib/elixir_script/prelude/process.ex b/lib/elixir_script/prelude/process.ex deleted file mode 100644 index caeed8f3..00000000 --- a/lib/elixir_script/prelude/process.ex +++ /dev/null @@ -1,90 +0,0 @@ -defmodule ElixirScript.Process do - @moduledoc false - def alive?(pid) do - Elixir.Core.processes.is_alive(pid) - end - - def delete(key) do - Elixir.Core.processes.erase(key) - end - - def exit(pid, reason) do - Elixir.Core.processes.exit(pid, reason) - end - - def flag(flag, value) do - Elixir.Core.processes.process_flag(flag, value) - end - - def flag(pid, flag, value) do - Elixir.Core.processes.process_flag(pid, flag, value) - end - - def get() do - Elixir.Core.processes.get_process_dict() - end - - def get(key, default \\ nil) do - Elixir.Core.processes.get(key, default) - end - - def get_keys() do - Elixir.Core.processes.get_keys() - end - - def get_keys(value) do - Elixir.Core.processes.get_keys(value) - end - - def put(key, value) do - Elixir.Core.processes.put(key, value) - end - - def link(pid) do - Elixir.Core.processes.link(pid) - end - - def unlink(pid) do - Elixir.Core.processes.unlink(pid) - end - - def monitor(item) do - Elixir.Core.processes.monitor(item) - end - - def demonitor(monitor_ref) do - Elixir.Core.processes.demonitor(monitor_ref) - end - - def register(pid, name) when is_atom(name) do - Elixir.Core.processes.register(name, pid) - end - - def registered() do - Elixir.Core.processes.registered() - end - - def whereis(name) do - Elixir.Core.processes.whereis(name) - end - - def unregister(name) do - Elixir.Core.processes.unregister(name) - end - - def list() do - Elixir.Core.processes.list() - end - - def sleep(duration) when is_integer(duration) do - Elixir.Core.processes.sleep(duration) - end - - def sleep(:infinity) do - Elixir.Core.processes.sleep(:infinity) - end - - def send(dest, msg, _) do - Elixir.Core.processes.send(dest, msg) - end -end diff --git a/lib/elixir_script/prelude/range.ex b/lib/elixir_script/prelude/range.ex deleted file mode 100644 index 9ae8bffe..00000000 --- a/lib/elixir_script/prelude/range.ex +++ /dev/null @@ -1,12 +0,0 @@ -defmodule ElixirScript.Range do - @moduledoc false - defstruct first: nil, last: nil - - def new(first, last) do - %ElixirScript.Range{first: first, last: last} - end - - def range?(%ElixirScript.Range{}), do: true - def range?(_), do: false - -end diff --git a/lib/elixir_script/prelude/string/chars.ex b/lib/elixir_script/prelude/string/chars.ex deleted file mode 100644 index ae5d0227..00000000 --- a/lib/elixir_script/prelude/string/chars.ex +++ /dev/null @@ -1,48 +0,0 @@ -defprotocol ElixirScript.String.Chars do - @moduledoc false - def to_string(item) -end - -defimpl ElixirScript.String.Chars, for: Atom do - def to_string(nil) do - "" - end - - def to_string(atom) do - Atom.to_string(atom) - end -end - -defimpl ElixirScript.String.Chars, for: BitString do - def to_string(thing) when is_binary(thing) do - thing - end - - def to_string(thing) do - thing.toString() - end -end - -defimpl ElixirScript.String.Chars, for: List do - def to_string(list) do - list.toString() - end -end - -defimpl ElixirScript.String.Chars, for: Tuple do - def to_string(tuple) do - tuple.toString() - end -end - -defimpl ElixirScript.String.Chars, for: Integer do - def to_string(integer) do - integer.toString() - end -end - -defimpl ElixirScript.String.Chars, for: Float do - def to_string(float) do - float.toString() - end -end diff --git a/lib/elixir_script/prelude/tuple.ex b/lib/elixir_script/prelude/tuple.ex deleted file mode 100644 index cdd7b765..00000000 --- a/lib/elixir_script/prelude/tuple.ex +++ /dev/null @@ -1,63 +0,0 @@ -defmodule ElixirScript.Tuple do - @moduledoc false - require JS - - def duplicate(data, size) do - JS.new(Elixir.Core.Tuple, do_duplicate(data, size, [])) - end - - defp do_duplicate(_, 0, list) do - list - end - - defp do_duplicate(data, size, list) do - do_duplicate(data, size - 1, list ++ [data]) - end - - def to_list(tuple) do - tuple["value"] - end - - def insert_at(tuple, index, value) do - JS.new(Elixir.Core.Tuple, do_insert_at(tuple, index, value, 0, [])) - end - - defp do_insert_at(tuple, index, value, current_index, list) do - if current_index == length(tuple) do - list - else - list = case index == current_index do - true -> - list ++ [value, tuple.get(current_index)] - false -> - list ++ [tuple.get(current_index)] - end - - do_insert_at(tuple, index, value, current_index + 1, list) - end - end - - def delete_at(tuple, index) do - JS.new(Elixir.Core.Tuple, do_delete_at(tuple, index, 0, [])) - end - - defp do_delete_at(tuple, index, current_index, list) do - if current_index == length(tuple) do - list - else - list = case index == current_index do - true -> - list - false -> - list ++ [tuple.get(current_index)] - end - - do_delete_at(tuple, index, current_index + 1, list) - end - end - - def append(tuple, value) do - JS.new(Elixir.Core.Tuple, to_list(tuple) ++ [value]) - end - -end diff --git a/lib/elixir_script/state.ex b/lib/elixir_script/state.ex new file mode 100644 index 00000000..37d0473f --- /dev/null +++ b/lib/elixir_script/state.ex @@ -0,0 +1,175 @@ +defmodule ElixirScript.State do + @moduledoc false + + # 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 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 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 list_javascript_modules(pid) do + Agent.get(pid, fn state -> + state.js_modules + |> Enum.map(fn {module, _name, _path} -> + module + end) + end) + end + + def js_modules(pid) do + Agent.get(pid, fn state -> + state.js_modules + end) + end + + 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_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 + + def remove_unused_functions(pid) do + Agent.get(pid, fn state -> + state.compiler_opts.remove_unused_functions + end) + end + + 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 + + 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 71095ac3..00000000 --- a/lib/elixir_script/translator.ex +++ /dev/null @@ -1,602 +0,0 @@ -defmodule ElixirScript.Translator do - @moduledoc false - alias ElixirScript.Translator.Primitive - alias ElixirScript.Translator.Identifier - alias ElixirScript.Translator.Expression - alias ElixirScript.Translator.Match - alias ElixirScript.Translator.Map - alias ElixirScript.Translator.Function - alias ElixirScript.Translator.Call - alias ElixirScript.Translator.Def - alias ElixirScript.Translator.Capture - alias ElixirScript.Translator.Cond - alias ElixirScript.Translator.Case - alias ElixirScript.Translator.For - alias ElixirScript.Translator.Try - alias ElixirScript.Translator.With - alias ElixirScript.Translator.Block - alias ElixirScript.Translator.Struct - alias ElixirScript.Translator.Defmodule - alias ElixirScript.Translator.Utils - alias ElixirScript.Translator.Bitstring - alias ElixirScript.Translator.Quote - alias ElixirScript.Translator.Utils - alias ElixirScript.Translator.JS, as: JSLib - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator.Rewriter - alias ElixirScript.Translator.Receive - - # A list of erlang modules. These are rewritten into equivalent - # JavaScript functions using ElixirScript.Translator.Rewriter - @erlang_modules [ - :erlang, - :maps, - :lists, - :gen, - :elixir_errors, - :supervisor, - :application, - :code, - :elixir_utils, - :file - ] - - @module_attributes_to_ignore [ - :doc, :moduledoc, :type, :typep, :spec, - :opaque, :callback, :macrocallback, :after_compile, - :before_compile, :behaviour, :compile, :file, - :on_definition, :on_load, :dialyzer, :vsn, :external_resource - ] - - @function_types [:def, :defp, :defgen, :defgenp] - @generator_types [:defgen, :defgenp] - - - @doc """ - Translates the given Elixir AST to JavaScript AST. The given `env` is a `ElixirScript.Macro.Env` - used to track the variables, imports, aliases, and scopes like `Macro.Env`. The JavaScript AST and - the an updated `ElixirScript.Macro.Env` is returned - """ - @spec translate(term, ElixirScript.Macro.Env.t) :: { ESTree.Node.t, ElixirScript.Macro.Env.t } - def translate(ast, env) do - do_translate(ast, env) - end - - - @doc """ - Same as `translate/2`, but returns only the JavaScript AST - """ - @spec translate!(term, ElixirScript.Macro.Env.t) :: ESTree.Node.t - def translate!(ast, env) do - { js_ast, _ } = translate(ast, env) - js_ast - end - - defp do_translate(ast, env) when is_number(ast) or is_binary(ast) or is_boolean(ast) or is_nil(ast) do - { Primitive.make_literal(ast), env } - end - - defp do_translate(ast, env) when is_atom(ast) do - { Primitive.make_atom(ast), env } - end - - defp do_translate([ {:|, _, [left, right] } ], env) do - quoted = quote do - [unquote(left)].concat(unquote(right)) - end - - translate(quoted, env) - end - - defp do_translate(ast, env) when is_list(ast) do - Primitive.make_list(ast, env) - end - - defp do_translate({ one, two }, env) do - quoted = quote do - JS.new(Elixir.Core.Tuple, [unquote(one), unquote(two)]) - end - - translate(quoted, env) - end - - defp do_translate({operator, _, [value]}, env) when operator in [:-, :!, :+] do - Expression.make_unary_expression(operator, value, env) - end - - defp do_translate({:not, _, [value]}, env) do - Expression.make_unary_expression(:!, 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({:and, _, [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({:div, _, [left, right]}, env) do - Expression.make_binary_expression(:/, left, right, env) - end - - defp do_translate({:rem, _, [left, right]}, env) do - Expression.make_binary_expression(:%, left, right, env) - end - - defp do_translate({:throw, _, [params]}, env) do - { result, env } = translate(params, env) - { JS.throw_statement(result), env } - end - - defp do_translate({:<>, context, [left, right]}, env) do - translate({:+, context, [left, right]}, env) - end - - defp do_translate({:++, _, [left, right]}, env) do - translate({{:., [], [left, :concat]}, [], [right]}, env) - end - - defp do_translate({:&, _, [number]}, env) when is_number(number) do - { Identifier.make_identifier(String.to_atom("__#{number}")), env } - end - - defp do_translate({:&, _, [{:/, _, [{{:., _, [module_name, function_name]}, _, []}, arity]}]}, env) do - { Capture.make_capture(module_name, function_name, arity, env), env } - end - - defp do_translate({:&, _, [{:/, _, [{function_name, _, _}, arity]}]}, env) do - { Capture.make_capture(function_name, arity, env), 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, _, _}]}, env) - when name in @module_attributes_to_ignore do - { %ElixirScript.Translator.Empty{}, env } - end - - defp do_translate({:@, _, [{:on_js_load, _, [value]}]}, env) do - Call.make_function_call(value, [], env) - end - - defp do_translate({:@, _, [{name, _, [value]}]}, env) do - { Defmodule.make_attribute(name, value, env), env } - end - - defp do_translate({:@, _, [{name, _, _}]}, env) do - { Identifier.make_identifier(name), env } - end - - defp do_translate({:%, _, [alias_info, data]}, env) do - { Struct.new_struct(alias_info, data, env), 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), 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({{:., _, [erlang_module, _]}, _, _} = erlang_function_call, env) when erlang_module in @erlang_modules do - Rewriter.rewrite(erlang_function_call) - |> translate(env) - end - - defp do_translate({{:., _, [Access, :get]}, _, [target, property]}, env) do - { Map.make_get_property(target, property, env), env } - end - - defp do_translate({{:., _, [function_name]}, _, params}, env) do - Call.make_function_call(function_name, params, env) - end - - defp do_translate({:., _, [module_name, function_name]} = ast, env) do - expanded_ast = Macro.expand(ast, env.env) - - if expanded_ast == ast do - module_name = create_module_name(module_name, env) - Call.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.env) - - if expanded_ast == ast do - module_name = create_module_name(module_name, env) - Call.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.env) - - if expanded_ast == ast do - module_name = create_module_name(module_name, env) - Call.make_function_call(module_name, params, env) - else - translate(expanded_ast, env) - end - end - - defp do_translate({{:., context1, [{:__aliases__, context2, [:Enum]}, function_name]}, context3, params }, env) do - translate({{:., context1, [{:__aliases__, context2, [:Elixir, :Enum]}, function_name]}, context3, params }, env) - end - - defp do_translate({{:., _, [{:__aliases__, _, [:JS]}, function_name]}, _, params }, env) do - JSLib.translate_js_function(function_name, params, env) - end - - defp do_translate({{:., _, [module_name, function_name]}, _, params } = ast, env) do - expanded_ast = Macro.expand(ast, env.env) - - if expanded_ast == ast do - module_name = create_module_name(module_name, env) - Call.make_function_call(module_name, function_name, params, env) - else - translate(expanded_ast, env) - end - end - - defp do_translate({:_, _, _}, env) do - { Identifier.make_identifier(:undefined), env } - end - - defp do_translate({:__aliases__, _, aliases} = ast, env) do - module_name = create_module_name(ast, env) - Call.make_module_name(module_name, env) - end - - defp do_translate({:__MODULE__, _, _ }, env) do - translate(env.module, env) - end - - defp do_translate({:__block__, _, expressions }, env) do - Block.make_block(expressions, env) - end - - defp do_translate({:__DIR__, _, _}, env) do - case env.file do - nil -> - { JS.identifier(:null), env } - filepath -> - { JS.literal(Path.dirname(filepath)), env } - end - end - - defp do_translate({:try, _, [ blocks ]}, env) do - Try.make_try(blocks, env) - end - - defp do_translate({:with, _, args }, env ) do - With.make_with(args, env) - end - - defp do_translate({:super, _, _expressions }, _ ) do - raise ElixirScript.Translator.UnsupportedError, "super" - end - - defp do_translate({:__CALLER__, _, _expressions }, env) do - env_to_translate = ElixirScript.Translator.LexicalScope.caller(env) - - quoted = Macro.escape(env_to_translate) - translate(quoted, env) - end - - defp do_translate({:__ENV__, _, _expressions }, env) do - env_to_translate = ElixirScript.Translator.LexicalScope.env(env) - - quoted = Macro.escape(env_to_translate) - translate(quoted, env) - end - - defp do_translate({:quote, _, [[do: expr]]}, env) do - { Quote.make_quote([], expr, env), env } - end - - defp do_translate({:quote, _, [opts, [do: expr]]}, env) do - { Quote.make_quote(opts, expr, env), env } - end - - defp do_translate({:import, _, [{{:., _, [{:__aliases__, _, head_import_name}, :{}]}, _, tail_imports }]}, env) do - env = Enum.reduce(tail_imports, env, fn({:__aliases__, context, name}, acc) -> - full_module_name = { :__aliases__, context, head_import_name ++ name } - - module_name = Utils.quoted_to_name(full_module_name) - ElixirScript.Translator.LexicalScope.add_import(acc, module_name) - end) - - { %ElixirScript.Translator.Empty{}, env } - end - - - defp do_translate({:import, _, [{:__aliases__, _, _} = module_name]}, env) do - module_name = Utils.quoted_to_name(module_name) - - env = ElixirScript.Translator.LexicalScope.add_import(env, module_name) - { %ElixirScript.Translator.Empty{}, env } - end - - defp do_translate({:import, _, [{:__aliases__, _, _} = module_name, options]}, env) do - module_name = Utils.quoted_to_name(module_name) - - env = ElixirScript.Translator.LexicalScope.add_import(env, module_name, options) - - { %ElixirScript.Translator.Empty{}, env } - end - - defp do_translate({:alias, _, [{{:., _, [{:__aliases__, _, head_alias_name}, :{}]}, _, tail_aliases }]}, env) do - env = Enum.reduce(tail_aliases, env, fn({:__aliases__, context, name}, acc) -> - full_module_name = { :__aliases__, context, head_alias_name ++ name } - - module_name = Utils.quoted_to_name(full_module_name) - alias_name = Utils.quoted_to_name({:__aliases__, [], [List.last(name)] }) - - ElixirScript.Translator.LexicalScope.add_alias(acc, module_name, alias_name) - end) - - { %ElixirScript.Translator.Empty{}, env } - end - - defp do_translate({:alias, _, [{:__aliases__, _, _} = module_name] }, env) do - {_, _, name} = module_name - name = [List.last(name)] - - module_name = Utils.quoted_to_name(module_name) - alias_name = Utils.quoted_to_name({:__aliases__, [], name }) - - env = ElixirScript.Translator.LexicalScope.add_alias(env, module_name, alias_name) - { %ElixirScript.Translator.Empty{}, env } - end - - defp do_translate({:alias, _, [{:__aliases__, _, _} = module_name, [as: {:__aliases__, _, _} = alias_name]]}, env) do - module_name = Utils.quoted_to_name(module_name) - alias_name = Utils.quoted_to_name(alias_name) - - env = ElixirScript.Translator.LexicalScope.add_alias(env, module_name, alias_name) - { %ElixirScript.Translator.Empty{}, env } - end - - defp do_translate({:require, _, [{{:., _, [{:__aliases__, _, head_require_name}, :{}]}, _, tail_requires }]}, env) do - env = Enum.reduce(tail_requires, env, fn({:__aliases__, context, name}, acc) -> - full_module_name = { :__aliases__, context, head_require_name ++ name } - - module_name = Utils.quoted_to_name(full_module_name) - ElixirScript.Translator.LexicalScope.add_require(acc, module_name) - end) - - { %ElixirScript.Translator.Empty{}, env } - end - - defp do_translate({:require, _, [{:__aliases__, _, _} = module_name] }, env) do - module_name = Utils.quoted_to_name(module_name) - env = ElixirScript.Translator.LexicalScope.add_require(env, module_name) - { %ElixirScript.Translator.Empty{}, env } - end - - defp do_translate({:require, _, [{:__aliases__, _, _} = module_name, [as: {:__aliases__, _, _} = alias_name]]}, env) do - module_name = Utils.quoted_to_name(module_name) - alias_name = Utils.quoted_to_name(alias_name) - - env = ElixirScript.Translator.LexicalScope.add_require(env, module_name, alias_name) - { %ElixirScript.Translator.Empty{}, env } - 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({:receive, _, [expressions] }, env) do - Receive.make_receive(expressions, env) - end - - defp do_translate({:{}, _, elements}, env) do - quoted = quote do - JS.new(Elixir.Core.Tuple, unquote(elements)) - end - - translate(quoted, env) - end - - defp do_translate({:=, _, [left, right]}, env) do - Match.make_match(left, right, env) - end - - defp do_translate({function, _, [{:when, _, [{name, _, _params} | _guards] }, _] } = ast, env) when function in @generator_types do - Def.process_function(name, [ast], %{ env | context: :generator}) - end - - defp do_translate({function, _, [{name, _, params}, _]} = ast, env) when function in @generator_types and is_atom(params) do - Def.process_function(name, [ast], %{ env | context: :generator}) - end - - defp do_translate({function, _, [{name, _, _params}, _]} = ast, env) when function in @generator_types do - Def.process_function(name, [ast], %{ env | context: :generator}) - end - - defp do_translate({function, _, [{:when, _, [{name, _, _params} | _guards] }, _] } = ast, env) when function in @function_types do - Def.process_function(name, [ast], env) - end - - defp do_translate({function, _, [{name, _, params}, _]} = ast, env) when function in @function_types and is_atom(params) do - Def.process_function(name, [ast], env) - end - - defp do_translate({function, _, [{name, _, _params}, _]} = ast, env) when function in @function_types do - Def.process_function(name, [ast], env) - end - - defp do_translate({:defstruct, _, attributes}, env) do - { Struct.make_defstruct(attributes, env), env } - end - - defp do_translate({:defexception, _, attributes}, env) do - { Struct.make_defexception(attributes, env), env } - end - - defp do_translate({:defmodule, _, [{:__aliases__, _, module_name_list}, [do: body]]}, env) do - { Defmodule.make_module(module_name_list, body, env), env } - end - - defp do_translate({:defprotocol, _, _}, env) do - { %ElixirScript.Translator.Empty{}, env } - end - - defp do_translate({:defmacro, _, _}, env) do - { %ElixirScript.Translator.Empty{}, env } - end - - defp do_translate({:defmacrop, _, _}, env) do - { %ElixirScript.Translator.Empty{}, env } - end - - defp do_translate({:defimpl, _, _}, env) do - { %ElixirScript.Translator.Empty{}, env } - end - - defp do_translate({:|, _, [item, list]}, env) do - quoted = quote do - [unquote(item)].concat(unquote(list)) - end - - translate(quoted, env) - end - - defp do_translate({:raise, _, [alias_info, attributes]}, env) when is_list(attributes) do - js_ast = JS.throw_statement( - Struct.new_struct(alias_info, {:%{}, [], attributes }, env) - ) - - { js_ast, env } - end - - defp do_translate({:raise, _, [alias_info, message]}, env) do - js_ast = JS.throw_statement( - Struct.new_struct(alias_info, {:%{}, [], [message: message] }, env) - ) - - { js_ast, env } - end - - defp do_translate({:raise, _, [message]}, env) do - js_ast = JS.throw_statement( - JS.object_expression( - [ - Map.make_property(translate!(:__struct__, env), translate!(:RuntimeError, env)), - Map.make_property(translate!(:__exception__, env), translate!(true, env)), - Map.make_property(translate!(:message, env), translate!(message, env)) - ] - ) - ) - - { js_ast, env } - end - - defp do_translate({name, _, params} = ast, env) when is_list(params) do - expanded_ast = Macro.expand(ast, env.env) - if expanded_ast == ast do - name_arity = {name, length(params)} - module = ElixirScript.Translator.State.get_module(env.module) - - cond do - name_arity in module.functions or name_arity in module.private_functions -> - Call.make_function_call(name, params, env) - ElixirScript.Translator.LexicalScope.find_module(env, name_arity) -> - imported_module_name = ElixirScript.Translator.LexicalScope.find_module(env, name_arity) - Call.make_function_call(imported_module_name, name, params, env) - true -> - Call.make_function_call(name, params, env) - end - - else - translate(expanded_ast, env) - end - end - - defp do_translate({ name, _, params }, env) when is_atom(params) do - cond do - ElixirScript.Translator.LexicalScope.has_var?(env, name) -> - { Identifier.make_identifier(name), env } - has_function?(env.module, {name, 0}) -> - Call.make_function_call(name, [], env) - ElixirScript.Translator.LexicalScope.find_module(env, {name, 0}) -> - imported_module_name = ElixirScript.Translator.LexicalScope.find_module(env, {name, 0}) - Call.make_function_call(imported_module_name, name, params, env) - true -> - { Identifier.make_identifier(name), env } - end - end - - - def create_module_name(module_name, env) do - case module_name do - {:__aliases__, _, _} -> - candiate_module_name = Utils.quoted_to_name(module_name) - |> ElixirScript.Translator.State.get_module_name - - if ElixirScript.Translator.LexicalScope.get_module_name(env, candiate_module_name) in ElixirScript.Translator.State.list_module_names() do - ElixirScript.Translator.LexicalScope.get_module_name(env, candiate_module_name) - else - module_name - end - _ -> - module_name - end - end - - def has_function?(module_name, name_arity) do - case ElixirScript.Translator.State.get_module(module_name) do - nil -> - false - module -> - name_arity in module.functions or name_arity in module.private_functions - end - end - -end diff --git a/lib/elixir_script/translator/empty.ex b/lib/elixir_script/translator/empty.ex deleted file mode 100644 index 1456cc4f..00000000 --- a/lib/elixir_script/translator/empty.ex +++ /dev/null @@ -1,8 +0,0 @@ -defmodule ElixirScript.Translator.Empty do - @moduledoc false - - # Represents no translation to JS AST - - @type t :: %ElixirScript.Translator.Empty{} - defstruct type: "Empty" -end diff --git a/lib/elixir_script/translator/group.ex b/lib/elixir_script/translator/group.ex deleted file mode 100644 index 391315ff..00000000 --- a/lib/elixir_script/translator/group.ex +++ /dev/null @@ -1,30 +0,0 @@ -defmodule ElixirScript.Translator.Group do - @moduledoc false - - # Represents a collection of JavaScript AST. - # Contents in body are expanded within outer AST before JS code generation - - @type t :: %ElixirScript.Translator.Group{ - type: binary, - body: [ESTree.Statement.t] - } - defstruct type: "Group", body: [] - - def inflate_groups(body) do - Enum.map(body, fn(x) -> - case x do - %ElixirScript.Translator.Empty{} -> - [] - %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 -end diff --git a/lib/elixir_script/translator/kernel/def.ex b/lib/elixir_script/translator/kernel/def.ex deleted file mode 100644 index 5f6f1c45..00000000 --- a/lib/elixir_script/translator/kernel/def.ex +++ /dev/null @@ -1,17 +0,0 @@ -defmodule ElixirScript.Translator.Def do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator.Function - alias ElixirScript.Translator.Identifier - - def process_function(name, functions, env) do - { result, _ } = Function.make_anonymous_function(functions, env, name) - - declarator = JS.variable_declarator( - Identifier.make_identifier(name), - result - ) - - { JS.variable_declaration([declarator], :const), env } - end -end diff --git a/lib/elixir_script/translator/kernel/defimpl.ex b/lib/elixir_script/translator/kernel/defimpl.ex deleted file mode 100644 index 996ca689..00000000 --- a/lib/elixir_script/translator/kernel/defimpl.ex +++ /dev/null @@ -1,125 +0,0 @@ -defmodule ElixirScript.Translator.Defimpl do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator.Defmodule - - def make(name, type, body, env) do - - type = map_to_js(type, env) - module = Defmodule.make_module(name, body, env) - - protocol_name = Atom.to_string(name) |> String.split(".DefImpl.") |> hd |> String.to_atom - - %ESTree.ExportDefaultDeclaration{ declaration: export } = List.last(module.body) - export = JS.object_expression([ - JS.property( - JS.literal("Type"), - type - ), - JS.property( - JS.literal("Implementation"), - export - ) - ]) - - body = Enum.reverse(module.body) - |> tl - |> Enum.reverse - - %{ module | body: body ++ [JS.export_default_declaration(export)] } - |> Map.put(:protocol, protocol_name) - end - - defp map_to_js({:__aliases__, _, [:Integer]}, _) do - JS.member_expression( - JS.member_expression( - JS.identifier(:Elixir), - JS.identifier(:Core) - ), - JS.identifier(:Integer) - ) - end - - defp map_to_js({:__aliases__, _, [:Tuple]}, _) do - JS.member_expression( - JS.member_expression( - JS.identifier(:Elixir), - JS.identifier(:Core) - ), - JS.identifier(:Tuple) - ) - end - - defp map_to_js({:__aliases__, _, [:Atom]}, _) do - JS.identifier(:Symbol) - end - - defp map_to_js({:__aliases__, _, [:List]}, _) do - JS.identifier(:Array) - end - - defp map_to_js({:__aliases__, _, [:BitString]}, _) do - JS.member_expression( - JS.member_expression( - JS.identifier(:Elixir), - JS.identifier(:Core) - ), - JS.identifier(:BitString) - ) - end - - defp map_to_js({:__aliases__, _, [:Float]}, _) do - JS.member_expression( - JS.member_expression( - JS.identifier(:Elixir), - JS.identifier(:Core) - ), - JS.identifier(:Float) - ) - end - - defp map_to_js({:__aliases__, _, [:Function]}, _) do - JS.identifier(:Function) - end - - defp map_to_js({:__aliases__, _, [:PID]}, _) do - JS.member_expression( - JS.member_expression( - JS.identifier(:Elixir), - JS.identifier(:Core) - ), - JS.identifier(:PID) - ) - end - - defp map_to_js({:__aliases__, _, [:Port]}, _) do - JS.member_expression( - JS.identifier(:Elixir), - JS.identifier(:Port) - ) - end - - defp map_to_js({:__aliases__, _, [:Reference]}, _) do - JS.member_expression( - JS.identifier(:Elixir), - JS.identifier(:Reference) - ) - end - - defp map_to_js({:__aliases__, _, [:Map]}, _) do - JS.identifier(:Object) - end - - defp map_to_js({:__aliases__, _, [:Any]}, _) do - JS.identifier(:null) - end - - - defp map_to_js({:__aliases__, _, _} = module, env) do - ElixirScript.Translator.Struct.get_struct_class( - module, - env - ) - end - -end diff --git a/lib/elixir_script/translator/kernel/defmodule.ex b/lib/elixir_script/translator/kernel/defmodule.ex deleted file mode 100644 index 8199692f..00000000 --- a/lib/elixir_script/translator/kernel/defmodule.ex +++ /dev/null @@ -1,237 +0,0 @@ -defmodule ElixirScript.Translator.Defmodule do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.State - alias ElixirScript.Translator.Utils - alias ElixirScript.Translator.Group - alias ElixirScript.Translator.Def - alias ElixirScript.ModuleSystems - alias ElixirScript.Translator.Identifier - - def make_module(ElixirScript.Temp, body, env) do - { body, _ } = translate_body(body, env) - %{ name: ElixirScript.Temp, body: body |> Group.inflate_groups, app_name: ElixirScript.Translator.State.get().compiler_opts.app } - end - - def make_module(module, nil, _) do - %{ name: module, body: [], app_name: ElixirScript.Translator.State.get().compiler_opts.app } - end - - def make_module(module, body, env) do - { body, functions } = extract_functions_from_module(body) - - { body, env } = translate_body(body, env) - - { exported_functions, private_functions } = process_functions(functions, env) - - module_refs = ElixirScript.Translator.State.get_module_references(module) -- [env.module] - - {imports, body} = extract_imports_from_body(body) - {structs, body} = extract_structs_from_body(body, env) - - app_name = State.get_module(module).app - - imports = imports ++ make_std_lib_import() ++ make_imports(app_name, module_refs) - - #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 = Group.inflate_groups(body) - - exported_object = JS.object_expression( - make_defstruct_property(module, structs) ++ - Enum.map(exported_functions, fn({key, _value}) -> - JS.property(Identifier.make_identifier(key), Identifier.make_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 = ModuleSystems.export_module(exported_object) - - result = %{ - name: Utils.quoted_to_name({:__aliases__, [], module }), - body: imports ++ structs ++ private_functions ++ exported_functions ++ body ++ [default], - app_name: app_name - } - - result - end - - def translate_body(body, env) do - { body, env } = Translator.translate(body, env) - - body = case body do - [%ESTree.BlockStatement{ body: body }] -> - body - %ESTree.BlockStatement{ body: body } -> - body - _ -> - List.wrap(body) - end - - { body, env } - end - - def extract_functions_from_module({:__block__, meta, body_list}) do - { body_list, functions } = Enum.map_reduce(body_list, - %{exported: HashDict.new(), private: HashDict.new(), exported_generators: HashDict.new(), private_generators: 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]) } - } - ({:defgen, _, [{:when, _, [{name, _, _} | _guards] }, _] } = function, state) -> - { - nil, - %{ state | exported_generators: HashDict.put(state.exported_generators, name, HashDict.get(state.exported_generators, name, []) ++ [function]) } - } - ({:defgen, _, [{name, _, _}, _]} = function, state) -> - { - nil, - %{ state | exported_generators: HashDict.put(state.exported_generators, name, HashDict.get(state.exported_generators, name, []) ++ [function]) } - } - ({:defgenp, _, [{:when, _, [{name, _, _} | _guards] }, _] } = function, state) -> - { - nil, - %{ state | private_generators: HashDict.put(state.private_generators, name, HashDict.get(state.private_generators, name, []) ++ [function]) } - } - ({:defgenp, _, [{name, _, _}, _]} = function, state) -> - { - nil, - %{ state | private_generators: HashDict.put(state.private_generators, name, HashDict.get(state.private_generators, 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 - - def extract_functions_from_module(body) do - extract_functions_from_module({:__block__, [], List.wrap(body)}) - end - - def extract_imports_from_body(body) do - Enum.partition(body, fn(x) -> - case x do - %ESTree.ImportDeclaration{} -> - true - _ -> - false - end - end) - end - - def extract_structs_from_body(body, env) do - module_js_name = Utils.name_to_js_name(env.module) - - Enum.partition(body, fn(x) -> - case x do - %ESTree.VariableDeclaration{declarations: [%ESTree.VariableDeclarator{id: %ESTree.Identifier{name: ^module_js_name} } ] } -> - true - _ -> - false - end - end) - end - - defp make_defstruct_property(_, []) do - [] - end - - defp make_defstruct_property(module_name, [the_struct]) do - module_js_name = Utils.name_to_js_name(module_name) - - case the_struct do - %ESTree.VariableDeclaration{declarations: [%ESTree.VariableDeclarator{id: %ESTree.Identifier{name: ^module_js_name} } ] } -> - [JS.property(JS.identifier(module_js_name), JS.identifier(module_js_name), :init, true)] - end - end - - def make_std_lib_import() do - compiler_opts = ElixirScript.Translator.State.get().compiler_opts - case compiler_opts.import_standard_libs do - true -> - [ModuleSystems.import_module(:Elixir, Utils.make_local_file_path(:elixir, compiler_opts.core_path))] - false -> - [] - end - end - - def process_functions(%{ exported: exported, private: private, exported_generators: exported_generators, private_generators: private_generators }, env) do - exported_functions = Enum.map(Dict.keys(exported), fn(key) -> - functions = Dict.get(exported, key) - - { functions, _ } = Def.process_function(key, functions, env) - { key, functions } - end) - - exported_generators = Enum.map(Dict.keys(exported_generators), fn(key) -> - functions = Dict.get(exported_generators, key) - - { functions, _ } = Def.process_function(key, functions, %{ env | context: :generator}) - { key, functions } - end) - - private_functions = Enum.map(Dict.keys(private), fn(key) -> - functions = Dict.get(private, key) - { functions, _ } = Def.process_function(key, functions, env) - { key, functions } - end) - - private_generators = Enum.map(Dict.keys(private_generators), fn(key) -> - functions = Dict.get(private_generators, key) - { functions, _ } = Def.process_function(key, functions, %{ env | context: :generator}) - { key, functions } - end) - - { exported_functions ++ exported_generators, private_functions ++ private_generators } - end - - def make_attribute(name, value, env) do - declarator = JS.variable_declarator( - Identifier.make_identifier(name), - ElixirScript.Translator.translate!(value, env) - ) - - JS.variable_declaration([declarator], :const) - end - - def make_imports(current_app_name, enum) do - Enum.map(enum, fn(x) -> - module_name = Utils.name_to_js_name(x) - app_name = State.get_module(x).app - path = Utils.make_local_file_path(app_name, Utils.name_to_js_file_name(x)) - ModuleSystems.import_module(module_name, path) - end) - end - -end diff --git a/lib/elixir_script/translator/kernel/defprotocol.ex b/lib/elixir_script/translator/kernel/defprotocol.ex deleted file mode 100644 index 8f56f64e..00000000 --- a/lib/elixir_script/translator/kernel/defprotocol.ex +++ /dev/null @@ -1,139 +0,0 @@ -defmodule ElixirScript.Translator.Defprotocol do - @moduledoc false - - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator.Defmodule - alias ElixirScript.Translator.Map - alias ElixirScript.Translator.Utils - alias ElixirScript.Translator.State - alias ElixirScript.ModuleSystems - alias ElixirScript.Translator.Identifier - require Logger - - @doc """ - Takes a protocol and turns it into a module - """ - def make(name, functions, env) do - { body, _ } = Defmodule.translate_body( {:__block__, [], [] }, env) - - module_refs = ElixirScript.Translator.State.get_module_references(name) - - {imports, body} = Defmodule.extract_imports_from_body(body) - - app_name = State.get_module(name).app - - imports = imports ++ Defmodule.make_std_lib_import() ++ - Defmodule.make_imports(app_name, module_refs) ++ - [ElixirScript.ModuleSystems.import_module("Implementations", Utils.make_local_file_path(app_name, Utils.name_to_js_file_name(name) <> ".Defimpl"))] - - object = process_spec_functions(functions) - |> Enum.map(fn({key, value}) -> - Map.make_property(Identifier.make_identifier(key), value) - end) - |> JS.object_expression - - declarator = JS.variable_declarator( - JS.identifier(Utils.name_to_js_name(name)), - JS.call_expression( - JS.member_expression( - JS.identifier(:Elixir), - JS.member_expression( - JS.identifier(:Core), - JS.member_expression( - JS.identifier(:Functions), - JS.identifier(:defprotocol) - ) - ) - ), - [object] - ) - ) - - declaration = JS.variable_declaration([declarator], :const) - - implementations = JS.for_of_statement( - JS.variable_declaration([JS.variable_declarator( - JS.object_pattern([ - JS.assignment_property(JS.identifier("Type")), - JS.assignment_property(JS.identifier("Implementation")) - ]), - nil - )], :let), - JS.identifier("Implementations"), - JS.call_expression( - JS.member_expression( - JS.identifier(:Elixir), - JS.member_expression( - JS.identifier(:Core), - JS.member_expression( - JS.identifier(:Functions), - JS.identifier(:defimpl) - ) - ) - ), - [ - JS.identifier(Utils.name_to_js_name(name)), - JS.identifier("Type"), - JS.identifier("Implementation") - ] - ) - ) - - default = JS.export_default_declaration(JS.identifier(Utils.name_to_js_name(name))) - - %{ - name: name, - body: imports ++ body ++ [declaration] ++ [implementations] ++ [default], - app_name: app_name - } - end - - defp process_spec_functions(functions) do - Enum.map(Keyword.keys(functions), fn(function_name) -> - {function_name, JS.function_expression([], [], JS.block_statement([]))} - end) - end - - @doc """ - Makes the protocol implementation module for the given implementation name. - This is used to consolidate all of the protocol implementations. - """ - def make_defimpl(name, implementations \\ [], compiler_opts) do - imports = [ModuleSystems.import_module(:Elixir, Utils.make_local_file_path(:elixir, compiler_opts.core_path, compiler_opts.root))] - - declarator = JS.variable_declarator( - JS.identifier("impls"), - JS.array_expression([]) - ) - - declaration = JS.variable_declaration([declarator], :let) - - default = JS.export_default_declaration(JS.identifier("impls")) - - protocol_name = Atom.to_string(name) - - app_name = State.get_module(name).app - - body = Enum.flat_map(implementations, fn({impl_app_name, x}) -> - x = if is_atom(x), do: Atom.to_string(x), else: x - x = String.to_atom(protocol_name <> ".DefImpl." <> x) - name = Utils.name_to_js_name(x) - imports = ModuleSystems.import_module(name, Utils.make_local_file_path(impl_app_name, Utils.name_to_js_file_name(x), compiler_opts.root)) - call = JS.call_expression( - JS.member_expression( - JS.identifier("impls"), - JS.identifier("push") - ), - [JS.identifier(name)] - ) - - [imports, call] - end) - - %{ - name: String.to_atom(protocol_name <> ".DefImpl"), - body: imports ++ [declaration] ++ body ++ [default], - app_name: app_name - } - end -end diff --git a/lib/elixir_script/translator/kernel/expression.ex b/lib/elixir_script/translator/kernel/expression.ex deleted file mode 100644 index 583e52a4..00000000 --- a/lib/elixir_script/translator/kernel/expression.ex +++ /dev/null @@ -1,18 +0,0 @@ -defmodule ElixirScript.Translator.Expression do - @moduledoc false - alias ESTree.Tools.Builder - alias ElixirScript.Translator - - def make_unary_expression(operator, expr, env) do - { js_ast, env } = Translator.translate(expr, env) - { Builder.unary_expression(operator, true, js_ast), env } - end - - def make_binary_expression(operator, left, right, env) do - { left, _ } = Translator.translate(left, env) - { right, _ } = Translator.translate(right, env) - - { Builder.binary_expression(operator, left, right), env } - end - -end diff --git a/lib/elixir_script/translator/kernel/js.ex b/lib/elixir_script/translator/kernel/js.ex deleted file mode 100644 index 49df2d9f..00000000 --- a/lib/elixir_script/translator/kernel/js.ex +++ /dev/null @@ -1,106 +0,0 @@ -defmodule ElixirScript.Translator.JS do - @moduledoc false - - alias ESTree.Tools.Builder - alias ElixirScript.Translator - alias ElixirScript.Translator.Identifier - alias ElixirScript.ModuleSystems - - @doc false - def translate_js_function(name, params, env) do - { do_translate({name, [], params}, env), env } - end - - defp do_translate({op, _, [param]}, env) when op in [:typeof, :delete, :void, :-, :+, :!, :"~"] do - Builder.unary_expression( - op, - true, - Translator.translate!(param, env) - ) - end - - defp do_translate({op, _, [value, type]}, env) when op in [:"**", :==, :!=, :===, :!==, :<, :<=, :>, :>=, :"<<", :">>", :<<<, :+, :-, :*, :/, :%, :|, :^, :&, :in, :instanceof] do - Builder.binary_expression( - op, - Translator.translate!(value, env), - Translator.translate!(type, env) - ) - end - - defp do_translate({op, _, [value, type]}, env) when op in [:||, :&&] do - Builder.logical_expression( - op, - Translator.translate!(value, env), - Translator.translate!(type, env) - ) - end - - defp do_translate({:yield, _, []}, env) do - Builder.yield_expression() - end - - defp do_translate({:yield, _, [term]}, env) do - Builder.yield_expression( - Translator.translate!(term, env) - ) - end - - defp do_translate({:yield_to, _, [term]}, env) do - Builder.yield_expression( - Translator.translate!(term, env), - true - ) - end - - defp do_translate({:throw, _, [term]}, env) do - Builder.throw_statement( - Translator.translate!(term, env) - ) - end - - defp do_translate({:new, _, [module_name, params]}, env) when not is_list(params) do - Builder.new_expression( - Translator.translate!(module_name, env), - [Builder.rest_element(Translator.translate!(params, 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, map]}, env) do - quoted = quote do - Object.assign(unquote(object), unquote(map)) - end - - Translator.translate!(quoted, env) - end - - defp do_translate({:import, _, [module_name, from, [default: false]]}, env) do - ModuleSystems.import_namespace_module(module_name, from, env) - end - - defp do_translate({:import, _, [module_name, from, [default: true]]}, env) do - ModuleSystems.import_module(module_name, from, env) - end - - defp do_translate({:import, _, [module_name, from]}, env) do - ModuleSystems.import_module(module_name, from, env) - end - - defp do_translate({:object, _, [args]}, env) do - args = Enum.map(args, fn - { k, v } when Kernel.is_atom(k) -> - { Atom.to_string(k), v } - pair -> - pair - end) - - Translator.translate!({ :%{}, [], args }, env) - end - -end diff --git a/lib/elixir_script/translator/kernel/special_forms/bitstring.ex b/lib/elixir_script/translator/kernel/special_forms/bitstring.ex deleted file mode 100644 index d4f2d657..00000000 --- a/lib/elixir_script/translator/kernel/special_forms/bitstring.ex +++ /dev/null @@ -1,147 +0,0 @@ -defmodule ElixirScript.Translator.Bitstring do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - - - def make_bitstring(elements, env) do - js_ast = JS.new_expression( - JS.member_expression( - JS.member_expression( - JS.identifier("Elixir"), - JS.identifier("Core") - ), - JS.identifier("BitString") - ), - Enum.map(elements, &make_bitstring_element(&1, env)) - ) - - { js_ast, env } - end - - def make_bitstring_element(element, env) when is_number(element) do - do_make_bitstring_element({:integer, Translator.translate!(element, env)}) - end - - def make_bitstring_element(element, env) when is_binary(element) do - do_make_bitstring_element({:binary, Translator.translate!(element, env)}) - end - - def make_bitstring_element({:<<>>, [], elements}, env) do - {ast, _} = make_bitstring(elements, env) - ast - end - - def make_bitstring_element({:::, _, [element, {type, _, _}]}, env) when type in [:integer, :float, :bitstring, :bits, :binary, :bytes, :utf8, :utf16, :utf32, :signed, :unsigned] do - do_make_bitstring_element({type, translate_element(element, env)}) - end - - def make_bitstring_element({:::, _, [element, {type, _, params}]}, env) when type in [:size, :unit] do - do_make_bitstring_element({type, translate_element(element, env), Enum.map(params, &translate_element(&1, env))}) - end - - def make_bitstring_element({:::, _, [element, {:*, _, [size, unit]}]}, env) do - size_ast = do_make_bitstring_element({:size, translate_element(element, env), [translate_element(size, env)]}) - do_make_bitstring_element({:unit, size_ast, [translate_element(unit, env)]}) - end - - def make_bitstring_element({:::, _, [element, {:-, _, types}]}, env) do - handle_type_adjectives({:-, [], types}, translate_element(element, env), env) - end - - def make_bitstring_element({:::, _, [element, size]}, env) do - do_make_bitstring_element({:size, translate_element(element, env), [translate_element(size, env)]}) - end - - def make_bitstring_element(element, env) do - do_make_bitstring_element({:binary, translate_element(element, env)}) - end - - def translate_element(ElixirScript.Translator.PatternMatching, _) do - JS.object_expression([JS.property( - JS.literal("value"), - ElixirScript.Translator.PatternMatching.parameter() - ) - ]) - end - - def translate_element(element, env) do - Translator.translate!(element, 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 bitstring_class() do - JS.member_expression( - JS.member_expression( - JS.identifier("Elixir"), - JS.identifier("Core") - ), - JS.identifier("BitString") - ) - end - - defp do_make_bitstring_element({type, ast}) do - JS.call_expression( - JS.member_expression( - bitstring_class, - JS.identifier(type) - ), - [ - ast - ] - ) - end - - defp do_make_bitstring_element({type, ast, params}) when is_list(params) do - JS.call_expression( - JS.member_expression( - bitstring_class, - JS.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), env } - end - - defp do_make_interpolated_string([], ast, _) do - ast - end - - defp do_make_interpolated_string(elements, ast, env) do - JS.binary_expression( - :+, - ast, - do_make_interpolated_string(tl(elements), hd(elements), env) - ) - end - -end diff --git a/lib/elixir_script/translator/kernel/special_forms/block.ex b/lib/elixir_script/translator/kernel/special_forms/block.ex deleted file mode 100644 index 52689574..00000000 --- a/lib/elixir_script/translator/kernel/special_forms/block.ex +++ /dev/null @@ -1,14 +0,0 @@ -defmodule ElixirScript.Translator.Block do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - - def make_block(expressions, env) do - { list, env } = Enum.map_reduce(expressions, env, fn(x, updated_env) -> - Translator.translate(x, updated_env) - end) - - { JS.block_statement(list), env } - end - -end diff --git a/lib/elixir_script/translator/kernel/special_forms/call.ex b/lib/elixir_script/translator/kernel/special_forms/call.ex deleted file mode 100644 index 8ed21f61..00000000 --- a/lib/elixir_script/translator/kernel/special_forms/call.ex +++ /dev/null @@ -1,206 +0,0 @@ -defmodule ElixirScript.Translator.Call do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Utils - alias ElixirScript.Translator.Identifier - - def make_module_name(module_name, env) do - the_name = get_module_name_for_function(module_name, env) - { make_module_expression_tree(the_name, false, env), env } - end - - - def make_function_or_property_call(module_name, function_name, env) do - the_name = get_module_name_for_function(module_name, env) - - js_ast = JS.call_expression( - JS.member_expression( - JS.member_expression( - JS.identifier("Elixir"), - JS.member_expression( - JS.identifier("Core"), - JS.identifier("Functions") - ) - ), - JS.identifier("call_property") - ), - [ - make_module_expression_tree(the_name, false, env), - Translator.translate!(to_string(function_name), env) - ] - ) - - { js_ast, env } - end - - - def get_module_name_for_function(module_name, env) do - case module_name do - {:__aliases__, _, name} -> - module_name = Utils.quoted_to_name(name) - get_js_name(module_name, env) - {name, _, _} when is_atom(name) -> - get_js_name(name, env) - {{:., _, [_, _]}, _, _ } = ast -> - ast - {{:., _, [{:__aliases__, _, _}]}, _, _} = ast -> - ast - ast when is_list(ast) -> - ast - name -> - get_js_name(name, env) - end - end - - - def make_function_call(function_name, params, env) when is_tuple(function_name) do - { make_call_expression(function_name, params, env), env } - end - - def make_function_call(function_name, params, env) do - { make_call_expression(function_name, params, env), env } - end - - def make_function_call(module_name, function_name, params, env) when is_list(module_name) do - call = JS.call_expression( - JS.member_expression( - Translator.translate!(module_name, env), - Identifier.make_identifier(function_name) - ), - Enum.map(params, &Translator.translate!(&1, env)) - ) - - { call, env } - end - - def make_function_call(module_name, function_name, params, env) do - the_name = get_module_name_for_function(module_name, env) - { make_call_expression(the_name, function_name, params, env), env } - end - - defp 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 - - defp 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 - - defp make_call_expression(function_name, params, env) do - JS.call_expression( - Identifier.make_identifier(function_name), - Enum.map(params, &Translator.translate!(&1, env)) - ) - end - - - def get_js_name([Elixir | _] = list, _) do - list - end - - def get_js_name({:__aliases__, _, _} = name, env) do - Utils.quoted_to_name(name) - |> get_js_name(env) - end - - def get_js_name(module_name, env) when is_list(module_name) do - Utils.quoted_to_name({:__aliases__, [], module_name}) - |> get_js_name(env) - end - - def get_js_name(module_name, env) do - - cond do - module_name in env.requires -> - Utils.name_to_js_name(module_name) - - module_name in ElixirScript.Translator.State.list_module_names -> - ElixirScript.Translator.State.add_module_reference(env.module, module_name) - Utils.name_to_js_name(module_name) - - true -> - case Atom.to_string(module_name) do - "Elixir." <> _ -> - {:__aliases__, _, name } = Utils.name_to_quoted(module_name) - name - _ -> - module_name - end - end - 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, - Identifier.make_identifier(function_name), - computed - ) - modules when is_list(modules) and length(modules) == 1 -> - JS.member_expression( - Identifier.make_identifier(hd(modules)), - Identifier.make_identifier(function_name), - computed - ) - {{:., _, [_module_name, _function_name]}, _, _params } = ast -> - JS.member_expression( - Translator.translate!(ast, env), - Identifier.make_identifier(function_name), - computed - ) - {{:., _, [{:__aliases__, _, _}]}, _, _} = ast -> - JS.member_expression( - Translator.translate!(ast, env), - Identifier.make_identifier(function_name), - computed - ) - {:., _, _} = ast -> - JS.member_expression( - Translator.translate!(ast, env), - Identifier.make_identifier(function_name), - computed - ) - _ -> - JS.member_expression( - Identifier.make_identifier(module_name), - Identifier.make_identifier(function_name), - computed - ) - end - end - - defp make_module_expression_tree([module], computed, env) do - make_module_expression_tree(module, computed, env) - end - - defp 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(Identifier.make_identifier(x), nil, computed) - %ESTree.MemberExpression{ property: nil } -> - %{ ast | property: Identifier.make_identifier(x) } - _ -> - JS.member_expression(ast, Identifier.make_identifier(x), computed) - end - end) - end - - defp make_module_expression_tree(module, _, _) when is_binary(module) or is_atom(module) do - Identifier.make_identifier(module) - end - - defp make_module_expression_tree(module, _, env) do - Translator.translate!(module, env) - end -end diff --git a/lib/elixir_script/translator/kernel/special_forms/capture.ex b/lib/elixir_script/translator/kernel/special_forms/capture.ex deleted file mode 100644 index 05bb62f7..00000000 --- a/lib/elixir_script/translator/kernel/special_forms/capture.ex +++ /dev/null @@ -1,34 +0,0 @@ -defmodule ElixirScript.Translator.Capture do - @moduledoc false - - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator.PatternMatching - alias ElixirScript.Translator.Function - alias ElixirScript.Translator.Call - alias ElixirScript.Translator.Identifier - alias ElixirScript.Translator - - def make_capture(function_name, arity, env) do - Identifier.make_identifier(function_name) - end - - def make_capture(module_name, function_name, arity, env) do - JS.member_expression( - Translator.translate!(module_name, env), - JS.identifier(function_name) - ) - 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 diff --git a/lib/elixir_script/translator/kernel/special_forms/case.ex b/lib/elixir_script/translator/kernel/special_forms/case.ex deleted file mode 100644 index 5d1215e3..00000000 --- a/lib/elixir_script/translator/kernel/special_forms/case.ex +++ /dev/null @@ -1,17 +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 - { func, env } = Function.make_anonymous_function(clauses, env) - - js_ast = JS.call_expression( - JS.member_expression( func, JS.identifier("call")), - [JS.identifier(:this), Translator.translate!(condition, env)] - ) - - { js_ast, env } - end -end diff --git a/lib/elixir_script/translator/kernel/special_forms/cond.ex b/lib/elixir_script/translator/kernel/special_forms/cond.ex deleted file mode 100644 index a9959972..00000000 --- a/lib/elixir_script/translator/kernel/special_forms/cond.ex +++ /dev/null @@ -1,34 +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 - - def make_cond(clauses, env) do - js_ast = JS.call_expression( - JS.member_expression( - Primitive.special_forms(), - JS.identifier("cond") - ), - process_cond(clauses, env) - ) - - { js_ast, env } - end - - defp process_cond(clauses, env) do - Enum.map(clauses, fn({:->, _, [clause, clause_body]}) -> - { translated_body, env } = Function.prepare_function_body(clause_body, env) - - translated_body = JS.block_statement(translated_body) - function = JS.function_expression([], [], translated_body) - translated_clause = Translator.translate!(hd(clause), env) - - - Primitive.make_list_no_translate([translated_clause, function]) - end) - end - -end diff --git a/lib/elixir_script/translator/kernel/special_forms/fn.ex b/lib/elixir_script/translator/kernel/special_forms/fn.ex deleted file mode 100644 index 26c4f44a..00000000 --- a/lib/elixir_script/translator/kernel/special_forms/fn.ex +++ /dev/null @@ -1,229 +0,0 @@ -defmodule ElixirScript.Translator.Function do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Group - alias ElixirScript.Translator.PatternMatching - alias ElixirScript.Translator.Block - - @patterns JS.member_expression( - JS.member_expression( - JS.identifier("Elixir"), - JS.identifier("Core") - ), - JS.identifier("Patterns") - ) - - def make_anonymous_function(functions, env, name \\ nil) do - clauses = functions - |> Enum.map(fn - {:->, _, [ [{:when, _, [params | guards]}], body ]} -> - process_function_body(params, body, env, name, guards) - - ({:->, _, [params, body]}) -> - process_function_body(params, body, env, name) - - ({_, _, [{:when, _, [{_, _, params} | guards] }, body]}) -> - body = convert_to_try(body) - process_function_body(params, body, env, name, guards) - - ({_, _, [{_, _, params}, body]}) -> - body = convert_to_try(body) - process_function_body(params, body, env, name) - - ({_, _, [{_, _, params}]}) -> - process_function_body(params, [], env, name) - end) - - { make_defmatch(clauses, env.context == :generator), env } - end - - def convert_to_try([do: body]) do - body - end - - def convert_to_try(function_kw_list) do - { :__block__, [], [{ :try, [], [function_kw_list] }] } - end - - def make_defmatch(clauses, true) do - JS.call_expression( - JS.member_expression( - @patterns, - JS.identifier("defmatchgen") - ), - clauses - ) - end - - def make_defmatch(clauses, _) do - JS.call_expression( - JS.member_expression( - @patterns, - JS.identifier("defmatch") - ), - clauses - ) - end - - defp process_function_body(params, body, env, name, guards \\ nil) do - env = ElixirScript.Translator.LexicalScope.function_scope(env, {name, get_arity(params)}) - - { patterns, params, env } = process_params(params, env) - { body, _ } = make_function_body(body, env) - - if guards do - { guard_body, _ } = hd(List.wrap(guards)) - |> prepare_function_body(%{ env | context: :guard}) - - guard_body = JS.block_statement(guard_body) - make_function_clause(patterns, params, body, guard_body, env.context == :generator) - else - make_function_clause(patterns, params, body, nil, env.context == :generator) - end - end - - def wrap_params(params) when is_atom(params), do: [] - def wrap_params(params), do: List.wrap(params) - - def make_function_body(body, env) do - { body, _ } = body - |> prepare_function_body(env) - - - { JS.block_statement(body), env } - end - - defp get_arity(params) when is_atom(params), do: 0 - defp get_arity(params) when is_tuple(params), do: 1 - defp get_arity(params), do: length(params) - - defp make_params(params) do - Enum.filter(params, fn - (%ESTree.Identifier{name: :undefined}) -> false - (_) -> true - end) - end - - defp process_params(params, env) do - params = wrap_params(params) - { patterns, params, env } = PatternMatching.process_match(params, env) - { patterns, make_params(params), env } - end - - def make_function_clause(patterns, params, body, guard_body, is_generator?) do - arguments = case guard_body do - nil -> - [ - JS.array_expression(patterns), - JS.function_expression(params, [], body, is_generator?) - ] - _ -> - [ - JS.array_expression(patterns), - JS.function_expression(params, [], body, is_generator?), - JS.function_expression(params, [], guard_body) - ] - end - - - JS.call_expression( - JS.member_expression( - @patterns, - JS.identifier("clause") - ), - arguments - ) - end - - def prepare_function_body(body, env) do - { list, env } = case body do - nil -> - { [], env } - list when is_list(list) -> - t = Translator.translate!(list, env) - {[t], env} - {:__block__, _, list} -> - Enum.map_reduce(list, env, fn(x, env) -> - Translator.translate(x, env) - end) - _ -> - - Enum.map_reduce(List.wrap(body), env, fn(x, env) -> - Translator.translate(x, env) - end) - end - - list = Group.inflate_groups(list) - |> return_last_expression - - { list, env } - 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.YieldExpression{} -> - JS.return_statement(last_item) - %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 diff --git a/lib/elixir_script/translator/kernel/special_forms/for.ex b/lib/elixir_script/translator/kernel/special_forms/for.ex deleted file mode 100644 index 1e97f5d5..00000000 --- a/lib/elixir_script/translator/kernel/special_forms/for.ex +++ /dev/null @@ -1,79 +0,0 @@ -defmodule ElixirScript.Translator.For do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.PatternMatching - 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_ast = JS.call_expression( - JS.member_expression( - Primitive.special_forms(), - JS.identifier("_for") - ), - [collections, fun, filter, into] - ) - - { js_ast, env } - end - - defp handle_args(generators, env) do - Enum.reduce(generators, %{collections: [], args: [], filter: nil, fun: nil, into: nil}, fn - - ({:<<>>, [], body}, state) -> - { bs_parts, collection } = Enum.map_reduce(body, nil, fn - {:::, _, _} = ast, state -> - {ast, state} - {:<-, [], [var, collection]}, _ -> - { var, collection } - end) - - { patterns, params, env } = PatternMatching.process_match([{:<<>>, [], bs_parts}], env) - list = Primitive.make_list_no_translate([hd(patterns), Translator.translate!(collection, env)]) - %{state | collections: state.collections ++ [list], args: state.args ++ params } - - ({:<-, _, [identifier, enum]}, state) -> - { patterns, params, env } = PatternMatching.process_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 = create_function_expression(expression2, env, state) - - %{ state | into: Translator.translate!(expression, env), fun: fun } - - ([do: expression], state) -> - fun = create_function_expression(expression, env, state) - - %{ state | fun: fun } - (filter, state) -> - fun = create_function_expression(filter, env, state) - - %{ state | filter: fun } - end) - end - - - defp create_function_expression(ast, env, state) do - { ast, _ } = Function.make_function_body(ast, env) - - JS.function_expression( - state.args, - [], - ast - ) - end -end diff --git a/lib/elixir_script/translator/kernel/special_forms/identifier.ex b/lib/elixir_script/translator/kernel/special_forms/identifier.ex deleted file mode 100644 index 37d9d2b5..00000000 --- a/lib/elixir_script/translator/kernel/special_forms/identifier.ex +++ /dev/null @@ -1,73 +0,0 @@ -defmodule ElixirScript.Translator.Identifier do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - - @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({:__aliases__, _, aliases}) do - aliases - |> Enum.reverse - |> make_alias - end - - def make_identifier([ast]) do - make_identifier(ast) - end - - def make_identifier(ast) do - ast - |> filter_name - |> JS.identifier - end - - defp filter_name(reserved_word) when reserved_word in @js_reserved_words do - "__#{Atom.to_string(reserved_word)}__" - end - - defp filter_name(name) do - to_string(name) - |> String.replace("?", "__qmark__") - |> String.replace("!", "__emark__") - end - - defp make_alias([x]) do - make_identifier(x) - end - - defp make_alias([h|t]) do - JS.member_expression(make_alias(t), make_identifier(h)) - end - -end diff --git a/lib/elixir_script/translator/kernel/special_forms/map.ex b/lib/elixir_script/translator/kernel/special_forms/map.ex deleted file mode 100644 index 02f17691..00000000 --- a/lib/elixir_script/translator/kernel/special_forms/map.ex +++ /dev/null @@ -1,79 +0,0 @@ -defmodule ElixirScript.Translator.Map do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Primitive - - def make_map(object_expression) do - JS.call_expression( - JS.member_expression( - JS.identifier(:Object), - JS.identifier(:freeze) - ), - [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 - properties - |> Enum.map(fn - ({x, {:__aliases__, _, [value]}}) -> make_property(Translator.translate!(x, env), JS.identifier(value)) - ({x, y}) -> - case x do - {_, _, atom } when is_atom(atom) -> - JS.property(Translator.translate!(x, env), Translator.translate!(y, env), :init, false, false, true) - _ -> - make_property(Translator.translate!(x, env), Translator.translate!(y, env)) - end - 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}, value) when is_binary(k) do - key = case String.contains?(k, "-") do - true -> - JS.literal(k) - false -> - JS.identifier(k) - end - - JS.property(key, value) - end - - def make_property(key, value) do - JS.property(key, value, :init, false, false, true) - end - - def make_shorthand_property(%ESTree.Identifier{} = key) do - JS.property(key, key, :init, true) - end - - def make_map_update(map, data, env) do - map = Translator.translate!(map, env) - data = Translator.translate!({:%{}, [], data}, env) - - js_ast = JS.call_expression( - JS.member_expression( - Primitive.special_forms(), - JS.identifier("map_update") - ), - [map, data] - ) - - { js_ast, env } - end - -end diff --git a/lib/elixir_script/translator/kernel/special_forms/match.ex b/lib/elixir_script/translator/kernel/special_forms/match.ex deleted file mode 100644 index 2263c391..00000000 --- a/lib/elixir_script/translator/kernel/special_forms/match.ex +++ /dev/null @@ -1,93 +0,0 @@ -defmodule ElixirScript.Translator.Match do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.PatternMatching - alias ElixirScript.Translator.Primitive - - def make_match(left, right, env) do - { right_ast, env } = Translator.translate(right, env) - - { patterns, params, env } = PatternMatching.process_match([left], env) - - declarator = JS.variable_declarator( - JS.array_pattern(params), - JS.call_expression( - JS.member_expression( - JS.member_expression( - JS.member_expression( - JS.identifier("Elixir"), - JS.identifier("Core") - ), - JS.identifier("Patterns") - ), - JS.identifier("match") - ), - [hd(patterns), right_ast] - ) - ) - - array_pattern = JS.variable_declaration([declarator], :let) - - 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) - _ -> - array_pattern - end - - { js_ast, env } - end - - defp make_list_ref(array_pattern, params) do - {ref, params} = make_params(params) - - ref_declarator = JS.variable_declarator( - ref, - Primitive.do_make_list(params) - ) - - make_variable_declaration_and_group(ref_declarator, array_pattern) - end - - defp make_tuple_ref(array_pattern, params) do - {ref, params} = make_params(params) - - ref_declarator = JS.variable_declarator( - ref, - JS.new_expression( - JS.member_expression( - JS.identifier("Elixir"), - JS.member_expression( - JS.identifier("Core"), - JS.identifier("Tuple") - ) - ), - params - ) - ) - - make_variable_declaration_and_group(ref_declarator, array_pattern) - end - - - defp make_params(params) do - ref = JS.identifier("_ref") - - params = Enum.map(params, fn - (nil) -> JS.identifier(:undefined) - (x) -> x - end) - - { ref, params } - end - - defp make_variable_declaration_and_group(ref_declarator, array_pattern) do - ref_declaration = JS.variable_declaration([ref_declarator], :let) - %ElixirScript.Translator.Group{ body: [array_pattern, ref_declaration] } - end -end diff --git a/lib/elixir_script/translator/kernel/special_forms/primitive.ex b/lib/elixir_script/translator/kernel/special_forms/primitive.ex deleted file mode 100644 index 25cea0c3..00000000 --- a/lib/elixir_script/translator/kernel/special_forms/primitive.ex +++ /dev/null @@ -1,99 +0,0 @@ -defmodule ElixirScript.Translator.Primitive do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Quote - - def special_forms() do - JS.member_expression( - JS.identifier("Elixir"), - JS.member_expression( - JS.identifier("Core"), - JS.identifier("SpecialForms") - ) - ) - end - - def tuple_class() do - JS.member_expression( - JS.member_expression( - JS.identifier("Elixir"), - JS.identifier("Core") - ), - JS.identifier("Tuple") - ) - end - - def list_ast() do - JS.member_expression( - JS.member_expression( - JS.identifier("Elixir"), - JS.member_expression( - JS.identifier("Core"), - JS.identifier("SpecialForms") - ) - ), - JS.identifier("list") - ) - end - - def make_atom(ast) when is_atom(ast) do - JS.call_expression( - JS.member_expression( - JS.identifier("Symbol"), - JS.identifier("for") - ), - [JS.literal(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_list(ast, env) when is_list(ast) do - js_ast = Enum.map(ast, &Translator.translate!(&1, env)) - |> do_make_list - - { js_ast, env } - end - - def make_list_quoted(opts, ast, env) when is_list(ast) do - Enum.map(ast, fn(x) -> Quote.make_quote(opts, x, env) end) - |> do_make_list - end - - def make_list_no_translate(ast) when is_list(ast) do - do_make_list(ast) - end - - def do_make_list(ast) do - JS.call_expression( - JS.member_expression( - JS.identifier("Object"), - JS.identifier("freeze") - ), - [JS.array_expression(ast)] - ) - end - - def make_tuple({ one, two }, env) do - make_tuple([one, two], env) - end - - def make_tuple(elements, env) do - list = Enum.map(elements, &Translator.translate!(&1, env)) - - js_ast = JS.new_expression(tuple_class, list) - - { js_ast, env } - end - - def make_tuple_quoted(opts, elements, env) do - JS.new_expression( - tuple_class, - Enum.map(elements, fn(x) -> Quote.make_quote(opts, x, env) end) - ) - end - -end diff --git a/lib/elixir_script/translator/kernel/special_forms/quote.ex b/lib/elixir_script/translator/kernel/special_forms/quote.ex deleted file mode 100644 index a02168a8..00000000 --- a/lib/elixir_script/translator/kernel/special_forms/quote.ex +++ /dev/null @@ -1,87 +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("Elixir"), - 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([context: {_, _, [new_context]}] = opts, {name, context, params}, env) do - updated_context = Keyword.put(context, :context, new_context) - Primitive.make_tuple_quoted(opts, [name, updated_context, params], env) - end - - def make_quote(_, {:alias!, _, [the_alias]}, _) do - the_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 diff --git a/lib/elixir_script/translator/kernel/special_forms/receive.ex b/lib/elixir_script/translator/kernel/special_forms/receive.ex deleted file mode 100644 index da13b442..00000000 --- a/lib/elixir_script/translator/kernel/special_forms/receive.ex +++ /dev/null @@ -1,65 +0,0 @@ -defmodule ElixirScript.Translator.Receive do - @moduledoc false - - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Primitive - alias ElixirScript.Translator.LexicalScope - - def make_receive([do: clauses], env) do - {made_case, _} = ElixirScript.Translator.Case.make_case({:__aliases__, [], [:message]}, clauses, env) - - js = call_processes_func("receive", [ - JS.function_expression( - [JS.identifier(:message)], - [], - JS.block_statement([ - JS.return_statement( - made_case - ) - ]) - ) - ]) - - {js, env} - end - - def make_receive([do: clauses, after: [{:->, _, [[time], _body]}] = after_clause], env) do - {made_case, _} = ElixirScript.Translator.Case.make_case({:__aliases__, [], [:message]}, clauses, env) - {anon_func, _} = ElixirScript.Translator.Function.make_anonymous_function(after_clause, env) - - - js = call_processes_func("receive", [ - JS.function_expression( - [JS.identifier(:message)], - [], - JS.block_statement([ - JS.return_statement( - made_case - ) - ]) - ), - Translator.translate!(time, env), - anon_func - ]) - - {js, env} - end - - def call_processes_func(func_name, params) do - JS.call_expression( - JS.member_expression( - JS.member_expression( - JS.identifier("Elixir"), - JS.member_expression( - JS.identifier("Core"), - JS.identifier("processes") - ) - ), - JS.identifier(func_name) - ), - params - ) - end - -end diff --git a/lib/elixir_script/translator/kernel/special_forms/struct.ex b/lib/elixir_script/translator/kernel/special_forms/struct.ex deleted file mode 100644 index f4f24277..00000000 --- a/lib/elixir_script/translator/kernel/special_forms/struct.ex +++ /dev/null @@ -1,130 +0,0 @@ -defmodule ElixirScript.Translator.Struct do - @moduledoc false - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Utils - alias ElixirScript.Translator.Map - alias ElixirScript.Translator.Identifier - - def get_struct_class(module_name, env) do - candiate_module_name = Utils.quoted_to_name(module_name) - |> ElixirScript.Translator.State.get_module_name - - if ElixirScript.Translator.LexicalScope.get_module_name(env, candiate_module_name) in ElixirScript.Translator.State.list_module_names() do - name = ElixirScript.Translator.LexicalScope.get_module_name(env, candiate_module_name) - ident = JS.identifier(Utils.name_to_js_name(name)) - JS.member_expression(ident, ident) - - else - Identifier.make_identifier(module_name) - end - - end - - def new_struct(module_name, data, env) do - JS.call_expression( - JS.member_expression( - get_struct_class(module_name, env), - JS.identifier(:create) - ), - [Translator.translate!(data, env)] - ) - end - - def make_defstruct(attributes, env) when length(attributes) == 1 do - attributes = Enum.flat_map(attributes, fn(x) -> x end) - - defaults = Enum.map(attributes, fn - ({x, y}) -> - Map.make_property( - Translator.translate!(x, env), - Translator.translate!(y, env) - ) - (attribute) -> - Map.make_property( - Translator.translate!(attribute, env), - Translator.translate!(nil, env) - ) - end) - |> JS.object_expression - - do_make_defstruct(:defstruct, defaults, env) - end - - def make_defstruct(attributes, env) do - defaults = Enum.map(attributes, fn(x) -> - Map.make_property( - Translator.translate!(x, env), - Translator.translate!(nil, env) - ) - end) - |> JS.object_expression - - do_make_defstruct(:defstruct, defaults, env) - end - - def make_defexception(attributes, env) when length(attributes) == 1 do - exception_key_value = Map.make_property(Translator.translate!(:__exception__, env), Translator.translate!(true, env)) - - attributes = Enum.flat_map(attributes, fn(x) -> x end) - - defaults = [exception_key_value] ++ Enum.map(attributes, fn - ({x, y}) -> - Map.make_property( - Translator.translate!(x, env), - Translator.translate!(y, env) - ) - (x) -> - Map.make_property( - Translator.translate!(x, env), - Translator.translate!(nil, env) - ) - end) - |> JS.object_expression - - do_make_defstruct(:defexception, defaults, env) - end - - def make_defexceptions(attributes, env) do - exception_key_value = Map.make_property(Translator.translate!(:__exception__, env), Translator.translate!(true, env)) - - defaults = [exception_key_value] ++ Enum.map(attributes, fn - (x) -> - Map.make_property( - Translator.translate!(x, env), - Translator.translate!(nil, env) - ) - end) - |> JS.object_expression - - do_make_defstruct(:defexception, defaults, env) - end - - defp do_make_defstruct(name, defaults, env) do - struct_name = Map.make_property(Translator.translate!(:__struct__, env), Translator.translate!({:__MODULE__, [], []}, env)) - - defaults = %{ defaults | properties: [struct_name] ++ defaults.properties } - - ref = JS.identifier(Utils.name_to_js_name(env.module)) - - ref_declarator = JS.variable_declarator( - ref, - JS.call_expression( - JS.member_expression( - JS.member_expression( - JS.identifier("Elixir"), - JS.member_expression( - JS.identifier("Core"), - JS.identifier("Functions") - ) - ), - JS.identifier(name) - ), - [defaults] - ) - ) - - JS.variable_declaration([ref_declarator], :const) - end - -end diff --git a/lib/elixir_script/translator/kernel/special_forms/try.ex b/lib/elixir_script/translator/kernel/special_forms/try.ex deleted file mode 100644 index 81db4a39..00000000 --- a/lib/elixir_script/translator/kernel/special_forms/try.ex +++ /dev/null @@ -1,112 +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) - - translated_body = JS.block_statement(translated_body) - try_block = JS.function_expression([], [], translated_body) - - rescue_block = if rescue_block do - process_rescue_block(rescue_block, env) - else - JS.identifier(:null) - end - - catch_block = if catch_block do - process_catch_block(catch_block, env) - else - JS.identifier(:null) - end - - after_block = if after_block do - process_after_block(after_block, env) - else - JS.identifier(:null) - end - - else_block = if else_block do - Function.make_anonymous_function(else_block, env) - |> elem(0) - else - JS.identifier(:null) - end - - js_ast = JS.call_expression( - JS.member_expression( - Primitive.special_forms(), - JS.identifier("_try") - ), - [ - try_block, - rescue_block, - catch_block, - else_block, - after_block - ] - ) - - { js_ast, env } - end - - defp process_rescue_block(rescue_block, env) do - { func, _ } = 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) - - func - end - - defp process_catch_block(catch_block, env) do - {func, _} = catch_block - |> Function.make_anonymous_function(env) - - func - end - - defp process_after_block(after_block, env) do - { translated_body, _ } = Function.prepare_function_body(after_block, env) - translated_body = JS.block_statement(translated_body) - - 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 diff --git a/lib/elixir_script/translator/kernel/special_forms/with.ex b/lib/elixir_script/translator/kernel/special_forms/with.ex deleted file mode 100644 index dbd8b7bf..00000000 --- a/lib/elixir_script/translator/kernel/special_forms/with.ex +++ /dev/null @@ -1,52 +0,0 @@ -defmodule ElixirScript.Translator.With do - @moduledoc false - - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator.Function - alias ElixirScript.Translator.Primitive - alias ElixirScript.Translator.PatternMatching - - def make_with(args, env) do - result = Enum.reduce(args, %{ expressions: [], arguments: [] }, fn - {symbol, _, [pattern, expr] }, state when symbol in [:<-, :=] -> - {body , _} = Function.prepare_function_body(expr, env) - translated_body = JS.block_statement(body) - expr_function = JS.function_expression(state.arguments, [], translated_body) - - { patterns, params, _ } = PatternMatching.process_match([pattern], env) - - %{state | arguments: state.arguments ++ params, - expressions: state.expressions ++ [ JS.array_expression([hd(patterns), expr_function]) ] } - - [do: expr], state -> - expr_function = process_do_block(expr, state.arguments, env) - - %{state | expressions: state.expressions ++ [ expr_function ] } - [do: do_expr, else: else_expr], state -> - do_function = process_do_block(do_expr, state.arguments, env) - - { else_function, _ } = Function.make_anonymous_function(else_expr, env) - - %{state | expressions: state.expressions ++ [ do_function, else_function ] } - end) - - expressions = result.expressions - - js_ast = JS.call_expression( - JS.member_expression( - Primitive.special_forms(), - JS.identifier("_with") - ), - expressions - ) - - { js_ast, env } - - end - - defp process_do_block(expr, arguments, env) do - {body , _} = Function.prepare_function_body(expr, env) - translated_body = JS.block_statement(body) - JS.function_expression(arguments, [], translated_body) - end -end diff --git a/lib/elixir_script/translator/lexical_scope.ex b/lib/elixir_script/translator/lexical_scope.ex deleted file mode 100644 index 2c20d61c..00000000 --- a/lib/elixir_script/translator/lexical_scope.ex +++ /dev/null @@ -1,374 +0,0 @@ -defmodule ElixirScript.Translator.LexicalScope do - @moduledoc false - - @type t :: %ElixirScript.Translator.LexicalScope{ - module: atom, - file: binary, - line: non_neg_integer, - function: { atom, non_neg_integer } | nil, - context: :match | :guard | :generator | nil, - aliases: [{atom, atom}], - requires: [atom], - functions: [{atom, [{ atom, non_neg_integer }]}], - macros: [{atom, [{ atom, non_neg_integer }]}], - macro_aliases: [{atom, {integer, atom}}], - context_modules: [atom], - vars: [{atom, atom | non_neg_integer}], - export_vars: [{atom, atom | non_neg_integer}] | nil, - lexical_tracker: nil, - caller: t | nil, - env: nil - } - - defstruct [ - module: nil, - file: nil, - line: 0, - function: nil, - context: nil, - aliases: [], - requires: [], - functions: [], - macros: [], - macro_aliases: [], - context_modules: [], - vars: [], - export_vars: nil, - lexical_tracker: nil, - caller: nil, - env: nil - ] - - def env(scope) do - %Macro.Env{ - module: scope.module, - file: scope.file, - line: scope.line, - function: scope.function, - context: scope.context, - aliases: scope.aliases, - requires: scope.requires, - functions: scope.functions, - macros: scope.macros, - macro_aliases: scope.macro_aliases, - context_modules: scope.context_modules, - vars: Enum.map(scope.vars, fn({key, _}) -> {key, nil} end), - export_vars: scope.export_vars, - lexical_tracker: scope.lexical_tracker - } - end - - def caller(scope) do - %Macro.Env{ - module: scope.caller.module, - file: scope.caller.file, - line: scope.caller.line, - function: scope.caller.function, - context: scope.caller.context, - aliases: scope.caller.aliases, - requires: scope.caller.requires, - functions: scope.caller.functions, - macros: scope.caller.macros, - macro_aliases: scope.caller.macro_aliases, - context_modules: scope.caller.context_modules, - vars: Enum.map(scope.vars, fn({key, _}) -> {key, nil} end), - export_vars: scope.caller.export_vars, - lexical_tracker: scope.caller.lexical_tracker - } - end - - def module_scope(ElixirScript.Temp, filename, env) do - - env = %ElixirScript.Translator.LexicalScope { - module: ElixirScript.Temp, file: filename, requires: [], - functions: [], - macros: [], - env: env - } - - add_import(env, ElixirScript.Kernel) - end - - def module_scope(module_name, filename, env) do - module = ElixirScript.Translator.State.get_module(module_name) - - env = %ElixirScript.Translator.LexicalScope { - module: module_name, file: filename, requires: [], - functions: [{ module.name, module.functions}], - macros: [{module.name, module.macros}], - env: env - } - - env = add_import(env, ElixirScript.Kernel) - - cond do - module_name == JS -> - env - ElixirScript.Translator.State.is_module_loaded?(module_name) and length(module.macros) > 0 -> - add_import(env, module_name, [only: :macros]) - true -> - env - end - end - - def function_scope(env, { _, _ } = func) do - %{ env | function: func, caller: env, vars: [] } - end - - def function_scope(env, nil) do - %{ env | function: nil, caller: env } - end - - def find_module(env, name_arity) do - result = Enum.find(env.functions ++ env.macros, fn({_, functions}) -> - name_arity in functions - end) - - if result == nil do - nil - else - elem(result, 0) - end - - end - - def add_var(env, variable_name) when is_binary(variable_name) do - add_var(env, String.to_atom(variable_name)) - end - - def add_var(env, variable_name) do - %{ env | vars: Keyword.update(env.vars, variable_name, 0, &(&1 + 1)) } - end - - def get_var(env, variable_name) when is_binary(variable_name) do - get_var(env, String.to_atom(variable_name)) - end - - def get_var(env, variable_name) do - count = Keyword.get(env.vars, variable_name, nil) - - case count do - nil -> - nil - 0 -> - String.to_atom(Atom.to_string(variable_name)) - _ -> - String.to_atom(Atom.to_string(variable_name) <> to_string(count)) - end - end - - def has_var?(env, variable_name) when is_binary(variable_name) do - has_var?(env, String.to_atom(variable_name)) - end - - def has_var?(env, variable_name) do - Keyword.get(env.vars, variable_name, nil) != nil - end - - defp get_module(env, Kernel) do - get_module(env, ElixirScript.Kernel) - end - - defp get_module(env, module_name) do - module = get_module_name(env, module_name) |> ElixirScript.Translator.State.get_module - - unless module do - module_name = case module_name do - {:__aliases__, _, _} -> - ElixirScript.Translator.Utils.quoted_to_name(module_name) - |> Atom.to_string - |> String.split(".") - |> tl - |> Enum.join(".") - _ -> - module_name - end - - raise "Module #{inspect module_name} not found" - end - - if Map.get(module, :load_only, false) == false do - ElixirScript.Translator.State.add_module_reference(env.module, module.name) - end - - module - end - - defp has_module?(env, module_name) do - try do - module = get_module(env, module_name) - case module do - %{load_only: true} -> - false - _ -> - true - end - rescue - _ -> - false - end - end - - def add_alias(env, module_name, alias_name) do - module = get_module(env, module_name) - %{ env | aliases: Enum.uniq(env.aliases ++ [{alias_name, module.name}]) } - end - - def add_import(env, module_name) do - check_for_module_existence(env, module_name) - - env = if ElixirScript.Translator.State.is_module_loaded?(module_name) do - add_import_macro(env, module_name, []) - else - env - end - - if has_module?(env, module_name) do - module = get_module(env, module_name) - %{ env | requires: Enum.uniq(env.requires ++ [module.name]), - functions: env.functions ++ [{ module.name, module.functions }] } - else - env - end - end - - def add_import(env, module_name, [only: :functions]) do - module = get_module(env, module_name) - - %{ env | functions: List.keydelete(env.functions, module_name, 0) ++ [{ module.name, module.functions }], - requires: Enum.uniq(env.requires ++ [module.name]) } - end - - def add_import(env, module_name, [only: :macros]) do - if !ElixirScript.Translator.State.is_module_loaded?(module_name) do - raise "Module #{inspect module_name} not found" - end - - add_import_macro(env, module_name, [only: :macros]) - end - - def add_import(env, module_name, [only: only]) do - check_for_module_existence(env, module_name) - - env = if ElixirScript.Translator.State.is_module_loaded?(module_name) do - list = module_name.__info__(:macros) - list = Enum.filter(list, fn(mac) -> mac in only end) - add_import_macro(env, module_name, [only: list]) - else - env - end - - if has_module?(env, module_name) do - module = get_module(env, module_name) - - functions = Enum.filter(module.functions, fn(func) -> - func in only - end) - - %{ env | requires: Enum.uniq(env.requires ++ [module.name]), - functions: List.keydelete(env.functions, module.name, 0) ++ [{ module.name, functions }] } - else - env - end - end - - def add_import(env, module_name, [except: except]) do - check_for_module_existence(env, module_name) - - env = if ElixirScript.Translator.State.is_module_loaded?(module_name) do - list = module_name.__info__(:macros) - list = Enum.filter(list, fn(mac) -> mac in except end) - add_import_macro(env, module_name, [except: list]) - else - env - end - - if has_module?(env, module_name) do - module = get_module(env, module_name) - {_, current_functions } = List.keyfind(env.functions, module.name, 0, { module.name, module.functions }) - - functions = Enum.filter(current_functions, fn(func) -> not(func in except) end) - - %{ env | requires: env.requires ++ [module.name], - functions: List.keydelete(env.functions, module.name, 0) ++ [{ module.name, functions }] } - else - env - end - end - - def add_require(env, module_name) do - check_for_module_existence(env, module_name) - - env = if ElixirScript.Translator.State.is_module_loaded?(module_name) do - add_require_macro(env, module_name, []) - else - env - end - - if has_module?(env, module_name) do - module = get_module(env, module_name) - %{ env | requires: Enum.uniq(env.requires ++ [module.name]) } - else - env - end - end - - def add_require(env, module_name, alias_name) do - check_for_module_existence(env, module_name) - - env = if ElixirScript.Translator.State.is_module_loaded?(module_name) do - add_require_macro(env, module_name, [as: alias_name]) - else - env - end - - if has_module?(env, module_name) do - module = get_module(env, module_name) - %{ env | aliases: Enum.uniq(env.aliases ++ [{alias_name, module.name}]), - requires: Enum.uniq(env.requires ++ [module.name]) } - else - env - end - end - - def get_module_name(env, module_name) do - module_name = ElixirScript.Translator.State.get_module_name(module_name) - - if Keyword.has_key?(env.aliases, module_name) do - Keyword.fetch!(env.aliases, module_name) - else - module_name - end - - end - - defp check_for_module_existence(env, module_name) do - if ElixirScript.Translator.State.is_module_loaded?(module_name) == false and has_module?(env, module_name) == false do - raise "Module #{inspect module_name} not found" - end - end - - - defp add_import_macro(elixirscript_env, module, opts) do - eval = """ - import #{inspect module}, #{inspect opts} - __ENV__ - """ - - do_macro(eval, elixirscript_env) - end - - defp add_require_macro(elixirscript_env, module, opts) do - eval = """ - require #{inspect module}, #{inspect opts} - __ENV__ - """ - - do_macro(eval, elixirscript_env) - end - - defp do_macro(eval, elixirscript_env) do - {env, _} = Code.eval_string(eval, [], elixirscript_env.env) - %{ elixirscript_env | env: env } - end -end diff --git a/lib/elixir_script/translator/pattern_matching.ex b/lib/elixir_script/translator/pattern_matching.ex deleted file mode 100644 index 946e920e..00000000 --- a/lib/elixir_script/translator/pattern_matching.ex +++ /dev/null @@ -1,278 +0,0 @@ -defmodule ElixirScript.Translator.PatternMatching do - @moduledoc false - - alias ESTree.Tools.Builder, as: JS - alias ElixirScript.Translator - alias ElixirScript.Translator.Primitive - alias ElixirScript.Translator.Identifier - alias ElixirScript.Translator.Map - alias ElixirScript.Translator.Struct - alias ElixirScript.Translator.Bitstring - - @patterns JS.member_expression( - JS.member_expression( - JS.identifier("Elixir"), - JS.identifier("Core") - ), - JS.identifier("Patterns") - ) - - @wildcard JS.member_expression( - @patterns, - JS.identifier(:wildcard) - ) - - @parameter JS.member_expression( - @patterns, - JS.identifier(:variable) - ) - - @head_tail JS.member_expression( - @patterns, - JS.identifier(:headTail) - ) - - @starts_with JS.member_expression( - @patterns, - JS.identifier(:startsWith) - ) - - @capture JS.member_expression( - @patterns, - JS.identifier(:capture) - ) - - @bound JS.member_expression( - @patterns, - JS.identifier(:bound) - ) - - @_type JS.member_expression( - @patterns, - JS.identifier(:type) - ) - - @bitstring_match JS.member_expression( - @patterns, - JS.identifier(:bitStringMatch) - ) - - def wildcard() do - JS.call_expression( - @wildcard, - [] - ) - end - - def parameter() do - JS.call_expression( - @parameter, - [] - ) - end - - def parameter(default_value) do - JS.call_expression( - @parameter, - [default_value] - ) - end - - def head_tail(headParameter, tailParameter) do - JS.call_expression( - @head_tail, - [headParameter, tailParameter] - ) - 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 type(prototype, value) do - JS.call_expression( - @_type, - [prototype, value] - ) - end - - def bitstring_match(values) do - JS.call_expression( - @bitstring_match, - values - ) - end - - - def process_match(params, env) do - build_match(params, env) - |> update_env(env) - end - - defp update_env({ patterns, params }, env) do - - { params, env } = Enum.map_reduce(params, env, fn - (%ESTree.Identifier{name: :undefined} = param, env) -> - { param, env } - - (%ESTree.Identifier{} = param, env) -> - env = ElixirScript.Translator.LexicalScope.add_var(env, param.name) - new_name = ElixirScript.Translator.LexicalScope.get_var(env, param.name) - - { %{ param | name: new_name }, env } - - (param, env) -> - { param, env } - end) - - { patterns, params, env } - 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({:_, _, _}, _) do - { [wildcard()], [JS.identifier(:undefined)] } - end - - defp do_build_match({:<<>>, _, elements}, env) do - params = Enum.reduce(elements, [], fn - ({:::, _, [{ variable, _, params }, _]}, state) when is_atom(params) -> - state ++ [JS.identifier(variable)] - _, state -> - state - end) - - elements = Enum.map(elements, fn - ({:::, context, [{ _, _, params }, options]}) when is_atom(params) -> - Bitstring.make_bitstring_element({:::, context, [ElixirScript.Translator.PatternMatching, options]}, env) - x -> - Bitstring.make_bitstring_element(x, env) - end) - - { [bitstring_match(elements)], params } - end - - defp do_build_match([{:|, _, [head, tail]}], env) do - { head_patterns, head_params } = do_build_match(head, env) - { tail_patterns, tail_params } = do_build_match(tail, env) - params = head_params ++ tail_params - - { [head_tail(hd(head_patterns), hd(tail_patterns))], params } - 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) - property = case key do - {:^, _, [the_key]} -> - JS.property(Translator.translate!(the_key, env), hd(List.wrap(pattern)), :init, false, false, true) - _ -> - Map.make_property(Translator.translate!(key, env), hd(List.wrap(pattern))) - end - - { property, 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 - struct_name = Struct.get_struct_class(name, env) - {pattern, params} = do_build_match({:%{}, meta, props}, env) - - { [type(struct_name, pattern)], params } - 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 - - pattern = JS.object_expression([ - JS.property( - JS.identifier("values"), - JS.array_expression(patterns) - ) - ]) - - { [type(Primitive.tuple_class, pattern)], params } - end - - defp do_build_match({:\\, _, [{name, _, _}, default]}, env) do - { [parameter(Translator.translate!(default, env))], [Identifier.make_identifier(name)] } - end - - - defp do_build_match({name, _, _}, _) do - { [parameter()], [Identifier.make_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 ++ [Identifier.make_identifier(target)] } - end - -end diff --git a/lib/elixir_script/translator/rewriter.ex b/lib/elixir_script/translator/rewriter.ex deleted file mode 100644 index 5039fe0a..00000000 --- a/lib/elixir_script/translator/rewriter.ex +++ /dev/null @@ -1,523 +0,0 @@ -defmodule ElixirScript.Translator.Rewriter do - @moduledoc false - - # :erlang, :lists, :maps, :beam_lib, :binary, :calendar, :digraph, - # :epp, :erl_lint, :erl_internal, :erl_expand_records, :erl_eval, - # :ets, :filename, :gen_event, :gen_server, :io, :io_lib, :math, - # :ordsets, :proc_lib, :rand, :re, :sets, :supervisor,:sys, :timer, - # :unicode, :os, :application, :code, :gen_tcp, :error_logger, :gen, - # :file - # http://erlang.org/doc/applications.html - - - def rewrite({{:., _, [:erlang, :abs]}, _, [number]}) do - quote do: Math.abs(unquote(number)) - end - - def rewrite({{:., _, [:erlang, :apply]}, _, [fun, args]}) do - quote do: unquote(fun).apply(nil, unquote(args)) - end - - def rewrite({{:., _, [:erlang, :apply]}, _, [module, fun, args]}) do - quote do: unquote(module).unquote(fun).apply(nil, unquote(args)) - end - - def rewrite({{:., _, [:erlang, :binary_part]}, _, [binary, start, length]})do - quote do: unquote(binary).substring(unquote(start), unquote(length)) - end - - def rewrite({{:., _, [:erlang, :bit_size]}, _, [bitstring]})do - quote do: unquote(bitstring).bit_size - end - - def rewrite({{:., _, [:erlang, :byte_size]}, _, [bitstring]})do - quote do: unquote(bitstring).byte_size - end - - def rewrite({{:., _, [:erlang, :div]}, _, [left, right]}) do - quote do: unquote(left) / unquote(right) - end - - def rewrite({{:., _, [:erlang, :exit]}, _, [reason]}) do - # TODO: implement exit - quote do: nil - end - - def rewrite({{:., _, [:erlang, :hd]}, _, [list]}) do - quote do: unquote(list)[0] - end - - def rewrite({{:., _, [:erlang, :is_atom]}, _, [term]}) do - quote do: JS.typeof(unquote(term)) === "symbol" - end - - def rewrite({{:., _, [:erlang, :is_binary]}, _, [term]}) do - quote do: JS.typeof(unquote(term)) === "string" - end - - def rewrite({{:., _, [:erlang, :is_bitstring]}, _, [term]}) do - quote do: is_binary(unquote(term)) || JS.instanceof(unquote(term), Elixir.Core.BitString) - end - - def rewrite({{:., _, [:erlang, :is_boolean]}, _, [term]}) do - quote do: JS.typeof(unquote(term)) === "boolean" || JS.instanceof(unquote(term), Boolean) - end - - def rewrite({{:., _, [:erlang, :is_float]}, _, [term]}) do - quote do: (JS.typeof(unquote(term)) === "number" || JS.instanceof(unquote(term), Number)) && !Number.isInteger(unquote(term)) - end - - def rewrite({{:., _, [:erlang, :is_function]}, _, [term]}) do - quote do: JS.typeof(unquote(term)) === "function" || JS.instanceof(unquote(term), Function) - end - - def rewrite({{:., _, [:erlang, :is_function]}, _, [term, _]}) do - quote do: JS.typeof(unquote(term)) === "function" || JS.instanceof(unquote(term), Function) - end - - def rewrite({{:., _, [:erlang, :is_integer]}, _, [term]}) do - quote do: Number.isInteger(unquote(term)) - end - - def rewrite({{:., _, [:erlang, :is_list]}, _, [term]}) do - quote do: Array.isArray(unquote(term)) - end - - def rewrite({{:., _, [:erlang, :is_number]}, _, [term]}) do - quote do: JS.typeof(unquote(term)) === "number" || JS.instanceof(unquote(term), Number) - end - - def rewrite({{:., _, [:erlang, :is_pid]}, _, [term]}) do - quote do: JS.instanceof(unquote(term), Elixir.Core.PID) - end - - def rewrite({{:., _, [:erlang, :is_port]}, _, [_term]}) do - #TODO implement is_port - quote do: false - end - - def rewrite({{:., _, [:erlang, :is_reference]}, _, [_term]}) do - #TODO implement is_reference - quote do: false - end - - def rewrite({{:., _, [:erlang, :is_tuple]}, _, [term]}) do - quote do: JS.instanceof(unquote(term), Elixir.Core.Tuple) - end - - def rewrite({{:., _, [:erlang, :is_map]}, _, [term]}) do - quote do: JS.typeof(unquote(term)) === "object" || JS.instanceof(unquote(term), Object) - end - - def rewrite({{:., _, [:erlang, :length]}, _, [list]}) do - quote do: unquote(list).length - end - - def rewrite({{:., _, [:erlang, :make_ref]}, _, []}) do - #TODO: implement make_ref - quote do: nil - end - - def rewrite({{:., _, [:erlang, :map_size]}, _, [map]}) do - quote do: Object.keys(unquote(map)).length - end - - def rewrite({{:., _, [:erlang, :max]}, _, [first, second]}) do - quote do: Math.max(unquote(first), unquote(second)) - end - - def rewrite({{:., _, [:erlang, :min]}, _, [first, second]}) do - quote do: Math.min(unquote(first), unquote(second)) - end - - def rewrite({{:., _, [:erlang, :node]}, _, []}) do - quote do: :nonode@nohost - end - - def rewrite({{:., _, [:erlang, :node]}, _, [_]}) do - quote do: :nonode@nohost - end - - def rewrite({{:., _, [:erlang, :rem]}, _, [first, second]}) do - {:%, [], [first, second]} - end - - def rewrite({{:., _, [:erlang, :round]}, _, [number]}) do - quote do: Math.round(unquote(number)) - end - - def rewrite({{:., _, [:erlang, :send]}, _, [dest, msg]}) do - #TODO implement send - quote do: unquote(msg) - end - - def rewrite({{:., _, [:erlang, :self]}, _, []}) do - #TODO: implement self - quote do: nil - end - - def rewrite({{:., _, [:erlang, :spawn]}, _, [_fun]}) do - #TODO: implement spawn - quote do: nil - end - - def rewrite({{:., _, [:erlang, :spawn]}, _, [_module, _fun, _args]}) do - #TODO: implement spawn - quote do: nil - end - - def rewrite({{:., _, [:erlang, :spawn_link]}, _, [_fun]}) do - #TODO: implement spawn_link - quote do: nil - end - - def rewrite({{:., _, [:erlang, :spawn_link]}, _, [_module, _fun, _args]}) do - #TODO: implement spawn_link - quote do: nil - end - - def rewrite({{:., _, [:erlang, :spawn_monitor]}, _, [_fun]}) do - #TODO: implement spawn_monitor - quote do: nil - end - - def rewrite({{:., _, [:erlang, :spawn_monitor]}, _, [_module, _fun, _args]}) do - #TODO: implement spawn_monitor - quote do: nil - end - - def rewrite({{:., _, [:erlang, :throw]}, _, [term]}) do - quote do: JS.throw(unquote(term)) - end - - def rewrite({{:., _, [:erlang, :tl]}, _, [list]}) do - quote do: unquote(list).splice(1) - end - - def rewrite({{:., _, [:erlang, :trunc]}, _, [number]}) do - quote do: Math.floor(unquote(number)) - end - - def rewrite({{:., _, [:erlang, :tuple_size]}, _, [tuple]}) do - quote do: unquote(tuple).length - end - - def rewrite({{:., _, [:erlang, operator]}, _, [left, right]}) when operator in [:+, :-, :*, :/, :<, :>, :>=, :==] do - {operator, [], [left, right]} - end - - def rewrite({{:., _, [:erlang, operator]}, _, [value]}) when operator in [:+, :-] do - {operator, [], [value]} - end - - def rewrite({{:., _, [:erlang, :++]}, _, [left, right]}) do - quote do: unquote(left).concat(unquote(right)) - end - - def rewrite({{:., _, [:erlang, :--]}, _, [list, element]}) do - quote do: unquote(list).slice(unquote(list).indexOf(unquote(element)) + 1) - end - - def rewrite({{:., _, [:erlang, :not]}, _, [value]}) do - {:!, [], [value]} - end - - def rewrite({{:., _, [:erlang, :"=<"]}, _, [left, right]}) do - {:<=, [], [left, right]} - end - - def rewrite({{:., _, [:erlang, :"/="]}, _, [left, right]}) do - {:!=, [], [left, right]} - end - - def rewrite({{:., _, [:erlang, :"=:="]}, _, [left, right]}) do - {:===, [], [left, right]} - end - - def rewrite({{:., _, [:erlang, :"=/="]}, _, [left, right]}) do - {:!==, [], [left, right]} - end - - def rewrite({{:., _, [:erlang, :element]}, _, [index, tuple]}) do - quote do: unquote(tuple).get(unquote(index) - 1) - end - - def rewrite({{:., _, [:erlang, :setelement]}, _, [index, tuple, value]}) do - quote do: unquote(tuple).put_elem(unquote(index) - 1, unquote(value)) - end - - def rewrite({{:., _, [:erlang, :orelse]}, _, [left, right]}) do - {:||, [], [left, right]} - end - - def rewrite({{:., _, [:erlang, :or]}, _, [left, right]}) do - {:||, [], [left, right]} - end - - def rewrite({{:., _, [:erlang, :andalso]}, _, [left, right]}) do - {:&&, [], [left, right]} - end - - def rewrite({{:., _, [:erlang, :error]}, _, [error]}) do - quote do: JS.throw(unquote(error)) - end - - def rewrite({{:., _, [:erlang, :raise]}, _, [_class, reason, _stacktrace]}) do - quote do: JS.throw(unquote(reason)) - end - - def rewrite({{:., _, [:erlang, :atom_to_binary]}, _, [atom, _]}) do - quote do: Symbol.keyFor(unquote(atom)) - end - - def rewrite({{:., _, [:erlang, :atom_to_list]}, _, [atom]}) do - quote do: to_string(unquote(atom)).split("") - end - - def rewrite({{:., _, [:erlang, :bnot]}, _, [expr]}) do - {:"~", [], [expr]} - end - - def rewrite({{:., _, [:erlang, :band]}, _, [left, right]}) do - {:&, [], [left, right]} - end - - def rewrite({{:., _, [:erlang, :bor]}, _, [left, right]}) do - {:|, [], [left, right]} - end - - def rewrite({{:., _, [:erlang, :bxor]}, _, [left, right]}) do - {:^, [], [left, right]} - end - - def rewrite({{:., _, [:erlang, :bsl]}, _, [left, right]}) do - {:"<<", [], [left, right]} - end - - def rewrite({{:., _, [:erlang, :bsr]}, _, [left, right]}) do - {:">>", [], [left, right]} - end - - def rewrite({{:., _, [:erlang, :function_exported]}, _, [_, _, _]}) do - quote do: true - end - - def rewrite({{:., _, [:erlang, :make_tuple]}, _, [size, data]}) do - quote do: JS.new(Elixir.Core.Tuple, List.duplicate(unquote(size), unquote(data))) - end - - def rewrite({{:., _, [:erlang, :insert_element]}, _, [index, tuple, term]}) do - quote do: unquote(tuple).put_elem(unquote(index) - 1, unquote(term)) - end - - def rewrite({{:., _, [:erlang, :tuple_to_list]}, _, [tuple]}) do - quote do: unquote(tuple).values - end - - def rewrite({{:., _, [:erlang, :append_element]}, _, [tuple, value]}) do - quote do: unquote(tuple).put_elem(unquote(tuple).length, unquote(value)) - end - - def rewrite({{:., _, [:erlang, :delete_element]}, _, [index, tuple]}) do - quote do: unquote(tuple).remove_elem(unquote(index)) - end - - def rewrite({{:., _, [:lists, :map]}, _, [fun, list]}) do - quote do: unquote(list).map(unquote(fun)) - end - - def rewrite({{:., _, [:lists, :member]}, _, [elem, list]}) do - quote do: unquote(list).indexOf(unquote(elem)) > -1 - end - - def rewrite({{:., _, [:lists, :foldl]}, _, [fun, acc, list]}) do - quote do: Elixir.Core.Functions.foldl(unquote(fun), unquote(acc), unquote(list)) - end - - def rewrite({{:., _, [:lists, :foldr]}, _, [fun, acc, list]}) do - quote do: Elixir.Core.Functions.foldr(unquote(fun), unquote(acc), unquote(list)) - end - - def rewrite({{:., _, [:lists, :keymember]}, _, [key, n, list]}) do - quote do: Elixir.Core.Functions.keymember(unquote(key), unquote(n), unquote(list)) - end - - def rewrite({{:., _, [:lists, :keydelete]}, _, [key, n, list]}) do - quote do: Elixir.Core.Functions.keydelete(unquote(key), unquote(n), unquote(list)) - end - - def rewrite({{:., _, [:lists, :keystore]}, _, [key, n, list, newtuple]}) do - quote do: Elixir.Core.Functions.keystore(unquote(key), unquote(n), unquote(list), unquote(newtuple)) - end - - def rewrite({{:., _, [:lists, :keytake]}, _, [key, n, list]}) do - quote do: Elixir.Core.Functions.keystore(unquote(key), unquote(n), unquote(list)) - end - - def rewrite({{:., _, [:lists, :keyfind]}, _, [key, n, list]}) do - quote do: Elixir.Core.Functions.keyfind(unquote(key), unquote(n), unquote(list)) - end - - def rewrite({{:., _, [:lists, :keyreplace]}, _, [key, n, list, newtuple]}) do - quote do: Elixir.Core.Functions.keyreplace(unquote(key), unquote(n), unquote(list), unquote(newtuple)) - end - - def rewrite({{:., _, [:lists, :keysort]}, _, [n, tuplelist]}) do - #TODO: implement keysort - quote do: unquote(tuplelist) - end - - def rewrite({{:., _, [:lists, :reverse]}, _, [list]}) do - quote do: Elixir.Core.Functions.reverse(unquote(list)) - end - - def rewrite({{:., _, [:lists, :reverse]}, _, [list, tail]}) do - quote do: Elixir.Core.Functions.reverse(unquote(list)) ++ unquote(tail) - end - - def rewrite({{:., _, [:lists, :flatten]}, _, [list]}) do - quote do: Elixir.Core.Functions.flatten(unquote(list)) - end - - def rewrite({{:., _, [:lists, :flatten]}, _, [list, tail]}) do - quote do: Elixir.Core.Functions.flatten(unquote(list), unquote(tail)) - end - - def rewrite({{:., _, [:lists, :delete]}, _, [elem, list]}) do - quote do: Elixir.Core.Functions.remove_from_list(unquote(list), unquote(elem)) - end - - def rewrite({{:., _, [:lists, :duplicate]}, _, [n, elem]}) do - quote do: Elixir.Core.Functions.duplicate(unquote(n), unquote(elem)) - end - - def rewrite({{:., _, [:lists, :mapfoldl]}, _, [fun, acc, list]}) do - quote do: Elixir.Core.Functions.mapfoldl(unquote(fun), unquote(acc), unquote(list)) - end - - def rewrite({{:., _, [:lists, :sort]}, _, [list]}) do - quote do: unquote(list).sort() - end - - #TODO: Implement sort - def rewrite({{:., _, [:lists, :sort]}, _, [_fun, list]}) do - quote do: unquote(list) - end - - def rewrite({{:., _, [:lists, :filter]}, _, [pred, list]}) do - quote do: unquote(list).filter(unquote(pred)) - end - - def rewrite({{:., _, [:lists, :filtermap]}, _, [fun, list]}) do - quote do: Elixir.Core.Functions.filtermap(unquote(fun), unquote(list)) - end - - def rewrite({{:., _, [:lists, :concat]}, _, [things]}) do - quote do: unquote(things).join("") - end - - def rewrite({{:., _, [:erlang, :binary_to_atom]}, _, [binary, _]}) do - quote do: Symbol.for(unquote(binary)) - end - - def rewrite({{:., _, [:erlang, :binary_to_existing_atom]}, _, [binary, _]}) do - quote do: Symbol.for(unquote(binary)) - end - - def rewrite({{:., _, [:erlang, :list_to_atom]}, _, [char_list]}) do - quote do: Symbol.for(unquote(char_list)) - end - - def rewrite({{:., _, [:erlang, :list_to_existing_atom]}, _, [char_list]}) do - quote do: Symbol.for(unquote(char_list)) - end - - def rewrite({{:., _, [:erlang, :list_to_tuple]}, _, [list]}) do - quote do: JS.new(Elixir.Core.Tuple, unquote(list)) - end - - def rewrite({{:., _, [:erlang, :list_to_float]}, _, [list]}) do - quote do: parseFloat(unquote(list)) - end - - def rewrite({{:., _, [:erlang, :list_to_integer]}, _, [list]}) do - quote do: parseInt(unquote(list)) - end - - def rewrite({{:., _, [:erlang, :list_to_integer]}, _, [list, base]}) do - quote do: parseInt(unquote(list), unquote(base)) - end - - def rewrite({{:., _, [:erlang, :integer_to_binary]}, _, [integer]}) do - quote do: unquote(integer).toString() - end - - def rewrite({{:., _, [:erlang, :integer_to_binary]}, _, [integer, base]}) do - quote do: unquote(integer).toString(unquote(base)) - end - - def rewrite({{:., _, [:erlang, :integer_to_list]}, _, [integer]}) do - quote do: unquote(integer).toString() - end - - def rewrite({{:., _, [:erlang, :integer_to_list]}, _, [integer, base]}) do - quote do: unquote(integer).toString(unquote(base)) - end - - def rewrite({{:., _, [:erlang, :float_to_binary]}, _, [float]}) do - quote do: unquote(float).toString() - end - - def rewrite({{:., _, [:erlang, :float_to_binary]}, _, [float, base]}) do - quote do: unquote(float).toString(unquote(base)) - end - - def rewrite({{:., _, [:erlang, :float_to_list]}, _, [float]}) do - quote do: unquote(float).toString() - end - - def rewrite({{:., _, [:erlang, :float_to_list]}, _, [float, base]}) do - quote do: unquote(float).toString(unquote(base)) - end - - def rewrite({{:., _, [:erlang, :binary_to_float]}, _, [binary]}) do - quote do: parseFloat(unquote(binary)) - end - - def rewrite({{:., _, [:erlang, :binary_to_integer]}, _, [binary]}) do - quote do: parseInt(unquote(binary)) - end - - def rewrite({{:., _, [:erlang, :binary_to_integer]}, _, [binary, base]}) do - quote do: parseInt(unquote(binary), unquote(base)) - end - - def rewrite({{:., _, [:maps, :is_key]}, _, [key, map]}) do - quote do: unquote(key) in Elixir.Core.Functions.get_object_keys(unquote(map)) - end - - def rewrite({{:., _, [:maps, :put]}, _, [key, value, map]}) do - quote do: Elixir.Core.Functions.add_property_to_map(unquote(map), unquote(key), unquote(value)) - end - - def rewrite({{:., _, [:maps, :update]}, _, [key, value, map]}) do - quote do: Elixir.Core.Functions.update_map(unquote(map), unquote(key), unquote(value)) - end - - def rewrite({{:., _, [:maps, :find]}, _, [key, map]}) do - quote do: Elixir.Core.Functions.maps_find(unquote(key), unquote(map)) - end - - def rewrite({{:., _, [:maps, :remove]}, _, [key, map]}) do - quote do: Elixir.Core.Functions.delete_property_from_map(unquote(map), unquote(key)) - end - - def rewrite({{:., _, [:maps, :fold]}, _, [fun, init, map]}) do - quote do: Elixir.Core.Functions.maps_fold(unquote(fun), unquote(init), unquote(map)) - end - - def rewrite({{:., _, [:maps, :from_list]}, _, [list]}) do - quote do: Elixir.Core.Functions.maps_fold(unquote(list)) - end - -end diff --git a/lib/elixir_script/translator/state.ex b/lib/elixir_script/translator/state.ex deleted file mode 100644 index bbd56fdb..00000000 --- a/lib/elixir_script/translator/state.ex +++ /dev/null @@ -1,178 +0,0 @@ -# This agent holds references to the compiler options, a map all of the modules, and -# a map of modules that define the standard library. -# -# The modules map has the module's name a the key and a ElixirScript.Module struct as the value. -# -# The std_lib_map holds a mapping of the Elixir standard lib module to the -# version implemented here in ElixirScript. -defmodule ElixirScript.Translator.State do - @moduledoc false - alias ElixirScript.Translator.Utils - - def start_link(compiler_opts, loaded_modules) do - Agent.start_link(fn -> - %{ compiler_opts: compiler_opts, modules: Keyword.new, std_lib_map: build_standard_lib_map(), loaded_modules: loaded_modules } - end, name: __MODULE__) - end - - def serialize() do - Agent.get(__MODULE__, fn(state) -> - modules = state.modules - modules = Enum.map(modules, fn {m, d} -> - d = Map.delete(d, :javascript_ast) - |> Map.delete(:javascript_code) - |> Map.delete(:javascript_name) - - {m, d} - end) - |> Enum.filter(fn {_, d} -> d.type != :consolidated end) - - state = Map.delete(state, :changed_modules) - |> Map.put(:modules, modules) - - :erlang.term_to_binary(state) - end) - end - - def deserialize(frozen_state, loaded_modules \\ []) do - Agent.update(__MODULE__, fn state -> - frozen_state = :erlang.binary_to_term(frozen_state) - modules = Keyword.delete(frozen_state.modules, ElixirScript.Temp) - %{ state | modules: modules, std_lib_map: frozen_state.std_lib_map, loaded_modules: loaded_modules } - end) - end - - defp build_standard_lib_map() do - Map.new - |> Map.put(Kernel, ElixirScript.Kernel) - |> Map.put(Tuple, ElixirScript.Tuple) - |> Map.put(Atom, ElixirScript.Atom) - |> Map.put(Collectable, ElixirScript.Collectable) - |> Map.put(String.Chars, ElixirScript.String.Chars) - |> Map.put(Enumerable, ElixirScript.Enumerable) - |> Map.put(Integer, ElixirScript.Integer) - |> Map.put(Macro.Env, ElixirScript.Macro.Env) - |> Map.put(View, ElixirScript.View) - |> Map.put(Agent, ElixirScript.Agent) - |> Map.put(Range, ElixirScript.Range) - |> Map.put(String, ElixirScript.String) - |> Map.put(Base, ElixirScript.Base) - |> Map.put(Module, ElixirScript.Module) - |> Map.put(Map, ElixirScript.Map) - |> Map.put(Keyword, ElixirScript.Keyword) - |> Map.put(Bitwise, ElixirScript.Bitwise) - |> Map.put(MapSet, ElixirScript.MapSet) - |> Map.put(List, ElixirScript.List) - |> Map.put(Process, ElixirScript.Process) - end - - def set_module_data(module_data) do - Agent.update(__MODULE__, fn state -> - data = Enum.filter(state.modules, fn {module_name, data} -> data.app == :elixir end) - %{ state | modules: Keyword.merge(data, module_data) } - end) - end - - def get_module_data() do - Agent.get(__MODULE__, fn state -> - state.modules - end) - end - - def set_loaded_modules(modules) do - Agent.update(__MODULE__, fn state -> - %{ state | loaded_modules: modules } - end) - end - - def get do - Agent.get(__MODULE__, &(&1)) - end - - def get_module_name({:__aliases__, _, _} = name) do - get_module_name(Utils.quoted_to_name(name)) - end - - def get_module_name(module_name) do - Agent.get(__MODULE__, fn(state) -> - do_get_module_name(module_name, state) - end) - end - - defp do_get_module_name(module_name, state) do - std_lib_map = state.std_lib_map - case Map.get(std_lib_map, module_name) do - nil -> - module_name - actual_module_name -> - actual_module_name - end - end - - def is_module_loaded?(module) when is_atom(module) do - Agent.get(__MODULE__, fn(state) -> - (module in state.loaded_modules) - end) - end - - def is_module_loaded?({:__aliases__, _, _} = module) do - Utils.quoted_to_name(module) - |> is_module_loaded? - end - - def get_module(module) when is_atom(module) do - do_get_module(module) - end - - def get_module({:__aliases__, _, _} = name) do - do_get_module(Utils.quoted_to_name(name)) - end - - def get_module(module_name_list) when is_list(module_name_list) do - do_get_module(Utils.quoted_to_name({:__aliases__, [], module_name_list})) - end - - defp do_get_module(name) do - Agent.get(__MODULE__, fn(state) -> - Keyword.get(state.modules, do_get_module_name(name, state)) - end) - end - - def add_module_reference(module_name, module_ref) do - Agent.update(__MODULE__, fn(state) -> - case Keyword.get(state.modules, do_get_module_name(module_name, state)) do - nil -> - state - module -> - module = Map.update(module, :deps, [module_ref], fn(x) -> Enum.uniq(x ++ [module_ref]) end) - modules = Keyword.put(state.modules, module.name, module) - %{ state | modules: modules } - end - end) - end - - def get_module_references(module_name) do - case get_module(module_name) do - nil -> - [] - module -> - Map.get(module, :deps, []) - end - end - - def list_modules() do - Agent.get(__MODULE__, fn(state) -> - Keyword.values(state.modules) - end) - end - - def list_module_names() do - Agent.get(__MODULE__, fn(state) -> - Keyword.keys(state.modules) - end) - end - - def stop do - Agent.stop(__MODULE__) - end -end diff --git a/lib/elixir_script/translator/unsupported_error.ex b/lib/elixir_script/translator/unsupported_error.ex deleted file mode 100644 index 34bb1b6d..00000000 --- a/lib/elixir_script/translator/unsupported_error.ex +++ /dev/null @@ -1,8 +0,0 @@ -defmodule ElixirScript.Translator.UnsupportedError do - defexception [:message] - - def exception(value) do - msg = "Currently unsupported #{inspect value}" - %ElixirScript.Translator.UnsupportedError{message: msg} - end -end diff --git a/lib/elixir_script/translator/utils.ex b/lib/elixir_script/translator/utils.ex deleted file mode 100644 index 66fd6538..00000000 --- a/lib/elixir_script/translator/utils.ex +++ /dev/null @@ -1,67 +0,0 @@ -defmodule ElixirScript.Translator.Utils do - @moduledoc false - - def quoted_to_name(the_alias) do - {name, _} = Code.eval_quoted(the_alias) - name - end - - def name_to_quoted(name) when is_list(name) do - { :__aliases__, [], name } - end - - def name_to_quoted(name) do - name = name - |> Atom.to_string - |> String.split(".") - |> tl - |> Enum.map(fn x -> String.to_atom(x) end) - - { :__aliases__, [], name } - end - - def name_to_js_name(name) do - { :__aliases__, _, name } = name_to_quoted(name) - Enum.join([:Elixir] ++ name, "$") - end - - def name_to_js_file_name(name) do - { :__aliases__, _, name } = name_to_quoted(name) - Enum.join([:Elixir] ++ name, ".") - end - - def make_local_file_path(module_app_name, file_name) when is_atom(module_app_name) do - root = ElixirScript.Translator.State.get().compiler_opts.root - app_name = to_string(module_app_name) - - case root do - nil -> - Path.join(["..", app_name, file_name]) - root -> - Path.join([root, app_name, file_name]) - end - end - - def make_local_file_path(file_name) do - root = ElixirScript.Translator.State.get().compiler_opts.root - - case root do - nil -> - Path.join([".", file_name]) - root -> - Path.join([root, file_name]) - end - end - - def make_local_file_path(module_app_name, file_name, root) do - app_name = to_string(module_app_name) - - case root do - nil -> - Path.join(["..", app_name, file_name]) - root -> - Path.join([root, app_name, file_name]) - end - end - -end diff --git a/lib/elixir_script/watcher.ex b/lib/elixir_script/watcher.ex deleted file mode 100644 index ba4861d2..00000000 --- a/lib/elixir_script/watcher.ex +++ /dev/null @@ -1,47 +0,0 @@ -defmodule ElixirScript.Watcher do - use GenServer - require Logger - - @moduledoc """ - Watches the input folder for changes and calls the ElixirScript compiler - """ - - - def start_link(input, options) do - GenServer.start_link(__MODULE__, [input: input, options: options]) - end - - def init(args) do - {:ok, _} = Application.ensure_all_started(:elixir_script) - {:ok, _} = Application.ensure_all_started(:fs) - - :fs.subscribe() - {:ok, args} - end - - def handle_info({_pid, {:fs, :file_event}, {path, event}}, state) do - - try do - if input_changed?(to_string(path), state) do - Logger.debug "Event: #{inspect event} Path: #{path}" - ElixirScript.compile_path(state[:input], state[:options]) - end - rescue - x -> - Logger.error(x.message) - end - - {:noreply, state} - end - - defp input_changed?(path, state) do - file = Path.basename(path) - - case file do - "." <> _ -> - false - _ -> - path == Path.absname(Path.join([state[:input], file])) - end - end -end 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 index cbafeb5d..b529251e 100644 --- a/lib/mix/tasks/compile.elixir_script.ex +++ b/lib/mix/tasks/compile.elixir_script.ex @@ -1,51 +1,130 @@ defmodule Mix.Tasks.Compile.ElixirScript do - use Mix.Task + 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` key in your mix project config + 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: "src/exjs", output: "dest/js"], - compilers: [:elixir_script] ++ Mix.compilers - ] + [ + 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 - elixirscript_config = get_elixirscript_config() - input_path = Keyword.fetch!(elixirscript_config, :input) - output_path = Keyword.fetch!(elixirscript_config, :output) + {input, opts} = get_compiler_params() - ElixirScript.compile_path(input_path, %{ output: output_path }) - :ok + 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 - elixirscript_config = get_elixirscript_config() - input_path = Keyword.fetch!(elixirscript_config, :input) - output_path = Keyword.fetch!(elixirscript_config, :output) - - File.ls!(output_path) - |> Enum.each(fn(x) -> - if String.contains?(Path.basename(x), "Elixir.") do - File.rm!(Path.join(output_path, x)) - end + 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 - Keyword.fetch!(config, :elixir_script) + 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 index cc075cfb..7cfb3d33 100644 --- a/lib/mix/tasks/elixirscript.ex +++ b/lib/mix/tasks/elixirscript.ex @@ -1,4 +1,7 @@ defmodule Mix.Tasks.Elixirscript do + @moduledoc """ + A command-line interface to the elixirscript compiler + """ use Mix.Task @shortdoc "Translate Elixir to JavaScript" 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/elixirscript.watch.ex b/lib/mix/tasks/elixirscript.watch.ex deleted file mode 100644 index a5695712..00000000 --- a/lib/mix/tasks/elixirscript.watch.ex +++ /dev/null @@ -1,41 +0,0 @@ -defmodule Mix.Tasks.Elixirscript.Watch do - use Mix.Task - - @shortdoc "Watches ElixirScript files for changes" - - @moduledoc """ - Watches ElixirScript files for changes - - Looks for the `elixir_script` key in your mix project config - - def project do - [ - app: :my_app, - version: "0.1.0", - elixir: "~> 1.0", - deps: deps, - elixir_script: [ input: "src/exjs", output: "dest/js"], - compilers: [:elixir_script] ++ Mix.compilers - ] - end - """ - - - - def run(_) do - Mix.Task.run "app.start" - - elixirscript_config = get_elixirscript_config() - input_path = Keyword.fetch!(elixirscript_config, :input) - output_path = Keyword.fetch!(elixirscript_config, :output) - {:ok, _} = ElixirScript.Watcher.start_link(input_path, %{ output: output_path }) - - :timer.sleep :infinity - end - - defp get_elixirscript_config() do - config = Mix.Project.config - Keyword.fetch!(config, :elixir_script) - end - -end diff --git a/mix.exs b/mix.exs index da64ef93..66184c98 100644 --- a/mix.exs +++ b/mix.exs @@ -4,42 +4,40 @@ defmodule ElixirScript.Mixfile do def project do [ app: :elixir_script, - version: "0.23.3", - 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: [ - extras: ["GettingStarted.md", "FAQ.md"] + main: "ElixirScript", + extras: ["JavaScriptInterop.md", "CompilerInternals.md"] ] ] end def application do [ - applications: [:logger, :estree] + extra_applications: [:logger] ] end defp deps do [ - {:estree, "~> 2.3.0" }, - {:fs, "~> 0.9.1"}, - {:earmark, "~> 1.0", only: :dev }, - {:ex_doc, "~> 0.14", only: :dev }, - {:excoveralls, "~> 0.5", only: :test}, - {:credo, "~> 0.4", only: [:dev, :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: "elixirscript"] - end + defp elixirc_paths(:test), do: ["lib", "test/support"] + defp elixirc_paths(_), do: ["lib"] defp description do """ @@ -49,64 +47,13 @@ defmodule ElixirScript.Mixfile do defp package do [ - files: ["lib", "priv/**/*.js", "mix.exs", "README.md", "LICENSE", "CHANGELOG.md"], + files: ["lib", "priv/**/*.*", "mix.exs", "README.md", "LICENSE", "CHANGELOG.md"], maintainers: ["Bryan Joseph"], licenses: ["MIT"], links: %{ - "GitHub" => "https://github.com/bryanjos/elixirscript" + "GitHub" => "https://github.com/elixirscript/elixirscript" }, build_tools: ["mix"] ] end - - defp aliases do - [dist: &dist/1, - install: &install/1, - std_lib: &std_lib/1] - end - - def std_lib(_) do - Mix.Task.run "app.start" - { _ , _ } = System.cmd("npm", ["run", "build"]) - ElixirScript.compile_std_lib() - end - - def dist(_) do - Mix.Task.run "app.start" - - dist_folder = "dist" - folder_name = "#{dist_folder}/elixirscript" - archive_file_name = "#{dist_folder}/elixirscript.tar.gz" - - Mix.Tasks.Escript.Build.run([]) - - if File.exists?(dist_folder) do - File.rm_rf(dist_folder) - end - - File.mkdir_p(folder_name <> "/bin") - File.cp!("elixirscript", "#{folder_name}/bin/elixirscript") - if File.exists?("priv/.DS_Store") do - File.rm!("priv/.DS_Store") - end - File.cp_r!("priv/", "#{folder_name}") - File.cp!("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/elixirscript.tar.gz"]) - - File.rm_rf!("/usr/local/elixirscript") - - System.cmd("mv", ["dist/elixirscript", "/usr/local/elixirscript"]) - - IO.puts("installed at /usr/local/elixirscript") - end - end diff --git a/mix.lock b/mix.lock index 70217318..3e10cac4 100644 --- a/mix.lock +++ b/mix.lock @@ -1,15 +1,24 @@ -%{"bunt": {:hex, :bunt, "0.1.6", "5d95a6882f73f3b9969fdfd1953798046664e6f77ec4e486e6fafc7caad97c6f", [:mix], []}, - "certifi": {:hex, :certifi, "0.4.0", "a7966efb868b179023618d29a407548f70c52466bf1849b9e8ebd0e34b7ea11f", [:rebar3], []}, - "credo": {:hex, :credo, "0.4.12", "f5e1973405ea25c6e64959fb0b6bf92950147a0278cc2a002a491b45f78f7b87", [:mix], [{:bunt, "~> 0.1.6", [hex: :bunt, optional: false]}]}, - "earmark": {:hex, :earmark, "1.0.2", "a0b0904d74ecc14da8bd2e6e0248e1a409a2bc91aade75fcf428125603de3853", [:mix], []}, - "estree": {:hex, :estree, "2.3.0", "96a190aee9b7ecb05a9486fe0f1e0a912c98b2d98419a84c74956687b6e53c71", [:mix], []}, - "ex_doc": {:hex, :ex_doc, "0.14.3", "e61cec6cf9731d7d23d254266ab06ac1decbb7651c3d1568402ec535d387b6f7", [:mix], [{:earmark, "~> 1.0", [hex: :earmark, optional: false]}]}, - "excoveralls": {:hex, :excoveralls, "0.5.7", "5d26e4a7cdf08294217594a1b0643636accc2ad30e984d62f1d166f70629ff50", [:mix], [{:exjsx, "~> 3.0", [hex: :exjsx, optional: false]}, {:hackney, ">= 0.12.0", [hex: :hackney, optional: false]}]}, - "exjsx": {:hex, :exjsx, "3.2.1", "1bc5bf1e4fd249104178f0885030bcd75a4526f4d2a1e976f4b428d347614f0f", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, optional: false]}]}, - "fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [:rebar], []}, - "hackney": {:hex, :hackney, "1.6.1", "ddd22d42db2b50e6a155439c8811b8f6df61a4395de10509714ad2751c6da817", [:rebar3], [{:certifi, "0.4.0", [hex: :certifi, optional: false]}, {:idna, "1.2.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.0", [hex: :ssl_verify_fun, optional: false]}]}, - "idna": {:hex, :idna, "1.2.0", "ac62ee99da068f43c50dc69acf700e03a62a348360126260e87f2b54eced86b2", [:rebar3], []}, - "jsx": {:hex, :jsx, "2.8.0", "749bec6d205c694ae1786d62cea6cc45a390437e24835fd16d12d74f07097727", [:mix, :rebar], []}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []}, - "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.0", "edee20847c42e379bf91261db474ffbe373f8acb56e9079acb6038d4e0bf414f", [:rebar, :make], []}} +%{ + "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 02cbdac2..3acb3b42 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,20 @@ { "name": "elixirscript", - "version": "0.21.0", + "version": "0.26.1", "description": "Convert Elixir to JavaScript", "main": "elixir.js", "bin": { "elixirscript": "./elixirscript" }, + "engines": { + "node": ">=7.1" + }, "scripts": { "lint": "eslint src/javascript/lib/**/*.js src/javascript/tests/**/*.js", - "build": "rollup -c rollup.config.js", - "clean": "rm -rf priv", - "test": "mocha src/javascript/tests --recursive --compilers js:babel-core/register" + "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", @@ -19,26 +23,35 @@ "author": "", "license": "MIT", "dependencies": { - "erlang-types": "^1.0.0", - "erlang-processes": "^2.0.0", - "tailored": "^2.0.0" + "erlang-types": "^1.0.1", + "grapheme-splitter": "^1.0.2", + "rollup-plugin-commonjs": "^10.0.2", + "tailored": "^2.7.4" }, "devDependencies": { - "babel": "^6.5.2", - "babel-core": "^6.7.7", - "babel-plugin-transform-class-properties": "^6.9.1", - "babel-preset-es2015": "^6.6.0", - "babel-preset-es2015-rollup": "^1.1.1", - "babel-preset-react": "^6.5.0", - "babel-preset-stage-0": "^6.5.0", - "chai": "^3.5.0", - "eslint": "^2.9.0", - "gulp": "^3.9.1", - "gulp-babel": "^6.1.2", - "gulp-sourcemaps": "^2.0.0-alpha", - "mocha": "^2.4.5", - "rollup": "^0.28.0", - "rollup-plugin-babel": "^2.5.1", - "rollup-plugin-node-resolve": "^1.7.0" + "@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/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 index e71e5b51..dbdce767 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,16 +1,52 @@ -import { rollup } from 'rollup'; -import nodeResolve from 'rollup-plugin-node-resolve'; -import babel from 'rollup-plugin-babel'; +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') -export default { - entry: 'src/javascript/elixir.js', - dest: 'priv/elixir/Elixir.Bootstrap.js', - sourceMap: 'inline', - format: 'es6', - plugins: [ - nodeResolve({ jsnext: true }), - babel({ - babelrc: false +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 index 1972ade5..3f58d0a3 100644 --- a/src/javascript/elixir.js +++ b/src/javascript/elixir.js @@ -1,7 +1,5 @@ import Core from './lib/core'; -import Enum from './lib/enum'; export default { Core, - Enum }; diff --git a/src/javascript/lib/core.js b/src/javascript/lib/core.js index e1b229aa..7f87d1da 100644 --- a/src/javascript/lib/core.js +++ b/src/javascript/lib/core.js @@ -1,25 +1,80 @@ -import Processes from 'erlang-processes'; 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'; - -let processes = new Processes.ProcessSystem(); +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 { - ProcessSystem: Processes.ProcessSystem, - processes: processes, Tuple: ErlangTypes.Tuple, PID: ErlangTypes.PID, BitString: ErlangTypes.BitString, + Reference: ErlangTypes.Reference, Patterns, Integer, Float, Functions, SpecialForms, - Store + 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 index f5228161..91bf31e7 100644 --- a/src/javascript/lib/core/functions.js +++ b/src/javascript/lib/core/functions.js @@ -1,439 +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){ - 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); +function call_property(item, property) { + if (!property) { + if (item instanceof Function || typeof item === 'function') { + return item(); } - } 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 }`); + return item; } - if(item[prop] instanceof Function){ - return item[prop](); - }else{ - return item[prop]; - } -} - -function apply(...args){ - if(args.length === 2){ - args[0].apply(null, args.slice(1)); - }else{ - args[0][args[1]].apply(null, args.slice(2)); - } -} + if (item instanceof Map) { + let prop = null; -function contains(left, right){ - for(let x of right){ - if(Core.Patterns.match_or_default(left, x) != null){ - return true; + if (item.has(property)) { + prop = property; + } else if (item.has(Symbol.for(property))) { + prop = Symbol.for(property); } - } - return false; -} + if (prop === null) { + throw new Error(`Property ${property} not found in ${item}`); + } -function get_global(){ - if(typeof(self) !== "undefined"){ - return self; - }else if(typeof(window) !== "undefined"){ - return window; - }else if(typeof(global) !== "undefined"){ - return global; + return item.get(prop); } - throw new Error("No global state found"); -} - -function defstruct(defaults){ - return class { - constructor(update = {}){ - let the_values = Object.assign(defaults, update); - Object.assign(this, the_values); - } + let prop = null; - static create(updates = {}){ - let x = new this(updates); - return Object.freeze(x); + 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); } - }; -} - - -function defexception(defaults){ - return class extends Error { - constructor(update = {}){ - let message = update.message || ""; - super(message); - - let the_values = Object.assign(defaults, update); - Object.assign(this, the_values); + } else if (property in item) { + prop = property; + } else if (Symbol.for(property) in item) { + prop = Symbol.for(property); + } - this.name = this.constructor.name; - this.message = message; - this[Symbol.for("__exception__")] = true; - Error.captureStackTrace(this, this.constructor.name); - } + if (prop === null) { + throw new Error(`Property ${property} not found in ${item}`); + } - static create(updates = {}){ - let x = new this(updates); - return Object.freeze(x); - } - }; + if (item[prop] instanceof Function || typeof item[prop] === 'function') { + return item[prop](); + } + return item[prop]; } -function defprotocol(spec){ +function defprotocol(spec) { return new Protocol(spec); } -function defimpl(protocol, type, impl){ +function defimpl(protocol, type, impl) { protocol.implementation(type, impl); } -function get_object_keys(obj){ - return Object.keys(obj).concat(Object.getOwnPropertySymbols(obj)); -} +function build_namespace(ns, ns_string) { + let parts = ns_string.split('.'); + const root = ns; + let parent = ns; -function is_valid_character(codepoint){ - try{ - return String.fromCodePoint(codepoint) != null; - }catch(e){ - return false; + if (parts[0] === 'Elixir') { + parts = parts.slice(1); } -} - -//https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#Solution_2_%E2%80%93_rewrite_the_DOMs_atob()_and_btoa()_using_JavaScript's_TypedArrays_and_UTF-8 -function b64EncodeUnicode(str) { - return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) { - return String.fromCharCode('0x' + p1); - })); -} - -function delete_property_from_map(map, property){ - let new_map = Object.assign(Object.create(map.constructor.prototype), map); - delete new_map[property]; - - return Object.freeze(new_map); -} - -function class_to_obj(map){ - let new_map = Object.assign({}, map); - return Object.freeze(new_map); -} - -function add_property_to_map(map, property, value){ - let new_map = Object.assign({}, map); - new_map[property] = value; - return Object.freeze(new_map); -} - -function update_map(map, property, value){ - if(property in get_object_keys(map)){ - return add_property_to_map(map, property, value); + for (const part of parts) { + if (typeof parent[part] === 'undefined') { + parent[part] = {}; } - throw "map does not have key"; -} - -function bnot(expr){ - return ~expr; -} - -function band(left, right){ - return left & right; -} - -function bor(left, right){ - return left | right; -} - -function bsl(left, right){ - return left << right; -} - -function bsr(left, right){ - return left >> right; -} - -function bxor(left, right){ - return left ^ right; -} - -function zip(list_of_lists){ - if(list_of_lists.length === 0){ - return Object.freeze([]); + parent = parent[part]; } - let new_value = []; - let smallest_length = list_of_lists[0]; + root.__table__ = ns.__table__ || {}; + root.__table__[Symbol.for(ns_string)] = parent; - 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(new Core.Tuple(...current_value)); - } - - return Object.freeze(new_value); -} - -function can_decode64(data) { - try{ - atob(data); - return true; - }catch(e){ - return false; - } + return parent; } -function remove_from_list(list, element){ - let found = false; - - return list.filter((elem) => { - if(!found && elem === element){ - found = true; - return false; - } +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); - return true; - }); -} + const object = {}; -function foldl(fun, acc, list){ - let acc1 = acc; + for (const entry of map.entries()) { + let key = entry[0]; + const value = entry[1]; - for(const el of list){ - acc1 = fun(el, acc1); + 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); } - return acc1; -} - - -function foldr(fun, acc, list){ - let acc1 = acc; - - for(let i = list.length - 1; i >= 0; i--){ - acc1 = fun(list[i], acc1); - } - - return acc1; -} - -function keyfind(key, n, tuplelist){ - - for(let i = tuplelist.length - 1; i >= 0; i--){ - if(tuplelist[i].get(n) === key){ - return tuplelist[i]; + 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 false; + return object; } -function keydelete(key, n, tuplelist){ - - for(let i = tuplelist.length - 1; i >= 0; i--){ - if(tuplelist[i].get(n) === key){ - return tuplelist.concat([]).splice(i, 1); - } - } +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; - return tuplelist; -} + 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); + } -function keystore(key, n, list, newtuple){ - for(let i = list.length - 1; i >= 0; i--){ - if(list[i].get(n) === key){ - return list.concat([]).splice(i, 1, newtuple); - } - } - - return list.concat([]).push(newtuple); -} - -function keymember(key, n, list){ - for(let i = list.length - 1; i >= 0; i--){ - if(list[i].get(n) === key){ - return true; - } + 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; + }); } - - return false; + throw new Error(`Object ${object} is not an native object or array`); } -function keytake(key, n, list){ - if(!keymember(key, n, list)){ - return false; +class Recurse { + constructor(func) { + this.func = func; } - - let tuple = keyfind(key, n, list); - - return new Tuple(tuple.get(n), tuple, keydelete(key, n, list)); } -function keyreplace(key, n, list, newtuple){ +function trampoline(f) { + let currentValue = f; - for(let i = tuplelist.length - 1; i >= 0; i--){ - if(tuplelist[i].get(n) === key){ - return tuplelist.concat([]).splice(i, 1, newtuple); - } + while (currentValue && currentValue instanceof Recurse) { + currentValue = currentValue.func(); } - return tuplelist; + return currentValue; } +function split_at(value, position) { + const splitter = new GraphemeSplitter(); + const splitValues = splitter.splitGraphemes(value); -function reverse(list){ - return list.concat([]).reverse(); -} - -function maps_find(key, map){ - if(key in get_object_keys(map)){ - return new Core.Tuple(Symbol.for("ok"), map[key]); - }else{ - return Symbol.for("error"); + if (position < 0) { + const newPosition = splitValues.length + position; + if (newPosition < 0) { + return new Core.Tuple('', value); } -} - -function flatten(list, tail = []) { - let new_list = []; - - for(let e of list){ - if(Array.isArray(e)){ - new_list = new_list.concat(flatten(e)); - }else{ - new_list.push(e); - } - } - - return Object.freeze(new_list.concat(tail)); -} - -function duplicate(n, elem){ - let list = []; - - for(let i = 0; i < n; i++){ - list.push(elem); - } - - return Object.freeze(list); -} - -function mapfoldl(fun, acc, list){ - let newlist = []; - for(let x of list){ - let tup = fun(x, acc); - newlist.push(tup.get(0)); - acc = tup.get(1); + return split_at(value, newPosition); } + let first = ''; + let second = ''; + let index = 0; - return new Core.Tuple(Object.freeze(newlist), acc); -} - -function filtermap(fun, list){ - let newlist = []; - - for(x of list){ - let result = fun(x); - - if(result === true){ - newlist.push(x); - }else if(result instanceof Core.Tuple){ - newlist.push(result.get(1)); + for (const character of splitValues) { + if (index < position) { + first += character; + } else { + second += character; } - } - - return Object.freeze(newlist); -} - -function maps_fold(fun, acc, map){ - let acc1 = acc; - for(let k of get_object_keys(map)){ - acc1 = fun(k, map[k], acc1); + index += 1; } - return acc1; + return new Core.Tuple(first, second); } -function maps_from_list(list){ - let m = {}; - - for(x of list){ - m[x.get(0)] = x.get(1); - } - - return Object.freeze(m); +function graphemes(str) { + const splitter = new GraphemeSplitter(); + return splitter.splitGraphemes(str); } -function* sleep_forever(){ - yield* Core.processes.sleep(Symbol("infinity")); +function concat(head, tail) { + return [head].concat(tail); } export default { call_property, - apply, - contains, - get_global, - defstruct, - defexception, defprotocol, defimpl, - get_object_keys, - is_valid_character, - b64EncodeUnicode, - delete_property_from_map, - add_property_to_map, - class_to_obj, - can_decode64, - bnot, - band, - bor, - bsl, - bsr, - bxor, - zip, - foldl, - foldr, - remove_from_list, - keydelete, - keystore, - keyfind, - keytake, - keyreplace, - reverse, - update_map, - maps_find, - flatten, - duplicate, - mapfoldl, - filtermap, - maps_fold, - sleep_forever + 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 index 488dee04..15466ed8 100644 --- a/src/javascript/lib/core/protocol.js +++ b/src/javascript/lib/core/protocol.js @@ -1,59 +1,72 @@ import Core from '../core'; -//https://github.com/airportyh/protomorphism -class Protocol{ - constructor(spec){ +// https://github.com/airportyh/protomorphism +class Protocol { + constructor(spec) { this.registry = new Map(); this.fallback = null; - for (let funName in spec){ - this[funName] = createFun(funName).bind(this); - } - - function createFun(funName){ - - return function(...args) { - let thing = args[0]; + function createFun(funName) { + return function (...args) { + const thing = args[0]; let fun = null; - if(Number.isInteger(thing) && this.hasImplementation(Core.Integer)){ + 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)){ + } 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)){ + } else if (typeof thing === 'string' && this.hasImplementation(Core.BitString)) { fun = this.registry.get(Core.BitString)[funName]; - }else if(this.hasImplementation(thing)){ + } 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){ + } else if (this.fallback) { fun = this.fallback[funName]; } - if(fun != null){ - let retval = fun.apply(this, args); + if (fun != null) { + const retval = fun.apply(this, args); return retval; } - throw new Error("No implementation found for " + thing); + 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){ + implementation(type, implementation) { + if (type === null) { this.fallback = implementation; - }else{ + } else { this.registry.set(type, implementation); } } hasImplementation(thing) { - if (thing === Core.Integer || thing === Core.Float || Core.BitString){ + 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 index 4214cfea..7721612b 100644 --- a/src/javascript/lib/core/special_forms.js +++ b/src/javascript/lib/core/special_forms.js @@ -1,12 +1,12 @@ import Core from '../core'; -function _case(condition, clauses){ +function _case(condition, clauses) { return Core.Patterns.defmatch(...clauses)(condition); } -function cond(clauses){ - for(let clause of clauses){ - if(clause[0]){ +function cond(...clauses) { + for (const clause of clauses) { + if (clause[0]) { return clause[1](); } } @@ -14,167 +14,162 @@ function cond(clauses){ throw new Error(); } -function map_update(map, values){ - return Object.freeze( - Object.assign( - Object.create(map.constructor.prototype), map, values - ) - ); -} - -function _for(collections, fun, filter = () => true, into = [], previousValues = []){ - let pattern = collections[0][0]; - let collection = collections[0][1]; - - if(collections.length === 1){ - if(collection instanceof Core.BitString){ - let bsSlice = collection.slice(0, pattern.byte_size()); - let i = 1; - - while(bsSlice.byte_size == pattern.byte_size()){ - let r = Core.Patterns.match_or_default(pattern, bsSlice); - let args = previousValues.concat(r); - - if(r && filter.apply(this, args)){ - into = into.concat([fun.apply(this, args)]); - } - - bsSlice = collection.slice(pattern.byte_size() * i, pattern.byte_size() * (i + 1)); - i++; - } - - return into; - }else{ - for(let elem of collection){ - let r = Core.Patterns.match_or_default(pattern, elem); - let args = previousValues.concat(r); - - if(r && filter.apply(this, args)){ - into = into.concat([fun.apply(this, args)]); - } +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(); - return into; + const next_gen = []; + for (const j of list()) { + for (const i of generator) { + next_gen.push([j].concat(i)); } - }else{ - let _into = []; + } - if(collection instanceof Core.BitString){ - let bsSlice = collection.slice(0, pattern.byte_size()); - let i = 1; + return run_list_generators(next_gen, generators); +} - while(bsSlice.byte_size == pattern.byte_size()){ - let r = Core.Patterns.match_or_default(pattern, bsSlice); - if(r){ - _into = into.concat(this._for(collections.slice(1), fun, filter, _into, previousValues.concat(r))); - } +function _for(expression, generators, collectable_protocol, into = []) { + const [result, fun] = collectable_protocol.into(into); + let accumulatingResult = result; - bsSlice = collection.slice(pattern.byte_size() * i, pattern.byte_size() * (i + 1)); - i++; - } - }else{ - for(let elem of collection){ - let r = Core.Patterns.match_or_default(pattern, elem); - if(r){ - _into = into.concat(this._for(collections.slice(1), fun, filter, _into, previousValues.concat(r))); - } - } - } + const generatedValues = run_list_generators(generators.pop()(), generators); - return _into; + 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){ +function _try(do_fun, rescue_function, catch_fun, else_function, after_function) { let result = null; - try{ + try { result = do_fun(); - }catch(e){ + } catch (e) { let ex_result = null; + if (rescue_function) { + try { + let value = e; + if (e.__reason) { + value = e.__reason; + value.set('__reason', e.__reason); + } - if(rescue_function){ - try{ - ex_result = rescue_function(e); + ex_result = rescue_function(value); return ex_result; - }catch(ex){ - if(ex instanceof Core.Patterns.MatchError){ - throw ex; - } + } catch (ex) { + throw ex; } } - if(catch_fun){ - try{ + if (catch_fun) { + try { ex_result = catch_fun(e); return ex_result; - }catch(ex){ - if(ex instanceof Core.Patterns.MatchError){ - throw ex; - } + } catch (ex) { + throw ex; } } throw e; - - }finally{ - if(after_function){ + } finally { + if (after_function) { after_function(); } } - if(else_function){ - try{ + if (else_function) { + try { return else_function(result); - }catch(ex){ - if(ex instanceof Core.Patterns.MatchError){ - throw new Error("No Match Found in Else"); - } + } catch (ex) { + if (ex instanceof Core.Patterns.MatchError) { + throw new Error('No Match Found in Else'); + } throw ex; } - }else{ + } else { return result; } } -function _with(...args){ +function _with(...args) { let argsToPass = []; let successFunction = null; let elseFunction = null; - if(typeof(args[args.length - 2]) === 'function'){ + if (typeof args[args.length - 2] === 'function') { [successFunction, elseFunction] = args.splice(-2); - }else{ + } else { successFunction = args.pop(); } - for(let i = 0; i < args.length; i++){ - let [pattern, func] = args[i]; + for (let i = 0; i < args.length; i++) { + const [pattern, func] = args[i]; - let result = func.apply(null, argsToPass); + const result = func(...argsToPass); - let patternResult = Core.Patterns.match_or_default(pattern, result); + const patternResult = Core.Patterns.match_or_default(pattern, result); - if(patternResult == null){ - if(elseFunction){ + if (patternResult == null) { + if (elseFunction) { return elseFunction.call(null, result); - }else{ - return result; } - }else{ - argsToPass = argsToPass.concat(patternResult); + 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 successFunction.apply(null, argsToPass); + return null; } export default { _case, cond, - map_update, _for, _try, - _with + _with, + receive, }; diff --git a/src/javascript/lib/core/store.js b/src/javascript/lib/core/store.js index 202946c4..231334eb 100644 --- a/src/javascript/lib/core/store.js +++ b/src/javascript/lib/core/store.js @@ -1,47 +1,47 @@ -let store = new Map(); -let names = new Map(); +import Core from '../core'; -function get_key(key){ +function get_key(key) { let real_key = key; - if(names.has(key)){ - real_key = names.get(key); + if (Core.global.__elixirscript_names__.has(key)) { + real_key = Core.global.__elixirscript_names__.get(key); } - if(store.has(real_key)){ - return real_key + if (Core.global.__elixirscript_store__.has(real_key)) { + return real_key; } - return new Error('Key Not Found'); + throw new Error(`Key ${real_key} not found`); } -function create(key, value, name = null){ +function create(value, name = null) { + const key = new Core.PID(); - if(name != null){ - names.set(name, key); + if (name !== null) { + Core.global.__elixirscript_names__.set(name, key); } - store.set(key, value); + return Core.global.__elixirscript_store__.set(key, value); } -function update(key, value){ - let real_key = get_key(key); - store.set(real_key, value); +function update(key, value) { + const real_key = get_key(key); + return Core.global.__elixirscript_store__.set(real_key, value); } -function read(key){ - let real_key = get_key(key); - return store.get(real_key); +function read(key) { + const real_key = get_key(key); + return Core.global.__elixirscript_store__.get(real_key); } -function remove(key){ - let real_key = get_key(key); - return store.delete(real_key); +function remove(key) { + const real_key = get_key(key); + return Core.global.__elixirscript_store__.delete(real_key); } export default { create, - read, update, - remove + read, + remove, }; diff --git a/src/javascript/lib/enum.js b/src/javascript/lib/enum.js deleted file mode 100644 index 13916e50..00000000 --- a/src/javascript/lib/enum.js +++ /dev/null @@ -1,203 +0,0 @@ -import Core from './core'; - -let Enum = { - - all__qmark__: function(collection, fun = (x) => x){ - for(let elem of collection){ - if(!fun(elem)){ - return false; - } - } - - return true; - }, - - any__qmark__: function(collection, fun = (x) => x){ - for(let elem of collection){ - if(fun(elem)){ - return true; - } - } - - return false; - }, - - 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(Array.isArray(collection)){ - if(n < this.count(collection) && n >= 0){ - return new Core.Tuple(Symbol.for("ok"), collection[n]); - }else{ - return Symbol.for("error"); - } - } - - throw new Error("collection is not an Enumerable"); - }, - - fetch__emark__: function(collection, n){ - if(Array.isArray(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){ - let result = []; - - for(let elem of collection){ - if(fun(elem)){ - result.push(elem); - } - } - - return result; - }, - - filter_map: function(collection, filter, mapper){ - return Enum.map(Enum.filter(collection, filter), mapper); - }, - - find: function(collection, if_none = null, fun){ - for(let elem of collection){ - if(fun(elem)){ - return elem; - } - } - - return if_none; - }, - - into: function(collection, list){ - return list.concat(collection); - }, - - map: function(collection, fun){ - let result = []; - - for(let elem of collection){ - result.push(fun(elem)); - } - - return result; - }, - - map_reduce: function(collection, acc, fun){ - let mapped = Object.freeze([]); - let the_acc = acc; - - for (var i = 0; i < this.count(collection); i++) { - let tuple = fun(collection[i], the_acc); - - the_acc = tuple.get(1); - mapped = Object.freeze(mapped.concat([tuple.get(0)])); - } - - return new Core.Tuple(mapped, the_acc); - }, - - member__qmark__: function(collection, value){ - return collection.includes(value); - }, - - reduce: function(collection, acc, fun){ - let the_acc = acc; - - for (var i = 0; i < this.count(collection); i++) { - let tuple = fun(collection[i], the_acc); - - the_acc = tuple.get(1); - } - - return the_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 Object.freeze(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/src/javascript/tests/case.spec.js b/src/javascript/tests/case.spec.js index 963c8f0a..8715ae11 100644 --- a/src/javascript/tests/case.spec.js +++ b/src/javascript/tests/case.spec.js @@ -1,32 +1,21 @@ -import Core from "../lib/core"; +import test from 'ava'; +import Core from '../lib/core'; + const Patterns = Core.Patterns; const SpecialForms = Core.SpecialForms; const Tuple = Core.Tuple; -import Enum from "../lib/enum"; - -import chai from 'chai'; -var expect = chai.expect; - - -describe('case', () => { - - it('case', () => { - let clauses = [ - Patterns.clause( - [new Tuple(Symbol.for("selector"), Patterns.variable(), Patterns.variable())], - function(i, value){ return value; }, - function(i){ return Kernel.is_integer(i); } - ), - Patterns.clause( - [Patterns.variable()], - function(value){ return value; } - ) - ]; - - let result = SpecialForms._case("thing", clauses); +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), + ]; - expect(result).to.equal("thing"); - }); + 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 index 2fea8baa..f1a09736 100644 --- a/src/javascript/tests/cond.spec.js +++ b/src/javascript/tests/cond.spec.js @@ -1,25 +1,16 @@ -import Core from "../lib/core"; -const Patterns = Core.Patterns; -const SpecialForms = Core.SpecialForms; - -import Enum from "../lib/enum"; - -import chai from 'chai'; -var expect = chai.expect; +import test from 'ava'; +import Core from '../lib/core'; +const SpecialForms = Core.SpecialForms; -describe('cond', () => { - - it('cond', () => { - let clauses = [ - [ 1 + 1 == 1, () => "This will never match"], - [ 2 * 2 != 4, () => "Nor this"], - [ true, () => "This will"], - ]; - - let result = SpecialForms.cond(clauses); +test('cond', (t) => { + const clauses = [ + [1 + 1 === 1, () => 'This will never match'], + [2 * 2 !== 4, () => 'Nor this'], + [true, () => 'This will'], + ]; - expect(result).to.equal("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 index aef7192d..d3ed14e1 100644 --- a/src/javascript/tests/core/functions.spec.js +++ b/src/javascript/tests/core/functions.spec.js @@ -1,20 +1,86 @@ -import Core from "../../lib/core"; +import test from 'ava'; +import Core from '../../lib/core'; + const Functions = Core.Functions; -import chai from 'chai'; -var expect = chai.expect; +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']])); +}); -describe('Functions', function(){ +test('object_to_map/2', (t) => { + let obj = {}; + let result = Functions.object_to_map(obj, []); + t.deepEqual(result, new Map()); - it('call_property', function(){ - expect(Functions.call_property(1, "toString")).to.equal("1"); - expect(Functions.call_property([], "toString")).to.equal(""); - expect(Functions.call_property([], "length")).to.equal(0); - expect(Functions.call_property("", "toString")).to.equal(""); - expect(Functions.call_property("", "length")).to.equal(0); - expect(Functions.call_property(Symbol("test"), "toString")).to.equal("Symbol(test)"); - expect(Functions.call_property({completed: false}, "completed")).to.equal(false); - expect(Functions.call_property({id: 0}, "id")).to.equal(0); - }); + 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 index 5ae6f667..9bcd1894 100644 --- a/src/javascript/tests/for.spec.js +++ b/src/javascript/tests/for.spec.js @@ -1,91 +1,126 @@ -import Core from "../lib/core"; +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; -import Enum from "../lib/enum"; +const $ = Patterns.variable(); -import chai from 'chai'; -var expect = chai.expect; +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]); +}); -const $ = Patterns.variable(); +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]); +}); -describe('for', () => { - it('simple for', () => { - let collections = [ - [$, [1, 2, 3, 4]] - ]; - - let result = 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 = 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 = 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 = SpecialForms._for(collections, (name) => name.toUpperCase()); - - expect(result).to.eql(["JOHN", "MEG"]); - }); - - - it('for with bitstring', () => { - //for <> >>, do: {r, g, b} - - let collections = [ - [ - 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) - ) - ] - ]; - - let result = SpecialForms._for(collections, (r, g, b) => new Tuple(r, g, b)); - - expect(result).to.eql([new Tuple(213, 45, 132), new Tuple(64, 76, 32), new Tuple(76, 0, 0), new Tuple(234, 32, 15)]); - }); +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/special_forms.spec.js b/src/javascript/tests/special_forms.spec.js deleted file mode 100644 index cc540546..00000000 --- a/src/javascript/tests/special_forms.spec.js +++ /dev/null @@ -1,33 +0,0 @@ -import Core from "../lib/core"; -const SpecialForms = Core.SpecialForms; - -import chai from 'chai'; -var expect = chai.expect; - -describe('SpecialForms', function(){ - - describe('map_update', function(){ - it('creates new object', function(){ - const foo = Object.freeze({foo: "bar", fizz: "buzz"}); - const bar = SpecialForms.map_update(foo, {baz: "bar", fizz: "fizzbuzz"}); - - expect(foo instanceof Object).to.equal(bar instanceof Object); - expect(foo.foo).to.equal(bar.foo); - expect(bar.fizz).to.equal("fizzbuzz"); - expect(foo === bar).to.equal(false); - }); - - it('creates new class', function(){ - function MyClass(foo){ - this.foo = foo; - } - - const foo = new MyClass("bar"); - const bar = SpecialForms.map_update(foo, {baz: "bar", fizz: "fizzbuzz"}); - - expect(foo instanceof MyClass).to.equal(bar instanceof MyClass); - expect(foo.foo).to.equal(bar.foo); - expect(bar.fizz).to.equal("fizzbuzz"); - }); - }); -}); diff --git a/src/javascript/tests/try.spec.js b/src/javascript/tests/try.spec.js index d4b7cf60..fdcbaefd 100644 --- a/src/javascript/tests/try.spec.js +++ b/src/javascript/tests/try.spec.js @@ -1,17 +1,10 @@ -import Core from "../lib/core"; -const Patterns = Core.Patterns; -const SpecialForms = Core.SpecialForms; +import test from 'ava'; +import Core from '../lib/core'; -import Enum from "../lib/enum"; +const { Patterns, SpecialForms } = Core; -import chai from 'chai'; -var expect = chai.expect; - - -describe('try', () => { - - it('try', () => { - /* +test('try', (t) => { + /* try do 1 / x else @@ -23,19 +16,18 @@ describe('try', () => { */ - let x = 1; - - let value = SpecialForms._try(function() { - return 1 / x; - }, null, null, Patterns.defmatch(Patterns.clause([Patterns.variable()], function(y) { - return Symbol.for('small'); - }, function(y) { - return (y < 1) && (y > -1); - }), Patterns.clause([Patterns.wildcard()], function() { - return Symbol.for('large'); - })), null); + const x = 1; - expect(value).to.equal(Symbol.for('large')); - }); + 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 index 70f5c233..31fb2c49 100644 --- a/src/javascript/tests/with.spec.js +++ b/src/javascript/tests/with.spec.js @@ -1,28 +1,23 @@ -import Core from "../lib/core"; +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; -import Enum from "../lib/enum"; - -import chai from 'chai'; -var expect = chai.expect; - const $ = Patterns.variable(); -function map_fetch(map, key){ - if(key in map){ +function map_fetch(map, key) { + if (key in map) { return new Tuple(Symbol.for('ok'), map[key]); } return Symbol.for('error'); } -describe('with', () => { - - it('normal', () => { - /* +test('with', (t) => { + /* opts = %{width: 10, height: 15} with {:ok, width} <- Map.fetch(opts, :width), @@ -32,20 +27,19 @@ describe('with', () => { {:ok, 150} */ - let opts = { width: 10, height: 15 }; + const opts = { width: 10, height: 15 }; - let value = SpecialForms._with( - [new Tuple(Symbol.for('ok'), $), () => map_fetch(opts, "width")], - [new Tuple(Symbol.for('ok'), $), (width) => map_fetch(opts, "height")], - (width, height) => new Tuple(Symbol.for('ok'), width * height) - ); - - expect(value).to.eql(new Tuple(Symbol.for('ok'), 150)); - }); + 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)); +}); - it('without match', () => { - /* +test('with without match', (t) => { + /* opts = %{width: 10} with {:ok, width} <- Map.fetch(opts, :width), @@ -55,20 +49,19 @@ describe('with', () => { :error */ - let opts = { width: 10 }; + const opts = { width: 10 }; - let value = SpecialForms._with( - [new Tuple(Symbol.for('ok'), $), () => map_fetch(opts, "width")], - [new Tuple(Symbol.for('ok'), $), (width) => map_fetch(opts, "height")], - (width, height) => new Tuple(Symbol.for('ok'), width * height) - ); - - expect(value).to.eql(Symbol.for('error')); - }); + 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')); +}); - it('bare expression', () => { - /* +test('with bare expression', (t) => { + /* opts = %{width: 10} with {:ok, width} <- Map.fetch(opts, :width), @@ -79,21 +72,20 @@ describe('with', () => { {:ok, 300} */ - let opts = { width: 10, height: 15 }; + const opts = { width: 10, height: 15 }; - let value = SpecialForms._with( - [new Tuple(Symbol.for('ok'), $), () => map_fetch(opts, "width")], - [$, (width) => width * 2], - [new Tuple(Symbol.for('ok'), $), (width, double_width) => map_fetch(opts, "height")], - (width, double_width, height) => new Tuple(Symbol.for('ok'), double_width * height) - ); - - expect(value).to.eql(new Tuple(Symbol.for('ok'), 300)); - }); + 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)); +}); - it('with else', () => { - /* +test('with else', (t) => { + /* opts = %{width: 10} with {:ok, width} <- Map.fetch(opts, :width), @@ -106,22 +98,25 @@ describe('with', () => { {:error, :wrong_data} */ - let opts = { width: 10 }; - - let value = SpecialForms._with( - [new Tuple(Symbol.for('ok'), $), () => map_fetch(opts, "width")], - [new Tuple(Symbol.for('ok'), $), (width) => 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'))) - ) - ); - - expect(value).to.eql(new Tuple(Symbol.for('error'), Symbol.for('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'))); +}); - it('with else that don`t match', () => { - /* +test('with else that don`t match', (t) => { + /* opts = %{width: 10} with {:ok, width} <- Map.fetch(opts, :width), @@ -134,18 +129,20 @@ describe('with', () => { {:error, :wrong_data} */ - let opts = { width: 10 }; - - let withFunction = SpecialForms._with.bind( - null, - [new Tuple(Symbol.for('ok'), $), () => map_fetch(opts, "width")], - [new Tuple(Symbol.for('ok'), $), (width) => 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'))) - ) - ); - - expect(withFunction).to.throw(MatchError, 'No match for: Symbol(error)'); - }); + 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/cache_test.exs b/test/compiler/cache_test.exs deleted file mode 100644 index d31b6f1c..00000000 --- a/test/compiler/cache_test.exs +++ /dev/null @@ -1,47 +0,0 @@ -defmodule ElixirScript.Compiler.Cache.Test do - use ExUnit.Case - alias ElixirScript.Compiler.Cache - - - test "no changes found when old and new are empty" do - old = [] - new = [] - assert Cache.get_changed_files(old, new) == [] - end - - test "all new changes found when old empty and new has files" do - old = [] - new = [{"file.ex", %{mtime: 1}}] - assert Cache.get_changed_files(old, new) == new - end - - test "all new changes found when old not emtpy and new is empty" do - old = [{"file.ex", %{mtime: 1}}] - new = [] - assert Cache.get_changed_files(old, new) == new - end - - test "all new changes found when old and new have same size but different file" do - old = [{"file.ex", %{mtime: 1}}] - new = [{"new_file.ex", %{mtime: 1}}] - assert Cache.get_changed_files(old, new) == new - end - - test "all new changes found when old and new have same file and different size" do - old = [{"file.ex", %{mtime: 1}}] - new = [{"file.ex", %{mtime: 2}}] - assert Cache.get_changed_files(old, new) == new - end - - test "no changes found when old and new have same file and size" do - old = [{"file.ex", %{mtime: 1}}] - new = [{"file.ex", %{mtime: 1}}] - assert Cache.get_changed_files(old, new) == [] - end - - test "all new changes found when old has less files than new" do - old = [{"file.ex", %{mtime: 1}}] - new = [{"file.ex", %{mtime: 1}}, {"new_file.ex", %{mtime: 1}}] - assert Cache.get_changed_files(old, new) == new - 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 a40365af..00000000 --- a/test/elixir_script_test.exs +++ /dev/null @@ -1,65 +0,0 @@ -defmodule ElixirScript.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "turn javascript ast into javascript code strings" do - js_code = ElixirScript.compile(":atom") - assert Enum.join(js_code, "\n") |> String.trim == "Symbol.for('atom')" - end - - - test "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 Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const something_else = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return 1 * 1; - })); - const sandwich = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return null; - })); - export default { - sandwich - }; - """, List.last(js_code) - end - - - test "set standard lib path" 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, core_path: "elixirscript"} ) - - assert_js_matches """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const something_else = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return 1 * 1; - })); - const sandwich = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return null; - })); - export default { - sandwich - }; - """, List.last(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/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/prelude/js_test.exs b/test/prelude/js_test.exs deleted file mode 100644 index 8673a8d8..00000000 --- a/test/prelude/js_test.exs +++ /dev/null @@ -1,40 +0,0 @@ -defmodule ElixirScript.Lib.JS.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "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 - - test "translate update" do - ex_ast = quote do - JS.update A, %{"b" => [1, 2, 3]} - end - - js_code = """ - Object.assign(A, Object.freeze({ - b: Object.freeze([1, 2, 3]) - })) - """ - - assert_translation(ex_ast, js_code) - end -end diff --git a/test/prelude/kernel_test.exs b/test/prelude/kernel_test.exs deleted file mode 100644 index 4eb7f06a..00000000 --- a/test/prelude/kernel_test.exs +++ /dev/null @@ -1,19 +0,0 @@ -defmodule ElixirScript.Lib.Elixir.Kernel.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "translate range" do - ex_ast = quote do - 1..4 - end - - js_code = """ - Elixir$ElixirScript$Range.Elixir$ElixirScript$Range.create(Object.freeze({ - [Symbol.for('first')]: 1, - [Symbol.for('last')]: 4 - })) - """ - - assert_translation(ex_ast, js_code) - end -end diff --git a/test/test_helper.exs b/test/test_helper.exs index d5b1bcbf..30fac768 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,70 +1,3 @@ exclude = if Node.alive?, do: [], else: [skip: true] ExUnit.start(exclude: exclude) - -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 ExUnit.Case - - def make_custom_env do - use ElixirScript - 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) |> Elixir.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 diff --git a/test/translator/access_test.exs b/test/translator/access_test.exs deleted file mode 100644 index fb7080e1..00000000 --- a/test/translator/access_test.exs +++ /dev/null @@ -1,16 +0,0 @@ -defmodule ElixirScript.Translator.Access.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "translate access" do - ex_ast = quote do: a[:b] - js_code = "a[Symbol.for('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 diff --git a/test/translator/atom_test.exs b/test/translator/atom_test.exs deleted file mode 100644 index c06273a5..00000000 --- a/test/translator/atom_test.exs +++ /dev/null @@ -1,14 +0,0 @@ -defmodule ElixirScript.Translator.Atom.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "translate atom" do - ex_ast = quote do: :atom - assert_translation(ex_ast, "Symbol.for('atom')") - end - - test "Call Atom module" do - ex_ast = quote do: Atom.to_string(:atom) - assert_translation(ex_ast, "Elixir$ElixirScript$Atom.to_string(Symbol.for('atom'))") - end -end diff --git a/test/translator/bitstring_test.exs b/test/translator/bitstring_test.exs deleted file mode 100644 index 9d0fdae3..00000000 --- a/test/translator/bitstring_test.exs +++ /dev/null @@ -1,76 +0,0 @@ -defmodule ElixirScript.Translator.Bitstring.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "translate bitstring" do - ex_ast = quote do: <<1, 2, 3>> - assert_translation(ex_ast, "new Elixir.Core.BitString(Elixir.Core.BitString.integer(1), Elixir.Core.BitString.integer(2), Elixir.Core.BitString.integer(3))") - - ex_ast = quote do: <<1, "foo">> - assert_translation(ex_ast, "new Elixir.Core.BitString(Elixir.Core.BitString.integer(1), Elixir.Core.BitString.binary('foo'))") - - ex_ast = quote do: <<1, "foo" :: binary>> - assert_translation(ex_ast, "new Elixir.Core.BitString(Elixir.Core.BitString.integer(1), Elixir.Core.BitString.binary('foo'))") - - ex_ast = quote do: <<1, "foo" :: utf8, "bar" :: utf32>> - assert_translation(ex_ast, "new Elixir.Core.BitString(Elixir.Core.BitString.integer(1), Elixir.Core.BitString.utf8('foo'), Elixir.Core.BitString.utf32('bar'))") - - ex_ast = quote do: <<102 :: integer-native, rest :: binary>> - assert_translation(ex_ast, "new Elixir.Core.BitString(Elixir.Core.BitString.native(Elixir.Core.BitString.integer(102)), Elixir.Core.BitString.binary(rest))") - - ex_ast = quote do: <<102 :: unsigned-big-integer, rest :: binary>> - assert_translation(ex_ast, "new Elixir.Core.BitString(Elixir.Core.BitString.integer(Elixir.Core.BitString.big(Elixir.Core.BitString.unsigned(102))), Elixir.Core.BitString.binary(rest))") - - ex_ast = quote do: <<102, _rest :: size(16)>> - assert_translation(ex_ast, "new Elixir.Core.BitString(Elixir.Core.BitString.integer(102), Elixir.Core.BitString.size(_rest, 16))") - - ex_ast = quote do: <<102, _rest :: size(16)-unit(4)>> - assert_translation(ex_ast, "new Elixir.Core.BitString(Elixir.Core.BitString.integer(102), Elixir.Core.BitString.unit(Elixir.Core.BitString.size(_rest, 16), 4))") - - ex_ast = quote do: <<102, _rest :: 16 * 4>> - assert_translation(ex_ast, "new Elixir.Core.BitString(Elixir.Core.BitString.integer(102), Elixir.Core.BitString.unit(Elixir.Core.BitString.size(_rest, 16), 4))") - - ex_ast = quote do: <<102, _rest :: _ * 4>> - assert_translation(ex_ast, "new Elixir.Core.BitString(Elixir.Core.BitString.integer(102), Elixir.Core.BitString.unit(Elixir.Core.BitString.size(_rest, undefined), 4))") - - ex_ast = quote do: <<102, _rest :: 16>> - assert_translation(ex_ast, "new Elixir.Core.BitString(Elixir.Core.BitString.integer(102), Elixir.Core.BitString.size(_rest, 16))") - - ex_ast = quote do: << 1, <<2>> >> - assert_translation(ex_ast, "new Elixir.Core.BitString(Elixir.Core.BitString.integer(1), new Elixir.Core.BitString(Elixir.Core.BitString.integer(2)))") - end - - test "translate pattern matching bitstring" do - ex_ast = quote do: <> = <<"Frank the Walrus">> - js_code = """ - let [name,species] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.bitStringMatch(Elixir.Core.BitString.size(Elixir.Core.BitString.binary({ - 'value': Elixir.Core.Patterns.variable() - }),5),Elixir.Core.BitString.binary(' the '),Elixir.Core.BitString.binary({ - 'value': Elixir.Core.Patterns.variable() - })),'Frank the Walrus'); - """ - - assert_translation(ex_ast, js_code) - - - ex_ast = quote do: <> = <<-100>> - js_code = """ - let [int] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.bitStringMatch(Elixir.Core.BitString.integer({ - 'value': Elixir.Core.Patterns.variable() - })),new Elixir.Core.BitString(Elixir.Core.BitString.binary(-100))); - """ - - assert_translation(ex_ast, js_code) - - - ex_ast = quote do: <<-100::signed, _rest::binary>> = <<-100, "foo">> - js_code = """ - let [_rest] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.bitStringMatch(Elixir.Core.BitString.signed(-100),Elixir.Core.BitString.binary({ - 'value': Elixir.Core.Patterns.variable() - })),new Elixir.Core.BitString(Elixir.Core.BitString.binary(-100),Elixir.Core.BitString.binary('foo'))); - """ - - assert_translation(ex_ast, js_code) - end - -end diff --git a/test/translator/bug_test.exs b/test/translator/bug_test.exs deleted file mode 100644 index cfb474bc..00000000 --- a/test/translator/bug_test.exs +++ /dev/null @@ -1,157 +0,0 @@ -defmodule ElixirScript.Translator.Bug.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "Translate function with 0 arguments" do - ex_ast = quote do - def test do - :atom - end - end - - js_code = """ - const test = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function(){ - return Symbol.for('atom'); - })); - """ - - assert_translation(ex_ast, js_code) - end - - test "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,Object.freeze({ - style: Elixir.Core.Functions.call_property(styles,'welcome') - }),'Welcome to React Native!') - """ - - assert_translation(ex_ast, js_code) - - end - - test "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 JQuery from 'jquery'; - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - JQuery(Elixir.Core.Functions.call_property(e,'target')); - export default {}; - """ - - assert_translation(ex_ast, js_code) - end - - test "correctly translate module names when used" do - ex_ast = quote do - @graphic_store App.Stores.GraphicStore.create_store() - end - - js_code = """ - const graphic_store = Elixir.Core.Functions.call_property(App.Stores.GraphicStore, 'create_store'); - - """ - - assert_translation(ex_ast, js_code) - end - - test "replace !" do - ex_ast = quote do - Elixir.Enum.fetch!(data, i) - end - - js_code = """ - Elixir.Enum.fetch__emark__(data, i) - """ - - assert_translation(ex_ast, js_code) - end - - test "chain calls correctly" do - ex_ast = quote do - :this.getRawCanvas().getContext("2d") - end - - js_code = """ - Elixir.Core.Functions.call_property(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 - - test "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 = """ - const getDispatcher = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return DeLorean.Flux.createDispatcher(Object.freeze({ - [Symbol.for('startPainting')]: Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return this.dispatch('startPainting'); - })), [Symbol.for('stopPainting')]: Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return this.dispatch('stopPainting'); - })), [Symbol.for('addPoint')]: Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(data) { - return this.dispatch('addPoint',data); - })), [Symbol.for('getStores')]: Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return Object.freeze({ - [Symbol.for('graphic')]: GraphicStore - }); - })) - })); - })); - """ - - assert_translation(ex_ast, js_code) - end - - - test "test array returns correctly" do - ex_ast = quote do - def my_func(x) do - [x.a, x.b] - end - end - - js_code = """ - const my_func = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(x){ - return Object.freeze([Elixir.Core.Functions.call_property(x,'a'), Elixir.Core.Functions.call_property(x,'b')]); - })); - """ - - 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 f7a55e9c..00000000 --- a/test/translator/capture_test.exs +++ /dev/null @@ -1,121 +0,0 @@ -defmodule ElixirScript.Translator.Capture.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "translate capture operator with Module, function, and arity" do - ex_ast = quote do - fun = &Elixir.Kernel.is_atom/1 - end - - js_code = """ - let [fun] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),Elixir$ElixirScript$Kernel.is_atom); - """ - - assert_translation(ex_ast, js_code) - - end - - test "translate capture operator with function, and parameters" do - - ex_ast = quote do - fun = &is_atom(&1) - end - - js_code = """ - let [fun] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(__1) { - return Elixir$ElixirScript$Kernel.is_atom(__1); - }))); - """ - - assert_translation(ex_ast, js_code) - - - end - - test "translate capture operator with function, and arity" do - - ex_ast = quote do - fun = &local_function/1 - end - - js_code = """ - let [fun] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),local_function); - """ - - assert_translation(ex_ast, js_code) - - end - - test "translate capture operator with anonymous function" do - - ex_ast = quote do - fun = &(&1 * 2) - end - - js_code = """ - let [fun] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(__1) { - return __1 * 2; - }))); - """ - - assert_translation(ex_ast, js_code) - - end - - test "translate capture operator with anonymous function tuple" do - - ex_ast = quote do - fun = &{&1, &2} - end - - js_code = """ - let [fun] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()],function(__1,__2) { - return new Elixir.Core.Tuple(__1,__2); - }))); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - fun = &{&1, &2, &3} - end - - js_code = """ - let [fun] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()],function(__1,__2,__3) { - return new Elixir.Core.Tuple(__1,__2,__3); - }))); - """ - - assert_translation(ex_ast, js_code) - - - end - - test "translate capture operator with anonymous functions as parameters" do - - ex_ast = quote do - Elixir.Enum.map(items, &process(&1)) - end - - js_code = """ - Elixir.Enum.map(items,Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.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(Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(__1) { - return process_event(__1); - }))) - """ - - assert_translation(ex_ast, js_code) - end -end diff --git a/test/translator/case_test.exs b/test/translator/case_test.exs deleted file mode 100644 index 6a1477b6..00000000 --- a/test/translator/case_test.exs +++ /dev/null @@ -1,230 +0,0 @@ -defmodule ElixirScript.Translator.Case.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "translate case" do - - ex_ast = quote do - case data do - :ok -> value - :error -> nil - end - end - - js_code = """ - Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Symbol.for('ok')],function() { - return value; - }),Elixir.Core.Patterns.clause([Symbol.for('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 = """ - Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([false],function() { - let [value] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),13); - return value; - }),Elixir.Core.Patterns.clause([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 = """ - Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([false],function() { - let [value] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),13); - return value; - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.wildcard()],function() { - return true; - })).call(this,data) - """ - - assert_translation(ex_ast, js_code) - end - - test "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 = """ - Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(number) { - let [value] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),13); - return value; - },function(number) { - return Elixir.Core.Functions.contains(number,Object.freeze([1, 2, 3, 4])); - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.wildcard()],function() { - return true; - })).call(this,data) - """ - - assert_translation(ex_ast, js_code) - end - - test "translate case with multiple statements in body" do - ex_ast = quote do - case data do - :ok -> - :console.info("info") - Todo.add(data) - :error -> - nil - end - end - - js_code = """ - Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Symbol.for('ok')],function() { - console.info('info'); - return Todo.add(data); - }),Elixir.Core.Patterns.clause([Symbol.for('error')],function() { - return null; - })).call(this,data) - """ - - assert_translation(ex_ast, js_code) - end - - test "translate case with destructing" do - ex_ast = quote do - case data do - { one, two } -> - :console.info(one) - :error -> - nil - end - end - - js_code = """ - Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.type(Elixir.Core.Tuple, { - values: [Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()] - })], function(one, two) { - return console.info(one); - }), Elixir.Core.Patterns.clause([Symbol.for('error')], function() { - return null; - })).call(this, data) - """ - - assert_translation(ex_ast, js_code) - end - - test "translate case with nested destructing" do - ex_ast = quote do - case data do - { {one, two} , three } -> - :console.info(one) - :error -> - nil - end - end - - js_code = """ - Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.type(Elixir.Core.Tuple, { - values: [Elixir.Core.Patterns.type(Elixir.Core.Tuple, { - values: [Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()] - }), Elixir.Core.Patterns.variable()] - })], function(one, two, three) { - return console.info(one); - }), Elixir.Core.Patterns.clause([Symbol.for('error')], function() { - return null; - })).call(this, data) - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - case data do - { one, {two, three} } -> - :console.info(one) - :error -> - nil - end - end - - js_code = """ - Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.type(Elixir.Core.Tuple, { - values: [Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.type(Elixir.Core.Tuple, { - values: [Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()] - })] - })], function(one, two, three) { - return console.info(one); - }), Elixir.Core.Patterns.clause([Symbol.for('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 }} -> - :console.info(value) - :error -> - nil - end - end - - js_code = """ - Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.type(AStruct, { - [Symbol.for('key')]: Elixir.Core.Patterns.type(BStruct, { - [Symbol.for('key2')]: Elixir.Core.Patterns.variable() - }) - })], function(value) { - return console.info(value); - }), Elixir.Core.Patterns.clause([Symbol.for('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 } }} -> - :console.info(value) - :error -> - nil - end - end - - js_code = """ - Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.type(AStruct, { - [Symbol.for('key')]: Elixir.Core.Patterns.type(BStruct, { - [Symbol.for('key2')]: Elixir.Core.Patterns.variable(), [Symbol.for('key3')]: Elixir.Core.Patterns.type(CStruct, { - [Symbol.for('key4')]: Elixir.Core.Patterns.variable() - }) - }) - })], function(value, value2) { - return console.info(value); - }), Elixir.Core.Patterns.clause([Symbol.for('error')], function() { - return null; - })).call(this, data) - """ - - assert_translation(ex_ast, js_code) - end -end diff --git a/test/translator/cond_test.exs b/test/translator/cond_test.exs deleted file mode 100644 index 7666d4f5..00000000 --- a/test/translator/cond_test.exs +++ /dev/null @@ -1,58 +0,0 @@ -defmodule ElixirScript.Translator.Cond.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "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 = """ - Elixir.Core.SpecialForms.cond(Object.freeze([1 + 1 == 1, function() { - return 'This will never match'; - }]),Object.freeze([2 * 2 != 4, function() { - return 'Nor this'; - }]),Object.freeze([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 = """ - Elixir.Core.SpecialForms.cond(Object.freeze([1 + 1 == 1, function() { - let [a] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),1); - return 'This will never match'; - }]),Object.freeze([2 * 2 != 4, function() { - let [a] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),2); - return 'Nor this'; - }]),Object.freeze([true, function() { - let [a] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),3); - return 'This will'; - }])) - """ - - assert_translation(ex_ast, js_code) - end -end diff --git a/test/translator/defmodule_test.exs b/test/translator/defmodule_test.exs deleted file mode 100644 index e49ffc2b..00000000 --- a/test/translator/defmodule_test.exs +++ /dev/null @@ -1,332 +0,0 @@ -defmodule ElixirScript.Translator.Defmodule.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "translate empty module" do - ex_ast = quote do - defmodule Elephant do - end - end - - js_code = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - export default {}; - """ - - assert_translation(ex_ast, js_code) - end - - test "translate defmodules" do - ex_ast = quote do - defmodule Elephant do - @ul JQuery.("#todo-list") - - def something() do - @ul - end - - defgenp something_else() do - end - end - end - - js_code = """ - - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const something_else = Elixir.Core.Patterns.defmatchgen(Elixir.Core.Patterns.clause([],function*() { - return null; - })); - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return ul; - })); - const ul = JQuery('#todo-list'); - export default { - something - }; - """ - - assert_translation(ex_ast, js_code) - end - - test "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 = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - import Elixir$Animals$Elephant from '../app/Elixir.Animals.Elephant'; - const something_else = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return null; - })); - - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return Elixir$Animals$Elephant.Elixir$Animals$Elephant.create(Object.freeze({})); - })); - - export default { - something - }; - - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const Elixir$Animals$Elephant = Elixir.Core.Functions.defstruct({ - [Symbol.for('__struct__')]: Symbol.for('Elixir.Animals.Elephant'), [Symbol.for('trunk')]: true - }); - - export default { - Elixir$Animals$Elephant - }; - """ - - assert_translation(ex_ast, js_code) - end - - - test "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 = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - import Elixir$Animals$Elephant from '../app/Elixir.Animals.Elephant'; - const something_else = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return null; - })); - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return Elixir$Animals$Elephant.Elixir$Animals$Elephant.create(Object.freeze({})); - })); - - export default { - something - }; - - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - import Elixir$Animals$Elephant$Bear from '../app/Elixir.Animals.Elephant.Bear'; - const Elixir$Animals$Elephant = Elixir.Core.Functions.defstruct({ - [Symbol.for('__struct__')]: Symbol.for('Elixir.Animals.Elephant'), - [Symbol.for('trunk')]: true - }); - export default { - Elixir$Animals$Elephant - }; - - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const Elixir$Animals$Elephant$Bear = Elixir.Core.Functions.defstruct({ - [Symbol.for('__struct__')]: Symbol.for('Elixir.Animals.Elephant.Bear'), - [Symbol.for('trunk')]: true - }); - - export default { - Elixir$Animals$Elephant$Bear - }; - """ - - assert_translation(ex_ast, js_code) - end - - test "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 Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - import Elixir$Lions$Tigers from '../app/Elixir.Lions.Tigers'; - Elixir.Core.Functions.call_property(Elixir$Lions$Tigers,'oh_my'); - export default {}; - - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - Elixir.Core.Functions.call_property(Lions.Tigers.Bears,'oh_my'); - export default {}; - """ - - assert_translation(ex_ast, js_code) - end - - test "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 = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - import Elixir$Lions$Tigers from '../app/Elixir.Lions.Tigers'; - Elixir.Core.Functions.call_property(Elixir$Lions$Tigers,'oh_my'); - export default {}; - - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const oh_my = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return null; - })); - Elixir.Core.Functions.call_property(Lions.Tigers.Bears,'oh_my'); - export default { - oh_my - }; - """ - - assert_translation(ex_ast, js_code) - end - - test "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: 0] - - oh_my() - end - end - - js_code = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const oh_my2 = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return null; - })); - - const oh_my = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return null; - })); - - export default { - oh_my2, oh_my - }; - - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - import Elixir$Lions$Tigers from '../app/Elixir.Lions.Tigers'; - Elixir$Lions$Tigers.oh_my(); - export default {}; - """ - - assert_translation(ex_ast, js_code) - end - - test "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 = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const oh_my2 = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return null; - })); - - const oh_my = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return null; - })); - - export default { - oh_my2, - oh_my - }; - - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - import Elixir$Lions$Tigers from '../app/Elixir.Lions.Tigers'; - Elixir$Lions$Tigers.oh_my2(); - export default {}; - """ - - assert_translation(ex_ast, js_code) - end - - - test "translate inner module has another inner module alias" do - ex_ast = quote do - defmodule Version do - defmodule Parser do - import Parser.DSL - end - - defmodule Parser.DSL do - - end - end - end - - js_code = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - import Elixir$Version$Parser from '../app/Elixir.Version.Parser'; - import Elixir$Version$Parser$DSL from '../app/Elixir.Version.Parser.DSL'; - export default {}; - - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - import Elixir$Version$Parser from '../app/Elixir.Version.Parser'; - export default {}; - - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - import Elixir$Version$Parser$DSL from '../app/Elixir.Version.Parser.DSL'; - export default {}; - """ - - 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 945c8bd5..00000000 --- a/test/translator/for_test.exs +++ /dev/null @@ -1,154 +0,0 @@ -defmodule ElixirScript.Translator.For.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "translate simple for" do - ex_ast = quote do - for n <- [1, 2, 3, 4], do: n * 2 - end - - js_code = """ - Elixir.Core.SpecialForms._for(Object.freeze([Object.freeze([Elixir.Core.Patterns.variable(), Object.freeze([1, 2, 3, 4])])]),function(n) { - return n * 2; - },function() { - return true; - },Object.freeze([])) - """ - - assert_translation(ex_ast, js_code) - end - - test "translate simple for with into" do - ex_ast = quote do - for n <- [1, 2, 3, 4], into: [], do: n * 2 - end - - js_code = """ - Elixir.Core.SpecialForms._for( - Object.freeze([Object.freeze([Elixir.Core.Patterns.variable(), Object.freeze([1, 2, 3, 4])])]), - function(n) { - return n * 2; - },function() { - return true; - }, - Object.freeze([])) - """ - - assert_translation(ex_ast, js_code) - end - - test "translate for with string" do - ex_ast = quote do - for n <- "Opera", do: n - end - - js_code = """ - Elixir.Core.SpecialForms._for(Object.freeze([Object.freeze([Elixir.Core.Patterns.variable(), 'Opera'])]),function(n) { - return n; - },function() { - return true; - },Object.freeze([])) - """ - - assert_translation(ex_ast, js_code) - end - - test "translate multiple generator for" do - ex_ast = quote do - for x <- [1, 2], y <- [2, 3], do: x*y - end - - js_code = """ - Elixir.Core.SpecialForms._for(Object.freeze([Object.freeze([Elixir.Core.Patterns.variable(), Object.freeze([1, 2])]), Object.freeze([Elixir.Core.Patterns.variable(), Object.freeze([2, 3])])]),function(x,y) { - return x * y; - },function() { - return true; - },Object.freeze([])) - """ - - assert_translation(ex_ast, js_code) - end - - test "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] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),Elixir.Core.SpecialForms._for(Object.freeze([Object.freeze([Elixir.Core.Patterns.variable(), Object.freeze([1, 2])]), Object.freeze([Elixir.Core.Patterns.variable(), Object.freeze([2, 3])])]),function(x,y) { - return x * y; - },function() { - return true; - },Object.freeze([]))); - """ - - assert_translation(ex_ast, js_code) - end - - test "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 = """ - Elixir.Core.SpecialForms._for(Object.freeze([Object.freeze([Elixir.Core.Patterns.variable(), Object.freeze([1, 2, 3, 4, 5, 6])])]),function(n) { - return n; - },function(n) { - return n % 2 == 0; - },Object.freeze([])) - """ - - assert_translation(ex_ast, js_code) - end - - test "translate for with pattern matched input" do - ex_ast = quote do - for {:user, name} <- [user: "john", admin: "john", user: "meg"] do - Elixir.String.upcase(name) - end - end - - js_code = """ - Elixir.Core.SpecialForms._for(Object.freeze([Object.freeze([Elixir.Core.Patterns.type(Elixir.Core.Tuple,{ - values: [Symbol.for('user'), Elixir.Core.Patterns.variable()] - }), Object.freeze([new Elixir.Core.Tuple(Symbol.for('user'),'john'), new Elixir.Core.Tuple(Symbol.for('admin'),'john'), new Elixir.Core.Tuple(Symbol.for('user'),'meg')])])]),function(name) { - return Elixir$ElixirScript$String.upcase(name); - },function() { - return true; - },Object.freeze([])) - """ - - assert_translation(ex_ast, js_code) - end - - test "translate for with bitstring" do - ex_ast = quote do - for <> do - {r, g, b} - end - end - - js_code = """ - Elixir.Core.SpecialForms._for(Object.freeze([Object.freeze([Elixir.Core.Patterns.bitStringMatch(Elixir.Core.BitString.size({ - 'value': Elixir.Core.Patterns.variable() - },8), - Elixir.Core.BitString.size({ - 'value': Elixir.Core.Patterns.variable() - },8), - Elixir.Core.BitString.size({ - 'value': Elixir.Core.Patterns.variable() - },8)), pixels])]), - function(r,g,b) { - return new Elixir.Core.Tuple(r,g,b); - }, - function() { - return true; - }, - Object.freeze([])) - """ - - assert_translation(ex_ast, js_code) - end -end diff --git a/test/translator/function_test.exs b/test/translator/function_test.exs deleted file mode 100644 index a8986098..00000000 --- a/test/translator/function_test.exs +++ /dev/null @@ -1,844 +0,0 @@ -defmodule ElixirScript.Translator.Function.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "call fun" do - ex_ast = quote do - fun.(:atom) - end - - js_code = """ - fun(Symbol.for('atom')) - """ - - assert_translation(ex_ast, js_code) - - end - - - test "translate function with a macro" do - ex_ast = quote do - def test1() do - ElixirScript.Math.squared(1) - end - end - - js_code = """ - const test1 = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return 1 * 1; - })); - """ - - assert_translation(ex_ast, js_code) - end - - test "translate functions" do - ex_ast = quote do - def test1() do - end - end - - js_code = """ - const test1 = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - def test1(alpha, beta) do - end - end - - js_code = """ - const test1 = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.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 = """ - const test1 = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()],function(alpha,beta) { - let [a] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),alpha); - return a; - })); - """ - - 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 = """ - const test1 = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()],function(alpha,beta) { - return Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(x) { - return 2; - },function(x) { - return (x === null) || (x === false); - }),Elixir.Core.Patterns.clause([Elixir.Core.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 = """ - const test1 = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()],function(alpha,beta) { - return Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(x) { - return 2; - },function(x) { - return (x === null) || (x === false); - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.wildcard()],function() { - return Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(x) { - let [a] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),1); - return a; - },function(x) { - return (x === null) || (x === false); - }),Elixir.Core.Patterns.clause([Elixir.Core.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 = """ - const test1 = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()], function(alpha, beta) { - let [a, b] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.type(Elixir.Core.Tuple, { - values: [Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()] - }), new Elixir.Core.Tuple(1, 2)); - let _ref = new Elixir.Core.Tuple(a, b); - return _ref; - })); - """ - - assert_translation(ex_ast, js_code) - end - - test "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 = "Elixir.Core.Functions.call_property(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 - - - test "translate anonymous functions" do - ex_ast = quote do - Elixir.Enum.map(list, fn(x) -> x * 2 end) - end - - js_code = """ - Elixir.Enum.map(list,Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(x) { - return x * 2; - }))) - """ - - assert_translation(ex_ast, js_code) - end - - test "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 = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const example = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return null; - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(oneArg) { - return null; - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()],function(oneArg,twoArg) { - return null; - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()],function(oneArg,twoArg,redArg) { - return null; - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()],function(oneArg,twoArg,redArg,blueArg) { - return null; - })); - export default {}; - """ - 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 = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const example = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([],function() { - return null; - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(oneArg) { - return null; - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()],function(oneArg,twoArg) { - return null; - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()],function(oneArg,twoArg,redArg) { - return null; - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()],function(oneArg,twoArg,redArg,blueArg) { - return null; - })); - export default { - example - }; - """ - assert_translation(ex_ast, js_code) - - - ex_ast = quote do - defmodule Example do - def example(oneArg) do - end - end - end - - js_code = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const example = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(oneArg) { - return null; - })); - export default { - example - }; - """ - assert_translation(ex_ast, js_code) - - end - - test "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 - - - test "test Elixir.Kernel function" do - ex_ast = quote do - is_atom(:atom) - end - - js_code = "Elixir$ElixirScript$Kernel.is_atom(Symbol.for('atom'))" - - assert_translation(ex_ast, js_code) - end - - test "guards" do - ex_ast = quote do - def something(one) when is_number(one) do - end - end - - - js_code = """ - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(one) { - return null; - },function(one) { - return Elixir$ElixirScript$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 = """ - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(one) { - return null; - },function(one) { - return Elixir$ElixirScript$Kernel.is_number(one) || Elixir$ElixirScript$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 = """ - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(one) { - return null; - },function(one) { - return Elixir$ElixirScript$Kernel.is_number(one) || Elixir$ElixirScript$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 = """ - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()],function(one,two) { - return null; - },function(one,two) { - return Elixir.Core.Functions.contains(one,Object.freeze([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 = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(one) { - return null; - },function(one) { - return Elixir.Core.Functions.contains(one,Object.freeze([1, 2, 3])); - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(one) { - return null; - },function(one) { - return Elixir$ElixirScript$Kernel.is_number(one) || Elixir$ElixirScript$Kernel.is_atom(one); - })); - export default { - something - }; - """ - assert_translation(ex_ast, js_code) - - end - - test "pattern match function with literal" do - ex_ast = quote do - def something(1) do - end - end - - - js_code = """ - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([1],function() { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - end - - test "pattern match function with list" do - ex_ast = quote do - def something([apple | fruits]) do - end - end - - - js_code = """ - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.headTail(Elixir.Core.Patterns.variable(),Elixir.Core.Patterns.variable())],function(apple,fruits) { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - end - - test "pattern match function with multiple items in list" do - ex_ast = quote do - def something([apple, pear, banana]) do - end - end - - - js_code = """ - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Object.freeze([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()])],function(apple,pear,banana) { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - end - - test "pattern match function with tuple" do - ex_ast = quote do - def something({ apple , fruits }) do - end - end - - - js_code = """ - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.type(Elixir.Core.Tuple, { - values: [Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()] - })], function(apple, fruits) { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - end - - test "pattern match function with struct" do - ex_ast = quote do - def something(%AStruct{}) do - end - end - - - js_code = """ - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.type(AStruct, {})], function() { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - end - - test "pattern match function with struct reference" do - ex_ast = quote do - def something(%AStruct{} = a) do - end - end - - js_code = """ - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.capture(Elixir.Core.Patterns.type(AStruct, {}))], function(a) { - return null; - })); - """ - assert_translation(ex_ast, js_code) - end - - test "pattern match function with map reference" do - ex_ast = quote do - def something(%{ which: 13 } = a) do - end - end - - js_code = """ - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.capture({ - [Symbol.for('which')]: 13 - })],function(a) { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - end - - test "pattern match function with struct decontructed" do - ex_ast = quote do - def something(%AStruct{key: value, key1: 2}) do - end - end - - - js_code = """ - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.type(AStruct, { - [Symbol.for('key')]: Elixir.Core.Patterns.variable(), [Symbol.for('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 = """ - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.type(AStruct, { - [Symbol.for('key')]: Elixir.Core.Patterns.variable(), [Symbol.for('key1')]: 2 - })], function(value) { - return null; - }, function(value) { - return Elixir$ElixirScript$Kernel.is_number(value); - })); - """ - - assert_translation(ex_ast, js_code) - end - - test "pattern match function with binary part" do - ex_ast = quote do - def something("Bearer " <> token) do - end - end - - - js_code = """ - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.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 = """ - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.startsWith('Bearer '), Elixir.Core.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 = """ - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.startsWith('Bearer '), Elixir.Core.Patterns.variable(), 1],function(token,hotel) { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - end - - test "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 = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const something = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([1],function() { - return null; - }),Elixir.Core.Patterns.clause([2],function() { - return null; - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(one) { - return null; - },function(one) { - return Elixir$ElixirScript$Kernel.is_binary(one); - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(one) { - return null; - })); - export default { - something - }; - """ - - assert_translation(ex_ast, js_code) - - end - - test "translate varible declaration correctly" do - ex_ast = quote do - def test1(alpha, beta) do - a = 1 - a = 2 - end - end - - js_code = """ - const test1 = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()],function(alpha,beta) { - let [a] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),1); - let [a1] = Elixir.Core.Patterns.match(Elixir.Core.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 = """ - const test1 = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()],function(alpha,beta) { - let [a] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),1); - let [a1] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),a); - let [a2] = Elixir.Core.Patterns.match(Elixir.Core.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 = """ - const test1 = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()],function(alpha,beta) { - let [a] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),1); - let [a1,b,c] = Elixir.Core.Patterns.match(Object.freeze([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()]),Object.freeze([a, 2, 3])); - let _ref = Object.freeze([a1, b, c]); - return _ref; - })); - """ - - assert_translation(ex_ast, js_code) - end - - test "translate function variables with ? or !" do - ex_ast = quote do - def test1(alpha?, beta!) do - a? = 1 - b! = 2 - end - end - - js_code = """ - const test1 = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()],function(alpha__qmark__,beta__emark__) { - let [a__qmark__] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),1); - let [b__emark__] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),2); - return b__emark__; - })); - """ - - assert_translation(ex_ast, js_code) - end - - test "translate function params with defaults" do - ex_ast = quote do - def test1(alpha, beta \\ 0) do - end - end - - js_code = """ - const test1 = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable(0)],function(alpha,beta) { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - def test1(alpha \\ fn x -> x end) do - end - end - - js_code = """ - const test1 = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable(Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(x) { - return x; - })))], - function(alpha) { - return null; - })); - """ - - assert_translation(ex_ast, js_code) - end - - test "def with catch" do - ex_ast = quote do - defp func(param) do - if true do - nil - else - :error - end - catch - :invalid -> :error - end - end - - js_code = """ - const func = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()], - function(param) { - return Elixir.Core.SpecialForms._try(function() { - return Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()], function(x) { - return Symbol.for('error'); - }, - function(x) { - return (x === null) || (x === false); - }), - Elixir.Core.Patterns.clause([Elixir.Core.Patterns.wildcard()], function() { - return null; - })).call(this, true); - }, - null, - Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Symbol.for('invalid')], function() { - return Symbol.for('error'); - })), - null, - null - ); - })); - """ - - assert_translation(ex_ast, js_code) - end - - - test "translate anonymous function with variable bound" do - ex_ast = quote do - key = "test" - fn ^key -> :ok end - end - - js_code = """ - let [key] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),'test'); - - Elixir.Core.Patterns.defmatch( - Elixir.Core.Patterns.clause( - [Elixir.Core.Patterns.bound(key)], - function() { - return Symbol.for('ok'); - } - ) - ) - """ - - assert_translation(ex_ast, js_code) - end -end diff --git a/test/translator/kernel/receive_test.exs b/test/translator/kernel/receive_test.exs deleted file mode 100644 index e1bf547b..00000000 --- a/test/translator/kernel/receive_test.exs +++ /dev/null @@ -1,65 +0,0 @@ - -defmodule ElixirScript.Translator.Receive.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "translate receive without after in process" do - ex_ast = quote do - receive do - :ok -> - value - :error -> - value - _ -> - IO.puts "Unexpected message received" - end - - end - - js_code = """ - Elixir.Core.processes.receive(function(message) { - return Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Symbol.for('ok')],function() { - return value; - }),Elixir.Core.Patterns.clause([Symbol.for('error')],function() { - return value; - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.wildcard()],function() { - return IO.puts('Unexpected message received'); - })).call(this,message); - }) - """ - - assert_translation(ex_ast, js_code) - end - - test "translate receive with after in process" 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 = """ - Elixir.Core.processes.receive(function(message) { - return Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Symbol.for('ok')],function() { - return value; - }),Elixir.Core.Patterns.clause([Symbol.for('error')],function() { - return value; - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.wildcard()],function() { - return IO.puts('Unexpected message received'); - })).call(this,message); - },5000,Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([5000],function() { - return IO.puts('No message in 5 seconds'); - }))) - """ - - assert_translation(ex_ast, js_code) - end -end diff --git a/test/translator/kernel/send_test.exs b/test/translator/kernel/send_test.exs deleted file mode 100644 index 0c0daf7a..00000000 --- a/test/translator/kernel/send_test.exs +++ /dev/null @@ -1,16 +0,0 @@ -defmodule ElixirScript.Translator.Send.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "call send outside process" do - ex_ast = quote do - send(pid, "hello") - end - - js_code = """ - Elixir$ElixirScript$Kernel.send(pid, 'hello') - """ - - assert_translation(ex_ast, js_code) - end -end diff --git a/test/translator/kernel/spawn_test.exs b/test/translator/kernel/spawn_test.exs deleted file mode 100644 index a2f9152a..00000000 --- a/test/translator/kernel/spawn_test.exs +++ /dev/null @@ -1,30 +0,0 @@ -defmodule ElixirScript.Translator.Spawn.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "call spawn with function apply" do - ex_ast = quote do - spawn(Tuple, :to_list, [{1, 2, 3}]) - end - - js_code = """ - Elixir$ElixirScript$Kernel.spawn(Elixir$ElixirScript$Tuple,Symbol.for('to_list'),Object.freeze([new Elixir.Core.Tuple(1,2,3)])) - """ - - assert_translation(ex_ast, js_code) - end - - - test "call spawn with JS function" do - ex_ast = quote do - spawn(Window, :call, [{1, 2, 3}]) - end - - js_code = """ - Elixir$ElixirScript$Kernel.spawn(Window,Symbol.for('call'),Object.freeze([new Elixir.Core.Tuple(1,2,3)])) - """ - - 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 533cf51b..00000000 --- a/test/translator/kernel_test.exs +++ /dev/null @@ -1,97 +0,0 @@ -defmodule ElixirScript.Translator.Kernel.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "raise with bitstring" do - ex_ast = quote do - raise ArgumentError, "cannot convert list to string. The list must contain only integers, strings or nested such lists; got: #{inspect list}" - end - - js_code = """ - throw ArgumentError.create(Object.freeze({ - [Symbol.for('message')]: 'cannot convert list to string. The list must contain only integers, strings or nested such lists; got: ' + Elixir$ElixirScript$String$Chars.to_string(inspect(list)) - })); - """ - - assert_translation(ex_ast, js_code) - end - - test "raise with string" do - ex_ast = quote do - raise ArgumentError, "cannot convert list to string. The list must contain only integers, strings or nested such lists; got" - end - - js_code = """ - throw ArgumentError.create(Object.freeze({ - [Symbol.for('message')]: 'cannot convert list to string. The list must contain only integers, strings or nested such lists; got' - })); - """ - - assert_translation(ex_ast, js_code) - end - - test "max" do - ex_ast = quote do - max(1, 2) - end - - js_code = """ - Elixir$ElixirScript$Kernel.max(1, 2) - """ - - assert_translation(ex_ast, js_code) - - end - - test "apply/2" do - ex_ast = quote do - apply(fun, [1, 2, 3]) - end - - js_code = """ - Elixir$ElixirScript$Kernel.apply(fun, Object.freeze([1, 2, 3])) - """ - - assert_translation(ex_ast, js_code) - - end - - test "apply/3" do - ex_ast = quote do - apply(Enum, :reverse, [1, 2, 3]) - end - - js_code = """ - Elixir$ElixirScript$Kernel.apply(Enum,Symbol.for('reverse'),Object.freeze([1, 2, 3])) - """ - - assert_translation(ex_ast, js_code) - - end - - test "hd" do - ex_ast = quote do - hd([1, 2, 3]) - end - - js_code = """ - Elixir$ElixirScript$Kernel.hd(Object.freeze([1, 2, 3])) - """ - - assert_translation(ex_ast, js_code) - - end - - test "tl" do - ex_ast = quote do - tl([1, 2, 3]) - end - - js_code = """ - Elixir$ElixirScript$Kernel.tl(Object.freeze([1, 2, 3])) - """ - - assert_translation(ex_ast, js_code) - - end -end diff --git a/test/translator/list_test.exs b/test/translator/list_test.exs deleted file mode 100644 index ed25bc2c..00000000 --- a/test/translator/list_test.exs +++ /dev/null @@ -1,60 +0,0 @@ -defmodule ElixirScript.Translator.List.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "translate list" do - ex_ast = quote do: [1, 2, 3] - js_code = "Object.freeze([1, 2, 3])" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do: ["a", "b", "c"] - js_code = "Object.freeze(['a', 'b', 'c'])" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do: [:a, :b, :c] - js_code = "Object.freeze([Symbol.for('a'), Symbol.for('b'), Symbol.for('c')])" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do: [:a, 2, "c"] - js_code = "Object.freeze([Symbol.for('a'), 2, 'c'])" - - assert_translation(ex_ast, js_code) - end - - test "concatenate lists" do - ex_ast = quote do: [1, 2, 3] ++ [4, 5, 6] - js_code = "Object.freeze([1, 2, 3]).concat(Object.freeze([4, 5, 6]))" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do: this.list ++ [4, 5, 6] - js_code = "Elixir.Core.Functions.call_property(this,'list').concat(Object.freeze([4, 5, 6]))" - - assert_translation(ex_ast, js_code) - end - - test "prepend element" do - ex_ast = quote do: [x | list] - - js_code = "Object.freeze([x]).concat(list)" - - assert_translation(ex_ast, js_code) - end - - test "prepend element in function" do - ex_ast = quote do - fn (_) -> [x|list] end - end - - js_code = """ - Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.wildcard()],function(){ - return Object.freeze([x]).concat(list); - })) - """ - - assert_translation(ex_ast, js_code) - end -end diff --git a/test/translator/map_test.exs b/test/translator/map_test.exs deleted file mode 100644 index f06e701f..00000000 --- a/test/translator/map_test.exs +++ /dev/null @@ -1,73 +0,0 @@ -defmodule ElixirScript.Translator.Map.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "translate empty map" do - ex_ast = quote do: %{} - js_code = "Object.freeze({})" - - assert_translation(ex_ast, js_code) - end - - test "translate map with elements" do - ex_ast = quote do: %{one: "one", two: "two"} - js_code = "Object.freeze({[Symbol.for('one')]: 'one', [Symbol.for('two')]: 'two'})" - - assert_translation(ex_ast, js_code) - end - - test "translate map within map" do - ex_ast = quote do: %{one: "one", two: %{three: "three"}} - js_code = """ - Object.freeze({ - [Symbol.for('one')]: 'one', - [Symbol.for('two')]: Object.freeze({ - [Symbol.for('three')]: 'three' - }) - }) - """ - - assert_translation(ex_ast, js_code) - end - - test "translate map with string keys" do - ex_ast = quote do: %{"one" => "one", "two" => "two"} - js_code = """ - Object.freeze({ - one: 'one', two: 'two' - }) - """ - - assert_translation(ex_ast, js_code) - end - - - test "translate map update" do - ex_ast = quote do: %{ map | value: 1 } - js_code = """ - Elixir.Core.SpecialForms.map_update(map,Object.freeze({ - [Symbol.for('value')]: 1 - })) - """ - - assert_translation(ex_ast, js_code) - end - - test "translate variable key" do - ex_ast = quote do: %{key => value} - js_code = "Object.freeze({ [key]: value })" - assert_translation(ex_ast, js_code) - end - - test "translate bound map key" do - ex_ast = quote do: %{^key => value} = %{key => value} - js_code = """ - let [value] = Elixir.Core.Patterns.match( - { [key]: Elixir.Core.Patterns.variable() }, - Object.freeze({ [key]: value }) - ); - """ - assert_translation(ex_ast, js_code) - end - -end diff --git a/test/translator/match_test.exs b/test/translator/match_test.exs deleted file mode 100644 index d233b873..00000000 --- a/test/translator/match_test.exs +++ /dev/null @@ -1,80 +0,0 @@ -defmodule ElixirScript.Translator.Match.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "translate simple match" do - ex_ast = quote do: a = 1 - js_code = "let [a] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(), 1);" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do: a = :atom - js_code = "let [a] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(), Symbol.for('atom'));" - - assert_translation(ex_ast, js_code) - end - - test "translate tuple match" do - ex_ast = quote do - {a, b} = {1, 2} - end - js_code = """ - let [a, b] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.type(Elixir.Core.Tuple, { - values: [Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()] - }), new Elixir.Core.Tuple(1, 2)); - let _ref = new Elixir.Core.Tuple(a, b); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do: {a, _, c} = {1, 2, 3} - js_code = """ - let [a, undefined, c] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.type(Elixir.Core.Tuple, { - values: [Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.wildcard(), Elixir.Core.Patterns.variable()] - }), new Elixir.Core.Tuple(1, 2, 3)); - let _ref = new Elixir.Core.Tuple(a, undefined, c); - """ - - assert_translation(ex_ast, js_code) - - - ex_ast = quote do: {^a, _, c} = {1, 2, 3} - js_code = """ - let [, undefined, c] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.type(Elixir.Core.Tuple, { - values: [Elixir.Core.Patterns.bound(a), Elixir.Core.Patterns.wildcard(), Elixir.Core.Patterns.variable()] - }), new Elixir.Core.Tuple(1, 2, 3)); - let _ref = new Elixir.Core.Tuple(undefined, undefined, c); - """ - - assert_translation(ex_ast, js_code) - end - - test "translate bound match" do - ex_ast = quote do: ^a = 1 - js_code = """ - let [] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.bound(a),1); - """ - - assert_translation(ex_ast, js_code) - end - - test "translate list match" do - ex_ast = quote do: [a, b] = [1, 2] - js_code = """ - let [a,b] = Elixir.Core.Patterns.match(Object.freeze([Elixir.Core.Patterns.variable(), Elixir.Core.Patterns.variable()]),Object.freeze([1, 2])); - let _ref = Object.freeze([a, b]); - """ - - assert_translation(ex_ast, js_code) - end - - test "translate head/tail match" do - ex_ast = quote do: [a | b] = [1, 2, 3, 4] - js_code = """ - let [a,b] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.headTail(Elixir.Core.Patterns.variable(),Elixir.Core.Patterns.variable()),Object.freeze([1, 2, 3, 4])); - let _ref = Object.freeze([a, b]); - """ - - 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 4c77bed1..00000000 --- a/test/translator/nil_test.exs +++ /dev/null @@ -1,9 +0,0 @@ -defmodule ElixirScript.Translator.Nil.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "translate nil" do - ex_ast = quote do: nil - assert_translation(ex_ast, "null") - end -end diff --git a/test/translator/number_test.exs b/test/translator/number_test.exs deleted file mode 100644 index 6d2fe4f4..00000000 --- a/test/translator/number_test.exs +++ /dev/null @@ -1,18 +0,0 @@ -defmodule ElixirScript.Translator.Number.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "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 diff --git a/test/translator/pattern_matching_test.exs b/test/translator/pattern_matching_test.exs deleted file mode 100644 index c01d8840..00000000 --- a/test/translator/pattern_matching_test.exs +++ /dev/null @@ -1,235 +0,0 @@ -defmodule ElixirScript.Translator.PatternMatching.Test do - use ExUnit.Case - alias ElixirScript.Translator - alias ElixirScript.Translator.Primitive - alias ElixirScript.Translator.PatternMatching - alias ElixirScript.Translator.Map - alias ESTree.Tools.Builder, as: JS - - @std_lib_state File.read!(File.cwd!() <> "/lib/elixir_script/translator/stdlib_state.bin") - - setup do - ElixirScript.Translator.State.start_link(%{env: __ENV__}, []) - ElixirScript.Translator.State.deserialize(@std_lib_state) - - # Returns extra metadata, it must be a dict - {:ok, []} - end - - test "match wildcard" do - params = [{:_, [], Test}] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - expected_result = { [PatternMatching.wildcard], [JS.identifier(:undefined)] } - - assert result == expected_result - end - - test "match one identifier param" do - params = [{:a, [], Test}] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - expected_result = {[PatternMatching.parameter], [JS.identifier("a")]} - - assert result == expected_result - end - - test "match multiple identifier params" do - params = [{:a, [], Test}, {:b, [], Test}, {:c, [], Test}] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - expected_result = { - List.duplicate(PatternMatching.parameter, 3), - [JS.identifier("a"), JS.identifier("b"), JS.identifier("c")] - } - - assert result == expected_result - end - - test "match head and tail param" do - params = [[{:|, [], [{:head, [], Elixir}, {:tail, [], Elixir}]}]] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - expected_result = { - [PatternMatching.head_tail(PatternMatching.parameter, PatternMatching.parameter)], - [JS.identifier("head"), JS.identifier("tail")] - } - - assert result == expected_result - end - - test "match prefix param" do - params = [{:<>, [context: Elixir, import: Elixir.Kernel], ["Bearer ", {:token, [], Elixir}]}] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - expected_result = { - [PatternMatching.starts_with("Bearer ")], - [JS.identifier("token")] - } - - assert result == expected_result - end - - test "match list" do - params = [[{:a, [], Elixir}, {:b, [], Elixir}, {:c, [], Elixir}]] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - expected_result = { - [Primitive.make_list_no_translate(List.duplicate(PatternMatching.parameter, 3))], - [JS.identifier("a"), JS.identifier("b"), JS.identifier("c")] - } - - assert result == expected_result - end - - test "match list with a literal" do - params = [[1, {:b, [], Elixir}, {:c, [], Elixir}]] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - expected_result = { - [Primitive.make_list_no_translate([JS.literal(1), PatternMatching.parameter, PatternMatching.parameter])], - [JS.identifier("b"), JS.identifier("c")] - } - - assert result == expected_result - end - - test "match number" do - params = [1] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - expected_result = { - [JS.literal(1)], - [] - } - - assert result == expected_result - end - - test "match struct pattern" do - params = [{:%, [], [{:__aliases__, [alias: false], [:Hello]}, {:%{}, [], []}]}] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - expected_result = { - [PatternMatching.type(JS.identifier("Hello"), JS.object_expression([]))], - [] - } - - assert result == expected_result - end - - test "match struct pattern with property" do - params = [{:%, [], [{:__aliases__, [alias: false], [:Hello]}, {:%{}, [], [key: 1]}]}] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - expected_result = { - [PatternMatching.type(JS.identifier("Hello"), JS.object_expression([ - Map.make_property(Translator.translate!(:key, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ), Translator.translate!(1, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) )) - ])) - ], - [] - } - - assert result == expected_result - end - - test "match struct pattern with property param" do - params = [{:%, [], [{:__aliases__, [alias: false], [:Hello]}, {:%{}, [], [key: {:key, [], Elixir }]}]}] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - expected_result = { - [PatternMatching.type(JS.identifier("Hello"), JS.object_expression([ - Map.make_property(Translator.translate!(:key, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ), PatternMatching.parameter) - ])) - ], - [JS.identifier("key")] - } - - assert result == expected_result - end - - test "capture parameter when assigning it" do - params = [{:=, [], [1, {:a, [], Elixir}]}] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - expected_result = { - [PatternMatching.capture(JS.literal(1))], - [JS.identifier("a")] - } - - assert result == expected_result - - - params = [{:=, [], [{:a, [], Elixir}, 1]}] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - expected_result = { - [PatternMatching.capture(JS.literal(1))], - [JS.identifier("a")] - } - - assert result == expected_result - - - params = [{:=, [], [{:%, [], [{:__aliases__, [alias: false], [:AStruct]}, {:%{}, [], []}]}, {:a, [], ElixirScript.Translator.Function.Test}]}] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - expected_result = { - [PatternMatching.capture(PatternMatching.type(JS.identifier("AStruct"), JS.object_expression([])))], - [JS.identifier("a")] - } - - assert result == expected_result - end - - test "match and assign list" do - params = [{:=, [], [[{:a, [], Elixir}, {:b, [], Elixir}, {:c, [], Elixir}], {:d, [], Elixir}]}] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - expected_result = { - [PatternMatching.capture(Primitive.make_list_no_translate([PatternMatching.parameter, PatternMatching.parameter, PatternMatching.parameter]))], - [JS.identifier("a"), JS.identifier("b"), JS.identifier("c"), JS.identifier("d")] - } - - assert result == expected_result - end - - test "match on tuple" do - params = [{:{}, [], [1, {:b, [], Elixir}, 3]}] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - expected_result = { - [PatternMatching.type(Primitive.tuple_class, JS.object_expression([JS.property( - JS.identifier("values"), - JS.array_expression([JS.literal(1), PatternMatching.parameter, JS.literal(3)]) - ) ] )) ], - [JS.identifier("b")] - } - - assert result == expected_result - - params = [{1, {:b, [], Elixir}}] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - expected_result = { - [PatternMatching.type(Primitive.tuple_class, JS.object_expression([JS.property( - JS.identifier("values"), - JS.array_expression([JS.literal(1), PatternMatching.parameter]) - ) ] )) ], - [JS.identifier("b")] - } - - assert result == expected_result - end - - test "match on map" do - params = [{:%{}, [], [which: 13]}] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - - expected_result = { - [JS.object_expression([ - Map.make_property(Translator.translate!(:which, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ), JS.literal(13)) - ])], - [] - } - - assert result == expected_result - end - - - test "match on bound value" do - params = [{:^, [], [{:a, [], Elixir}]}] - result = PatternMatching.build_match(params, ElixirScript.Translator.LexicalScope.module_scope(ElixirScript.Temp, "temp.ex", __ENV__) ) - - expected_result = { - [PatternMatching.bound(JS.identifier("a"))], - [nil] - } - - assert result == expected_result - end - -end diff --git a/test/translator/protocol_test.exs b/test/translator/protocol_test.exs deleted file mode 100644 index efcfbc5f..00000000 --- a/test/translator/protocol_test.exs +++ /dev/null @@ -1,88 +0,0 @@ -defmodule ElixirScript.Translator.Defprotocol.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "parse protocol spec with implementations" do - ex_ast = quote do - defprotocol Blank do - def blank?(data) - end - - defimpl Blank, for: Integer do - def blank?(number), do: false - end - - defimpl Blank, for: List do - def blank?([]), do: true - def blank?(_), do: false - end - - defimpl Blank, for: Atom do - def blank?(false), do: true - def blank?(nil), do: true - def blank?(_), do: false - end - end - - js_code = """ - import Elixir from '../elixir/Elixir.Bootstrap'; - let impls = []; - import Elixir$Blank$DefImpl$Elixir$Integer from '../app/Elixir.Blank.DefImpl.Elixir.Integer'; - impls.push(Elixir$Blank$DefImpl$Elixir$Integer) - import Elixir$Blank$DefImpl$Elixir$List from '../app/Elixir.Blank.DefImpl.Elixir.List'; - impls.push(Elixir$Blank$DefImpl$Elixir$List) - import Elixir$Blank$DefImpl$Elixir$Atom from '../app/Elixir.Blank.DefImpl.Elixir.Atom'; - impls.push(Elixir$Blank$DefImpl$Elixir$Atom) - export default impls; - - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - import Implementations from '../app/Elixir.Blank.Defimpl'; - const Elixir$Blank = Elixir.Core.Functions.defprotocol({ - blank__qmark__: function() { - - } - }); - for(let {Type,Implementation} of Implementations) Elixir.Core.Functions.defimpl(Elixir$Blank,Type,Implementation) - export default Elixir$Blank; - - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const blank__qmark__ = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(number) { - return false; - })); - export default { - 'Type': Elixir.Core.Integer, 'Implementation': { - blank__qmark__ - } - }; - - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const blank__qmark__ = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Object.freeze([])],function() { - return true; - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.wildcard()],function() { - return false; - })); - export default { - 'Type': Array, 'Implementation': { - blank__qmark__ - } - }; - - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const blank__qmark__ = Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([false],function() { - return true; - }),Elixir.Core.Patterns.clause([null],function() { - return true; - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.wildcard()],function() { - return false; - })); - export default { - 'Type': Symbol, 'Implementation': { - blank__qmark__ - } - }; - """ - - assert_translation(ex_ast, js_code) - end - -end diff --git a/test/translator/quote_test.exs b/test/translator/quote_test.exs deleted file mode 100644 index 27f7c33d..00000000 --- a/test/translator/quote_test.exs +++ /dev/null @@ -1,140 +0,0 @@ -defmodule ElixirScript.Translator.Quote.Test do - use ExUnit.Case - 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 = "Symbol.for('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 = "new Elixir.Core.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 = "new Elixir.Core.Tuple(Symbol.for('{}'), Object.freeze([]), Object.freeze([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 = """ - new Elixir.Core.Tuple( - Symbol.for('test'), - Object.freeze([new Elixir.Core.Tuple(Symbol.for('context'),Symbol.for('Elixir.ElixirScript.Translator.Quote.Test')), new Elixir.Core.Tuple(Symbol.for('import'),Symbol.for('Elixir.ExUnit.Case'))]),Object.freeze([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 = """ - new Elixir.Core.Tuple( - Symbol.for('test'), - Object.freeze([new Elixir.Core.Tuple(Symbol.for('context'), Symbol.for('Elixir.ElixirScript.Translator.Quote.Test')), new Elixir.Core.Tuple(Symbol.for('import'),Symbol.for('Elixir.ExUnit.Case'))]),Object.freeze([new Elixir.Core.Tuple(Symbol.for('x'),Object.freeze([]),Symbol.for('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 = """ - new Elixir.Core.Tuple( - Symbol.for('test'), - Object.freeze([new Elixir.Core.Tuple(Symbol.for('context'),Symbol.for('Elixir.ElixirScript.Translator.Quote.Test')), new Elixir.Core.Tuple(Symbol.for('import'),Symbol.for('Elixir.ExUnit.Case'))]),Object.freeze([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 = """ - new Elixir.Core.Tuple(Symbol.for('sum'),Object.freeze([]),Elixir.Enum.concat(Object.freeze([1]),values,Object.freeze([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 = """ - new Elixir.Core.Tuple( - Symbol.for('*'), - Object.freeze([new Elixir.Core.Tuple(Symbol.for('context'), Symbol.for('Elixir.ElixirScript.Translator.Quote.Test')), new Elixir.Core.Tuple(Symbol.for('import'),Symbol.for('Elixir.ElixirScript.Kernel'))]),Object.freeze([x, x]) - ) - """ - - assert_translation(ex_ast, js_code) - end - - test "quote with context option" do - ex_ast = quote do - quote context: Elixir do - test(1) - end - end - - js_code = """ - new Elixir.Core.Tuple( - Symbol.for('test'), - Object.freeze([new Elixir.Core.Tuple(Symbol.for('context'),Symbol.for('Elixir')), new Elixir.Core.Tuple(Symbol.for('import'),Symbol.for('Elixir.ExUnit.Case'))]),Object.freeze([1]) - ) - """ - - 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 16d3d6b9..00000000 --- a/test/translator/string_test.exs +++ /dev/null @@ -1,43 +0,0 @@ -defmodule ElixirScript.Translator.String.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "translate string" do - ex_ast = quote do: "Hello" - assert_translation(ex_ast, "'Hello'") - end - - test "translate multiline string" do - ex_ast = quote do: """ - Hello - This is another line - """ - assert_translation(ex_ast, "'Hello\\nThis is another line\\n'") - end - - test "translate string concatenation" do - ex_ast = quote do: "Hello" <> "World" - assert_translation(ex_ast, "'Hello' + 'World'") - end - - test "translate string interpolation" do - ex_ast = quote do: "Hello #{"world"}" - assert_translation(ex_ast, "'Hello ' + Elixir$ElixirScript$String$Chars.to_string('world')") - - ex_ast = quote do: "Hello #{length([])}" - assert_translation(ex_ast, "'Hello ' + Elixir$ElixirScript$String$Chars.to_string(Elixir$ElixirScript$Kernel.length(Object.freeze([])))") - end - - test "translate multiline string interpolation" do - ex_ast = quote do: """ - Hello #{length([])} - """ - assert_translation(ex_ast, "'Hello ' + (Elixir$ElixirScript$String$Chars.to_string(Elixir$ElixirScript$Kernel.length(Object.freeze([]))) + '\\n')") - - ex_ast = quote do: """ - Hello #{length([])} - How are you, #{length([])}? - """ - assert_translation(ex_ast, "'Hello ' + (Elixir$ElixirScript$String$Chars.to_string(Elixir$ElixirScript$Kernel.length(Object.freeze([]))) + ('\\nHow are you, ' + (Elixir$ElixirScript$String$Chars.to_string(Elixir$ElixirScript$Kernel.length(Object.freeze([]))) + '?\\n')))") - end -end diff --git a/test/translator/struct_test.exs b/test/translator/struct_test.exs deleted file mode 100644 index 4d7248b5..00000000 --- a/test/translator/struct_test.exs +++ /dev/null @@ -1,221 +0,0 @@ -defmodule ElixirScript.Translator.Struct.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "translate struct with default values" do - ex_ast = quote do - defmodule User do - defstruct name: "john", age: 27 - end - end - - js_code = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const Elixir$User = Elixir.Core.Functions.defstruct({ - [Symbol.for('__struct__')]: Symbol.for('Elixir.User'), - [Symbol.for('name')]: 'john', - [Symbol.for('age')]: 27 - }); - - export default { - Elixir$User - }; - """ - - assert_translation(ex_ast, js_code) - end - - test "translate struct without default values" do - - ex_ast = quote do - defmodule User do - defstruct [:name, :age] - end - end - - js_code = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const Elixir$User = Elixir.Core.Functions.defstruct({ - [Symbol.for('__struct__')]: Symbol.for('Elixir.User'), - [Symbol.for('name')]: null, - [Symbol.for('age')]: null - }); - export default { - Elixir$User - }; - """ - - assert_translation(ex_ast, js_code) - - end - - test "translate struct creation" do - ex_ast = quote do - defmodule User do - defstruct :name, :age - end - - user = %User{} - end - - js_code = """ - - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const Elixir$User = Elixir.Core.Functions.defstruct({ - [Symbol.for('__struct__')]: Symbol.for('Elixir.User'), - [Symbol.for('name')]: null, - [Symbol.for('age')]: null - }); - - export default { - Elixir$User - }; - - let [user] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(), Elixir$User.Elixir$User.create(Object.freeze({}))); - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - defmodule User do - defstruct :name, :age - end - - user = %User{name: "John"} - end - - js_code = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const Elixir$User = Elixir.Core.Functions.defstruct({ - [Symbol.for('__struct__')]: Symbol.for('Elixir.User'), - [Symbol.for('name')]: null, - [Symbol.for('age')]: null - }); - export default { - Elixir$User - }; - - let [user] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),Elixir$User.Elixir$User.create(Object.freeze({ - [Symbol.for('name')]: 'John' - }))); - """ - - assert_translation(ex_ast, js_code) - end - - test "translate struct update" do - ex_ast = quote do - user = %{ map | key: value } - end - - js_code = """ - let [user] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),Elixir.Core.SpecialForms.map_update(map,Object.freeze({ - [Symbol.for('key')]: value - }))); - """ - - assert_translation(ex_ast, js_code) - - - ex_ast = quote do - user = %{ map | key: value, key1: value1 } - end - - js_code = """ - let [user] = Elixir.Core.Patterns.match(Elixir.Core.Patterns.variable(),Elixir.Core.SpecialForms.map_update(map,Object.freeze({ - [Symbol.for('key')]: value, [Symbol.for('key1')]: value1 - }))); - """ - - assert_translation(ex_ast, js_code) - end - - test "translate defexception" do - ex_ast = quote do - defmodule MyAppError do - defexception message: "This is a message" - end - end - - js_code = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const Elixir$MyAppError = Elixir.Core.Functions.defexception({ - [Symbol.for('__struct__')]: Symbol.for('Elixir.MyAppError'), - [Symbol.for('__exception__')]: true, - [Symbol.for('message')]: 'This is a message' - }); - export default { - Elixir$MyAppError - }; - """ - - assert_translation(ex_ast, js_code) - - ex_ast = quote do - defmodule MyAppError do - defexception [:message] - end - end - - js_code = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const Elixir$MyAppError = Elixir.Core.Functions.defexception({ - [Symbol.for('__struct__')]: Symbol.for('Elixir.MyAppError'), - [Symbol.for('__exception__')]: true, - [Symbol.for('message')]: null - }); - - export default { - Elixir$MyAppError - }; - """ - - assert_translation(ex_ast, js_code) - - end - - test "translate raise exception" do - ex_ast = quote do - defmodule MyAppError do - defexception [:message] - end - - raise MyAppError, message: "did not get what was expected" - end - - js_code = """ - import Elixir$ElixirScript$Kernel from '../elixir/Elixir.ElixirScript.Kernel'; - const Elixir$MyAppError = Elixir.Core.Functions.defexception({ - [Symbol.for('__struct__')]: Symbol.for('Elixir.MyAppError'), - [Symbol.for('__exception__')]: true, - [Symbol.for('message')]: null - }); - - export default { - Elixir$MyAppError - }; - - throw Elixir$MyAppError.Elixir$MyAppError.create(Object.freeze({ - [Symbol.for('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 { - [Symbol.for('__struct__')]: Symbol.for('RuntimeError'), - [Symbol.for('__exception__')]: true, - [Symbol.for('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 cdfe85c8..00000000 --- a/test/translator/try_test.exs +++ /dev/null @@ -1,214 +0,0 @@ -defmodule ElixirScript.Translator.Try.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "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 = """ - Elixir.Core.SpecialForms._try(function() { - return do_something_that_may_fail(some_arg); - }, Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.type(ArgumentError, {})], function() { - return IO.puts('Invalid argument given'); - })), null, null, null) - """ - - assert_translation(ex_ast, js_code) - end - - test "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 = """ - Elixir.Core.SpecialForms._try(function() { - return do_something_that_may_fail(some_arg); - }, Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.type(ArgumentError, {})], function() { - return IO.puts('Invalid argument given'); - })), null, null, null) - """ - - assert_translation(ex_ast, js_code) - end - - test "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 = """ - Elixir.Core.SpecialForms._try(function() { - return do_something_that_may_fail(some_arg); - },Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(x) { - return IO.puts('Invalid argument given'); - },function(x) { - return Elixir.Core.Functions.contains(x,Object.freeze([ArgumentError.create(Object.freeze({}))])); - })),null,null,null) - """ - - assert_translation(ex_ast, js_code) - end - - test "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 = """ - Elixir.Core.SpecialForms._try(function() { - return do_something_that_may_fail(some_arg); - },Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(x) { - return IO.puts('Invalid argument given'); - })),null,null,null) - """ - - assert_translation(ex_ast, js_code) - end - - - test "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 = """ - Elixir.Core.SpecialForms._try(function() { - return do_something_that_may_fail(some_arg); - }, Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.type(ArgumentError, {})], function() { - return IO.puts('ArgumentError'); - }), Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()], function(x) { - return IO.puts('x'); - })), null, null, null) - """ - - assert_translation(ex_ast, js_code) - end - - test "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 = """ - Elixir.Core.SpecialForms._try(function() { - return do_something_that_may_fail(some_arg); - }, Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.type(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 - - test "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 = """ - Elixir.Core.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 - - test "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 = """ - Elixir.Core.SpecialForms._try(function() { - return 1 / x; - },null,null,Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.variable()],function(y) { - return Symbol.for('small'); - },function(y) { - return (y < 1) && (y > -1); - }),Elixir.Core.Patterns.clause([Elixir.Core.Patterns.wildcard()],function() { - return Symbol.for('large'); - })),null) - """ - - assert_translation(ex_ast, js_code) - end - - test "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 = """ - Elixir.Core.SpecialForms._try(function() { - return do_something_that_may_fail(some_arg); - }, Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Elixir.Core.Patterns.type(ArgumentError, {})], function() { - return IO.puts('Invalid argument given'); - })), Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Symbol.for('throw'), Symbol.for('Error')], function() { - return IO.puts('caught error'); - })), null, null) - """ - - assert_translation(ex_ast, js_code) - end -end diff --git a/test/translator/tuple_test.exs b/test/translator/tuple_test.exs deleted file mode 100644 index afe44e63..00000000 --- a/test/translator/tuple_test.exs +++ /dev/null @@ -1,35 +0,0 @@ -defmodule ElixirScript.Translator.Tuple.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - test "translate 2 item tuple" do - ex_ast = quote do: {1, 2} - js_code = "new Elixir.Core.Tuple(1, 2)" - - assert_translation(ex_ast, js_code) - end - - test "translate multiple item tuple" do - ex_ast = quote do: {1, 2, 3, 4, 5} - js_code = "new Elixir.Core.Tuple(1, 2, 3, 4, 5)" - - assert_translation(ex_ast, js_code) - end - - test "translate tuples of different typed items" do - ex_ast = quote do: {"a", "b", "c"} - js_code = "new Elixir.Core.Tuple('a', 'b', 'c')" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do: {:a, :b, :c} - js_code = "new Elixir.Core.Tuple(Symbol.for('a'), Symbol.for('b'), Symbol.for('c'))" - - assert_translation(ex_ast, js_code) - - ex_ast = quote do: {:a, 2, "c"} - js_code = "new Elixir.Core.Tuple(Symbol.for('a'), 2, 'c')" - - assert_translation(ex_ast, js_code) - end -end diff --git a/test/translator/with_test.exs b/test/translator/with_test.exs deleted file mode 100644 index 4a1fedb7..00000000 --- a/test/translator/with_test.exs +++ /dev/null @@ -1,85 +0,0 @@ -defmodule ElixirScript.Translator.With.Test do - use ExUnit.Case - import ElixirScript.TestHelper - - - test "translate with" do - ex_ast = quote do - with {:ok, width} <- Map.fetch(opts, :width), - {:ok, height} <- Map.fetch(opts, :height), - do: {:ok, width * height} - end - - js_code = """ - Elixir.Core.SpecialForms._with([Elixir.Core.Patterns.type(Elixir.Core.Tuple,{ - values: [Symbol.for('ok'), Elixir.Core.Patterns.variable()] - }), function() { - return Elixir$ElixirScript$Map.fetch(opts,Symbol.for('width')); - }],[Elixir.Core.Patterns.type(Elixir.Core.Tuple,{ - values: [Symbol.for('ok'), Elixir.Core.Patterns.variable()] - }), function(width) { - return Elixir$ElixirScript$Map.fetch(opts,Symbol.for('height')); - }],function(width,height) { - return new Elixir.Core.Tuple(Symbol.for('ok'),width * height); - }) - """ - - assert_translation(ex_ast, js_code) - end - - test "translate with with bare expression" do - ex_ast = quote do - with {:ok, width} <- Map.fetch(opts, :width), - double_width = width * 2, - {:ok, height} <- Map.fetch(opts, :height), - do: {:ok, double_width * height} - end - - js_code = """ - Elixir.Core.SpecialForms._with([Elixir.Core.Patterns.type(Elixir.Core.Tuple,{ - values: [Symbol.for('ok'), Elixir.Core.Patterns.variable()] - }), function() { - return Elixir$ElixirScript$Map.fetch(opts,Symbol.for('width')); - }],[Elixir.Core.Patterns.variable(), function(width) { - return width * 2; - }],[Elixir.Core.Patterns.type(Elixir.Core.Tuple,{ - values: [Symbol.for('ok'), Elixir.Core.Patterns.variable()] - }), function(width,double_width) { - return Elixir$ElixirScript$Map.fetch(opts,Symbol.for('height')); - }],function(width,double_width,height) { - return new Elixir.Core.Tuple(Symbol.for('ok'),double_width * height); - }) - """ - - assert_translation(ex_ast, js_code) - end - - test "translate with with else" do - ex_ast = quote do - with {:ok, width} <- Map.fetch(opts, :width), - {:ok, height} <- Map.fetch(opts, :height) do - {:ok, width * height} - else - :error -> {:error, :wrong_data} - end - end - - js_code = """ - Elixir.Core.SpecialForms._with([Elixir.Core.Patterns.type(Elixir.Core.Tuple,{ - values: [Symbol.for('ok'), Elixir.Core.Patterns.variable()] - }), function() { - return Elixir$ElixirScript$Map.fetch(opts,Symbol.for('width')); - }],[Elixir.Core.Patterns.type(Elixir.Core.Tuple,{ - values: [Symbol.for('ok'), Elixir.Core.Patterns.variable()] - }), function(width) { - return Elixir$ElixirScript$Map.fetch(opts,Symbol.for('height')); - }],function(width,height) { - return new Elixir.Core.Tuple(Symbol.for('ok'),width * height); - },Elixir.Core.Patterns.defmatch(Elixir.Core.Patterns.clause([Symbol.for('error')],function() { - return new Elixir.Core.Tuple(Symbol.for('error'),Symbol.for('wrong_data')); - }))) - """ - - assert_translation(ex_ast, js_code) - end -end 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