Compare commits

...

123 Commits

Author SHA1 Message Date
Sebastian McKenzie
20d19735fc v5.5.7 2015-06-13 02:22:20 +01:00
Sebastian McKenzie
b5b6bf4ad5 add isDirective method, 2015-06-13 02:21:22 +01:00
Sebastian McKenzie
844c10cac0 fix reference to inferers 2015-06-13 02:20:04 +01:00
Sebastian McKenzie
43583e4e9d pick only current constant violation if it's of the same scope 2015-06-13 02:19:57 +01:00
Sebastian McKenzie
f5b921cda9 better errorWithNode that's consolidated across paths and files 2015-06-13 02:19:44 +01:00
Sebastian McKenzie
763892aa79 remove unused variable 2015-06-13 02:19:30 +01:00
Sebastian McKenzie
3e6eae4d1a Merge branch 'master' of github.com:babel/babel 2015-06-13 02:00:22 +01:00
Sebastian McKenzie
7c090c8580 Merge pull request #1740 from zertosh/cli-fixes
Really fix "--help"
2015-06-13 01:56:58 +01:00
Andres Suarez
a5f6c1c389 Really fix "--help" 2015-06-12 20:54:53 -04:00
Sebastian McKenzie
c159f2d982 Merge branch 'master' of github.com:babel/babel 2015-06-13 01:54:33 +01:00
Sebastian McKenzie
9205f10244 Merge pull request #1719 from jmm/internals-docs2
Internals documentation
2015-06-13 01:53:04 +01:00
Sebastian McKenzie
4cd7bcad59 Merge pull request #1727 from zertosh/cli-fixes
Fix "--help" distinguish optional transforms
2015-06-13 01:52:46 +01:00
Andres Suarez
7e9660efd3 Fix "--help" distinguish optional transforms 2015-06-12 20:51:52 -04:00
Sebastian McKenzie
2d66ce5224 Merge pull request #1724 from arthurvr/bool
Update `util.booleanify()` return type
2015-06-13 01:48:30 +01:00
Sebastian McKenzie
1257b2cf40 Merge pull request #1736 from grncdr/patch-1
Remove duplicate keys from alias-keys.json
2015-06-12 20:33:54 +01:00
Stephen Sugden
f21d935de5 Add aliases from JSX* tags to Expression 2015-06-12 12:24:24 -07:00
Stephen Sugden
2e20364793 Remove duplicate keys from alias-keys.json
Fixes #1734
2015-06-12 20:58:41 +02:00
Sebastian McKenzie
e47e8a187a Merge branch 'master' of github.com:babel/babel 2015-06-11 19:06:41 +01:00
Ingvar Stepanyan
26924d5944 Fix dependency reference of Symbol.hasInstance 2015-06-10 22:07:40 +03:00
Arthur Verschaeve
5eb1850a55 Update util.booleanify() return type
Ref 62f37c1e62
2015-06-10 16:54:43 +02:00
Sebastian McKenzie
333e287226 remove special minification.removeConsole ExpressionStatement handling 2015-06-10 13:14:44 +01:00
Sebastian McKenzie
80a77bd6a2 fix linting error 2015-06-10 03:16:07 +01:00
Sebastian McKenzie
c9286a1de1 rewrite option handling - fixes #1636 2015-06-10 03:07:06 +01:00
Sebastian McKenzie
52f614dcdf add better path execution status algo 2015-06-10 01:36:36 +01:00
Sebastian McKenzie
600367ae25 add t.COMPARISON_BINARY_OPERATORS 2015-06-10 01:34:51 +01:00
Sebastian McKenzie
b761cba135 split auxiliary comment option into before and after - fixes #1721 2015-06-10 01:34:44 +01:00
Sebastian McKenzie
947d3e262d push newline after decorator when doing code gen - fixes #1713 2015-06-10 01:19:58 +01:00
Sebastian McKenzie
4061bea528 change execution order of module metadata visitor to resolve module source before building up metadata tree - fixes #1720 2015-06-10 01:15:11 +01:00
Sebastian McKenzie
de195e5bfc Merge branch 'master' of github.com:babel/babel 2015-06-10 01:10:41 +01:00
Sebastian McKenzie
3bcef86973 Merge pull request #1720 from chadhietala/failing-metadata-test
Metadata object does not take in account resolveModuleSource()
2015-06-10 01:10:09 +01:00
Sebastian McKenzie
fa670ac71e visually split up inference inferer methods 2015-06-09 22:52:21 +01:00
Sebastian McKenzie
572261f9ce add support for typecasts in path static evaluation 2015-06-09 22:52:00 +01:00
Chad Hietala
8a320d53a5 Metadata object does not take in account resolveModuleSource()
This adds a failing test to illustrate the metadata object not reflecting what is returned from resolveModuleSource(). I might also not understand resolveModuleSource's purpose. However, this was the hook mentioned here https://github.com/babel/babel/issues/1602.
2015-06-09 14:26:33 -07:00
Jesse McCarthy
0650eedeb6 Add reference to doc dir to CONTRIBUTING. 2015-06-09 15:37:01 -04:00
Jesse McCarthy
2282d066a2 Start doc dir for internals documentation. 2015-06-09 15:37:01 -04:00
Sebastian McKenzie
f4d7cc55c1 split inference logic into separate folder 2015-06-09 14:02:57 +01:00
Sebastian McKenzie
eaaa279aa5 add let binding collision todo 2015-06-09 04:08:44 +01:00
Sebastian McKenzie
0595e06e29 5.5.6 2015-06-09 04:08:36 +01:00
Sebastian McKenzie
9b27a170ae v5.5.6 2015-06-09 04:07:15 +01:00
Sebastian McKenzie
1db232da9e add 5.5.6 changelog 2015-06-09 04:06:06 +01:00
Sebastian McKenzie
4cc844f410 take into consideration assignment expressions in loop heads when replacing let references - fixes #1707 2015-06-09 04:04:14 +01:00
Sebastian McKenzie
024ae670cb fix traceur test blacklist 2015-06-09 03:48:22 +01:00
Sebastian McKenzie
429edda9c0 ignore StringIterator traceur test 2015-06-09 03:40:08 +01:00
Sebastian McKenzie
05b13b9ea3 fix mistyped parameter name of t.createTypeAnnotationBasedOnTypeof 2015-06-09 03:26:40 +01:00
Sebastian McKenzie
b7320ce400 add t.createTypeAnnotationBasedOnTypeof method 2015-06-09 03:23:28 +01:00
Sebastian McKenzie
0c37b7b973 add typeof conditional inference 2015-06-09 03:20:32 +01:00
Sebastian McKenzie
bb36dbd8d9 update to latest traceur and enable now passing tests 2015-06-09 03:20:02 +01:00
Sebastian McKenzie
2dd8c40618 heavily simplify constants transformer 2015-06-09 00:31:18 +01:00
Sebastian McKenzie
33128b0ccf remove unused declaration 2015-06-08 23:59:53 +01:00
Sebastian McKenzie
cf25424295 fix duplicate declaration 2015-06-08 23:49:00 +01:00
Sebastian McKenzie
7492074794 infer types of bindings inside of conditionals based on usage 2015-06-08 23:43:46 +01:00
Sebastian McKenzie
c4a491123e disallow line terminator after async contextual keyword - fixes #1711 2015-06-08 21:25:16 +01:00
Sebastian McKenzie
55ad88fe4e don't override types parameter 2015-06-08 14:59:19 +01:00
Sebastian McKenzie
7170dbced8 Merge branch 'master' of github.com:babel/babel 2015-06-08 14:53:54 +01:00
Sebastian McKenzie
b0971412a2 add inference for null, remove any type parameter from inferred arrays, add todo comment 2015-06-08 14:52:35 +01:00
Sebastian McKenzie
a6b374a681 save union type in _getTypeAnnotationBindingConstantViolations to prevent infinite recursion 2015-06-08 14:52:13 +01:00
Sebastian McKenzie
2d0355b3b9 merge previous bindings constantViolations and path onto new bindings constantViolations 2015-06-08 14:49:09 +01:00
Sebastian McKenzie
7fade101be move down module TypeAlias check 2015-06-08 14:47:58 +01:00
Sebastian McKenzie
0918da8569 Merge pull request #1708 from hawkrives/patch-2
Update README.md for the Slack channel
2015-06-08 12:35:08 +01:00
Hawken Rives
917db622c4 Update README.md
Switch out the gitter link for slack.
2015-06-08 20:33:09 +09:00
Sebastian McKenzie
f7ee6fbd20 move travis notifications from gitter to slack 2015-06-08 12:20:38 +01:00
Sebastian McKenzie
5899e9a0be don't consider type aliases to be a default declaration - fixes #1705 2015-06-08 12:20:30 +01:00
Sebastian McKenzie
d41cb11545 fix registerDeclaration for FlowDeclarations 2015-06-08 01:46:05 +01:00
Sebastian McKenzie
3ad909a4ae add 5.5.5 changelog 2015-06-08 01:45:56 +01:00
Sebastian McKenzie
4bafdf733c 5.5.5 2015-06-08 01:29:54 +01:00
Sebastian McKenzie
b825998c63 v5.5.5 2015-06-08 01:27:46 +01:00
Sebastian McKenzie
6b02ca47c3 add missing semicolon 2015-06-08 01:27:02 +01:00
Sebastian McKenzie
ea1b85bffa fix bug where templates were getting polluted with old traversal paths 2015-06-08 01:25:51 +01:00
Sebastian McKenzie
3cffe47eea fix NodePath#isGenericType method name 2015-06-08 01:00:01 +01:00
Sebastian McKenzie
e5d5a9fb27 remove unused variable 2015-06-08 00:33:41 +01:00
Sebastian McKenzie
ca97fa63a9 Merge branch 'master' of github.com:babel/babel 2015-06-08 00:30:05 +01:00
Sebastian McKenzie
f4cc27bc0e remove unused variable 2015-06-08 00:29:52 +01:00
Sebastian McKenzie
8cea575e2e change NodePath#findParent to only call callback with path instead of node 2015-06-08 00:29:46 +01:00
Sebastian McKenzie
c91baee4d5 add support for flow declarations in scope tracking 2015-06-08 00:04:17 +01:00
Sebastian McKenzie
8055ce29f7 add support for flow declarations in scope tracking 2015-06-07 23:57:19 +01:00
Sebastian McKenzie
4596ae48b8 remove acorn jsx tests as the jsx parser is no longer embedded 2015-06-07 23:57:11 +01:00
Sebastian McKenzie
6c268cdf21 split out path comment methods into a separate file 2015-06-07 23:49:29 +01:00
Sebastian McKenzie
fce977f1d7 update TraversalContext#shouldVisit to check for existence of visitor keys 2015-06-07 23:38:39 +01:00
Sebastian McKenzie
a298075949 check for loc value on comments before attempting to adjust it 2015-06-07 23:37:47 +01:00
Sebastian McKenzie
66599c3779 use scope paths hub instead of manually passing the hub to the scope 2015-06-07 23:37:33 +01:00
Sebastian McKenzie
60340244b1 when constructing a NodePath, inherit parent paths hub if one wasn't passed to us 2015-06-07 23:36:32 +01:00
Sebastian McKenzie
eb72ea3e5a rename path verification methods to introspection and add NodePath#getSource method 2015-06-07 23:36:12 +01:00
Sebastian McKenzie
ede6237b6f add NodePath#addComment method 2015-06-07 23:35:46 +01:00
Sebastian McKenzie
e91e10aae6 add FlowStatement and FlowDeclaration alias keys 2015-06-07 23:35:35 +01:00
Sebastian McKenzie
9c3cca0d25 rename NodePath#isTypeAnnotationGeneric to isTypeAnnotation 2015-06-07 23:35:09 +01:00
Sebastian McKenzie
8eee5367f3 add Noop node 2015-06-07 23:34:35 +01:00
Sebastian McKenzie
40d55a3d44 update makefile browser build filename 2015-06-07 20:24:21 +01:00
Sebastian McKenzie
75330304dc fix linting errors 2015-06-07 20:04:30 +01:00
Sebastian McKenzie
776c508418 add node build to Makefile 2015-06-07 19:41:28 +01:00
Sebastian McKenzie
e804741632 add module metadata - closes #1601 2015-06-07 19:41:20 +01:00
Sebastian McKenzie
3d3cb4be4f completely rework type inferrence, support coercing to union types and be more reliable in the inferrence and always be cautious 2015-06-07 19:39:53 +01:00
Sebastian McKenzie
64f4209119 recurse into type casts when trying to get it's expression - fixes #facebook/react-native#1526 2015-06-07 02:45:06 +01:00
Sebastian McKenzie
2ede226ef9 remove unused variables 2015-06-06 16:35:28 +01:00
Sebastian McKenzie
f5cf641c0a add support for async generators to type inferrence 2015-06-06 16:17:55 +01:00
Sebastian McKenzie
1abd3419f6 simplify NodePath.getScope 2015-06-06 16:17:43 +01:00
Sebastian McKenzie
75699db716 clean up options normalisation and add more comments 2015-06-06 16:17:30 +01:00
Sebastian McKenzie
7c3572f08c fix linting errors 2015-06-06 03:38:12 +01:00
Sebastian McKenzie
9dacde6d07 further implement the concept of a "Hub" that all traversal paths get access to, also add in some assertions to confirm path state when performing manipulation 2015-06-06 03:34:08 +01:00
Sebastian McKenzie
8c3aab9a26 add support for async functions to type inferrence 2015-06-06 03:33:32 +01:00
Sebastian McKenzie
ba4550c953 switch some node-parent based stuff to path-based 2015-06-06 03:33:22 +01:00
Sebastian McKenzie
d0ac65a934 add _paths in t.inherits 2015-06-06 03:32:35 +01:00
Sebastian McKenzie
a4c70bb029 this commit makes the following changes to the way paths are handled:
- store paths on parents instead of containers
 - implement one central hub that all traversal paths and scopes get access to in order to abstract out access to common functions
2015-06-06 03:32:22 +01:00
Sebastian McKenzie
795cf0c0b1 add ignore/only tests to ensure #1693 never happens again 2015-06-05 23:11:10 +01:00
Sebastian McKenzie
e64b90e322 5.5.4 2015-06-05 23:07:27 +01:00
Sebastian McKenzie
d69b0973e1 v5.5.4 2015-06-05 23:03:55 +01:00
Sebastian McKenzie
6296f49653 update minification.constantFolding transformer to deopt bindings that are reassigned in a different function scope 2015-06-05 23:01:31 +01:00
Sebastian McKenzie
9f2b739046 improve Scope#dump to print binding info 2015-06-05 23:00:50 +01:00
Sebastian McKenzie
da1d5e5577 simplify unary resolution and move operators to types 2015-06-05 23:00:06 +01:00
Sebastian McKenzie
7333b4e392 move staticPropBody class concat to after className check in es6.classes transformer 2015-06-05 22:44:03 +01:00
Sebastian McKenzie
b0442d0784 add back shouldIgnore check that went missing around 32f19aff99 - closes #1696, fixes #1693 2015-06-05 22:43:32 +01:00
Sebastian McKenzie
295e69f8f8 fix auxiliaryComment option name for istanbul interop - fixes #1695 2015-06-05 22:40:28 +01:00
Sebastian McKenzie
cfe844fa39 add boolean type to experimental option 2015-06-05 22:36:16 +01:00
Sebastian McKenzie
0f4ea2d2a6 use file.log.deprecate instead of throwing an error - fixes #1694 2015-06-05 22:35:46 +01:00
Sebastian McKenzie
4b85b05839 use actual parameter reference for non-last default parameters - fixes #1690 2015-06-05 14:08:18 +01:00
Sebastian McKenzie
2539d08dce 5.5.3 2015-06-05 14:07:34 +01:00
Sebastian McKenzie
c26fd7a819 fix regenerator version 2015-06-05 14:07:27 +01:00
Sebastian McKenzie
2053610429 v5.5.3 2015-06-05 12:20:22 +01:00
Sebastian McKenzie
cd4f83b299 fix linting errors 2015-06-05 12:19:32 +01:00
Sebastian McKenzie
ec29ba19a9 add 5.5.3 changelog 2015-06-05 12:18:43 +01:00
Sebastian McKenzie
9dc03e0978 traverse over ClassProperty path rather than node 2015-06-05 12:17:55 +01:00
Sebastian McKenzie
bc4258eca9 add type inferrence for template literals 2015-06-05 12:17:45 +01:00
Sebastian McKenzie
b0e58f9770 add completion statement test and enable experimental option on deadCodeElimination tests 2015-06-05 12:17:36 +01:00
Sebastian McKenzie
a1e2641c91 5.5.2 2015-06-05 12:17:18 +01:00
91 changed files with 2125 additions and 4629 deletions

View File

@@ -11,9 +11,4 @@ before_script: "npm install -g codeclimate-test-reporter"
script: "make test-travis"
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/acf1870e9d223c65e8d5
on_success: always
on_failure: always
on_start: false
slack: babeljs:5Wy4QX13KVkGy9CnU0rmvgeK

View File

@@ -13,6 +13,28 @@ _Note: Gaps between patch versions are faulty/broken releases._
See [CHANGELOG - 6to5](CHANGELOG-6to5.md) for the pre-4.0.0 version changelog.
## 5.5.6
* **Bug Fix**
* Fix `let` binding collision in loop head not properly replacing `AssignmentExpression`s.
## 5.5.5
* **Bug Fix**
* Fix `file.opts` not being set before `file.log.deprecate` was called causing a `ReferenceError` as it was checking for a property on it.
## 5.5.4
* **Bug Fix**
* Add back missing `shouldIgnore` check.
* Log message on deprecated options rather than throw an error.
* Fix name of `auxiliaryComment` option when attempting Istanbul interop in `babel/register`.
## 5.5.3
* **Bug Fix**
* Fix weird state bug when traversing overa `node` `ClassProperty` instead of `path` in the `es6.classes` transformer.
## 5.5.2
* **Bug Fix**

View File

@@ -12,6 +12,8 @@
<strong><a href="#dependencies">Dependencies</a></strong>
|
<strong><a href="#code-standards">Code Standards</a></strong>
|
<strong><a href="#internals">Internals</a></strong>
</p>
----
@@ -173,3 +175,6 @@ your [`$PATH`](http://unix.stackexchange.com/questions/26047/how-to-correctly-ad
* **Declaration**
* No unused variables
* No pollution of global variables and prototypes
#### Internals
Please see [`/doc`](/doc) for internals documentation relevant to developing babel.

View File

@@ -5,6 +5,7 @@ UGLIFY_CMD = node_modules/uglify-js/bin/uglifyjs
#UGLIFY_CMD = node_modules/uglify-js/bin/uglifyjs --mangle sort
MOCHA_CMD = node_modules/mocha/bin/_mocha
BABEL_CMD = node_modules/babel/bin/babel
BROWSERIFY_IGNORE = -i esprima-fb
export NODE_ENV = test
@@ -34,8 +35,10 @@ build:
node $(BROWSERIFY_CMD) -e lib/babel/polyfill.js >dist/polyfill.js
node $(UGLIFY_CMD) dist/polyfill.js >dist/polyfill.min.js
node $(BROWSERIFY_CMD) lib/babel/api/browser.js -s babel >dist/babel.js
node $(UGLIFY_CMD) dist/babel.js >dist/babel.min.js
node $(BROWSERIFY_CMD) lib/babel/api/browser.js -s babel $(BROWSERIFY_IGNORE) >dist/browser.js
node $(UGLIFY_CMD) dist/browser.js >dist/browser.min.js
node $(BROWSERIFY_CMD) lib/babel/api/node.js --node $(BROWSERIFY_IGNORE) >dist/node.js
node packages/babel-cli/bin/babel-external-helpers >dist/external-helpers.js
node $(UGLIFY_CMD) dist/external-helpers.js >dist/external-helpers.min.js
@@ -86,8 +89,8 @@ publish: lint
make build
cp dist/babel.js browser.js
cp dist/babel.min.js browser.min.js
cp dist/browser.js browser.js
cp dist/browser.min.js browser.min.js
cp dist/polyfill.js browser-polyfill.js
cp dist/polyfill.min.js browser-polyfill.min.js

View File

@@ -9,7 +9,7 @@
</p>
<p align="center">
For questions and support please visit the <a href="https://gitter.im/babel/babel">gitter room</a> or <a href="http://stackoverflow.com/questions/tagged/babeljs">StackOverflow</a>. The Babel issue tracker is <strong>exclusively</strong> for bug reports and feature requests.
For questions and support please visit the <a href="https://babel-slack.herokuapp.com">slack channel</a> or <a href="http://stackoverflow.com/questions/tagged/babeljs">StackOverflow</a>. The Babel issue tracker is <strong>exclusively</strong> for bug reports and feature requests.
</p>
<p align="center">

4
doc/index.md Normal file
View File

@@ -0,0 +1,4 @@
This is a collection of documentation about babel internals, for use in development of babel.
# [Properties of nodes](/doc/node-props.md)
These are properties babel stores in AST node objects for internal use, as opposed to properties that are part of the AST spec (ESTree at the time of this writing).

11
doc/node-props.md Normal file
View File

@@ -0,0 +1,11 @@
# Properties of nodes
These are properties babel stores in AST node objects for internal use, as opposed to properties that are part of the AST spec (ESTree at the time of this writing).
## `_blockHoist`
`node._blockHoist != null` triggers the [block-hoist transformer](/src/babel/transformation/transformers/internal/block-hoist.js). Value should be `true` or an integer in the range `0..3`. `true` is equivalent to `2`. The value indicates whether the node should be hoisted and to what degree. See the source code for more detailed information.
## `_paths`
Stores a representation of a node's position in the tree and relationship to other nodes.
## `shadow`
A truthy value on a function node triggers the [shadow-functions transformer](/src/babel/transformation/transformers/internal/shadow-functions.js), which transforms the node so that it references (or inherits) `arguments` and `this` from the parent scope. It is invoked for arrow functions, for example.

View File

@@ -1,7 +1,7 @@
{
"name": "babel-core",
"description": "A compiler for writing next generation JavaScript",
"version": "5.5.2",
"version": "5.5.7",
"author": "Sebastian McKenzie <sebmck@gmail.com>",
"homepage": "https://babeljs.io/",
"license": "MIT",
@@ -51,7 +51,7 @@
"output-file-sync": "^1.1.0",
"path-is-absolute": "^1.0.0",
"private": "^0.1.6",
"regenerator": "^0.8.28",
"regenerator": "0.8.28",
"regexpu": "^1.1.2",
"repeating": "^1.1.2",
"resolve": "^1.1.6",

View File

@@ -35,7 +35,7 @@ each(options, function (option, key) {
if (option.description) desc.push(option.description);
commander.option(arg, desc.join(" "));
})
});
commander.option("-x, --extensions [extensions]", "List of extensions to compile when a directory has been input [.es6,.js,.es,.jsx]");
commander.option("-w, --watch", "Recompile files on changes");
@@ -51,7 +51,7 @@ commander.on("--help", function () {
each(keys(obj).sort(), function (key) {
if (key[0] === "_") return;
if (obj[key].optional) key = "[" + key + "]";
if (obj[key].metadata && obj[key].metadata.optional) key = "[" + key + "]";
console.log(" - " + key);
});
@@ -118,11 +118,16 @@ if (errors.length) {
var opts = exports.opts = {};
each(options, function (opt, key) {
opts[key] = commander[key];
if (commander[key] !== undefined) {
opts[key] = commander[key];
}
});
opts.ignore = util.arrayify(opts.ignore, util.regexify);
opts.only = util.arrayify(opts.only, util.regexify);
if (opts.only) {
opts.only = util.arrayify(opts.only, util.regexify);
}
var fn;

View File

@@ -1,14 +1,14 @@
{
"name": "babel",
"description": "Turn ES6 code into readable vanilla ES5 with source maps",
"version": "5.5.1",
"version": "5.5.6",
"author": "Sebastian McKenzie <sebmck@gmail.com>",
"homepage": "https://babeljs.io/",
"license": "MIT",
"repository": "babel/babel",
"preferGlobal": true,
"dependencies": {
"babel-core": "^5.5.1",
"babel-core": "^5.5.6",
"chokidar": "^1.0.0",
"commander": "^2.6.0",
"convert-source-map": "^1.1.0",

View File

@@ -1,7 +1,7 @@
{
"name": "babel-runtime",
"description": "babel selfContained runtime",
"version": "5.5.1",
"version": "5.5.6",
"license": "MIT",
"repository": "babel/babel",
"author": "Sebastian McKenzie <sebmck@gmail.com>",

View File

@@ -292,7 +292,7 @@ pp.parseExprAtom = function(refShorthandDefaultPos) {
//
if (this.options.features["es7.asyncFunctions"]) {
// async functions!
if (id.name === "async") {
if (id.name === "async" && !this.canInsertSemicolon()) {
// arrow functions
if (this.type === tt.parenL) {
let expr = this.parseParenAndDistinguishExpression(start, true, true)

View File

@@ -9,6 +9,7 @@ var STATE_KEYS = [
"lastTokEnd",
"lineStart",
"startLoc",
"curLine",
"endLoc",
"start",
"pos",

View File

@@ -83,10 +83,13 @@ pp.parseStatement = function(declaration, topLevel) {
return starttype === tt._import ? this.parseImport(node) : this.parseExport(node)
case tt.name:
if (this.options.features["es7.asyncFunctions"] && this.value === "async" && this.lookahead().type === tt._function) {
this.next();
this.expect(tt._function);
return this.parseFunction(node, true, false, true);
if (this.options.features["es7.asyncFunctions"] && this.value === "async") {
var lookahead = this.lookahead();
if (lookahead.type === tt._function && !this.canInsertSemicolon.call(lookahead)) {
this.next();
this.expect(tt._function);
return this.parseFunction(node, true, false, true);
}
}
// If the statement does not start with a statement keyword or a

View File

@@ -8,7 +8,7 @@ export { util, acorn, transform };
export { pipeline } from "../transformation";
export { canCompile } from "../util";
export { default as options } from "../transformation/file/options";
export { default as options } from "../transformation/file/options/config";
export { default as Transformer } from "../transformation/transformer";
export { default as TransformerPipeline } from "../transformation/transformer-pipeline";
export { default as traverse } from "../traversal";

View File

@@ -1,6 +1,6 @@
import sourceMapSupport from "source-map-support";
import * as registerCache from "./cache";
import resolveRc from "../../tools/resolve-rc";
import resolveRc from "../../transformation/file/options/resolve-rc";
import extend from "lodash/object/extend";
import * as babel from "../node";
import each from "lodash/collection/each";
@@ -90,7 +90,7 @@ var shouldIgnore = function (filename) {
if (!ignore && !only) {
return getRelativePath(filename).split(path.sep).indexOf("node_modules") >= 0;
} else {
return util.shouldIgnore(filename, ignore || [], only || []);
return util.shouldIgnore(filename, ignore || [], only);
}
};
@@ -105,7 +105,7 @@ if (process.env.running_under_istanbul) {
if (istanbulMonkey[filename]) {
delete istanbulMonkey[filename];
var code = compile(filename, {
attachAuxiliaryComment: "istanbul ignore next"
auxiliaryComment: "istanbul ignore next"
});
istanbulMonkey[filename] = true;
return code;

View File

@@ -17,3 +17,7 @@ export function BlockStatement(node, print) {
this.rightBrace();
}
}
export function Noop() {
}

View File

@@ -70,6 +70,7 @@ export function Super() {
export function Decorator(node, print) {
this.push("@");
print.plain(node.expression);
this.newline();
}
export function CallExpression(node, print) {

View File

@@ -344,7 +344,7 @@ class CodeGenerator {
//
if (comment.type === "CommentBlock" && this.format.indent.adjustMultilineComment) {
var offset = comment.loc.start.column;
var offset = comment.loc && comment.loc.start.column;
if (offset) {
var newlineRegex = new RegExp("\\n\\s{1," + offset + "}", "g");
val = val.replace(newlineRegex, "\n");

View File

@@ -7,8 +7,6 @@ export const MESSAGES = {
classesIllegalSuperCall: "Direct super call is illegal in non-constructor, use super.$1() instead",
classesIllegalConstructorKind: "Illegal kind for constructor method",
scopeDuplicateDeclaration: "Duplicate declaration $1",
undeclaredVariable: "Reference to undeclared variable $1",
undeclaredVariableSuggestion: "Reference to undeclared variable $1 - did you mean $2?",
settersInvalidParamLength: "Setters must have exactly one parameter",
settersNoRest: "Setters aren't allowed to have a rest",
noAssignmentsInForHead: "No assignments allowed in for-in/of head",
@@ -24,6 +22,10 @@ export const MESSAGES = {
illegalMethodName: "Illegal method name $1",
lostTrackNodePath: "We lost track of this nodes position, likely because the AST was directly manipulated",
undeclaredVariable: "Reference to undeclared variable $1",
undeclaredVariableType: "Referencing a type alias outside of a type annotation",
undeclaredVariableSuggestion: "Reference to undeclared variable $1 - did you mean $2?",
traverseNeedsParent: "Must pass a scope and parentPath unless traversing a Program/File got a $1 node",
traverseVerifyRootFunction: "You passed `traverse()` a function when it expected a visitor object, are you sure you didn't mean `{ enter: Function }`?",
traverseVerifyVisitorProperty: "You passed `traverse()` a visitor object with the property $1 that has the invalid property $2",

View File

@@ -17,6 +17,8 @@ var or = types.Type.or;
// .build("program")
// .field("program", def("Program"));
def("Noop");
def("AssignmentPattern")
.bases("Pattern")
.build("left", "right")

View File

@@ -1,59 +1,5 @@
import stripJsonComments from "strip-json-comments";
import merge from "../helpers/merge";
import path from "path";
import fs from "fs";
console.trace("I know someone out there is hotlinking straight to this file. This is a " +
"PRIVATE API. I hate you, but I wont break your code just because you're a " +
"programmer who practices bad habits.");
var cache = {};
var jsons = {};
function exists(filename) {
if (!fs.existsSync) return false;
var cached = cache[filename];
if (cached != null) return cached;
return cache[filename] = fs.existsSync(filename);
}
export default function (loc, opts = {}) {
var rel = ".babelrc";
if (!opts.babelrc) {
opts.babelrc = [];
}
function find(start, rel) {
var file = path.join(start, rel);
if (opts.babelrc.indexOf(file) >= 0) {
return;
}
if (exists(file)) {
var content = fs.readFileSync(file, "utf8");
var json;
try {
json = jsons[content] = jsons[content] || JSON.parse(stripJsonComments(content));
} catch (err) {
err.message = `${file}: ${err.message}`;
throw err;
}
opts.babelrc.push(file);
if (json.breakConfig) return;
merge(opts, json);
}
var up = path.dirname(start);
if (up !== start) { // root
find(up, rel);
}
}
if (opts.babelrc.indexOf(loc) < 0 && opts.breakConfig !== true) {
find(loc, rel);
}
return opts;
}
export { default } from "../transformation/file/options/resolve-rc";

View File

@@ -1,5 +1,5 @@
import { validateOption, normaliseOptions, config as optionsConfig } from "./options";
import convertSourceMap from "convert-source-map";
import * as optionParsers from "./option-parsers";
import moduleFormatters from "../modules";
import PluginManager from "./plugin-manager";
import shebangRegex from "shebang-regex";
@@ -7,13 +7,14 @@ import NodePath from "../../traversal/path";
import Transformer from "../transformer";
import isFunction from "lodash/lang/isFunction";
import isAbsolute from "path-is-absolute";
import resolveRc from "../../tools/resolve-rc";
import resolveRc from "./options/resolve-rc";
import sourceMap from "source-map";
import generate from "../../generation";
import codeFrame from "../../helpers/code-frame";
import defaults from "lodash/object/defaults";
import includes from "lodash/collection/includes";
import traverse from "../../traversal";
import Hub from "../../traversal/hub";
import assign from "lodash/object/assign";
import Logger from "./logger";
import parse from "../../helpers/parse";
@@ -35,15 +36,27 @@ export default class File {
this.declarations = {};
this.usedHelpers = {};
this.dynamicData = {};
this.metadata = {};
this.data = {};
this.metadata = {
modules: {
imports: [],
exports: {
exported: [],
specifiers: []
}
}
};
this.pipeline = pipeline;
this.log = new Logger(this, opts.filename || "unknown");
this.opts = this.normalizeOptions(opts);
this.ast = {};
this.normaliseOptions(opts);
this.buildTransformers();
this.hub = new Hub(this);
}
static helpers = [
@@ -85,19 +98,19 @@ export default class File {
static soloHelpers = [];
static options = require("./options");
static options = optionsConfig;
normalizeOptions(opts: Object) {
opts = assign({}, opts);
normaliseOptions(opts: Object) {
opts = this.opts = normaliseOptions(assign({}, opts));
// resolve babelrc
if (opts.filename) {
var rcFilename = opts.filename;
if (!isAbsolute(rcFilename)) rcFilename = path.join(process.cwd(), rcFilename);
opts = resolveRc(rcFilename, opts);
}
//
// check for unknown options
for (let key in opts) {
if (key[0] === "_") continue;
@@ -105,26 +118,30 @@ export default class File {
if (!option) this.log.error(`Unknown option: ${key}`, ReferenceError);
}
// merge in environment options
var envKey = process.env.BABEL_ENV || process.env.NODE_ENV || "development";
if (opts.env) merge(opts, opts.env[envKey]);
if (opts.env) merge(opts, normaliseOptions(opts.env[envKey]));
// normalise options
for (let key in File.options) {
let option = File.options[key];
var val = opts[key];
var val = opts[key];
// optional
if (!val && option.optional) continue;
// deprecated
if (val && option.deprecated) {
throw new Error("Deprecated option " + key + ": " + option.deprecated);
this.log.deprecate("Deprecated option " + key + ": " + option.deprecated);
}
if (val == null) {
val = clone(option.default);
}
// default
if (val == null) val = clone(option.default);
var optionParser = optionParsers[option.type];
if (optionParser) val = optionParser(key, val, this.pipeline);
// validate
if (val) val = validateOption(key, val, this.pipeline);
// aaliases
if (option.alias) {
opts[option.alias] = opts[option.alias] || val;
} else {
@@ -149,7 +166,10 @@ export default class File {
opts.basename = path.basename(opts.filename, path.extname(opts.filename));
opts.ignore = util.arrayify(opts.ignore, util.regexify);
opts.only = util.arrayify(opts.only, util.regexify);
if (opts.only) {
opts.only = util.arrayify(opts.only, util.regexify);
}
defaults(opts, {
moduleRoot: opts.sourceRoot
@@ -326,14 +346,24 @@ export default class File {
}
attachAuxiliaryComment(node: Object): Object {
var comment = this.opts.auxiliaryComment;
if (comment) {
var beforeComment = this.opts.auxiliaryCommentBefore;
if (beforeComment) {
node.leadingComments = node.leadingComments || [];
node.leadingComments.push({
type: "CommentLine",
value: " " + comment
value: " " + beforeComment
});
}
var afterComment = this.opts.auxiliaryCommentAfter;
if (afterComment) {
node.trailingComments = node.trailingComments || [];
node.trailingComments.push({
type: "CommentLine",
value: " " + afterComment
});
}
return node;
}
@@ -385,11 +415,12 @@ export default class File {
errorWithNode(node, msg, Error = SyntaxError) {
var err;
if (node.loc) {
if (node && node.loc) {
var loc = node.loc.start;
err = new Error(`Line ${loc.line}: ${msg}`);
err.loc = loc;
} else {
// todo: find errors with nodes inside to at least point to something
err = new Error("There's been an error on a dynamic node. This is almost certainly an internal error. Please report it.");
}
return err;
@@ -466,12 +497,13 @@ export default class File {
}
_addAst(ast) {
this.path = NodePath.get({
this.path = NodePath.get({
hub: this.hub,
parentPath: null,
parent: ast,
container: ast,
key: "program"
}).setContext(null, this);
}).setContext();
this.scope = this.path.scope;
this.ast = ast;
}
@@ -486,15 +518,9 @@ export default class File {
if (modFormatter.init && this.transformers["es6.modules"].canTransform()) {
modFormatter.init();
}
this.populateModuleMetadata();
this.log.debug("End module formatter init");
}
populateModuleMetadata() {
var modules = {};
this.metadata.modules = modules;
}
transform() {
this.call("pre");
for (var pass of (this.transformerStack: Array)) {
@@ -509,7 +535,11 @@ export default class File {
code = code + "";
try {
return callback();
if (this.shouldIgnore()) {
return this.makeResult({ code, ignored: true });
} else {
return callback();
}
} catch (err) {
if (err._babel) {
throw err;

View File

@@ -1,27 +0,0 @@
import * as util from "../../util";
export function transformerList(key, val, pipeline) {
val = util.arrayify(val);
if (val.indexOf("all") >= 0 || val.indexOf(true) >= 0) {
val = Object.keys(pipeline.transformers);
}
return pipeline._ensureTransformerNames(key, val);
}
export function number(key, val) {
return +val;
}
export function boolean(key, val) {
return !!val;
}
export function booleanString(key, val) {
return util.booleanify(val);
}
export function list(key, val) {
return util.list(val);
}

View File

@@ -47,6 +47,7 @@
},
"experimental": {
"type": "boolean",
"description": "allow use of experimental transformers",
"default": false
},
@@ -77,7 +78,8 @@
"blacklist": {
"type": "transformerList",
"description": "blacklist of transformers to NOT use",
"shorthand": "b"
"shorthand": "b",
"default": []
},
"whitelist": {
@@ -89,7 +91,8 @@
"optional": {
"type": "transformerList",
"description": "list of optional transformers to enable"
"description": "list of optional transformers to enable",
"default": []
},
"modules": {
@@ -121,12 +124,14 @@
"plugins": {
"type": "list",
"description": ""
"description": "",
"default": []
},
"ignore": {
"type": "list",
"description": "list of glob paths to **not** compile"
"description": "list of glob paths to **not** compile",
"default": []
},
"only": {
@@ -172,12 +177,23 @@
},
"auxiliaryComment": {
"deprecated": "renamed to auxiliaryCommentBefore",
"shorthand": "a",
"alias": "auxiliaryCommentBefore"
},
"auxiliaryCommentBefore": {
"type": "string",
"default": "",
"shorthand": "a",
"description": "attach a comment before all helper declarations and auxiliary code"
},
"auxiliaryCommentAfter": {
"type": "string",
"default": "",
"description": "attach a comment after all helper declarations and auxiliary code"
},
"externalHelpers": {
"type": "boolean",
"default": false,

View File

@@ -0,0 +1,31 @@
import * as parsers from "./parsers";
import config from "./config";
export { config };
export function validateOption(key, val, pipeline) {
var opt = config[key];
var parser = opt && parsers[opt.type];
if (parser && parser.validate) {
return parser.validate(key, val, pipeline);
} else {
return val;
}
}
export function normaliseOptions(options = {}) {
for (var key in options) {
var val = options[key];
if (val == null) continue;
var opt = config[key];
if (!opt) continue;
var parser = parsers[opt.type];
if (parser) val = parser(val);
options[key] = val;
}
return options;
}

View File

@@ -0,0 +1,29 @@
import * as util from "../../../util";
export function transformerList(val) {
return util.arrayify(val);
}
transformerList.validate = function (key, val, pipeline) {
if (val.indexOf("all") >= 0 || val.indexOf(true) >= 0) {
val = Object.keys(pipeline.transformers);
}
return pipeline._ensureTransformerNames(key, val);
};
export function number(val) {
return +val;
}
export function boolean(val) {
return !!val;
}
export function booleanString(val) {
return util.booleanify(val);
}
export function list(val) {
return util.list(val);
}

View File

@@ -0,0 +1,62 @@
import stripJsonComments from "strip-json-comments";
import { normaliseOptions } from "./index";
import merge from "../../../helpers/merge";
import path from "path";
import fs from "fs";
var cache = {};
var jsons = {};
function exists(filename) {
if (!fs.existsSync) return false;
var cached = cache[filename];
if (cached != null) return cached;
return cache[filename] = fs.existsSync(filename);
}
export default function (loc, opts = {}) {
var rel = ".babelrc";
if (!opts.babelrc) {
opts.babelrc = [];
}
function find(start, rel) {
var file = path.join(start, rel);
if (opts.babelrc.indexOf(file) >= 0) {
return;
}
if (exists(file)) {
var content = fs.readFileSync(file, "utf8");
var json;
try {
json = jsons[content] = jsons[content] || JSON.parse(stripJsonComments(content));
normaliseOptions(json);
} catch (err) {
err.message = `${file}: ${err.message}`;
throw err;
}
opts.babelrc.push(file);
if (json.breakConfig) return;
merge(opts, json);
}
var up = path.dirname(start);
if (up !== start) { // root
find(up, rel);
}
}
if (opts.babelrc.indexOf(loc) < 0 && opts.breakConfig !== true) {
find(loc, rel);
}
return opts;
}

View File

@@ -3,7 +3,10 @@ import * as t from "../../types";
export default function (node) {
var lastNonDefault = 0;
for (var i = 0; i < node.params.length; i++) {
if (!t.isAssignmentPattern(node.params[i])) lastNonDefault = i + 1;
var param = node.params[i];
if (!t.isAssignmentPattern(param) && !t.isRestElement(param)) {
lastNonDefault = i + 1;
}
}
return lastNonDefault;
}

View File

@@ -25,11 +25,13 @@ var referenceVisitor = {
}
};
export default function (node, callId, scope) {
export default function (path, callId) {
var node = path.node;
node.async = false;
node.generator = true;
scope.traverse(node, awaitVisitor, state);
path.traverse(awaitVisitor, state);
var call = t.callExpression(callId, [node]);
@@ -44,11 +46,11 @@ export default function (node, callId, scope) {
return declar;
} else {
if (id) {
var state = { id: id };
scope.traverse(node, referenceVisitor, state);
var state = { id };
path.traverse(referenceVisitor, state);
if (state.ref) {
scope.parent.push({ id: state.ref });
path.scope.parent.push({ id: state.ref });
return t.assignmentExpression("=", state.ref, call);
}
}

View File

@@ -69,40 +69,148 @@ var remapVisitor = {
};
var metadataVisitor = {
ModuleDeclaration(node, parent, scope, formatter) {
if (node.source) {
node.source.value = formatter.file.resolveModuleSource(node.source.value);
ModuleDeclaration: {
enter(node, parent, scope, formatter) {
if (node.source) {
node.source.value = formatter.file.resolveModuleSource(node.source.value);
}
}
},
ImportDeclaration(node, parent, scope, formatter) {
formatter.hasLocalImports = true;
extend(formatter.localImports, this.getBindingIdentifiers());
ImportDeclaration: {
exit(node, parent, scope, formatter) {
formatter.hasLocalImports = true;
var specifiers = [];
var imported = [];
formatter.metadata.imports.push({
source: node.source.value,
imported,
specifiers
});
for (var specifier of (this.get("specifiers"): Array)) {
var ids = specifier.getBindingIdentifiers();
extend(formatter.localImports, ids);
var local = specifier.node.local.name;
if (specifier.isImportDefaultSpecifier()) {
imported.push("default");
specifiers.push({
kind: "named",
imported: "default",
local
});
}
if (specifier.isImportSpecifier()) {
var importedName = specifier.node.imported.name;
imported.push(importedName);
specifiers.push({
kind: "named",
imported: importedName,
local
});
}
if (specifier.isImportNamespaceSpecifier()) {
imported.push("*");
specifiers.push({
kind: "namespace",
local
});
}
}
}
},
ExportDeclaration(node, parent, scope, formatter) {
formatter.hasLocalExports = true;
var source = node.source ? node.source.value : null;
var exports = formatter.metadata.exports;
// export function foo() {}
// export var foo = "bar";
var declar = this.get("declaration");
if (declar.isStatement()) {
var bindings = declar.getBindingIdentifiers();
for (var name in bindings) {
var binding = bindings[name];
formatter._addExport(name, binding);
exports.exported.push(name);
exports.specifiers.push({
kind: "local",
local: name,
exported: this.isExportDefaultDeclaration() ? "default" : name
});
}
}
if (this.isExportNamedDeclaration() && node.specifiers) {
for (var i = 0; i < node.specifiers.length; i++) {
var specifier = node.specifiers[i];
for (var specifier of (node.specifiers: Array)) {
var exported = specifier.exported.name;
exports.exported.push(exported);
// export foo from "bar";
if (t.isExportDefaultSpecifier(specifier)) {
exports.specifiers.push({
kind: "external",
local: exported,
exported,
source
});
}
// export * as foo from "bar";
if (t.isExportNamespaceSpecifier(specifier)) {
exports.specifiers.push({
kind: "external-namespace",
exported,
source
});
}
var local = specifier.local;
if (!local) continue;
formatter._addExport(local.name, specifier.exported);
// export { foo } from "bar";
// export { foo as bar } from "bar";
if (source) {
exports.specifiers.push({
kind: "external",
local: local.name,
exported,
source
});
}
// export { foo };
// export { foo as bar };
if (!source) {
exports.specifiers.push({
kind: "local",
local: local.name,
exported
});
}
}
}
if (!t.isExportDefaultDeclaration(node)) {
// export * from "bar";
if (this.isExportAllDeclaration()) {
exports.specifiers.push({
kind: "external-all",
source
});
}
if (!t.isExportDefaultDeclaration(node) && !declar.isTypeAlias()) {
var onlyDefault = node.specifiers && node.specifiers.length === 1 && t.isSpecifierDefault(node.specifiers[0]);
if (!onlyDefault) {
formatter.hasNonDefaultExports = true;
@@ -131,6 +239,7 @@ export default class DefaultFormatter {
this.localExports = object();
this.localImports = object();
this.metadata = file.metadata.modules;
this.getMetadata();
}

View File

@@ -0,0 +1 @@
if (VARIABLE_NAME === undefined) VARIABLE_NAME = DEFAULT_VALUE;

View File

@@ -116,6 +116,13 @@ function traverseReplace(node, parent, scope, remaps) {
replace(node, parent, scope, remaps);
}
if (t.isAssignmentExpression(node)) {
var ids = t.getBindingIdentifiers(node);
for (var name in ids) {
replace(ids[name], parent, scope, remaps);
}
}
scope.traverse(node, replaceVisitor, remaps);
}
@@ -330,6 +337,7 @@ class BlockScoping {
// this is the defining identifier of a declaration
var ref = letRefs[key];
// todo: could skip this if the colliding binding is in another function
if (scope.parentHasBinding(key) || scope.hasGlobal(key)) {
var uid = scope.generateUidIdentifier(ref.name).name;
ref.name = uid;

View File

@@ -200,6 +200,8 @@ class ClassTransformer {
}
}
body = body.concat(this.staticPropBody);
if (this.className) {
// named class with only a constructor
if (body.length === 1) return t.toExpression(body[0]);
@@ -214,8 +216,6 @@ class ClassTransformer {
t.inheritsComments(body[0], this.node);
}
body = body.concat(this.staticPropBody);
//
body.push(t.returnStatement(classRef));
@@ -293,7 +293,7 @@ class ClassTransformer {
this.pushMethod(node, path);
}
} else if (t.isClassProperty(node)) {
this.pushProperty(node);
this.pushProperty(node, path);
}
}
@@ -478,8 +478,8 @@ class ClassTransformer {
* Description
*/
pushProperty(node: { type: "ClassProperty" }) {
this.scope.traverse(node, collectPropertyReferencesVisitor, {
pushProperty(node: { type: "ClassProperty" }, path: NodePath) {
path.traverse(collectPropertyReferencesVisitor, {
references: this.instancePropRefs,
scope: this.scope
});

View File

@@ -1,41 +1,18 @@
import * as messages from "../../../messages";
function checkPath(path, file) {
var ids = path.getBindingIdentifiers();
for (var name in ids) {
var id = ids[name];
var binding = path.scope.getBinding(name);
// no binding exists
if (!binding) continue;
export function Scope(node, parent, scope) {
for (var name in scope.bindings) {
var binding = scope.bindings[name];
// not a constant
if (binding.kind !== "const" && binding.kind !== "module") continue;
// check if the assignment id matches the constant declaration id
// if it does then it was the id used to initially declare the
// constant so we can just ignore it
if (binding.identifier === id) continue;
throw file.errorWithNode(id, messages.get("readOnly", name));
for (var violation of (binding.constantViolations: Array)) {
throw violation.errorWithNode(messages.get("readOnly", name));
}
}
}
export function AssignmentExpression(node, parent, scope, file) {
checkPath(this, file);
}
export { AssignmentExpression as UpdateExpression };
export function VariableDeclaration(node) {
if (node.kind === "const") node.kind = "let";
}
export function ForXStatement(node, parent, scope, file) {
var left = this.get("left");
if (left.isIdentifier() || left.isPattern()) {
checkPath(left, file);
}
}

View File

@@ -56,15 +56,22 @@ export function ForOfStatement(node, parent, scope, file) {
export { ForOfStatement as ForInStatement };
export function Func/*tion*/(node, parent, scope, file) {
var hasDestructuring = false;
for (let pattern of (node.params: Array)) {
if (t.isPattern(pattern)) {
hasDestructuring = true;
break;
}
}
if (!hasDestructuring) return;
var nodes = [];
var hasDestructuring = false;
for (var i = 0; i < node.params.length; i++) {
let pattern = node.params[i];
if (!t.isPattern(pattern)) continue;
node.params = node.params.map(function (pattern, i) {
if (!t.isPattern(pattern)) return pattern;
hasDestructuring = true;
var ref = scope.generateUidIdentifier("ref");
var ref = node.params[i] = scope.generateUidIdentifier("ref");
t.inherits(ref, pattern);
var destructuring = new DestructuringTransformer({
@@ -74,12 +81,9 @@ export function Func/*tion*/(node, parent, scope, file) {
file: file,
kind: "let"
});
destructuring.init(pattern, ref);
return ref;
});
if (!hasDestructuring) return;
}
t.ensureBlock(node);
@@ -216,8 +220,8 @@ function hasRest(pattern) {
}
var arrayUnpackVisitor = {
enter(node, parent, scope, state) {
if (this.isReferencedIdentifier() && state.bindings[node.name]) {
ReferencedIdentifier(node, parent, scope, state) {
if (state.bindings[node.name]) {
state.deopt = true;
this.stop();
}

View File

@@ -70,6 +70,10 @@ export function _ForOfStatementArray(node, scope, file) {
loop.body.body.unshift(t.expressionStatement(t.assignmentExpression("=", left, iterationValue)));
}
if (this.parentPath.isLabeledStatement()) {
loop = t.labeledStatement(this.parentPath.node.label, loop);
}
nodes.push(loop);
return nodes;

View File

@@ -1,4 +1,5 @@
import callDelegate from "../../helpers/call-delegate";
import getFunctionArity from "../../helpers/get-function-arity";
import * as util from "../../../util";
import * as t from "../../../types";
@@ -24,37 +25,54 @@ var iifeVisitor = {
export function Func/*tion*/(node, parent, scope, file) {
if (!hasDefaults(node)) return;
// ensure it's a block, useful for arrow functions
t.ensureBlock(node);
var state = {
iife: false,
scope: scope
};
var body = [];
//
var argsIdentifier = t.identifier("arguments");
argsIdentifier._shadowedFunctionLiteral = true;
var lastNonDefaultParam = 0;
var state = { iife: false, scope: scope };
var pushDefNode = function (left, right, i) {
var defNode = util.template("default-parameter", {
VARIABLE_NAME: left,
DEFAULT_VALUE: right,
ARGUMENT_KEY: t.literal(i),
ARGUMENTS: argsIdentifier
}, true);
// push a default parameter definition
function pushDefNode(left, right, i) {
var defNode;
if (exceedsLastNonDefault(i) || t.isPattern(left) || file.transformers["es6.spec.blockScoping"].canTransform()) {
defNode = util.template("default-parameter", {
VARIABLE_NAME: left,
DEFAULT_VALUE: right,
ARGUMENT_KEY: t.literal(i),
ARGUMENTS: argsIdentifier
}, true);
} else {
defNode = util.template("default-parameter-assign", {
VARIABLE_NAME: left,
DEFAULT_VALUE: right
}, true);
}
defNode._blockHoist = node.params.length - i;
body.push(defNode);
};
}
// check if an index exceeds the functions arity
function exceedsLastNonDefault(i) {
return i + 1 > lastNonDefaultParam;
}
//
var lastNonDefaultParam = getFunctionArity(node);
//
var params = this.get("params");
for (var i = 0; i < params.length; i++) {
var param = params[i];
if (!param.isAssignmentPattern()) {
if (!param.isRestElement()) {
lastNonDefaultParam = i + 1;
}
if (!param.isIdentifier()) {
param.traverse(iifeVisitor, state);
}
@@ -69,9 +87,13 @@ export function Func/*tion*/(node, parent, scope, file) {
var left = param.get("left");
var right = param.get("right");
var placeholder = scope.generateUidIdentifier("x");
placeholder._isDefaultPlaceholder = true;
node.params[i] = placeholder;
if (exceedsLastNonDefault(i) || left.isPattern()) {
var placeholder = scope.generateUidIdentifier("x");
placeholder._isDefaultPlaceholder = true;
node.params[i] = placeholder;
} else {
node.params[i] = left.node;
}
if (!state.iife) {
if (right.isIdentifier() && scope.hasOwnBinding(right.node.name)) {

View File

@@ -1,14 +1,15 @@
import isNumber from "lodash/lang/isNumber";
import * as util from "../../../util";
import * as t from "../../../types";
var memberExpressionOptimisationVisitor = {
enter(node, parent, scope, state) {
Scope(node, parent, scope, state) {
// check if this scope has a local binding that will shadow the rest parameter
if (this.isScope() && !scope.bindingIdentifierEquals(state.name, state.outerBinding)) {
return this.skip();
if (!scope.bindingIdentifierEquals(state.name, state.outerBinding)) {
this.skip();
}
},
enter(node, parent, scope, state) {
var stop = () => {
state.canOptimise = false;
this.stop();
@@ -31,8 +32,8 @@ var memberExpressionOptimisationVisitor = {
if (!state.noOptimise && t.isMemberExpression(parent) && parent.computed) {
// if we know that this member expression is referencing a number then we can safely
// optimise it
var prop = parent.property;
if (isNumber(prop.value) || t.isUnaryExpression(prop) || t.isBinaryExpression(prop)) {
var prop = this.parentPath.get("property");
if (prop.isBaseType("number")) {
state.candidates.push(this);
return;
}
@@ -43,6 +44,8 @@ var memberExpressionOptimisationVisitor = {
};
function optimizeMemberExpression(parent, offset) {
if (offset === 0) return;
var newExpr;
var prop = parent.property;

View File

@@ -1,7 +1,7 @@
import * as t from "../../../types";
function getSpreadLiteral(spread, scope) {
if (scope.file.isLoose("es6.spread")) {
if (scope.hub.file.isLoose("es6.spread")) {
return spread.argument;
} else {
return scope.toArray(spread.argument, true);

View File

@@ -16,7 +16,7 @@ function crawl(path) {
if (path.is("_templateLiteralProduced")) {
crawl(path.get("left"));
crawl(path.get("right"));
} else if (!path.isTypeAnnotationGeneric("String") && !path.isTypeAnnotationGeneric("Number")) {
} else if (!path.isBaseType("string") && !path.isBaseType("number")) {
path.replaceWith(t.callExpression(t.identifier("String"), [path.node]));
}
}

View File

@@ -83,7 +83,7 @@ export function ExportNamedDeclaration(node, parent, scope) {
export var Program = {
enter(node) {
var imports = [];
var rest = [];
var rest = [];
for (var i = 0; i < node.body.length; i++) {
var bodyNode = node.body[i];

View File

@@ -8,7 +8,7 @@ function remap(path, key, create) {
// ensure that we're shadowed
if (!path.inShadow()) return;
var fnPath = path.findParent((node, path) => !node.shadow && (path.isFunction() || path.isProgram()));
var fnPath = path.findParent((path) => !path.is("shadow") && (path.isFunction() || path.isProgram()));
var cached = fnPath.getData(key);
if (cached) return cached;

View File

@@ -11,7 +11,7 @@ export function AssignmentExpression() {
if (!left.isIdentifier()) return;
var binding = this.scope.getBinding(left.node.name);
if (!binding || binding.deoptValue) return;
if (!binding || binding.hasDeoptValue) return;
var evaluated = this.get("right").evaluate();
if (evaluated.confident) {
@@ -23,7 +23,10 @@ export function AssignmentExpression() {
export function IfStatement() {
var evaluated = this.get("test").evaluate();
if (!evaluated.confident) return this.skip();
if (!evaluated.confident) {
// todo: deopt binding values for constant violations inside
return this.skip();
}
if (evaluated.value) {
this.skipKey("alternate");
@@ -33,6 +36,25 @@ export function IfStatement() {
}
export var Scopable = {
enter() {
var funcScope = this.scope.getFunctionParent();
for (var name in this.scope.bindings) {
var binding = this.scope.bindings[name];
var deopt = false;
for (var path of (binding.constantViolations: Array)) {
var funcViolationScope = path.scope.getFunctionParent();
if (funcViolationScope !== funcScope) {
deopt = true;
break;
}
}
if (deopt) binding.deoptValue();
}
},
exit() {
for (var name in this.scope.bindings) {
var binding = this.scope.bindings[name];

View File

@@ -44,7 +44,7 @@ export function ReferencedIdentifier(node, parent, scope) {
if (binding.path.scope.parent !== scope) return;
}
if (this.findParent((node) => node === replacement)) {
if (this.findParent((path) => path.node === replacement)) {
return;
}

View File

@@ -1,5 +1,3 @@
import * as t from "../../../types";
export var metadata = {
optional: true,
group: "builtin-pre"
@@ -7,10 +5,6 @@ export var metadata = {
export function CallExpression(node, parent) {
if (this.get("callee").matchesPattern("console", true)) {
if (t.isExpressionStatement(parent)) {
this.parentPath.dangerouslyRemove();
} else {
this.dangerouslyRemove();
}
this.dangerouslyRemove();
}
}

View File

@@ -5,7 +5,7 @@ export var metadata = {
};
export function ForOfStatement(node, parent, scope, file) {
if (this.get("right").isTypeAnnotationGeneric("Array")) {
if (this.get("right").isGenericType("Array")) {
return _ForOfStatementArray.call(this, node, scope, file);
}
}

View File

@@ -10,5 +10,5 @@ export var metadata = {
export function Func/*tion*/(node, parent, scope, file) {
if (!node.async || node.generator) return;
return remapAsyncToGenerator(node, file.addHelper("async-to-generator"), scope);
return remapAsyncToGenerator(this, file.addHelper("async-to-generator"));
}

View File

@@ -14,8 +14,7 @@ export function Func/*tion*/(node, parent, scope, file) {
if (!node.async || node.generator) return;
return remapAsyncToGenerator(
node,
t.memberExpression(file.addImport("bluebird", null, "absolute"), t.identifier("coroutine")),
scope
this,
t.memberExpression(file.addImport("bluebird", null, "absolute"), t.identifier("coroutine"))
);
}

View File

@@ -1,3 +1,5 @@
import * as t from "../../../types";
export var metadata = {
group: "builtin-trailing"
};
@@ -23,7 +25,10 @@ export function Func/*tion*/(node) {
}
export function TypeCastExpression(node) {
return node.expression;
do {
node = node.expression;
} while(t.isTypeCastExpression(node));
return node;
}
export function ImportDeclaration(node) {

View File

@@ -108,7 +108,7 @@
"Symbol": {
"for": "symbol/for",
"hasInstance": "symbol/for-instance",
"hasInstance": "symbol/has-instance",
"is-concat-spreadable": "symbol/is-concat-spreadable",
"iterator": "symbol/iterator",
"keyFor": "symbol/key-for",

View File

@@ -26,7 +26,7 @@ export var Program = {
};
export function ThisExpression() {
if (!this.findParent((node) => !node.shadow && THIS_BREAK_KEYS.indexOf(node.type) >= 0)) {
if (!this.findParent((path) => !path.is("shadow") && THIS_BREAK_KEYS.indexOf(path.type) >= 0)) {
return t.identifier("undefined");
}
}

View File

@@ -6,6 +6,11 @@ export var metadata = {
};
export function ReferencedIdentifier(node, parent, scope, file) {
var binding = scope.getBinding(node.name);
if (binding && binding.kind === "type" && !this.parentPath.isFlow()) {
throw this.errorWithNode(messages.get("undeclaredVariableType", node.name), ReferenceError);
}
if (scope.hasBinding(node.name)) return;
// get the closest declaration to offer as a suggestion
@@ -34,5 +39,5 @@ export function ReferencedIdentifier(node, parent, scope, file) {
//
throw file.errorWithNode(node, msg, ReferenceError);
throw this.errorWithNode(msg, ReferenceError);
}

View File

@@ -10,8 +10,19 @@ export default class TraversalContext {
}
shouldVisit(node) {
var opts = this.opts;
if (opts.enter || opts.exit) return true;
if (opts[node.type]) return true;
var keys = t.VISITOR_KEYS[node.type];
return !!(this.opts.enter || this.opts.exit || this.opts[node.type] || (keys && keys.length));
if (!keys || !keys.length) return false;
for (var key of (keys: Array)) {
if (node[key]) return true;
}
return false;
}
create(node, obj, key, containerKey) {

View File

@@ -0,0 +1,5 @@
export default class Hub {
constructor(file) {
this.file = file;
}
}

View File

@@ -49,28 +49,21 @@ const CLEAR_KEYS = [
"tokens", "range", "start", "end", "loc", "raw"
];
function clearNode(node) {
traverse.clearNode = function (node) {
for (var i = 0; i < CLEAR_KEYS.length; i++) {
let key = CLEAR_KEYS[i];
if (node[key] != null) node[key] = null;
}
for (let key in node) {
var val = node[key];
if (Array.isArray(val)) {
delete val._paths;
}
}
}
};
var clearVisitor = {
noScope: true,
exit: clearNode
exit: traverse.clearNode
};
traverse.removeProperties = function (tree) {
traverse(tree, clearVisitor);
clearNode(tree);
traverse.clearNode(tree);
return tree;
};

View File

@@ -2,9 +2,10 @@
- `context` - Methods responsible for maintaing TraversalContexts.
- `conversion` - Methods that convert the path node into another node or some other type of data.
- `evaluation` - Methods responsible for statically evaluating code.
- `inference` - Methods responsible for type inferrence etc.
- `modification` - Methods that modify the path/node in some ways.
- `resolution` - Methods responsible for type inferrence etc.
- `replacement` - Methods responsible for replacing a node with another.
- `removal` - Methods responsible for removing a node.
- `family` - Methods responsible for dealing with/retrieving children or siblings.
- `verification` - Methodsresponsible for introspecting the current path for certain values.
- `introspection` - Methods responsible for introspecting the current path for certain values.
- `comments` - Methods responsible for dealing with comments.

View File

@@ -5,12 +5,25 @@
export function findParent(callback) {
var path = this;
while (path) {
if (callback(path.node, path)) return path;
if (callback(path)) return path;
path = path.parentPath;
}
return null;
}
/**
* Description
*/
export function getStatementParent() {
var path = this;
do {
if (Array.isArray(path.container)) {
return path;
}
} while(path = path.parentPath);
}
/**
* Description
*/

View File

@@ -0,0 +1,51 @@
/**
* Share comments amongst siblings.
*/
export function shareCommentsWithSiblings() {
var node = this.node;
if (!node) return;
var trailing = node.trailingComments;
var leading = node.leadingComments;
if (!trailing && !leading) return;
var prev = this.getSibling(this.key - 1);
var next = this.getSibling(this.key + 1);
if (!prev.node) prev = next;
if (!next.node) next = prev;
prev.addComments("trailing", leading);
next.addComments("leading", trailing);
}
/**
* Description
*/
export function addComment(type, content, line?) {
this.addComments(type, [{
type: line ? "CommentLine" : "CommentBlock",
value: content
}]);
}
/**
* Give node `comments` of the specified `type`.
*/
export function addComments(type: string, comments: Array) {
if (!comments) return;
var node = this.node;
if (!node) return;
var key = `${type}Comments`;
if (node[key]) {
node[key] = node[key].concat(comments);
} else {
node[key] = comments;
}
}

View File

@@ -1,5 +1,3 @@
import * as messages from "../../messages";
import NodePath from "./index";
import traverse from "../index";
/**
@@ -111,11 +109,11 @@ export function stop() {
* Description
*/
export function setScope(file?) {
export function setScope() {
if (this.opts && this.opts.noScope) return;
var target = this.context || this.parentPath;
this.scope = NodePath.getScope(this, target && target.scope, file);
this.scope = this.getScope(target && target.scope);
if (this.scope) this.scope.init();
}
@@ -123,7 +121,7 @@ export function setScope(file?) {
* Description
*/
export function setContext(context, file) {
export function setContext(context) {
this.shouldSkip = false;
this.shouldStop = false;
this.removed = false;
@@ -135,10 +133,7 @@ export function setContext(context, file) {
this.opts = context.opts;
}
var log = file && this.type === "Program";
if (log) file.log.debug("Start scope building");
this.setScope(file);
if (log) file.log.debug("End scope building");
this.setScope();
return this;
}
@@ -152,11 +147,21 @@ export function setContext(context, file) {
export function resync() {
if (this.removed) return;
this._resyncParent();
this._resyncContainer();
this._resyncKey();
//this._resyncRemoved();
}
export function _resyncParent() {
if (this.parentPath) {
this.parent = this.parentPath.node;
}
}
export function _resyncKey() {
if (!this.container) return;
if (this.node === this.container[this.key]) return;
// grrr, path key is out of sync. this is likely due to a modification to the AST
@@ -176,7 +181,7 @@ export function _resyncKey() {
}
}
throw new Error(messages.get("lostTrackNodePath"));
this.key = null;
}
export function _resyncContainer() {
@@ -185,11 +190,21 @@ export function _resyncContainer() {
if (!containerKey || !parentPath) return;
var newContainer = parentPath.node[containerKey];
if (!newContainer || this.container === newContainer) return;
if (this.container === newContainer) return;
// container is out of sync. this is likely the result of it being reassigned
this.container = newContainer;
if (newContainer) {
this.container = newContainer;
} else {
this.container = null;
}
}
export function _resyncRemoved() {
if (this.key == null || !this.container || this.container[this.key] !== this.node) {
this._markRemoved();
}
}
/**
@@ -214,7 +229,10 @@ export function unshiftContext(context) {
* Description
*/
export function setup(parentPath, key) {
export function setup(parentPath, container, containerKey, key) {
this.containerKey = containerKey;
this.container = container;
this.parentPath = parentPath || this.parentPath;
this.setKey(key);
}

View File

@@ -76,6 +76,10 @@ export function evaluate(): { confident: boolean; value: any } {
}
}
if (path.isTypeCastExpression()) {
return evaluate(path.get("expression"));
}
if (path.isIdentifier() && !path.scope.hasBinding(node.name, true)) {
if (node.name === "undefined") {
return undefined;
@@ -102,15 +106,15 @@ export function evaluate(): { confident: boolean; value: any } {
if (path.isReferencedIdentifier()) {
var binding = path.scope.getBinding(node.name);
if (binding && binding.hasValue) return binding.value;
}
if ((path.isIdentifier() || path.isMemberExpression()) && path.isReferenced()) {
var resolved = path.resolve();
if (resolved === path) {
return confident = false;
if (binding && binding.hasValue) {
return binding.value;
} else {
return evaluate(resolved);
var resolved = path.resolve();
if (resolved === path) {
return confident = false;
} else {
return evaluate(resolved);
}
}
}

View File

@@ -1,4 +1,3 @@
import type File from "../../transformation/file";
import * as virtualTypes from "./lib/virtual-types";
import traverse from "../index";
import assign from "lodash/object/assign";
@@ -6,21 +5,24 @@ import Scope from "../scope";
import * as t from "../../types";
export default class NodePath {
constructor(parent, container, containerKey) {
this.containerKey = containerKey;
this.container = container;
this.contexts = [];
this.parent = parent;
this.data = {};
constructor(hub, parent) {
this.contexts = [];
this.parent = parent;
this.data = {};
this.hub = hub;
}
/**
* Description
*/
static get({ parentPath, parent, container, containerKey, key }) {
static get({ hub, parentPath, parent, container, containerKey, key }) {
if (!hub && parentPath) {
hub = parentPath.hub;
}
var targetNode = container[key];
var paths = container._paths = container._paths || [];
var paths = parent._paths = parent._paths || [];
var path;
for (var i = 0; i < paths.length; i++) {
@@ -32,11 +34,11 @@ export default class NodePath {
}
if (!path) {
path = new NodePath(parent, container, containerKey);
path = new NodePath(hub, parent);
paths.push(path);
}
path.setup(parentPath, key);
path.setup(parentPath, container, containerKey, key);
return path;
}
@@ -45,12 +47,12 @@ export default class NodePath {
* Description
*/
static getScope(path: NodePath, scope: Scope, file?: File) {
getScope(scope: Scope) {
var ourScope = scope;
// we're entering a new scope so let's construct it!
if (path.isScope()) {
ourScope = new Scope(path, scope, file);
if (this.isScope()) {
ourScope = new Scope(this, scope);
}
return ourScope;
@@ -79,10 +81,7 @@ export default class NodePath {
*/
errorWithNode(msg, Error = SyntaxError) {
var loc = this.node.loc.start;
var err = new Error(`Line ${loc.line}: ${msg}`);
err.loc = loc;
return err;
return this.hub.file.errorWithNode(this.node, msg, Error);
}
/**
@@ -95,15 +94,16 @@ export default class NodePath {
}
assign(NodePath.prototype, require("./ancestry"));
assign(NodePath.prototype, require("./resolution"));
assign(NodePath.prototype, require("./inference"));
assign(NodePath.prototype, require("./replacement"));
assign(NodePath.prototype, require("./evaluation"));
assign(NodePath.prototype, require("./conversion"));
assign(NodePath.prototype, require("./verification"));
assign(NodePath.prototype, require("./introspection"));
assign(NodePath.prototype, require("./context"));
assign(NodePath.prototype, require("./removal"));
assign(NodePath.prototype, require("./modification"));
assign(NodePath.prototype, require("./family"));
assign(NodePath.prototype, require("./comments"));
for (let type in virtualTypes) {
if (type[0] === "_") continue;

View File

@@ -0,0 +1,89 @@
import * as inferers from "./inferers";
import * as t from "../../../types";
/**
* Infer the type of the current `NodePath`.
*/
export function getTypeAnnotation() {
if (this.typeAnnotation) return this.typeAnnotation;
var type = this._getTypeAnnotation() || t.anyTypeAnnotation();
if (t.isTypeAnnotation(type)) type = type.typeAnnotation;
return this.typeAnnotation = type;
}
/**
* todo: split up this method
*/
export function _getTypeAnnotation(): ?Object {
var node = this.node;
if (!node) {
// handle initializerless variables, add in checks for loop initializers too
if (this.key === "init" && this.parentPath.isVariableDeclarator()) {
var declar = this.parentPath.parentPath;
var declarParent = declar.parentPath;
// for (var NODE in bar) {}
if (declar.key === "left" && declarParent.isForInStatement()) {
return t.stringTypeAnnotation();
}
// for (var NODE of bar) {}
if (declar.key === "left" && declarParent.isForOfStatement()) {
return t.anyTypeAnnotation();
}
return t.voidTypeAnnotation();
} else {
return;
}
}
if (node.typeAnnotation) {
return node.typeAnnotation;
}
var inferer = inferers[node.type];
if (inferer) {
return inferer.call(this, node);
}
inferer = inferers[this.parentPath.type];
if (inferer && inferer.validParent) {
return this.parentPath.getTypeAnnotation();
}
}
/**
* Description
*/
export function isBaseType(baseName: string): boolean {
var type = this.getTypeAnnotation();
if (baseName === "string") {
return t.isStringTypeAnnotation(type);
} else if (baseName === "number") {
return t.isNumberTypeAnnotation(type);
} else if (baseName === "boolean") {
return t.isBooleanTypeAnnotation(type);
} else if (baseName === "any") {
return t.isAnyTypeAnnotation(type);
} else if (baseName === "mixed") {
return t.isMixedTypeAnnotation(type);
} else {
throw new Error(`Unknown base type ${baseName}`);
}
}
/**
* Description
*/
export function isGenericType(genericName: string): boolean {
var type = this.getTypeAnnotation();
return t.isGenericTypeAnnotation(type) && t.isIdentifier(type.id, { name: genericName });
}

View File

@@ -0,0 +1,187 @@
import * as t from "../../../types";
export default function (node) {
if (!this.isReferenced()) return;
// check if a binding exists of this value and if so then return a union type of all
// possible types that the binding could be
var binding = this.scope.getBinding(node.name);
if (binding) {
if (binding.identifier.typeAnnotation) {
return binding.identifier.typeAnnotation;
} else {
return getTypeAnnotationBindingConstantViolations(this, node.name);
}
}
// built-in values
if (node.name === "undefined") {
return t.voidTypeAnnotation();
} else if (node.name === "NaN" || node.name === "Infinity") {
return t.numberTypeAnnotation();
} else if (node.name === "arguments") {
// todo
}
}
function getTypeAnnotationBindingConstantViolations(path, name) {
var binding = path.scope.getBinding(name);
var types = [];
path.typeAnnotation = t.unionTypeAnnotation(types);
var functionConstantViolations = [];
var constantViolations = getConstantViolationsBefore(binding, path, functionConstantViolations);
var testType = getConditionalAnnotation(path, name);
if (testType) {
var testConstantViolations = getConstantViolationsBefore(binding, testType.ifStatement);
// remove constant violations observed before the IfStatement
constantViolations = constantViolations.filter((path) => testConstantViolations.indexOf(path) < 0);
// clear current types and add in observed test type
types.push(testType.typeAnnotation);
}
if (constantViolations.length) {
// pick one constant from each scope which will represent the last possible
// control flow path that it could've taken/been
var rawConstantViolations = constantViolations.reverse();
var visitedScopes = [];
constantViolations = [];
for (let violation of (rawConstantViolations: Array)) {
var violationScope = violation.scope;
if (visitedScopes.indexOf(violationScope) >= 0) continue;
visitedScopes.push(violationScope);
constantViolations.push(violation);
if (violationScope === path.scope) {
constantViolations = [violation];
break;
}
}
// add back on function constant violations since we can't track calls
constantViolations = constantViolations.concat(functionConstantViolations);
// push on inferred types of violated paths
for (let violation of (constantViolations: Array)) {
types.push(violation.getTypeAnnotation());
}
}
if (types.length) {
return t.createUnionTypeAnnotation(types);
}
}
function getConstantViolationsBefore(binding, path, functions) {
var violations = binding.constantViolations.slice();
violations.unshift(binding.path);
return violations.filter((violation) => {
violation = violation.resolve();
var status = violation._guessExecutionStatusRelativeTo(path);
if (functions && status === "function") functions.push(violation);
return status === "before";
});
}
function inferAnnotationFromBinaryExpression(name, path) {
var operator = path.node.operator;
var right = path.get("right").resolve();
var left = path.get("left").resolve();
var target;
if (left.isIdentifier({ name })) {
target = right;
} else if (right.isIdentifier({ name })) {
target = left;
}
if (target) {
if (operator === "===") {
return target.getTypeAnnotation();
} else if (t.BOOLEAN_NUMBER_BINARY_OPERATORS.indexOf(operator) >= 0) {
return t.numberTypeAnnotation();
} else {
return;
}
} else {
if (operator !== "===") return;
}
//
var typeofPath;
var typePath;
if (left.isUnaryExpression({ operator: "typeof" })) {
typeofPath = left;
typePath = right;
} else if (right.isUnaryExpression({ operator: "typeof" })) {
typeofPath = right;
typePath = left;
}
if (!typePath && !typeofPath) return;
// ensure that the type path is a Literal
typePath = typePath.resolve();
if (!typePath.isLiteral()) return;
// and that it's a string so we can infer it
var typeValue = typePath.node.value;
if (typeof typeValue !== "string") return;
// and that the argument of the typeof path references us!
if (!typeofPath.get("argument").isIdentifier({ name })) return;
// turn type value into a type annotation
return t.createTypeAnnotationBasedOnTypeof(typePath.node.value);
}
function getParentConditionalPath(path) {
var parentPath;
while (parentPath = path.parentPath) {
if (parentPath.isIfStatement() || parentPath.isConditionalExpression()) {
if (path.key === "test") {
return;
} else {
return parentPath;
}
} else {
path = parentPath;
}
}
}
function getConditionalAnnotation(path, name) {
var ifStatement = getParentConditionalPath(path);
if (!ifStatement) return;
var test = ifStatement.get("test");
var paths = [test];
var types = [];
do {
let path = paths.shift().resolve();
if (path.isLogicalExpression()) {
paths.push(path.get("left"));
paths.push(path.get("right"));
}
if (path.isBinaryExpression()) {
var type = inferAnnotationFromBinaryExpression(name, path);
if (type) types.push(type);
}
} while(paths.length);
if (types.length) {
return {
typeAnnotation: t.createUnionTypeAnnotation(types),
ifStatement
};
} else {
return getConditionalAnnotation(ifStatement, name);
}
}

View File

@@ -0,0 +1,191 @@
import * as t from "../../../types";
export { default as Identifier } from "./inferer-reference";
//
export function VariableDeclarator() {
var id = this.get("id");
if (id.isIdentifier()) {
return this.get("init").getTypeAnnotation();
} else {
return;
}
}
//
export function TypeCastExpression(node) {
return node.typeAnnotation;
}
TypeCastExpression.validParent = true;
//
export function NewExpression(node) {
if (this.get("callee").isIdentifier()) {
// only resolve identifier callee
return t.genericTypeAnnotation(node.callee);
}
}
//
export function TemplateLiteral() {
return t.stringTypeAnnotation();
}
//
export function UnaryExpression(node) {
let operator = node.operator;
if (operator === "void") {
return t.voidTypeAnnotation();
} else if (t.NUMBER_UNARY_OPERATORS.indexOf(operator) >= 0) {
return t.numberTypeAnnotation();
} else if (t.STRING_UNARY_OPERATORS.indexOf(operator) >= 0) {
return t.stringTypeAnnotation();
} else if (t.BOOLEAN_UNARY_OPERATORS.indexOf(operator) >= 0) {
return t.booleanTypeAnnotation();
}
}
//
export function BinaryExpression(node) {
let operator = node.operator;
if (t.NUMBER_BINARY_OPERATORS.indexOf(operator) >= 0) {
return t.numberTypeAnnotation();
} else if (t.BOOLEAN_BINARY_OPERATORS.indexOf(operator) >= 0) {
return t.booleanTypeAnnotation();
} else if (operator === "+") {
var right = this.get("right");
var left = this.get("left");
if (left.isBaseType("number") && right.isBaseType("number")) {
// both numbers so this will be a number
return t.numberTypeAnnotation();
} else if (left.isBaseType("string") || right.isBaseType("string")) {
// one is a string so the result will be a string
return t.stringTypeAnnotation();
}
// unsure if left and right are strings or numbers so stay on the safe side
return t.unionTypeAnnotation([
t.stringTypeAnnotation(),
t.numberTypeAnnotation()
]);
}
}
//
export function LogicalExpression() {
return t.createUnionTypeAnnotation([
this.get("left").getTypeAnnotation(),
this.get("right").getTypeAnnotation()
]);
}
//
export function ConditionalExpression() {
return t.createUnionTypeAnnotation([
this.get("consequent").getTypeAnnotation(),
this.get("alternate").getTypeAnnotation()
]);
}
//
export function SequenceExpression(node) {
return this.get("expressions").pop().getTypeAnnotation();
}
//
export function AssignmentExpression(node) {
return this.get("right").getTypeAnnotation();
}
//
export function UpdateExpression(node) {
let operator = node.operator;
if (operator === "++" || operator === "--") {
return t.numberTypeAnnotation();
}
}
//
export function Literal(node) {
var value = node.value;
if (typeof value === "string") return t.stringTypeAnnotation();
if (typeof value === "number") return t.numberTypeAnnotation();
if (typeof value === "boolean") return t.booleanTypeAnnotation();
if (value === null) return t.voidTypeAnnotation();
if (node.regex) return t.genericTypeAnnotation(t.identifier("RegExp"));
}
//
export function ObjectExpression() {
return t.genericTypeAnnotation(t.identifier("Object"));
}
//
export function ArrayExpression () {
return t.genericTypeAnnotation(t.identifier("Array"));
}
//
export function RestElement() {
return ArrayExpression();
}
RestElement.validParent = true;
//
function Func() {
return t.genericTypeAnnotation(t.identifier("Function"));
}
export { Func as Function, Func as Class };
//
export function CallExpression() {
return resolveCall(this.get("callee"));
}
export function TaggedTemplateExpression() {
return resolveCall(this.get("tag"));
}
function resolveCall(callee) {
callee = callee.resolve();
if (callee.isFunction()) {
if (callee.is("async")) {
if (callee.is("generator")) {
return t.genericTypeAnnotation(t.identifier("AsyncIterator"));
} else {
return t.genericTypeAnnotation(t.identifier("Promise"));
}
} else {
if (callee.node.returnType) {
return callee.node.returnType;
} else {
// todo: get union type of all return arguments
}
}
}
}

View File

@@ -0,0 +1,380 @@
import type NodePath from "./index";
import includes from "lodash/collection/includes";
import * as t from "../../types";
/**
* Match the current node if it matches the provided `pattern`.
*
* For example, given the match `React.createClass` it would match the
* parsed nodes of `React.createClass` and `React["createClass"]`.
*/
export function matchesPattern(pattern: string, allowPartial?: boolean): boolean {
var parts = pattern.split(".");
// not a member expression
if (!this.isMemberExpression()) return false;
var search = [this.node];
var i = 0;
function matches(name) {
var part = parts[i];
return part === "*" || name === part;
}
while (search.length) {
var node = search.shift();
if (allowPartial && i === parts.length) {
return true;
}
if (t.isIdentifier(node)) {
// this part doesn't match
if (!matches(node.name)) return false;
} else if (t.isLiteral(node)) {
// this part doesn't match
if (!matches(node.value)) return false;
} else if (t.isMemberExpression(node)) {
if (node.computed && !t.isLiteral(node.property)) {
// we can't deal with this
return false;
} else {
search.push(node.object);
search.push(node.property);
continue;
}
} else {
// we can't deal with this
return false;
}
// too many parts
if (++i > parts.length) {
return false;
}
}
return true;
}
/**
* Check whether we have the input `key`. If the `key` references an array then we check
* if the array has any items, otherwise we just check if it's falsy.
*/
export function has(key): boolean {
var val = this.node[key];
if (val && Array.isArray(val)) {
return !!val.length;
} else {
return !!val;
}
}
/**
* Alias of `has`.
*/
export var is = has;
/**
* Opposite of `has`.
*/
export function isnt(key): boolean {
return !this.has(key);
}
/**
* Check whether the path node `key` strict equals `value`.
*/
export function equals(key, value): boolean {
return this.node[key] === value;
}
/**
* Check the type against our stored internal type of the node. This is handy when a node has
* been removed yet we still internally know the type and need it to calculate node replacement.
*/
export function isNodeType(type: string): boolean {
return t.isType(this.type, type);
}
/**
* This checks whether or now we're in one of the following positions:
*
* for (KEY in right);
* for (KEY;;);
*
* This is because these spots allow VariableDeclarations AND normal expressions so we need
* to tell the path replacement that it's ok to replace this with an expression.
*/
export function canHaveVariableDeclarationOrExpression() {
return (this.key === "init" || this.key === "left") && this.parentPath.isFor();
}
/**
* Check whether the current path references a completion record
*/
export function isCompletionRecord(allowInsideFunction?) {
var path = this;
var first = true;
do {
var container = path.container;
// we're in a function so can't be a completion record
if (path.isFunction() && !first) {
return !!allowInsideFunction;
}
first = false;
// check to see if we're the last item in the container and if we are
// we're a completion record!
if (Array.isArray(container) && path.key !== container.length - 1) {
return false;
}
} while ((path = path.parentPath) && !path.isProgram());
return true;
}
/**
* Description
*/
export function isDirective() {
if (this.isExpressionStatement()) {
return this.get("expression").isLiteral();
} else {
return this.isLiteral() && this.parentPath.isExpressionStatement();
}
}
/**
* Check whether or not the current `key` allows either a single statement or block statement
* so we can explode it if necessary.
*/
export function isStatementOrBlock() {
if (this.parentPath.isLabeledStatement() || t.isBlockStatement(this.container)) {
return false;
} else {
return includes(t.STATEMENT_OR_BLOCK_KEYS, this.key);
}
}
/**
* Check whether this node was a part of the original AST.
*/
export function isUser() {
return this.node && !!this.node.loc;
}
/**
* Check whether this node was generated by us and not a part of the original AST.
*/
export function isGenerated() {
return !this.isUser();
}
/**
* Check if the currently assigned path references the `importName` of `moduleSource`.
*/
export function referencesImport(moduleSource, importName) {
if (!this.isReferencedIdentifier()) return false;
var binding = this.scope.getBinding(this.node.name);
if (!binding || binding.kind !== "module") return false;
var path = binding.path;
if (!path.isImportDeclaration()) return false;
// check moduleSource
if (path.node.source.value === moduleSource) {
if (!importName) return true;
} else {
return false;
}
for (var specifier of (path.node.specifiers: Array)) {
if (t.isSpecifierDefault(specifier) && importName === "default") {
return true;
}
if (t.isImportNamespaceSpecifier(specifier) && importName === "*") {
return true;
}
if (t.isImportSpecifier(specifier) && specifier.imported.name === importName) {
return true;
}
}
return false;
}
/**
* Description
*/
export function getSource() {
var node = this.node;
if (node.end) {
return this.hub.file.code.slice(node.start, node.end);
} else {
return "";
}
}
/**
* Description
*/
export function willIMaybeExecuteBefore(target) {
return this._guessExecutionStatusRelativeTo(target) !== "after";
}
/**
* Given a `target` check the execution status of it relative to the current path.
*
* "Execution status" simply refers to where or not we **think** this will execuete
* before or after the input `target` element.
*/
export function _guessExecutionStatusRelativeTo(target) {
// check if the two paths are in different functions, we can't track execution of these
var targetFuncParent = target.scope.getFunctionParent();
var selfFuncParent = this.scope.getFunctionParent();
if (targetFuncParent !== selfFuncParent) {
return "function";
}
var targetPaths = getAncestry(target);
//if (targetPaths.indexOf(this) >= 0) return "after";
var selfPaths = getAncestry(this);
// get ancestor where the branches intersect
var commonPath;
var targetIndex;
var selfIndex;
for (selfIndex = 0; selfIndex < selfPaths.length; selfIndex++) {
var selfPath = selfPaths[selfIndex];
targetIndex = targetPaths.indexOf(selfPath);
if (targetIndex >= 0) {
commonPath = selfPath;
break;
}
}
if (!commonPath) {
return "before";
}
// get the relationship paths that associate these nodes to their common ancestor
var targetRelationship = targetPaths[targetIndex - 1];
var selfRelationship = selfPaths[selfIndex - 1];
if (!targetRelationship || !selfRelationship) {
return "before";
}
// container list so let's see which one is after the other
if (targetRelationship.containerKey && targetRelationship.container === selfRelationship.container) {
return targetRelationship.key > selfRelationship.key ? "before" : "after";
}
// otherwise we're associated by a parent node, check which key comes before the other
var targetKeyPosition = t.VISITOR_KEYS[targetRelationship.type].indexOf(targetRelationship.key);
var selfKeyPosition = t.VISITOR_KEYS[selfRelationship.type].indexOf(selfRelationship.key);
return targetKeyPosition > selfKeyPosition ? "before" : "after";
}
function getAncestry(path) {
var paths = [];
do {
paths.push(path);
} while(path = path.parentPath);
return paths;
}
/**
* Resolve a "pointer" `NodePath` to it's absolute path.
*/
export function resolve(dangerous, resolved) {
return this._resolve(dangerous, resolved) || this;
}
export function _resolve(dangerous?, resolved?): ?NodePath {
// detect infinite recursion
// todo: possibly have a max length on this just to be safe
if (resolved && resolved.indexOf(this) >= 0) return;
// we store all the paths we've "resolved" in this array to prevent infinite recursion
resolved = resolved || [];
resolved.push(this);
if (this.isVariableDeclarator()) {
if (this.get("id").isIdentifier()) {
return this.get("init").resolve(dangerous, resolved);
} else {
// otherwise it's a request for a pattern and that's a bit more tricky
}
} else if (this.isReferencedIdentifier()) {
var binding = this.scope.getBinding(this.node.name);
if (!binding) return;
// reassigned so we can't really resolve it
if (!binding.constant) return;
// todo - lookup module in dependency graph
if (binding.kind === "module") return;
if (binding.path !== this) {
return binding.path.resolve(dangerous, resolved);
}
} else if (this.isTypeCastExpression()) {
return this.get("expression").resolve(dangerous, resolved);
} else if (dangerous && this.isMemberExpression()) {
// this is dangerous, as non-direct target assignments will mutate it's state
// making this resolution inaccurate
var targetKey = this.toComputedKey();
if (!t.isLiteral(targetKey)) return;
var targetName = targetKey.value;
var target = this.get("object").resolve(dangerous, resolved);
if (target.isObjectExpression()) {
var props = target.get("properties");
for (var prop of (props: Array)) {
if (!prop.isProperty()) continue;
var key = prop.get("key");
// { foo: obj }
var match = prop.isnt("computed") && key.isIdentifier({ name: targetName });
// { "foo": "obj" } or { ["foo"]: "obj" }
match = match || key.isLiteral({ value: targetName });
if (match) return prop.get("value").resolve(dangerous, resolved);
}
} else if (target.isArrayExpression() && !isNaN(+targetName)) {
var elems = target.get("elements");
var elem = elems[targetName];
if (elem) return elem.resolve(dangerous, resolved);
}
}
}

View File

@@ -18,6 +18,17 @@ export var ReferencedIdentifier = {
}
};
export var Expression = {
types: ["Expression"],
checkPath(path) {
if (path.isIdentifier()) {
return path.isReferencedIdentifier();
} else {
return t.isExpression(path.node);
}
}
};
export var Scope = {
types: ["Scopable"],
checkPath(path) {

View File

@@ -7,6 +7,8 @@ import * as t from "../../types";
*/
export function insertBefore(nodes) {
this._assertUnremoved();
nodes = this._verifyNodeList(nodes);
if (this.parentPath.isExpressionStatement() || this.parentPath.isLabeledStatement()) {
@@ -27,6 +29,7 @@ export function insertBefore(nodes) {
} else {
throw new Error("No clue what to do with this node type.");
}
return [this];
}
@@ -78,6 +81,8 @@ export function _maybePopFromStatements(nodes) {
*/
export function insertAfter(nodes) {
this._assertUnremoved();
nodes = this._verifyNodeList(nodes);
if (this.parentPath.isExpressionStatement() || this.parentPath.isLabeledStatement()) {
@@ -102,6 +107,7 @@ export function insertAfter(nodes) {
} else {
throw new Error("No clue what to do with this node type.");
}
return [this];
}
@@ -110,7 +116,7 @@ export function insertAfter(nodes) {
*/
export function updateSiblingKeys(fromIndex, incrementBy) {
var paths = this.container._paths;
var paths = this.parent._paths;
for (var i = 0; i < paths.length; i++) {
let path = paths[i];
if (path.key >= fromIndex) {
@@ -153,6 +159,8 @@ export function _verifyNodeList(nodes) {
*/
export function unshiftContainer(containerKey, nodes) {
this._assertUnremoved();
nodes = this._verifyNodeList(nodes);
// get the first path and insert our nodes before it, if it doesn't exist then it
@@ -175,6 +183,8 @@ export function unshiftContainer(containerKey, nodes) {
*/
export function pushContainer(containerKey, nodes) {
this._assertUnremoved();
nodes = this._verifyNodeList(nodes);
// get an invisible path that represents the last node + 1 and replace it with our

View File

@@ -15,6 +15,8 @@ export function remove() {
*/
export function dangerouslyRemove() {
this._assertUnremoved();
this.resync();
if (this._callRemovalHooks("pre")) {
@@ -45,47 +47,13 @@ export function _remove() {
}
export function _markRemoved() {
this.node = null;
this.removed = true;
this.shouldSkip = true;
this.removed = true;
this.node = null;
}
/**
* Share comments amongst siblings.
*/
export function shareCommentsWithSiblings() {
var node = this.node;
if (!node) return;
var trailing = node.trailingComments;
var leading = node.leadingComments;
if (!trailing && !leading) return;
var prev = this.getSibling(this.key - 1);
var next = this.getSibling(this.key + 1);
if (!prev.node) prev = next;
if (!next.node) next = prev;
prev.giveComments("trailing", leading);
next.giveComments("leading", trailing);
}
/**
* Give node `comments` of the specified `type`.
*/
export function giveComments(type: string, comments: Array) {
if (!comments) return;
var node = this.node;
if (!node) return;
var key = `${type}Comments`;
if (node[key]) {
node[key] = node[key].concat(comments);
} else {
node[key] = comments;
export function _assertUnremoved() {
if (this.removed) {
throw this.errorWithNode("NodePath has been removed so is read-only.");
}
}

View File

@@ -158,7 +158,7 @@ export function replaceExpressionWithStatements(nodes: Array) {
for (var i = 0; i < last.length; i++) {
var lastNode = last[i];
if (lastNode.isExpressionStatement()) {
var loop = lastNode.findParent((node, path) => path.isLoop());
var loop = lastNode.findParent((path) => path.isLoop());
if (loop) {
var uid = this.get("callee").scope.generateDeclaredUidIdentifier("ret");
this.get("callee.body").pushContainer("body", t.returnStatement(uid));

View File

@@ -1,313 +0,0 @@
import type NodePath from "./index";
import * as t from "../../types";
const BOOLEAN_BINARY_OPERATORS = ["==", "===", "!=", "!==", ">", "<", ">=", "<=", "in", "instanceof"];
const NUMBER_BINARY_OPERATORS = ["-", "/", "*", "**", "&", "|", ">>", ">>>", "<<", "^"];
const BOOLEAN_UNARY_OPERATORS = ["delete"];
const NUMBER_UNARY_OPERATORS = ["+", "-", "++", "--", "~"];
const STRING_UNARY_OPERATORS = ["typeof"];
/**
* Description
*/
export function getTypeAnnotation() {
return this.getTypeAnnotationInfo().annotation;
}
/**
* Description
*/
export function getTypeAnnotationInfo(): {
inferred: boolean;
annotation: ?Object;
} {
if (this.typeInfo) {
return this.typeInfo;
}
var info = this.typeInfo = {
inferred: false,
annotation: null
};
var type = this.node && this.node.typeAnnotation;
if (!type) {
info.inferred = true;
type = this.inferTypeAnnotation();
}
if (t.isTypeAnnotation(type)) type = type.typeAnnotation;
info.annotation = type;
return info;
}
/**
* Resolves `NodePath` pointers until it resolves to an absolute path. ie. a data type instead of a
* call etc. If a data type can't be resolved then the last path we were at is returned.
*/
export function resolve(resolved) {
return this._resolve(resolved) || this;
}
export function _resolve(resolved?): ?NodePath {
// detect infinite recursion
// todo: possibly have a max length on this just to be safe
if (resolved && resolved.indexOf(this) >= 0) return;
// we store all the paths we've "resolved" in this array to prevent infinite recursion
resolved = resolved || [];
resolved.push(this);
if (this.isVariableDeclarator()) {
if (this.get("id").isIdentifier()) {
return this.get("init").resolve(resolved);
} else {
// otherwise it's a request for a pattern and that's a bit more tricky
}
} else if (this.isReferencedIdentifier()) {
var binding = this.scope.getBinding(this.node.name);
if (!binding) return;
// reassigned so we can't really resolve it
if (!binding.constant) return;
// todo - lookup module in dependency graph
if (binding.kind === "module") return;
if (binding.path !== this) {
return binding.path.resolve(resolved);
}
} else if (this.isMemberExpression()) {
// this is dangerous, as non-direct target assignments will mutate it's state
// making this resolution inaccurate
var targetKey = this.toComputedKey();
if (!t.isLiteral(targetKey)) return;
var targetName = targetKey.value;
var target = this.get("object").resolve(resolved);
if (target.isObjectExpression()) {
var props = target.get("properties");
for (var prop of (props: Array)) {
if (!prop.isProperty()) continue;
var key = prop.get("key");
// { foo: obj }
var match = prop.isnt("computed") && key.isIdentifier({ name: targetName });
// { "foo": "obj" } or { ["foo"]: "obj" }
match = match || key.isLiteral({ value: targetName });
if (match) return prop.get("value").resolve(resolved);
}
} else if (target.isArrayExpression() && !isNaN(+targetName)) {
var elems = target.get("elements");
var elem = elems[targetName];
if (elem) return elem.resolve(resolved);
}
}
}
/**
* Infer the type of the current `NodePath`.
*
* NOTE: This is not cached. Use `getTypeAnnotation()` which is cached.
*/
export function inferTypeAnnotation(force) {
return this._inferTypeAnnotation(force) || t.anyTypeAnnotation();
}
export function _inferTypeAnnotation(force?: boolean): ?Object {
var path = this.resolve();
var node = path.node;
if (!node) return;
if (node.typeAnnotation) {
return node.typeAnnotation;
}
if (path.isRestElement() || path.parentPath.isRestElement() || path.isArrayExpression()) {
return t.genericTypeAnnotation(t.identifier("Array"));
}
if (path.parentPath.isTypeCastExpression()) {
return path.parentPath.inferTypeAnnotation();
}
if (path.isTypeCastExpression()) {
return node.typeAnnotation;
}
if (path.parentPath.isReturnStatement() && !force) {
return path.parentPath.inferTypeAnnotation();
}
if (path.isReturnStatement()) {
var funcPath = this.findParent((node, path) => path.isFunction());
if (!funcPath) return;
var returnType = funcPath.node.returnType;
if (returnType) {
return returnType;
} else {
return this.get("argument").inferTypeAnnotation(true);
}
}
if (path.isNewExpression() && path.get("callee").isIdentifier()) {
// only resolve identifier callee
return t.genericTypeAnnotation(node.callee);
}
if (path.isReferencedIdentifier()) {
if (node.name === "undefined") {
return t.voidTypeAnnotation();
} else if (node.name === "NaN") {
return t.numberTypeAnnotation();
}
}
if (path.isObjectExpression()) {
return t.genericTypeAnnotation(t.identifier("Object"));
}
if (path.isFunction() && path.parentPath.isProperty({ kind: "get" })) {
return node.returnType;
}
if (path.isFunction() || path.isClass()) {
return t.genericTypeAnnotation(t.identifier("Function"));
}
if (path.isUnaryExpression()) {
let operator = node.operator;
if (operator === "void") {
return t.voidTypeAnnotation();
} else if (NUMBER_UNARY_OPERATORS.indexOf(operator) >= 0) {
return t.numberTypeAnnotation();
} else if (STRING_UNARY_OPERATORS.indexOf(operator) >= 0) {
return t.stringTypeAnnotation();
} else if (BOOLEAN_UNARY_OPERATORS.indexOf(operator) >= 0) {
return t.booleanTypeAnnotation();
}
}
if (path.isBinaryExpression()) {
let operator = node.operator;
if (NUMBER_BINARY_OPERATORS.indexOf(operator) >= 0) {
return t.numberTypeAnnotation();
} else if (BOOLEAN_BINARY_OPERATORS.indexOf(operator) >= 0) {
return t.booleanTypeAnnotation();
} else if (operator === "+") {
var right = path.get("right").resolve();
var left = path.get("left").resolve();
if (left || right) {
if (left.isTypeAnnotationGeneric("Number") && right.isTypeAnnotationGeneric("Number")) {
// both numbers so this will be a number
return t.numberTypeAnnotation();
} else if (left.isTypeAnnotationGeneric("String") || right.isTypeAnnotationGeneric("String")) {
// one is a string so the result will be a string
return t.stringTypeAnnotation();
}
}
// unsure if left and right are strings or numbers so stay on the safe side
return t.unionTypeAnnotation([
t.stringTypeAnnotation(),
t.numberTypeAnnotation()
]);
}
}
if (path.isLogicalExpression()) {
// todo: create UnionType of left and right annotations
}
if (path.isConditionalExpression()) {
// todo: create UnionType of consequent and alternate annotations
}
if (path.isSequenceExpression()) {
return this.get("expressions").pop().inferTypeAnnotation(force);
}
if (path.isAssignmentExpression()) {
return this.get("right").inferTypeAnnotation(force);
}
if (path.isUpdateExpression()) {
let operator = node.operator;
if (operator === "++" || operator === "--") {
return t.numberTypeAnnotation();
}
}
if (path.isUnaryExpression() && node.prefix) {
let operator = node.operator;
if (operator === "!") {
return t.booleanTypeAnnotation();
} else if (operator === "+" || operator === "-") {
return t.numberTypeAnnotation();
}
}
if (path.isLiteral()) {
var value = node.value;
if (typeof value === "string") return t.stringTypeAnnotation();
if (typeof value === "number") return t.numberTypeAnnotation();
if (typeof value === "boolean") return t.booleanTypeAnnotation();
if (node.regex) return t.genericTypeAnnotation(t.identifier("RegExp"));
}
if (path.isCallExpression()) {
var callee = path.get("callee").resolve();
// todo: read typescript/flow interfaces
if (callee.isNodeType("Function")) return callee.node.returnType;
}
}
/**
* Description
*/
export function isTypeAnnotationGeneric(genericName: string, opts = {}): boolean {
var typeInfo = this.getTypeAnnotationInfo();
var type = typeInfo.annotation;
if (!type) return false;
if (typeInfo.inferred && opts.inference === false) {
return false;
}
if (t.isGenericTypeAnnotation(type) && t.isIdentifier(type.id, { name: genericName })) {
if (opts.requireTypeParameters && !type.typeParameters) {
return false;
} else {
return true;
}
}
if (genericName === "String") {
return t.isStringTypeAnnotation(type);
} else if (genericName === "Number") {
return t.isNumberTypeAnnotation(type);
} else if (genericName === "Boolean") {
return t.isBooleanTypeAnnotation(type);
}
return false;
}

View File

@@ -1,212 +0,0 @@
import includes from "lodash/collection/includes";
import * as t from "../../types";
/**
* Match the current node if it matches the provided `pattern`.
*
* For example, given the match `React.createClass` it would match the
* parsed nodes of `React.createClass` and `React["createClass"]`.
*/
export function matchesPattern(pattern: string, allowPartial?: boolean): boolean {
var parts = pattern.split(".");
// not a member expression
if (!this.isMemberExpression()) return false;
var search = [this.node];
var i = 0;
function matches(name) {
var part = parts[i];
return part === "*" || name === part;
}
while (search.length) {
var node = search.shift();
if (allowPartial && i === parts.length) {
return true;
}
if (t.isIdentifier(node)) {
// this part doesn't match
if (!matches(node.name)) return false;
} else if (t.isLiteral(node)) {
// this part doesn't match
if (!matches(node.value)) return false;
} else if (t.isMemberExpression(node)) {
if (node.computed && !t.isLiteral(node.property)) {
// we can't deal with this
return false;
} else {
search.push(node.object);
search.push(node.property);
continue;
}
} else {
// we can't deal with this
return false;
}
// too many parts
if (++i > parts.length) {
return false;
}
}
return true;
}
/**
* Check whether we have the input `key`. If the `key` references an array then we check
* if the array has any items, otherwise we just check if it's falsy.
*/
export function has(key): boolean {
var val = this.node[key];
if (val && Array.isArray(val)) {
return !!val.length;
} else {
return !!val;
}
}
/**
* Alias of `has`.
*/
export var is = has;
/**
* Opposite of `has`.
*/
export function isnt(key): boolean {
return !this.has(key);
}
/**
* Check whether the path node `key` strict equals `value`.
*/
export function equals(key, value): boolean {
return this.node[key] === value;
}
/**
* Check the type against our stored internal type of the node. This is handy when a node has
* been removed yet we still internally know the type and need it to calculate node replacement.
*/
export function isNodeType(type: string): boolean {
return t.isType(this.type, type);
}
/**
* This checks whether or now we're in one of the following positions:
*
* for (KEY in right);
* for (KEY;;);
*
* This is because these spots allow VariableDeclarations AND normal expressions so we need
* to tell the path replacement that it's ok to replace this with an expression.
*/
export function canHaveVariableDeclarationOrExpression() {
return (this.key === "init" || this.key === "left") && this.parentPath.isFor();
}
/**
* Check whether the current path references a completion record
*/
export function isCompletionRecord(allowInsideFunction?) {
var path = this;
var first = true;
do {
var container = path.container;
// we're in a function so can't be a completion record
if (path.isFunction() && !first) {
return !!allowInsideFunction;
}
first = false;
// check to see if we're the last item in the container and if we are
// we're a completion record!
if (Array.isArray(container) && path.key !== container.length - 1) {
return false;
}
} while ((path = path.parentPath) && !path.isProgram());
return true;
}
/**
* Check whether or not the current `key` allows either a single statement or block statement
* so we can explode it if necessary.
*/
export function isStatementOrBlock() {
if (t.isLabeledStatement(this.parent) || t.isBlockStatement(this.container)) {
return false;
} else {
return includes(t.STATEMENT_OR_BLOCK_KEYS, this.key);
}
}
/**
* Check whether this node was a part of the original AST.
*/
export function isUser() {
return this.node && !!this.node.loc;
}
/**
* Check whether this node was generated by us and not a part of the original AST.
*/
export function isGenerated() {
return !this.isUser();
}
/**
* Check if the currently assigned path references the `importName` of `moduleSource`.
*/
export function referencesImport(moduleSource, importName) {
if (!this.isReferencedIdentifier()) return false;
var binding = this.scope.getBinding(this.node.name);
if (!binding || binding.kind !== "module") return false;
var path = binding.path;
if (!path.isImportDeclaration()) return false;
// check moduleSource
if (path.node.source.value === moduleSource) {
if (!importName) return true;
} else {
return false;
}
for (var specifier of (path.node.specifiers: Array)) {
if (t.isSpecifierDefault(specifier) && importName === "default") {
return true;
}
if (t.isImportNamespaceSpecifier(specifier) && importName === "*") {
return true;
}
if (t.isImportSpecifier(specifier) && specifier.imported.name === importName) {
return true;
}
}
return false;
}

View File

@@ -1,5 +1,5 @@
export default class Binding {
constructor({ identifier, scope, path, kind }) {
constructor({ existing, identifier, scope, path, kind }) {
this.constantViolations = [];
this.constant = true;
@@ -12,6 +12,14 @@ export default class Binding {
this.kind = kind;
this.clearValue();
if (existing) {
this.constantViolations = [].concat(
existing.path,
existing.constantViolations,
this.constantViolations
);
}
}
/**

View File

@@ -1,6 +1,6 @@
import includes from "lodash/collection/includes";
import repeating from "repeating";
import type NodePath from "../path";
import type File from "../../transformation/file";
import traverse from "../index";
import defaults from "lodash/object/defaults";
import * as messages from "../../messages";
@@ -42,7 +42,7 @@ var collectorVisitor = {
ForXStatement() {
var left = this.get("left");
if (left.isPattern() || left.isIdentifier()) {
this.scope.registerConstantViolation(left);
this.scope.registerConstantViolation(left, left);
}
},
@@ -78,15 +78,15 @@ var collectorVisitor = {
}
// register as constant violation
this.scope.registerConstantViolation(this.get("left"), this.get("right"));
this.scope.registerConstantViolation(this, this.get("left"), this.get("right"));
},
UpdateExpression(node, parent, scope) {
scope.registerConstantViolation(this.get("argument"), null);
scope.registerConstantViolation(this, this.get("argument"), null);
},
UnaryExpression(node, parent, scope) {
if (node.operator === "delete") scope.registerConstantViolation(this.get("left"), null);
if (node.operator === "delete") scope.registerConstantViolation(this, this.get("left"), null);
},
BlockScoped(node, parent, scope) {
@@ -139,7 +139,7 @@ export default class Scope {
* within.
*/
constructor(path: NodePath, parent?: Scope, file?: File) {
constructor(path: NodePath, parent?: Scope) {
if (parent && parent.block === path.node) {
return parent;
}
@@ -152,7 +152,7 @@ export default class Scope {
}
this.parent = parent;
this.file = parent ? parent.file : file;
this.hub = path.hub;
this.parentBlock = path.parent;
this.block = path.node;
@@ -176,17 +176,6 @@ export default class Scope {
traverse(node, opts, this, state, this.path);
}
/**
* Since `Scope` instances are unique to their traversal we need some other
* way to compare if scopes are the same. Here we just compare `this.bindings`
* as it will be the same across all instances.
*/
is(scope) {
return this.bindings === scope.bindings;
}
/**
* Description
*/
@@ -343,7 +332,7 @@ export default class Scope {
if (!duplicate) duplicate = local.kind === "param" && (kind === "let" || kind === "const");
if (duplicate) {
throw this.file.errorWithNode(id, messages.get("scopeDuplicateDeclaration", name), TypeError);
throw this.hub.file.errorWithNode(id, messages.get("scopeDuplicateDeclaration", name), TypeError);
}
}
@@ -373,7 +362,7 @@ export default class Scope {
state.binding.name = newName;
}
var file = this.file;
var file = this.hub.file;
if (file) {
this._renameFromMap(file.moduleFormatter.localImports, oldName, newName, state.binding);
//this._renameFromMap(file.moduleFormatter.localExports, oldName, newName);
@@ -392,11 +381,20 @@ export default class Scope {
*/
dump() {
var sep = repeating("-", 60);
console.log(sep);
var scope = this;
do {
console.log(scope.block.type, "Bindings:", Object.keys(scope.bindings));
console.log("#", scope.block.type);
for (var name in scope.bindings) {
var binding = scope.bindings[name];
console.log(" -", name, {
constant: binding.constant,
references: binding.references
});
}
} while(scope = scope.parent);
console.log("-------------");
console.log(sep);
}
/**
@@ -404,11 +402,11 @@ export default class Scope {
*/
toArray(node: Object, i?: number) {
var file = this.file;
var file = this.hub.file;
if (t.isIdentifier(node)) {
var binding = this.getBinding(node.name);
if (binding && binding.constant && binding.path.isTypeAnnotationGeneric("Array")) return node;
if (binding && binding.constant && binding.path.isGenericType("Array")) return node;
}
if (t.isArrayExpression(node)) {
@@ -426,7 +424,7 @@ export default class Scope {
} else if (i) {
args.push(t.literal(i));
helperName = "sliced-to-array";
if (this.file.isLoose("es6.forOf")) helperName += "-loose";
if (this.hub.file.isLoose("es6.forOf")) helperName += "-loose";
}
return t.callExpression(file.addHelper(helperName), args);
}
@@ -436,18 +434,19 @@ export default class Scope {
*/
registerDeclaration(path: NodePath) {
var node = path.node;
if (t.isFunctionDeclaration(node)) {
if (path.isFunctionDeclaration()) {
this.registerBinding("hoisted", path);
} else if (t.isVariableDeclaration(node)) {
} else if (path.isVariableDeclaration()) {
var declarations = path.get("declarations");
for (var declar of (declarations: Array)) {
this.registerBinding(node.kind, declar);
this.registerBinding(path.node.kind, declar);
}
} else if (t.isClassDeclaration(node)) {
} else if (path.isClassDeclaration()) {
this.registerBinding("let", path);
} else if (t.isImportDeclaration(node) || t.isExportDeclaration(node)) {
} else if (path.isImportDeclaration() || path.isExportDeclaration()) {
this.registerBinding("module", path);
} else if (path.isFlowDeclaration()) {
this.registerBinding("type", path);
} else {
this.registerBinding("unknown", path);
}
@@ -457,7 +456,7 @@ export default class Scope {
* Description
*/
registerConstantViolation(left: NodePath, right: NodePath) {
registerConstantViolation(root: NodePath, left: NodePath, right: NodePath) {
var ids = left.getBindingIdentifiers();
for (var name in ids) {
var binding = this.getBinding(name);
@@ -468,7 +467,7 @@ export default class Scope {
if (rightType && binding.isCompatibleWithType(rightType)) continue;
}
binding.reassign(left, right);
binding.reassign(root, left, right);
}
}
@@ -495,7 +494,13 @@ export default class Scope {
var local = this.getOwnBindingInfo(name);
if (local) {
// don't ever let a type alias shadow a local binding
if (kind === "type") continue;
// same identifier so continue safely as we're likely trying to register it
// multiple times
if (local.identifier === id) continue;
this.checkBlockScopedCollisions(local, kind, name, id);
}
@@ -503,6 +508,7 @@ export default class Scope {
this.bindings[name] = new Binding({
identifier: id,
existing: local,
scope: this,
path: path,
kind: kind
@@ -706,7 +712,7 @@ export default class Scope {
declar._generated = true;
declar._blockHoist = 2;
this.file.attachAuxiliaryComment(declar);
this.hub.file.attachAuxiliaryComment(declar);
var [declarPath] = path.unshiftContainer("body", [declar]);
this.registerBinding(kind, declarPath);

View File

@@ -76,31 +76,29 @@
"ThisExpression": ["Expression"],
"Super": ["Expression"],
"UpdateExpression": ["Expression"],
"JSXEmptyExpression": ["Expression"],
"JSXMemberExpression": ["Expression"],
"AnyTypeAnnotation": ["Flow"],
"AnyTypeAnnotation": ["Flow", "FlowBaseAnnotation"],
"ArrayTypeAnnotation": ["Flow"],
"BooleanTypeAnnotation": ["Flow"],
"BooleanTypeAnnotation": ["Flow", "FlowBaseAnnotation"],
"ClassImplements": ["Flow"],
"DeclareClass": ["Flow", "Statement"],
"DeclareFunction": ["Flow", "Statement"],
"DeclareModule": ["Flow", "Statement"],
"DeclareVariable": ["Flow", "Statement"],
"DeclareClass": ["Flow", "FlowDeclaration", "Statement", "Declaration"],
"DeclareFunction": ["Flow", "FlowDeclaration", "Statement", "Declaration"],
"DeclareModule": ["Flow", "FlowDeclaration", "Statement"],
"DeclareVariable": ["Flow", "FlowDeclaration", "Statement", "Declaration"],
"FunctionTypeAnnotation": ["Flow"],
"FunctionTypeParam": ["Flow"],
"GenericTypeAnnotation": ["Flow"],
"InterfaceExtends": ["Flow"],
"InterfaceDeclaration": ["Flow", "Statement", "Declaration"],
"InterfaceDeclaration": ["Flow", "FlowDeclaration", "Statement", "Declaration"],
"IntersectionTypeAnnotation": ["Flow"],
"MixedTypeAnnotation": ["Flow"],
"MixedTypeAnnotation": ["Flow", "FlowBaseAnnotation"],
"NullableTypeAnnotation": ["Flow"],
"NumberTypeAnnotation": ["Flow"],
"NumberTypeAnnotation": ["Flow", "FlowBaseAnnotation"],
"StringLiteralTypeAnnotation": ["Flow"],
"StringTypeAnnotation": ["Flow"],
"StringTypeAnnotation": ["Flow", "FlowBaseAnnotation"],
"TupleTypeAnnotation": ["Flow"],
"TypeofTypeAnnotation": ["Flow"],
"TypeAlias": ["Flow", "Statement"],
"TypeAlias": ["Flow", "FlowDeclaration", "Statement", "Declaration"],
"TypeAnnotation": ["Flow"],
"TypeCastExpression": ["Flow"],
"TypeParameterDeclaration": ["Flow"],
@@ -111,15 +109,15 @@
"ObjectTypeProperty": ["Flow", "UserWhitespacable"],
"QualifiedTypeIdentifier": ["Flow"],
"UnionTypeAnnotation": ["Flow"],
"VoidTypeAnnotation": ["Flow"],
"VoidTypeAnnotation": ["Flow", "FlowBaseAnnotation"],
"JSXAttribute": ["JSX", "Immutable"],
"JSXClosingElement": ["JSX", "Immutable"],
"JSXElement": ["JSX", "Immutable", "Expression"],
"JSXEmptyExpression": ["JSX", "Immutable"],
"JSXEmptyExpression": ["JSX", "Immutable", "Expression"],
"JSXExpressionContainer": ["JSX", "Immutable"],
"JSXIdentifier": ["JSX"],
"JSXMemberExpression": ["JSX"],
"JSXMemberExpression": ["JSX", "Expression"],
"JSXNamespacedName": ["JSX"],
"JSXOpeningElement": ["JSX", "Immutable"],
"JSXSpreadAttribute": ["JSX"]

116
src/babel/types/flow.js Normal file
View File

@@ -0,0 +1,116 @@
import * as t from "./index";
/**
* Takes an array of `types` and flattens them, removing duplicates and
* returns a `UnionTypeAnnotation` node containg them.
*/
export function createUnionTypeAnnotation(types) {
var flattened = removeTypeDuplicates(types);
if (flattened.length === 1) {
return flattened[0];
} else {
return t.unionTypeAnnotation(flattened);
}
}
/**
* Description
*/
export function removeTypeDuplicates(nodes) {
var generics = {};
var bases = {};
// store union type groups to circular references
var typeGroups = [];
var types = [];
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
if (!node) continue;
// detect duplicates
if (types.indexOf(node) >= 0) {
continue;
}
// this type matches anything
if (t.isAnyTypeAnnotation(node)) {
return [node];
}
//
if (t.isFlowBaseAnnotation(node)) {
bases[node.type] = node;
continue;
}
//
if (t.isUnionTypeAnnotation(node)) {
if (typeGroups.indexOf(node.types) < 0) {
nodes = nodes.concat(node.types);
typeGroups.push(node.types);
}
continue;
}
// find a matching generic type and merge and deduplicate the type parameters
if (t.isGenericTypeAnnotation(node)) {
let name = node.id.name;
if (generics[name]) {
var existing = generics[name];
if (existing.typeParameters) {
if (node.typeParameters) {
existing.typeParameters.params = removeTypeDuplicates(
existing.typeParameters.params.concat(node.typeParameters.params)
);
}
} else {
existing = node.typeParameters;
}
} else {
generics[name] = node;
}
continue;
}
types.push(node);
}
// add back in bases
for (var type in bases) {
types.push(bases[type]);
}
// add back in generics
for (let name in generics) {
types.push(generics[name]);
}
return types;
}
/**
* Description
*/
export function createTypeAnnotationBasedOnTypeof(type) {
if (type === "string") {
return t.stringTypeAnnotation();
} else if (type === "number") {
return t.numberTypeAnnotation();
} else if (type === "undefined") {
return t.voidTypeAnnotation();
} else if (type === "boolean") {
return t.booleanTypeAnnotation();
} else if (type === "function") {
return t.genericTypeAnnotation(t.identifier("Function"));
} else if (type === "object") {
return t.genericTypeAnnotation(t.identifier("Object"));
}
}

View File

@@ -24,11 +24,19 @@ function registerType(type: string, skipAliasCheck?: boolean) {
};
}
export var STATEMENT_OR_BLOCK_KEYS = ["consequent", "body", "alternate"];
export var NATIVE_TYPE_NAMES = ["Array", "ArrayBuffer", "Boolean", "DataView", "Date", "Error", "EvalError", "Float32Array", "Float64Array", "Function", "Int8Array", "Int16Array", "Int32Array", "Map", "Number", "Object", "Proxy", "Promise", "RangeError", "ReferenceError", "RegExp", "Set", "String", "Symbol", "SyntaxError", "TypeError", "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", "URIError", "WeakMap", "WeakSet"];
export var FLATTENABLE_KEYS = ["body", "expressions"];
export var FOR_INIT_KEYS = ["left", "init"];
export var COMMENT_KEYS = ["leadingComments", "trailingComments"];
export const STATEMENT_OR_BLOCK_KEYS = ["consequent", "body", "alternate"];
export const FLATTENABLE_KEYS = ["body", "expressions"];
export const FOR_INIT_KEYS = ["left", "init"];
export const COMMENT_KEYS = ["leadingComments", "trailingComments"];
export const BOOLEAN_NUMBER_BINARY_OPERATORS = [">", "<", ">=", "<="];
export const COMPARISON_BINARY_OPERATORS = ["==", "===", "!=", "!==", "in", "instanceof"];
export const BOOLEAN_BINARY_OPERATORS = [].concat(COMPARISON_BINARY_OPERATORS, BOOLEAN_NUMBER_BINARY_OPERATORS);
export const NUMBER_BINARY_OPERATORS = ["-", "/", "*", "**", "&", "|", ">>", ">>>", "<<", "^"];
export const BOOLEAN_UNARY_OPERATORS = ["delete", "!"];
export const NUMBER_UNARY_OPERATORS = ["+", "-", "++", "--", "~"];
export const STRING_UNARY_OPERATORS = ["typeof"];
export const VISITOR_KEYS = require("./visitor-keys");
export const BUILDER_KEYS = require("./builder-keys");
@@ -287,10 +295,12 @@ export function inherits(child: Object, parent: Object): Object {
if (!child || !parent) return child;
child._scopeInfo = parent._scopeInfo;
child.range = parent.range;
child.start = parent.start;
child.loc = parent.loc;
child.end = parent.end;
child._paths = parent._paths;
child.range = parent.range;
child.start = parent.start;
child.loc = parent.loc;
child.end = parent.end;
child.typeAnnotation = parent.typeAnnotation;
child.returnType = parent.returnType;
@@ -306,3 +316,4 @@ exports.__esModule = true;
assign(t, require("./retrievers"));
assign(t, require("./validators"));
assign(t, require("./converters"));
assign(t, require("./flow"));

View File

@@ -30,6 +30,13 @@ export function getBindingIdentifiers(node: Object): Object {
}
getBindingIdentifiers.keys = {
DeclareClass: "id",
DeclareFunction: "id",
DeclareModule: "id",
DeclareVariable: "id",
InterfaceDeclaration: "id",
TypeAlias: "id",
ComprehensionExpression: "blocks",
ComprehensionBlock: "left",

View File

@@ -43,6 +43,7 @@
"MetaProperty": ["meta", "property"],
"MethodDefinition": ["key", "value", "decorators"],
"NewExpression": ["callee", "arguments"],
"Noop": [],
"ObjectExpression": ["properties"],
"ObjectPattern": ["properties", "typeAnnotation"],
"Program": ["body"],

View File

@@ -102,7 +102,7 @@ export function arrayify(val: any, mapFn?: Function): Array {
return [val];
}
export function booleanify(val: any): boolean {
export function booleanify(val: any) {
if (val === "true") return true;
if (val === "false") return false;
return val;
@@ -111,7 +111,7 @@ export function booleanify(val: any): boolean {
export function shouldIgnore(filename, ignore, only) {
filename = slash(filename);
if (only.length) {
if (only) {
for (let pattern of (only: Array)) {
if (pattern.test(filename)) return false;
}
@@ -127,6 +127,7 @@ export function shouldIgnore(filename, ignore, only) {
var templateVisitor = {
noScope: true,
enter(node, parent, scope, nodes) {
if (t.isExpressionStatement(node)) {
node = node.expression;
@@ -136,6 +137,10 @@ var templateVisitor = {
this.skip();
this.replaceInline(nodes[node.name]);
}
},
exit(node) {
traverse.clearNode(node);
}
};

View File

@@ -6,7 +6,6 @@
require("./tests.js");
require("./tests-harmony.js");
require("./tests-flow.js");
require("./tests-jsx.js");
require("./tests-babel.js");
require("babel/register")
acorn = require("../../lib/acorn")

View File

@@ -620,6 +620,63 @@ testFail("function foo(promise) { await promise; }", "Unexpected token (1:30)",
features: { "es7.asyncFunctions": true }
});
testFail("var x = async\n(x) => x + 1;", "Unexpected token (2:4)", {
ecmaVersion: 7,
features: { "es7.asyncFunctions": true }
});
test("async\nfunction foo() {}", {
type: "Program",
body: [
{
type: "ExpressionStatement",
expression: {
type: "Identifier",
name: "async",
loc: {
start: {line: 1, column: 0},
end: {line: 1, column: 5}
}
},
loc: {
start: {line: 1, column: 0},
end: {line: 1, column: 5}
}
},
{
type: "FunctionDeclaration",
id: {
type: "Identifier",
name: "foo",
loc: {
start: {line: 2, column: 9},
end: {line: 2, column: 12}
}
},
params: [],
body: {
type: "BlockStatement",
body: [],
loc: {
start: {line: 2, column: 15},
end: {line: 2, column: 17}
}
},
generator: false,
expression: false,
async: false,
loc: {
start: {line: 2, column: 0},
end: {line: 2, column: 17}
}
}
]
}, {
ecmaVersion: 7,
features: { "es7.asyncFunctions": true },
locations: true
});
test('async function foo(promise) { await promise; }', {
type: "Program",
body: [{

File diff suppressed because it is too large Load Diff

View File

@@ -8,27 +8,212 @@ var assert = require("assert");
var File = require("../../lib/babel/transformation/file");
suite("api", function () {
test("{ code: false }", function () {
test("code option false", function () {
var result = transform("foo('bar');", { code: false });
assert.ok(!result.code);
});
test("{ ast: false }", function () {
test("ast option false", function () {
var result = transform("foo('bar');", { ast: false });
assert.ok(!result.ast);
});
test("{ auxiliaryComment }", function () {
test("auxiliaryCommentBefore option", function () {
assert.ok(transform("class Foo {}", {
auxiliaryComment: "foobar"
auxiliaryCommentBefore: "foobar"
}).code.indexOf("foobar") >= 0);
assert.ok(transform("for (let i in bar) { foo(function () { i; }); break; continue; }", {
auxiliaryComment: "foobar"
auxiliaryCommentBefore: "foobar"
}).code.indexOf("foobar") >= 0);
});
suite("getModuleId() {} option", function () {
test("auxiliaryCommentAfter option", function () {
assert.ok(transform("class Foo {}", {
auxiliaryCommentAfter: "foobar"
}).code.indexOf("foobar") >= 0);
assert.ok(transform("for (let i in bar) { foo(function () { i; }); break; continue; }", {
auxiliaryCommentAfter: "foobar"
}).code.indexOf("foobar") >= 0);
});
test("modules metadata", function () {
assert.deepEqual(transform('import { externalName as localName } from "external";').metadata.modules.imports[0], {
source: "external",
imported: ["externalName"],
specifiers: [{
kind: "named",
imported: "externalName",
local: "localName"
}]
});
assert.deepEqual(transform('import * as localName2 from "external";').metadata.modules.imports[0], {
source: "external",
imported: ["*"],
specifiers: [{
kind: "namespace",
local: "localName2"
}]
});
assert.deepEqual(transform('import localName3 from "external";').metadata.modules.imports[0], {
source: "external",
imported: ["default"],
specifiers: [{
kind: "named",
imported: "default",
local: "localName3"
}]
});
assert.deepEqual(transform('import localName from "./array";', {
resolveModuleSource: function() {
return 'override-source';
}
}).metadata.modules.imports, [
{
source: "override-source",
imported: ["default"],
specifiers: [
{
"kind": "named",
"imported": "default",
"local": "localName"
}
]
}
]);
assert.deepEqual(transform('export * as externalName1 from "external";', {
stage: 0
}).metadata.modules.exports, {
exported: ['externalName1'],
specifiers: [{
kind: "external-namespace",
exported: "externalName1",
source: "external",
}]
});
assert.deepEqual(transform('export externalName2 from "external";', {
stage: 0
}).metadata.modules.exports, {
exported: ["externalName2"],
specifiers: [{
kind: "external",
local: "externalName2",
exported: "externalName2",
source: "external"
}]
});
assert.deepEqual(transform('export function namedFunction() {}').metadata.modules.exports, {
exported: ["namedFunction"],
specifiers: [{
kind: "local",
local: "namedFunction",
exported: "namedFunction"
}]
});
assert.deepEqual(transform('export var foo = "bar";').metadata.modules.exports, {
"exported": ["foo"],
specifiers: [{
kind: "local",
local: "foo",
exported: "foo"
}]
});
assert.deepEqual(transform("export { localName as externalName3 };").metadata.modules.exports, {
exported: ["externalName3"],
specifiers: [{
kind: "local",
local: "localName",
exported: "externalName3"
}]
});
assert.deepEqual(transform('export { externalName4 } from "external";').metadata.modules.exports, {
exported: ["externalName4"],
specifiers: [{
kind: "external",
local: "externalName4",
exported: "externalName4",
source: "external"
}]
});
assert.deepEqual(transform('export * from "external";').metadata.modules.exports, {
exported: [],
specifiers: [{
kind: "external-all",
source: "external"
}]
});
assert.deepEqual(transform("export default function defaultFunction() {}").metadata.modules.exports, {
exported: ["defaultFunction"],
specifiers: [{
kind: "local",
local: "defaultFunction",
exported: "default"
}]
});
});
test("ignore option", function () {
assert.ok(transform("", {
ignore: "node_modules",
filename: "/foo/node_modules/bar"
}).ignored);
assert.ok(transform("", {
ignore: "foo/node_modules",
filename: "/foo/node_modules/bar"
}).ignored);
assert.ok(transform("", {
ignore: "foo/node_modules/*.bar",
filename: "/foo/node_modules/foo.bar"
}).ignored);
});
test("only option", function () {
assert.ok(!transform("", {
only: "node_modules",
filename: "/foo/node_modules/bar"
}).ignored);
assert.ok(!transform("", {
only: "foo/node_modules",
filename: "/foo/node_modules/bar"
}).ignored);
assert.ok(!transform("", {
only: "foo/node_modules/*.bar",
filename: "/foo/node_modules/foo.bar"
}).ignored);
assert.ok(transform("", {
only: "node_modules",
filename: "/foo/node_module/bar"
}).ignored);
assert.ok(transform("", {
only: "foo/node_modules",
filename: "/bar/node_modules/foo"
}).ignored);
assert.ok(transform("", {
only: "foo/node_modules/*.bar",
filename: "/foo/node_modules/bar.foo"
}).ignored);
});
suite("getModuleId option", function () {
// As of this commit, `getModuleId` is the only option that isn't JSON
// compatible which is why it's not inside /test/core/fixtures/transformation
@@ -45,7 +230,7 @@ suite("api", function () {
assert.equal(result.code, expected);
}
test("{ modules: \"amd\" }", function () {
test("amd", function () {
var expected = [
"define('foo/bar', ['exports'], function (exports) {",
" 'use strict';",
@@ -57,7 +242,7 @@ suite("api", function () {
getModuleNameTest("amd", expected);
});
test("{ modules: \"umd\" }", function () {
test("umd", function () {
var expected = [
"(function (global, factory) {",
" if (typeof define === 'function' && define.amd) {",
@@ -81,7 +266,7 @@ suite("api", function () {
getModuleNameTest("umd", expected);
});
test("{ modules: \"system\" }", function () {
test("system", function () {
var expected = [
"System.register('foo/bar', [], function (_export) {",
" 'use strict';",
@@ -147,7 +332,7 @@ suite("api", function () {
}, /Unknown helper foob/);
});
test("resolveModuleSource", function () {
test("resolveModuleSource option", function () {
var actual = 'import foo from "foo-import-default";\nimport "foo-import-bare";\nexport { foo } from "foo-export-named";';
var expected = 'import foo from "resolved/foo-import-default";\nimport "resolved/foo-import-bare";\nexport { foo } from "resolved/foo-export-named";';

View File

@@ -0,0 +1,3 @@
function foo(a = "foo", b) {
}

View File

@@ -0,0 +1,5 @@
"use strict";
function foo(a, b) {
if (a === undefined) a = "foo";
}

View File

@@ -0,0 +1,13 @@
for (var key in foo) {
break;
foo();
}
function bar() {
yes();
bar();
return "wow";
nomore();
}
bar();

View File

@@ -0,0 +1,13 @@
"use strict";
for (var key in foo) {
break;
}
function bar() {
yes();
bar();
return "wow";
}
bar();

View File

@@ -1,4 +1,5 @@
{
"experimental": true,
"externalHelpers": true,
"noCheckAst": true,
"blacklist": ["regenerator"],

View File

@@ -11,17 +11,10 @@ require("./_transformation-helper")({
// weird environmental issue make these hard to test
"Modules",
// these are the responsibility of regenerator
"AsyncFunctions",
"Yield",
// these are the responsibility of core-js
"StringExtras",
"ArrayExtras",
"Collections",
// not supported
"ProperTailCalls",
// uses the old async generator proposal
"AsyncGenerators",
// these are all internal traceur tests or non-standard features
@@ -38,8 +31,30 @@ require("./_transformation-helper")({
],
ignoreTasks: [
// non-standard
"ObjectMixin",
// TODO
"Syntax/StrictKeywordsInPattern",
"Yield/GeneratorSend",
"Yield/BreakForOf",
"Yield/GeneratorThrow",
"Yield/ObjectModel",
"Yield/ReturnGenerator",
// TODO: core-js fails these
"Collections/Map",
"Collections/Set",
"ArrayExtras/From",
"ArrayExtras/FindIndex",
"ArrayExtras/Find",
"StringExtras/Includes",
"StringExtras/EndsWith",
// this tests pollutes Object.prototype which messes things up
"StringExtras/StartsWith",
// TODO
"Syntax/IsValidSimpleAssignmentTarget",
// babel has no way to check these :( TODO: add to caveats
"TemplateLiterals/TemplateObjectCaching.module",
@@ -52,11 +67,8 @@ require("./_transformation-helper")({
"Classes/ExtendStrange",
// these are the responsibility of core-js
"Symbol/GetOwnPropertySymbols",
"Spread/Type",
"Symbol/ObjectModel",
"Symbol/Inherited",
"Symbol/Object",
"Spread/NoIterator",
"Destructuring/Rest",
"Destructuring/Empty",
@@ -64,16 +76,8 @@ require("./_transformation-helper")({
// babel doesn't like non-closing comments :)
"Syntax/NoNewLineHereEndOfFile",
// traceur uses an old version of regexpu
"RegularExpression/Simple",
// class methods are still enumerable in traceur...
// TODO
"Classes/PrototypeDescriptor",
"NumericLiteral/Simple",
"Classes/Method",
// Object.mixin didn't make it into ES6
"ObjectMixin",
// Babel assumes that all code transformed is a module so this isn't necessary
"Strict",
@@ -87,6 +91,7 @@ require("./_transformation-helper")({
"GeneratorComprehension/Simple",
// yield has been added as a keyword in ES6
"Syntax/StrictKeywordsInPattern",
"Yield/YieldIdentifier",
"Syntax/StrictKeywords"
]

2
vendor/traceur vendored