Compare commits

...

45 Commits

Author SHA1 Message Date
Sebastian McKenzie
27f039488e v5.5.8 2015-06-13 18:53:41 +01:00
Sebastian McKenzie
e3ce82e12f remove console.log 2015-06-13 18:50:51 +01:00
Sebastian McKenzie
4934ea56a0 change NodePath#inType to use arguments instead of types 2015-06-13 18:50:19 +01:00
Sebastian McKenzie
ce03457b19 add getOpposite path method 2015-06-13 18:50:05 +01:00
Sebastian McKenzie
1298c67949 rename getOwnBindingInfo to getOwnBinding 2015-06-13 18:49:59 +01:00
Sebastian McKenzie
668274edcb remove resolve-rc file 2015-06-13 18:49:37 +01:00
Sebastian McKenzie
0694a7dd06 5.5.7 2015-06-13 02:23:28 +01:00
Sebastian McKenzie
20d19735fc v5.5.7 2015-06-13 02:22:20 +01:00
Sebastian McKenzie
b5b6bf4ad5 add isDirective method, 2015-06-13 02:21:22 +01:00
Sebastian McKenzie
844c10cac0 fix reference to inferers 2015-06-13 02:20:04 +01:00
Sebastian McKenzie
43583e4e9d pick only current constant violation if it's of the same scope 2015-06-13 02:19:57 +01:00
Sebastian McKenzie
f5b921cda9 better errorWithNode that's consolidated across paths and files 2015-06-13 02:19:44 +01:00
Sebastian McKenzie
763892aa79 remove unused variable 2015-06-13 02:19:30 +01:00
Sebastian McKenzie
3e6eae4d1a Merge branch 'master' of github.com:babel/babel 2015-06-13 02:00:22 +01:00
Sebastian McKenzie
7c090c8580 Merge pull request #1740 from zertosh/cli-fixes
Really fix "--help"
2015-06-13 01:56:58 +01:00
Andres Suarez
a5f6c1c389 Really fix "--help" 2015-06-12 20:54:53 -04:00
Sebastian McKenzie
c159f2d982 Merge branch 'master' of github.com:babel/babel 2015-06-13 01:54:33 +01:00
Sebastian McKenzie
9205f10244 Merge pull request #1719 from jmm/internals-docs2
Internals documentation
2015-06-13 01:53:04 +01:00
Sebastian McKenzie
4cd7bcad59 Merge pull request #1727 from zertosh/cli-fixes
Fix "--help" distinguish optional transforms
2015-06-13 01:52:46 +01:00
Andres Suarez
7e9660efd3 Fix "--help" distinguish optional transforms 2015-06-12 20:51:52 -04:00
Sebastian McKenzie
2d66ce5224 Merge pull request #1724 from arthurvr/bool
Update `util.booleanify()` return type
2015-06-13 01:48:30 +01:00
Sebastian McKenzie
1257b2cf40 Merge pull request #1736 from grncdr/patch-1
Remove duplicate keys from alias-keys.json
2015-06-12 20:33:54 +01:00
Stephen Sugden
f21d935de5 Add aliases from JSX* tags to Expression 2015-06-12 12:24:24 -07:00
Stephen Sugden
2e20364793 Remove duplicate keys from alias-keys.json
Fixes #1734
2015-06-12 20:58:41 +02:00
Sebastian McKenzie
e47e8a187a Merge branch 'master' of github.com:babel/babel 2015-06-11 19:06:41 +01:00
Ingvar Stepanyan
26924d5944 Fix dependency reference of Symbol.hasInstance 2015-06-10 22:07:40 +03:00
Arthur Verschaeve
5eb1850a55 Update util.booleanify() return type
Ref 62f37c1e62
2015-06-10 16:54:43 +02:00
Sebastian McKenzie
333e287226 remove special minification.removeConsole ExpressionStatement handling 2015-06-10 13:14:44 +01:00
Sebastian McKenzie
80a77bd6a2 fix linting error 2015-06-10 03:16:07 +01:00
Sebastian McKenzie
c9286a1de1 rewrite option handling - fixes #1636 2015-06-10 03:07:06 +01:00
Sebastian McKenzie
52f614dcdf add better path execution status algo 2015-06-10 01:36:36 +01:00
Sebastian McKenzie
600367ae25 add t.COMPARISON_BINARY_OPERATORS 2015-06-10 01:34:51 +01:00
Sebastian McKenzie
b761cba135 split auxiliary comment option into before and after - fixes #1721 2015-06-10 01:34:44 +01:00
Sebastian McKenzie
947d3e262d push newline after decorator when doing code gen - fixes #1713 2015-06-10 01:19:58 +01:00
Sebastian McKenzie
4061bea528 change execution order of module metadata visitor to resolve module source before building up metadata tree - fixes #1720 2015-06-10 01:15:11 +01:00
Sebastian McKenzie
de195e5bfc Merge branch 'master' of github.com:babel/babel 2015-06-10 01:10:41 +01:00
Sebastian McKenzie
3bcef86973 Merge pull request #1720 from chadhietala/failing-metadata-test
Metadata object does not take in account resolveModuleSource()
2015-06-10 01:10:09 +01:00
Sebastian McKenzie
fa670ac71e visually split up inference inferer methods 2015-06-09 22:52:21 +01:00
Sebastian McKenzie
572261f9ce add support for typecasts in path static evaluation 2015-06-09 22:52:00 +01:00
Chad Hietala
8a320d53a5 Metadata object does not take in account resolveModuleSource()
This adds a failing test to illustrate the metadata object not reflecting what is returned from resolveModuleSource(). I might also not understand resolveModuleSource's purpose. However, this was the hook mentioned here https://github.com/babel/babel/issues/1602.
2015-06-09 14:26:33 -07:00
Jesse McCarthy
0650eedeb6 Add reference to doc dir to CONTRIBUTING. 2015-06-09 15:37:01 -04:00
Jesse McCarthy
2282d066a2 Start doc dir for internals documentation. 2015-06-09 15:37:01 -04:00
Sebastian McKenzie
f4d7cc55c1 split inference logic into separate folder 2015-06-09 14:02:57 +01:00
Sebastian McKenzie
eaaa279aa5 add let binding collision todo 2015-06-09 04:08:44 +01:00
Sebastian McKenzie
0595e06e29 5.5.6 2015-06-09 04:08:36 +01:00
39 changed files with 884 additions and 641 deletions

View File

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

4
doc/index.md Normal file
View File

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

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

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

View File

@@ -1,7 +1,7 @@
{
"name": "babel-core",
"description": "A compiler for writing next generation JavaScript",
"version": "5.5.6",
"version": "5.5.8",
"author": "Sebastian McKenzie <sebmck@gmail.com>",
"homepage": "https://babeljs.io/",
"license": "MIT",

View File

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

View File

@@ -1,14 +1,14 @@
{
"name": "babel",
"description": "Turn ES6 code into readable vanilla ES5 with source maps",
"version": "5.5.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",

View File

@@ -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>",

View File

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

View File

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

View File

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

View File

@@ -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;

View File

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

View File

@@ -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,

View File

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

View File

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

View File

@@ -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);
}

View File

@@ -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") {

View File

@@ -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
});
}
}
}
},

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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]));
}
}

View File

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

View File

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

View File

@@ -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.

View File

@@ -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;

View File

@@ -76,6 +76,10 @@ export function evaluate(): { confident: boolean; value: any } {
}
}
if (path.isTypeCastExpression()) {
return evaluate(path.get("expression"));
}
if (path.isIdentifier() && !path.scope.hasBinding(node.name, true)) {
if (node.name === "undefined") {
return undefined;

View File

@@ -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
*/

View File

@@ -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"));

View File

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

View File

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

View File

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

View File

@@ -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);
}
}
}

View File

@@ -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;
}

View File

@@ -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);
}
/**

View File

@@ -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"]

View File

@@ -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"));
}
}

View File

@@ -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 = ["+", "-", "++", "--", "~"];

View File

@@ -102,7 +102,7 @@ export function arrayify(val: any, mapFn?: Function): Array {
return [val];
}
export function booleanify(val: any): boolean {
export function booleanify(val: any) {
if (val === "true") return true;
if (val === "false") return false;
return val;
@@ -111,7 +111,7 @@ export function booleanify(val: any): boolean {
export function shouldIgnore(filename, ignore, only) {
filename = slash(filename);
if (only.length) {
if (only) {
for (let pattern of (only: Array)) {
if (pattern.test(filename)) return false;
}

View File

@@ -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",
}]
});