Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27f039488e | ||
|
|
e3ce82e12f | ||
|
|
4934ea56a0 | ||
|
|
ce03457b19 | ||
|
|
1298c67949 | ||
|
|
668274edcb | ||
|
|
0694a7dd06 | ||
|
|
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 |
@@ -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.
|
||||
|
||||
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.6",
|
||||
"version": "5.5.8",
|
||||
"author": "Sebastian McKenzie <sebmck@gmail.com>",
|
||||
"homepage": "https://babeljs.io/",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -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.5",
|
||||
"version": "5.5.7",
|
||||
"author": "Sebastian McKenzie <sebmck@gmail.com>",
|
||||
"homepage": "https://babeljs.io/",
|
||||
"license": "MIT",
|
||||
"repository": "babel/babel",
|
||||
"preferGlobal": true,
|
||||
"dependencies": {
|
||||
"babel-core": "^5.5.5",
|
||||
"babel-core": "^5.5.7",
|
||||
"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.5",
|
||||
"version": "5.5.7",
|
||||
"license": "MIT",
|
||||
"repository": "babel/babel",
|
||||
"author": "Sebastian McKenzie <sebmck@gmail.com>",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,7 +7,7 @@ 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";
|
||||
@@ -52,7 +52,7 @@ export default class File {
|
||||
this.log = new Logger(this, opts.filename || "unknown");
|
||||
this.ast = {};
|
||||
|
||||
this.normalizeOptions(opts);
|
||||
this.normaliseOptions(opts);
|
||||
|
||||
this.buildTransformers();
|
||||
|
||||
@@ -98,10 +98,10 @@ export default class File {
|
||||
|
||||
static soloHelpers = [];
|
||||
|
||||
static options = require("./options");
|
||||
static options = optionsConfig;
|
||||
|
||||
normalizeOptions(opts: Object) {
|
||||
opts = this.opts = assign({}, opts);
|
||||
normaliseOptions(opts: Object) {
|
||||
opts = this.opts = normaliseOptions(assign({}, opts));
|
||||
|
||||
// resolve babelrc
|
||||
if (opts.filename) {
|
||||
@@ -120,26 +120,28 @@ export default class File {
|
||||
|
||||
// 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) {
|
||||
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 {
|
||||
@@ -164,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
|
||||
@@ -341,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;
|
||||
}
|
||||
|
||||
@@ -400,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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -78,7 +78,8 @@
|
||||
"blacklist": {
|
||||
"type": "transformerList",
|
||||
"description": "blacklist of transformers to NOT use",
|
||||
"shorthand": "b"
|
||||
"shorthand": "b",
|
||||
"default": []
|
||||
},
|
||||
|
||||
"whitelist": {
|
||||
@@ -90,7 +91,8 @@
|
||||
|
||||
"optional": {
|
||||
"type": "transformerList",
|
||||
"description": "list of optional transformers to enable"
|
||||
"description": "list of optional transformers to enable",
|
||||
"default": []
|
||||
},
|
||||
|
||||
"modules": {
|
||||
@@ -122,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": {
|
||||
@@ -173,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);
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import stripJsonComments from "strip-json-comments";
|
||||
import merge from "../helpers/merge";
|
||||
import { normaliseOptions } from "./index";
|
||||
import merge from "../../../helpers/merge";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
|
||||
@@ -34,6 +35,7 @@ export default function (loc, opts = {}) {
|
||||
|
||||
try {
|
||||
json = jsons[content] = jsons[content] || JSON.parse(stripJsonComments(content));
|
||||
normaliseOptions(json);
|
||||
} catch (err) {
|
||||
err.message = `${file}: ${err.message}`;
|
||||
throw err;
|
||||
@@ -42,6 +44,7 @@ export default function (loc, opts = {}) {
|
||||
opts.babelrc.push(file);
|
||||
|
||||
if (json.breakConfig) return;
|
||||
|
||||
merge(opts, json);
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ var visit = function (node, name, scope) {
|
||||
// check to see if we have a local binding of the id we're setting inside of
|
||||
// the function, this is important as there are caveats associated
|
||||
|
||||
var bindingInfo = scope.getOwnBindingInfo(name);
|
||||
var bindingInfo = scope.getOwnBinding(name);
|
||||
|
||||
if (bindingInfo) {
|
||||
if (bindingInfo.kind === "param") {
|
||||
|
||||
@@ -69,54 +69,58 @@ 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;
|
||||
ImportDeclaration: {
|
||||
exit(node, parent, scope, formatter) {
|
||||
formatter.hasLocalImports = true;
|
||||
|
||||
var specifiers = [];
|
||||
var imported = [];
|
||||
formatter.metadata.imports.push({
|
||||
source: node.source.value,
|
||||
imported,
|
||||
specifiers
|
||||
});
|
||||
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);
|
||||
for (var specifier of (this.get("specifiers"): Array)) {
|
||||
var ids = specifier.getBindingIdentifiers();
|
||||
extend(formatter.localImports, ids);
|
||||
|
||||
var local = specifier.node.local.name;
|
||||
var local = specifier.node.local.name;
|
||||
|
||||
if (specifier.isImportDefaultSpecifier()) {
|
||||
imported.push("default");
|
||||
specifiers.push({
|
||||
kind: "named",
|
||||
imported: "default",
|
||||
local
|
||||
});
|
||||
}
|
||||
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.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
|
||||
});
|
||||
if (specifier.isImportNamespaceSpecifier()) {
|
||||
imported.push("*");
|
||||
specifiers.push({
|
||||
kind: "namespace",
|
||||
local
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -337,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;
|
||||
|
||||
@@ -33,7 +33,7 @@ var memberExpressionOptimisationVisitor = {
|
||||
// if we know that this member expression is referencing a number then we can safely
|
||||
// optimise it
|
||||
var prop = this.parentPath.get("property");
|
||||
if (prop.isGenericType("Number")) {
|
||||
if (prop.isBaseType("number")) {
|
||||
state.candidates.push(this);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ function crawl(path) {
|
||||
if (path.is("_templateLiteralProduced")) {
|
||||
crawl(path.get("left"));
|
||||
crawl(path.get("right"));
|
||||
} else if (!path.isGenericType("String") && !path.isGenericType("Number")) {
|
||||
} else if (!path.isBaseType("string") && !path.isBaseType("number")) {
|
||||
path.replaceWith(t.callExpression(t.identifier("String"), [path.node]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
- `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.
|
||||
|
||||
@@ -44,12 +44,10 @@ export function getAncestry() {
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function inType(types) {
|
||||
if (!Array.isArray(types)) types = [types];
|
||||
|
||||
export function inType() {
|
||||
var path = this;
|
||||
while (path) {
|
||||
for (var type of (types: Array)) {
|
||||
for (var type of (arguments: Array)) {
|
||||
if (path.node.type === type) return true;
|
||||
}
|
||||
path = path.parentPath;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -23,6 +23,18 @@ export function getStatementParent(): ?NodePath {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function getOpposite() {
|
||||
if (this.key === "left") {
|
||||
return this.getSibling("right");
|
||||
} else if (this.key === "right") {
|
||||
return this.getSibling("left");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
@@ -81,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,7 +94,7 @@ 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"));
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import type NodePath from "./index";
|
||||
import includes from "lodash/collection/includes";
|
||||
import * as t from "../../types";
|
||||
|
||||
@@ -145,6 +146,18 @@ export function isCompletionRecord(allowInsideFunction?) {
|
||||
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.
|
||||
@@ -228,25 +241,140 @@ export function getSource() {
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function willIMaybeExecutesBefore(target) {
|
||||
export function willIMaybeExecuteBefore(target) {
|
||||
return this._guessExecutionStatusRelativeTo(target) !== "after";
|
||||
}
|
||||
|
||||
export function _guessExecutionStatusRelativeTo(target) {
|
||||
var self = this.getStatementParent();
|
||||
target = target.getStatementParent();
|
||||
/**
|
||||
* 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 = self.scope.getFunctionParent();
|
||||
var selfFuncParent = this.scope.getFunctionParent();
|
||||
if (targetFuncParent !== selfFuncParent) {
|
||||
return "function";
|
||||
}
|
||||
|
||||
do {
|
||||
if (target.container === self.container) {
|
||||
return target.key > self.key ? "before" : "after";
|
||||
}
|
||||
} while(self = self.parentPath);
|
||||
var targetPaths = getAncestry(target);
|
||||
//if (targetPaths.indexOf(this) >= 0) return "after";
|
||||
|
||||
return "before";
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,483 +0,0 @@
|
||||
import type NodePath from "./index";
|
||||
import * as t from "../../types";
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Infer the type of the current `NodePath`.
|
||||
*/
|
||||
|
||||
export function getTypeAnnotation(force) {
|
||||
if (this.typeAnnotation) return this.typeAnnotation;
|
||||
|
||||
var type = this._getTypeAnnotation(force) || t.anyTypeAnnotation();
|
||||
if (t.isTypeAnnotation(type)) type = type.typeAnnotation;
|
||||
return this.typeAnnotation = type;
|
||||
}
|
||||
|
||||
export function _getTypeAnnotationBindingConstantViolations(path, name) {
|
||||
var binding = this.scope.getBinding(name);
|
||||
|
||||
var types = [];
|
||||
this.typeAnnotation = t.unionTypeAnnotation(types);
|
||||
|
||||
var functionConstantViolations = [];
|
||||
var constantViolations = getConstantViolationsBefore(binding, path, functionConstantViolations);
|
||||
|
||||
var testType = getTypeAnnotationBasedOnConditional(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)) {
|
||||
if (visitedScopes.indexOf(violation.scope) >= 0) continue;
|
||||
visitedScopes.push(violation.scope);
|
||||
constantViolations.push(violation);
|
||||
}
|
||||
|
||||
// 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 checkBinary(name, path) {
|
||||
var right = path.get("right").resolve();
|
||||
var left = path.get("left").resolve();
|
||||
|
||||
if (left.isIdentifier({ name })) {
|
||||
return right.getTypeAnnotation();
|
||||
} else if (right.isIdentifier({ name })) {
|
||||
return left.getTypeAnnotation();
|
||||
}
|
||||
|
||||
//
|
||||
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 getParentConditional(path) {
|
||||
var parentPath;
|
||||
while (parentPath = path.parentPath) {
|
||||
if (parentPath.isIfStatement() || parentPath.isConditionalExpression()) {
|
||||
if (path.key === "test") {
|
||||
return;
|
||||
} else {
|
||||
return parentPath;
|
||||
}
|
||||
} else {
|
||||
path = parentPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeAnnotationBasedOnConditional(path, name) {
|
||||
var ifStatement = getParentConditional(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({ operator: "===" })) {
|
||||
// todo: add in cases where operators imply a number
|
||||
var type = checkBinary(name, path);
|
||||
if (type) types.push(type);
|
||||
}
|
||||
} while(paths.length);
|
||||
|
||||
if (types.length) {
|
||||
return {
|
||||
typeAnnotation: t.createUnionTypeAnnotation(types),
|
||||
ifStatement
|
||||
};
|
||||
} else {
|
||||
return getTypeAnnotationBasedOnConditional(ifStatement, name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* todo: split up this method
|
||||
*/
|
||||
|
||||
export function _getTypeAnnotation(force?: boolean): ?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;
|
||||
}
|
||||
|
||||
//
|
||||
if (this.isVariableDeclarator()) {
|
||||
var id = this.get("id");
|
||||
|
||||
if (id.isIdentifier()) {
|
||||
return this.get("init").getTypeAnnotation();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
if (this.parentPath.isTypeCastExpression()) {
|
||||
return this.parentPath.getTypeAnnotation();
|
||||
}
|
||||
|
||||
if (this.isTypeCastExpression()) {
|
||||
return node.typeAnnotation;
|
||||
}
|
||||
|
||||
//
|
||||
if (this.isRestElement() || this.parentPath.isRestElement() || this.isArrayExpression()) {
|
||||
return t.genericTypeAnnotation(t.identifier("Array"));
|
||||
}
|
||||
|
||||
//
|
||||
if (!force && this.parentPath.isReturnStatement()) {
|
||||
return this.parentPath.getTypeAnnotation();
|
||||
}
|
||||
|
||||
if (this.isReturnStatement()) {
|
||||
var funcPath = this.findParent((path) => path.isFunction());
|
||||
if (!funcPath) return;
|
||||
|
||||
var returnType = funcPath.node.returnType;
|
||||
if (returnType) {
|
||||
return returnType;
|
||||
} else {
|
||||
return this.get("argument").getTypeAnnotation(true);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
if (this.isNewExpression() && this.get("callee").isIdentifier()) {
|
||||
// only resolve identifier callee
|
||||
return t.genericTypeAnnotation(node.callee);
|
||||
}
|
||||
|
||||
//
|
||||
if (this.isReferencedIdentifier()) {
|
||||
// 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 this._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
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
if (this.isObjectExpression()) {
|
||||
return t.genericTypeAnnotation(t.identifier("Object"));
|
||||
}
|
||||
|
||||
//
|
||||
if (this.isFunction() || this.isClass()) {
|
||||
return t.genericTypeAnnotation(t.identifier("Function"));
|
||||
}
|
||||
|
||||
//
|
||||
if (this.isTemplateLiteral()) {
|
||||
return t.stringTypeAnnotation();
|
||||
}
|
||||
|
||||
//
|
||||
if (this.isUnaryExpression()) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
if (this.isBinaryExpression()) {
|
||||
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.isGenericType("Number") && right.isGenericType("Number")) {
|
||||
// both numbers so this will be a number
|
||||
return t.numberTypeAnnotation();
|
||||
} else if (left.isGenericType("String") || right.isGenericType("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 (this.isLogicalExpression()) {
|
||||
return t.createUnionTypeAnnotation([
|
||||
this.get("left").getTypeAnnotation(),
|
||||
this.get("right").getTypeAnnotation()
|
||||
]);
|
||||
}
|
||||
|
||||
//
|
||||
if (this.isConditionalExpression()) {
|
||||
return t.createUnionTypeAnnotation([
|
||||
this.get("consequent").getTypeAnnotation(),
|
||||
this.get("alternate").getTypeAnnotation()
|
||||
]);
|
||||
}
|
||||
|
||||
//
|
||||
if (this.isSequenceExpression()) {
|
||||
return this.get("expressions").pop().getTypeAnnotation(force);
|
||||
}
|
||||
|
||||
//
|
||||
if (this.isAssignmentExpression()) {
|
||||
return this.get("right").getTypeAnnotation(force);
|
||||
}
|
||||
|
||||
//
|
||||
if (this.isUpdateExpression()) {
|
||||
let operator = node.operator;
|
||||
if (operator === "++" || operator === "--") {
|
||||
return t.numberTypeAnnotation();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
if (this.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 (value === null) return t.voidTypeAnnotation();
|
||||
if (node.regex) return t.genericTypeAnnotation(t.identifier("RegExp"));
|
||||
}
|
||||
|
||||
//
|
||||
var callPath;
|
||||
if (this.isCallExpression()) callPath = this.get("callee");
|
||||
if (this.isTaggedTemplateExpression()) callPath = this.get("tag");
|
||||
if (callPath) {
|
||||
var callee = callPath.resolve();
|
||||
// todo: read typescript/flow interfaces
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function isGenericType(genericName: string): boolean {
|
||||
var type = this.getTypeAnnotation();
|
||||
|
||||
if (t.isGenericTypeAnnotation(type) && t.isIdentifier(type.id, { name: genericName })) {
|
||||
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;
|
||||
}
|
||||
@@ -279,7 +279,7 @@ export default class Scope {
|
||||
/**
|
||||
* Determine whether evaluating the specific input `node` is a consequenceless reference. ie.
|
||||
* evaluating it wont result in potentially arbitrary code from being ran. The following are
|
||||
* whitelisted and determined not cause side effects:
|
||||
* whitelisted and determined not to cause side effects:
|
||||
*
|
||||
* - `this` expressions
|
||||
* - `super` expressions
|
||||
@@ -492,7 +492,7 @@ export default class Scope {
|
||||
for (var name in ids) {
|
||||
var id = ids[name];
|
||||
|
||||
var local = this.getOwnBindingInfo(name);
|
||||
var local = this.getOwnBinding(name);
|
||||
if (local) {
|
||||
// don't ever let a type alias shadow a local binding
|
||||
if (kind === "type") continue;
|
||||
@@ -819,7 +819,7 @@ export default class Scope {
|
||||
var scope = this;
|
||||
|
||||
do {
|
||||
var binding = scope.getOwnBindingInfo(name);
|
||||
var binding = scope.getOwnBinding(name);
|
||||
if (binding) return binding;
|
||||
} while (scope = scope.parent);
|
||||
}
|
||||
@@ -828,7 +828,7 @@ export default class Scope {
|
||||
* Description
|
||||
*/
|
||||
|
||||
getOwnBindingInfo(name: string) {
|
||||
getOwnBinding(name: string) {
|
||||
return this.bindings[name];
|
||||
}
|
||||
|
||||
@@ -855,7 +855,7 @@ export default class Scope {
|
||||
*/
|
||||
|
||||
hasOwnBinding(name: string) {
|
||||
return !!this.getOwnBindingInfo(name);
|
||||
return !!this.getOwnBinding(name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -76,8 +76,6 @@
|
||||
"ThisExpression": ["Expression"],
|
||||
"Super": ["Expression"],
|
||||
"UpdateExpression": ["Expression"],
|
||||
"JSXEmptyExpression": ["Expression"],
|
||||
"JSXMemberExpression": ["Expression"],
|
||||
|
||||
"AnyTypeAnnotation": ["Flow", "FlowBaseAnnotation"],
|
||||
"ArrayTypeAnnotation": ["Flow"],
|
||||
@@ -116,10 +114,10 @@
|
||||
"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"]
|
||||
|
||||
@@ -109,8 +109,8 @@ export function createTypeAnnotationBasedOnTypeof(type) {
|
||||
} else if (type === "boolean") {
|
||||
return t.booleanTypeAnnotation();
|
||||
} else if (type === "function") {
|
||||
// todo
|
||||
return t.genericTypeAnnotation(t.identifier("Function"));
|
||||
} else if (type === "object") {
|
||||
// todo
|
||||
return t.genericTypeAnnotation(t.identifier("Object"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,10 @@ export const FLATTENABLE_KEYS = ["body", "expressions"];
|
||||
export const FOR_INIT_KEYS = ["left", "init"];
|
||||
export const COMMENT_KEYS = ["leadingComments", "trailingComments"];
|
||||
|
||||
export const BOOLEAN_BINARY_OPERATORS = ["==", "===", "!=", "!==", ">", "<", ">=", "<=", "in", "instanceof"];
|
||||
export const NUMBER_BINARY_OPERATORS = ["-", "/", "*", "**", "&", "|", ">>", ">>>", "<<", "^"];
|
||||
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 = ["+", "-", "++", "--", "~"];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -18,13 +18,23 @@ suite("api", function () {
|
||||
assert.ok(!result.ast);
|
||||
});
|
||||
|
||||
test("auxiliaryComment option", 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);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
@@ -49,22 +59,40 @@ suite("api", function () {
|
||||
});
|
||||
|
||||
assert.deepEqual(transform('import localName3 from "external";').metadata.modules.imports[0], {
|
||||
source: 'external',
|
||||
imported: ['default'],
|
||||
source: "external",
|
||||
imported: ["default"],
|
||||
specifiers: [{
|
||||
kind: 'named',
|
||||
imported: 'default',
|
||||
local: 'localName3'
|
||||
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',
|
||||
kind: "external-namespace",
|
||||
exported: "externalName1",
|
||||
source: "external",
|
||||
}]
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user