Compare commits
123 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20d19735fc | ||
|
|
b5b6bf4ad5 | ||
|
|
844c10cac0 | ||
|
|
43583e4e9d | ||
|
|
f5b921cda9 | ||
|
|
763892aa79 | ||
|
|
3e6eae4d1a | ||
|
|
7c090c8580 | ||
|
|
a5f6c1c389 | ||
|
|
c159f2d982 | ||
|
|
9205f10244 | ||
|
|
4cd7bcad59 | ||
|
|
7e9660efd3 | ||
|
|
2d66ce5224 | ||
|
|
1257b2cf40 | ||
|
|
f21d935de5 | ||
|
|
2e20364793 | ||
|
|
e47e8a187a | ||
|
|
26924d5944 | ||
|
|
5eb1850a55 | ||
|
|
333e287226 | ||
|
|
80a77bd6a2 | ||
|
|
c9286a1de1 | ||
|
|
52f614dcdf | ||
|
|
600367ae25 | ||
|
|
b761cba135 | ||
|
|
947d3e262d | ||
|
|
4061bea528 | ||
|
|
de195e5bfc | ||
|
|
3bcef86973 | ||
|
|
fa670ac71e | ||
|
|
572261f9ce | ||
|
|
8a320d53a5 | ||
|
|
0650eedeb6 | ||
|
|
2282d066a2 | ||
|
|
f4d7cc55c1 | ||
|
|
eaaa279aa5 | ||
|
|
0595e06e29 | ||
|
|
9b27a170ae | ||
|
|
1db232da9e | ||
|
|
4cc844f410 | ||
|
|
024ae670cb | ||
|
|
429edda9c0 | ||
|
|
05b13b9ea3 | ||
|
|
b7320ce400 | ||
|
|
0c37b7b973 | ||
|
|
bb36dbd8d9 | ||
|
|
2dd8c40618 | ||
|
|
33128b0ccf | ||
|
|
cf25424295 | ||
|
|
7492074794 | ||
|
|
c4a491123e | ||
|
|
55ad88fe4e | ||
|
|
7170dbced8 | ||
|
|
b0971412a2 | ||
|
|
a6b374a681 | ||
|
|
2d0355b3b9 | ||
|
|
7fade101be | ||
|
|
0918da8569 | ||
|
|
917db622c4 | ||
|
|
f7ee6fbd20 | ||
|
|
5899e9a0be | ||
|
|
d41cb11545 | ||
|
|
3ad909a4ae | ||
|
|
4bafdf733c | ||
|
|
b825998c63 | ||
|
|
6b02ca47c3 | ||
|
|
ea1b85bffa | ||
|
|
3cffe47eea | ||
|
|
e5d5a9fb27 | ||
|
|
ca97fa63a9 | ||
|
|
f4cc27bc0e | ||
|
|
8cea575e2e | ||
|
|
c91baee4d5 | ||
|
|
8055ce29f7 | ||
|
|
4596ae48b8 | ||
|
|
6c268cdf21 | ||
|
|
fce977f1d7 | ||
|
|
a298075949 | ||
|
|
66599c3779 | ||
|
|
60340244b1 | ||
|
|
eb72ea3e5a | ||
|
|
ede6237b6f | ||
|
|
e91e10aae6 | ||
|
|
9c3cca0d25 | ||
|
|
8eee5367f3 | ||
|
|
40d55a3d44 | ||
|
|
75330304dc | ||
|
|
776c508418 | ||
|
|
e804741632 | ||
|
|
3d3cb4be4f | ||
|
|
64f4209119 | ||
|
|
2ede226ef9 | ||
|
|
f5cf641c0a | ||
|
|
1abd3419f6 | ||
|
|
75699db716 | ||
|
|
7c3572f08c | ||
|
|
9dacde6d07 | ||
|
|
8c3aab9a26 | ||
|
|
ba4550c953 | ||
|
|
d0ac65a934 | ||
|
|
a4c70bb029 | ||
|
|
795cf0c0b1 | ||
|
|
e64b90e322 | ||
|
|
d69b0973e1 | ||
|
|
6296f49653 | ||
|
|
9f2b739046 | ||
|
|
da1d5e5577 | ||
|
|
7333b4e392 | ||
|
|
b0442d0784 | ||
|
|
295e69f8f8 | ||
|
|
cfe844fa39 | ||
|
|
0f4ea2d2a6 | ||
|
|
4b85b05839 | ||
|
|
2539d08dce | ||
|
|
c26fd7a819 | ||
|
|
2053610429 | ||
|
|
cd4f83b299 | ||
|
|
ec29ba19a9 | ||
|
|
9dc03e0978 | ||
|
|
bc4258eca9 | ||
|
|
b0e58f9770 | ||
|
|
a1e2641c91 |
@@ -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
|
||||
|
||||
22
CHANGELOG.md
22
CHANGELOG.md
@@ -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**
|
||||
|
||||
@@ -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.
|
||||
|
||||
11
Makefile
11
Makefile
@@ -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
|
||||
|
||||
@@ -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
4
doc/index.md
Normal 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
11
doc/node-props.md
Normal 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.
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -9,6 +9,7 @@ var STATE_KEYS = [
|
||||
"lastTokEnd",
|
||||
"lineStart",
|
||||
"startLoc",
|
||||
"curLine",
|
||||
"endLoc",
|
||||
"start",
|
||||
"pos",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -17,3 +17,7 @@ export function BlockStatement(node, print) {
|
||||
this.rightBrace();
|
||||
}
|
||||
}
|
||||
|
||||
export function Noop() {
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -17,6 +17,8 @@ var or = types.Type.or;
|
||||
// .build("program")
|
||||
// .field("program", def("Program"));
|
||||
|
||||
def("Noop");
|
||||
|
||||
def("AssignmentPattern")
|
||||
.bases("Pattern")
|
||||
.build("left", "right")
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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,
|
||||
31
src/babel/transformation/file/options/index.js
Normal file
31
src/babel/transformation/file/options/index.js
Normal 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;
|
||||
}
|
||||
29
src/babel/transformation/file/options/parsers.js
Normal file
29
src/babel/transformation/file/options/parsers.js
Normal 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);
|
||||
}
|
||||
62
src/babel/transformation/file/options/resolve-rc.js
Normal file
62
src/babel/transformation/file/options/resolve-rc.js
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
if (VARIABLE_NAME === undefined) VARIABLE_NAME = DEFAULT_VALUE;
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
@@ -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"))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
5
src/babel/traversal/hub.js
Normal file
5
src/babel/traversal/hub.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export default class Hub {
|
||||
constructor(file) {
|
||||
this.file = 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;
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
51
src/babel/traversal/path/comments.js
Normal file
51
src/babel/traversal/path/comments.js
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
89
src/babel/traversal/path/inference/index.js
Normal file
89
src/babel/traversal/path/inference/index.js
Normal 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 });
|
||||
}
|
||||
187
src/babel/traversal/path/inference/inferer-reference.js
Normal file
187
src/babel/traversal/path/inference/inferer-reference.js
Normal 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);
|
||||
}
|
||||
}
|
||||
191
src/babel/traversal/path/inference/inferers.js
Normal file
191
src/babel/traversal/path/inference/inferers.js
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
380
src/babel/traversal/path/introspection.js
Normal file
380
src/babel/traversal/path/introspection.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
116
src/babel/types/flow.js
Normal 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"));
|
||||
}
|
||||
}
|
||||
@@ -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"));
|
||||
|
||||
@@ -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",
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
"MetaProperty": ["meta", "property"],
|
||||
"MethodDefinition": ["key", "value", "decorators"],
|
||||
"NewExpression": ["callee", "arguments"],
|
||||
"Noop": [],
|
||||
"ObjectExpression": ["properties"],
|
||||
"ObjectPattern": ["properties", "typeAnnotation"],
|
||||
"Program": ["body"],
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
205
test/core/api.js
205
test/core/api.js
@@ -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";';
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
function foo(a = "foo", b) {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
function foo(a, b) {
|
||||
if (a === undefined) a = "foo";
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
for (var key in foo) {
|
||||
break;
|
||||
foo();
|
||||
}
|
||||
|
||||
function bar() {
|
||||
yes();
|
||||
bar();
|
||||
return "wow";
|
||||
nomore();
|
||||
}
|
||||
|
||||
bar();
|
||||
@@ -0,0 +1,13 @@
|
||||
"use strict";
|
||||
|
||||
for (var key in foo) {
|
||||
break;
|
||||
}
|
||||
|
||||
function bar() {
|
||||
yes();
|
||||
bar();
|
||||
return "wow";
|
||||
}
|
||||
|
||||
bar();
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"experimental": true,
|
||||
"externalHelpers": true,
|
||||
"noCheckAst": true,
|
||||
"blacklist": ["regenerator"],
|
||||
|
||||
@@ -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
2
vendor/traceur
vendored
Submodule vendor/traceur updated: e484618da4...09d7e3023b
Reference in New Issue
Block a user