Merge branch 'experimental'

# Conflicts:
#	src/babel/transformation/transformers/playground/method-binding.js
This commit is contained in:
Sebastian McKenzie 2015-03-16 01:51:52 +11:00
commit 4afd0f553e
82 changed files with 1311 additions and 892 deletions

View File

@ -2,32 +2,38 @@
var commander = require("commander");
var transform = require("../../lib/babel/transformation");
var kebabCase = require("lodash/string/kebabCase");
var File = require("../../lib/babel/transformation/file");
var util = require("../../lib/babel/util");
var fs = require("fs");
var each = require("lodash/collection/each");
var keys = require("lodash/object/keys");
var fs = require("fs");
each(File.options, function (option, key) {
if (option.hidden) return;
var arg = kebabCase(key);
if (option.type !== "boolean") {
arg += ` [${option.type || "string"}]`;
}
if (option.type === "boolean" && option.default === true) {
arg = `no-${key}`;
}
arg = `--${arg}`;
if (option.shorthand) {
arg = `-${option.shorthand}, ${arg}`;
}
commander.option(arg, option.description);
})
commander.option("-t, --source-maps-inline", "Append sourceMappingURL comment to bottom of code");
commander.option("-s, --source-maps", "Save source map alongside the compiled code");
commander.option("-f, --filename [filename]", "Filename to use when reading from stdin - this will be used in source-maps, errors etc [stdin]", "stdin");
commander.option("-w, --watch", "Recompile files on changes");
commander.option("-r, --external-helpers", "Replace helpers with references to a `babelHelpers` global");
commander.option("-e, --experimental", "Enable experimental support for proposed ES7 features");
commander.option("-p, --playground", "Enable playground support");
commander.option("-c, --compact [mode]", "When set to \"auto\" compact is `true` when the input size exceeds 100KB. (auto|true|false)", "auto");
commander.option("-m, --modules [modules]", "Module formatter type to use [common]", "common");
commander.option("-l, --whitelist [whitelist]", "Whitelist of transformers to ONLY use", util.list);
commander.option("-b, --blacklist [blacklist]", "Blacklist of transformers to NOT use", util.list);
commander.option("-i, --optional [list]", "List of optional transformers to enable", util.list);
commander.option("-L, --loose [list]", "List of transformers to enable loose mode ON", util.list);
commander.option("-o, --out-file [out]", "Compile all input files into a single file");
commander.option("-d, --out-dir [out]", "Compile an input directory of modules into an output directory");
commander.option("-c, --remove-comments", "Remove comments from the compiled code", false);
commander.option("-M, --module-ids", "Insert module id in modules", false);
commander.option("-R, --react-compat", "Makes the react transformer produce pre-v0.12 code");
commander.option("--keep-module-id-extensions", "Keep extensions when generating module ids", false);
commander.option("-a, --auxiliary-comment [comment]", "Comment text to prepend to all auxiliary code");
commander.option("-D, --copy-files", "When compiling a directory copy over non-compilable files");
commander.on("--help", function () {
@ -38,9 +44,7 @@ commander.on("--help", function () {
each(keys(obj).sort(), function (key) {
if (key[0] === "_") return;
if (obj[key].optional) {
key = "[" + key + "]";
}
if (obj[key].optional) key = `[${key}]`;
console.log(" - " + key);
});
@ -100,24 +104,11 @@ if (errors.length) {
//
exports.opts = {
keepModuleIdExtensions: commander.keepModuleIdExtensions,
auxiliaryComment: commander.auxiliaryComment,
externalHelpers: commander.externalHelpers,
sourceMapName: commander.outFile,
experimental: commander.experimental,
reactCompat: commander.reactCompat,
playground: commander.playground,
moduleIds: commander.moduleIds,
blacklist: commander.blacklist,
whitelist: commander.whitelist,
sourceMap: commander.sourceMaps || commander.sourceMapsInline,
optional: commander.optional,
comments: !commander.removeComments,
modules: commander.modules,
compact: commander.compact,
loose: commander.loose
};
exports.opts = {};
each(File.options, function (opt, key) {
exports.opts[key] = commander[key];
});
var fn;

View File

@ -1,11 +1,10 @@
var resolveRc = require("../../lib/babel/api/register/resolve-rc");
var readdir = require("fs-readdir-recursive");
var index = require("./index");
var babel = require("../../lib/babel/api/node");
var util = require("../../lib/babel/util");
var path = require("path");
var fs = require("fs");
var _ = require("lodash");
var readdir = require("fs-readdir-recursive");
var index = require("./index");
var babel = require("../../lib/babel/api/node");
var util = require("../../lib/babel/util");
var path = require("path");
var fs = require("fs");
var _ = require("lodash");
exports.readdirFilter = function (filename) {
return readdir(filename).filter(function (filename) {
@ -24,7 +23,6 @@ exports.addSourceMappingUrl = function (code, loc) {
exports.transform = function (filename, code, opts) {
opts = _.defaults(opts || {}, index.opts);
opts.filename = filename;
resolveRc(filename, opts);
var result = babel.transform(code, opts);
result.filename = filename;

View File

@ -54,6 +54,7 @@
"leven": "^1.0.1",
"line-numbers": "0.2.0",
"lodash": "^3.2.0",
"minimatch": "^2.0.3",
"output-file-sync": "^1.1.0",
"path-is-absolute": "^1.0.0",
"private": "^0.1.6",

View File

@ -1,12 +1,13 @@
import isFunction from "lodash/lang/isFunction";
import transform from "../transformation";
import * as util from "../util";
import fs from "fs";
import * as util from "../util";
export { util as _util };
export { util };
export { canCompile } from "../util";
export { default as acorn } from "acorn-babel";
export { default as Transformer } from "../transformation/transformer";
export { default as transform } from "../transformation";
export { default as traverse } from "../traversal";
export { default as buildExternalHelpers } from "../tools/build-external-helpers";
@ -15,7 +16,7 @@ export { version } from "../../../package";
import * as t from "../types";
export { t as types };
export function register(opts) {
export function register(opts?: Object) {
var callback = require("./register/node");
if (opts != null) callback(opts);
return callback;
@ -25,7 +26,7 @@ export function polyfill() {
require("../polyfill");
}
export function transformFile(filename, opts, callback) {
export function transformFile(filename: string, opts?: Object, callback: Function) {
if (isFunction(opts)) {
callback = opts;
opts = {};
@ -48,7 +49,7 @@ export function transformFile(filename, opts, callback) {
});
}
export function transformFileSync(filename, opts = {}) {
export function transformFileSync(filename: string, opts?: Object = {}) {
opts.filename = filename;
return transform(fs.readFileSync(filename), opts);
}

View File

@ -2,7 +2,7 @@ import path from "path";
import os from "os";
import fs from "fs";
var FILENAME = process.env.BABEL_CACHE_PATH || path.join(os.tmpdir(), "babel.json");
const FILENAME = process.env.BABEL_CACHE_PATH || path.join(os.tmpdir(), "babel.json");
var data = {};
export function save() {

View File

@ -1,7 +1,7 @@
import "../../polyfill";
import sourceMapSupport from "source-map-support";
import * as registerCache from "./cache";
import resolveRc from "./resolve-rc";
import resolveRc from "../../tools/resolve-rc";
import extend from "lodash/object/extend";
import * as babel from "../node";
import each from "lodash/collection/each";
@ -44,6 +44,9 @@ var compile = function (filename) {
var result;
var opts = extend({}, transformOpts);
// this will be done when the file is transformed anyway but we need all
// the options so we can generate the cache key
resolveRc(filename, opts);
var cacheKey = `${filename}:${JSON.stringify(opts)}:${babel.version}`;

View File

@ -66,8 +66,10 @@ export function ImportDeclaration(node, print) {
if (specfiers && specfiers.length) {
var foundImportSpecifier = false;
each(node.specifiers, (spec, i) => {
if (+i > 0) {
for (var i = 0; i < node.specifiers.length; i++) {
var spec = node.specifiers[i];
if (i > 0) {
this.push(", ");
}
@ -79,7 +81,7 @@ export function ImportDeclaration(node, print) {
}
print(spec);
});
}
if (foundImportSpecifier) {
this.push(" }");

View File

@ -1,7 +1,7 @@
import each from "lodash/collection/each";
import * as t from "../../types";
var PRECEDENCE = {};
const PRECEDENCE = {};
each([
["||"],

View File

@ -55,10 +55,10 @@ var highlight = function (text) {
});
};
export default function (lines, lineNumber, colNumber) {
export default function (lines: number, lineNumber: number, colNumber: number, color?): string {
colNumber = Math.max(colNumber, 0);
if (chalk.supportsColor) {
if (color && chalk.supportsColor) {
lines = highlight(lines);
}

View File

@ -32,12 +32,12 @@ export default function (opts, code, callback) {
} catch (err) {
if (!err._babel) {
err._babel = true;
var message = `${opts.filename}: ${err.message}`;
var loc = err.loc;
if (loc) {
var frame = codeFrame(code, loc.line, loc.column + 1);
message += frame;
message += codeFrame(code, loc.line, loc.column + 1, opts.highlightErrors);
}
if (err.stack) {

View File

@ -1,6 +1,6 @@
import * as util from "util";
export var messages = {
export const MESSAGES = {
tailCallReassignmentDeopt: "Function reference has been reassigned so it's probably be dereferenced so we can't optimise this with confidence",
JSXNamespacedTags: "Namespace tags are not supported. ReactJSX is not XML.",
classesIllegalBareSuper: "Illegal use of bare super",
@ -25,7 +25,7 @@ export var messages = {
};
export function get(key: String, ...args) {
var msg = messages[key];
var msg = MESSAGES[key];
if (!msg) throw new ReferenceError(`Unknown message ${JSON.stringify(key)}`);
args = parseArgs(args);

View File

@ -1,20 +1,25 @@
import convertSourceMap from "convert-source-map";
import * as optionParsers from "./option-parsers";
import shebangRegex from "shebang-regex";
import TraversalPath from "../../traversal/path";
import isFunction from "lodash/lang/isFunction";
import isAbsolute from "path-is-absolute";
import resolveRc from "../../tools/resolve-rc";
import sourceMap from "source-map";
import transform from "./index";
import generate from "../generation";
import transform from "./../index";
import generate from "../../generation";
import defaults from "lodash/object/defaults";
import includes from "lodash/collection/includes";
import traverse from "../../traversal";
import assign from "lodash/object/assign";
import Logger from "./logger";
import parse from "../helpers/parse";
import Scope from "../traversal/scope";
import parse from "../../helpers/parse";
import Scope from "../../traversal/scope";
import slash from "slash";
import * as util from "../util";
import * as util from "../../util";
import path from "path";
import each from "lodash/collection/each";
import * as t from "../types";
import * as t from "../../types";
var checkTransformerVisitor = {
enter(node, parent, scope, state) {
@ -78,84 +83,37 @@ export default class File {
"self-global"
];
static validOptions = [
"filename",
"filenameRelative",
"blacklist",
"whitelist",
"optional",
"loose",
"playground",
"experimental",
"modules",
"moduleIds",
"moduleId",
"resolveModuleSource",
"keepModuleIdExtensions",
"code",
"ast",
"comments",
"compact",
"auxiliaryComment",
"externalHelpers",
"returnUsedHelpers",
"inputSourceMap",
"sourceMap",
"sourceMapName",
"sourceFileName",
"sourceRoot",
"moduleRoot",
// legacy
"format",
"reactCompat",
// these are used by plugins
"ignore",
"only",
"extensions",
"accept"
];
static options = require("./options");
normalizeOptions(opts: Object) {
opts = assign({}, opts);
for (var key in opts) {
if (key[0] !== "_" && File.validOptions.indexOf(key) < 0) {
throw new ReferenceError(`Unknown option: ${key}`);
}
if (opts.filename && isAbsolute(opts.filename)) {
opts = resolveRc(opts.filename, opts);
}
defaults(opts, {
keepModuleIdExtensions: false,
resolveModuleSource: null,
returnUsedHelpers: false,
externalHelpers: false,
auxilaryComment: "",
inputSourceMap: null,
experimental: false,
reactCompat: false,
playground: false,
moduleIds: false,
blacklist: [],
whitelist: [],
sourceMap: false,
optional: [],
comments: true,
filename: "unknown",
modules: "common",
compact: "auto",
loose: [],
code: true,
ast: true
});
//
for (let key in opts) {
if (key[0] === "_") continue;
let option = File.options[key];
if (!option) throw new ReferenceError(`Unknown option: ${key}`);
}
for (let key in File.options) {
let option = File.options[key];
var val = opts[key];
if (val == null) val = option.default || null;
var optionParser = optionParsers[option.type];
if (optionParser) {
val = optionParser(key, val);
}
opts[key] = val;
}
if (opts.inputSourceMap) {
opts.sourceMap = true;
@ -173,15 +131,8 @@ export default class File {
opts.basename = path.basename(opts.filename, path.extname(opts.filename));
opts.blacklist = util.arrayify(opts.blacklist);
opts.whitelist = util.arrayify(opts.whitelist);
opts.optional = util.arrayify(opts.optional);
opts.compact = util.booleanify(opts.compact);
opts.loose = util.arrayify(opts.loose);
if (includes(opts.loose, "all") || includes(opts.loose, true)) {
opts.loose = Object.keys(transform.transformers);
}
opts.ignore = util.arrayify(opts.ignore, util.regexify);
opts.only = util.arrayify(opts.only, util.regexify);
defaults(opts, {
moduleRoot: opts.sourceRoot
@ -200,33 +151,12 @@ export default class File {
sourceMapName: opts.filenameRelative
});
if (opts.playground) {
opts.experimental = true;
}
//
if (opts.externalHelpers) {
this.set("helpersNamespace", t.identifier("babelHelpers"));
}
opts.blacklist = transform._ensureTransformerNames("blacklist", opts.blacklist);
opts.whitelist = transform._ensureTransformerNames("whitelist", opts.whitelist);
opts.optional = transform._ensureTransformerNames("optional", opts.optional);
opts.loose = transform._ensureTransformerNames("loose", opts.loose);
if (opts.reactCompat) {
opts.optional.push("reactCompat");
console.error("The reactCompat option has been moved into the optional transformer `reactCompat`");
}
var ensureEnabled = function (key) {
var namespace = transform.transformerNamespaces[key];
if (namespace === "es7") opts.experimental = true;
if (namespace === "playground") opts.playground = true;
};
each(opts.whitelist, ensureEnabled);
each(opts.optional, ensureEnabled);
return opts;
};
@ -234,6 +164,10 @@ export default class File {
return includes(this.opts.loose, key);
}
buildPlugins(stack) {
}
buildTransformers() {
var file = this;
@ -242,6 +176,8 @@ export default class File {
var secondaryStack = [];
var stack = [];
this.buildPlugins(stack);
each(transform.transformers, function (transformer, key) {
var pass = transformers[key] = transformer.buildPass(file);
@ -377,7 +313,7 @@ export default class File {
this.usedHelpers[name] = true;
var generator = this.get("helperGenerator");
var runtime = this.get("helpersNamespace");
var runtime = this.get("helpersNamespace");
if (generator) {
return generator(name);
} else if (runtime) {
@ -410,7 +346,36 @@ export default class File {
return this.parseShebang(code);
}
shouldIgnore() {
var opts = this.opts;
var filename = opts.filename;
var ignore = opts.ignore;
var only = opts.only;
if (only.length) {
for (var i = 0; i < only.length; i++) {
if (only[i].test(filename)) return false;
}
return true;
} else if (ignore.length) {
for (var i = 0; i < ignore.length; i++) {
if (ignore[i].test(filename)) return true;
}
}
return false;
}
parse(code: string) {
if (this.shouldIgnore()) {
return {
code: code,
map: null,
ast: null
};
}
code = this.addCode(code);
var opts = this.opts;
@ -424,12 +389,28 @@ export default class File {
});
}
setAst(ast) {
this.path = TraversalPath.get(null, null, ast, ast, "program", this);
this.scope = this.path.scope;
this.ast = ast;
this.path.traverse({
enter(node, parent, scope) {
if (this.isScope()) {
for (var key in scope.bindings) {
scope.bindings[key].setTypeAnnotation();
}
}
}
});
}
transform(ast) {
this.log.debug();
this.ast = ast;
this.setAst(ast);
this.lastStatements = t.getLastStatements(ast.program);
this.scope = new Scope(ast.program, ast, null, this);
var modFormatter = this.moduleFormatter = this.getModuleFormatter(this.opts.modules);
if (modFormatter.init && this.transformers["es6.modules"].canRun()) {
@ -483,14 +464,14 @@ export default class File {
if (inputMap) {
map.sources[0] = inputMap.file;
var inputMapConsumer = new sourceMap.SourceMapConsumer(inputMap);
var outputMapConsumer = new sourceMap.SourceMapConsumer(map);
var inputMapConsumer = new sourceMap.SourceMapConsumer(inputMap);
var outputMapConsumer = new sourceMap.SourceMapConsumer(map);
var outputMapGenerator = sourceMap.SourceMapGenerator.fromSourceMap(outputMapConsumer);
outputMapGenerator.applySourceMap(inputMapConsumer);
var mergedMap = outputMapGenerator.toJSON();
mergedMap.sources = inputMap.sources
mergedMap.file = inputMap.file;
mergedMap.file = inputMap.file;
return mergedMap;
}
@ -498,6 +479,7 @@ export default class File {
}
generate(): {
usedHelpers?: Array<string>;
code: string;
map?: Object;
ast?: Object;
@ -507,8 +489,8 @@ export default class File {
var result = {
code: "",
map: null,
ast: null
map: null,
ast: null
};
if (this.opts.returnUsedHelpers) {

View File

@ -1,4 +1,4 @@
import * as util from "../util";
import * as util from "../../util";
export default class Logger {
constructor(file: File) {
@ -12,6 +12,12 @@ export default class Logger {
return parts;
}
deprecate(msg) {
if (!file.opts.suppressDeprecationMessages) {
console.error(msg);
}
}
debug(msg: string) {
util.debug(this._buildMessage(msg));
}

View File

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

View File

@ -0,0 +1,164 @@
{
"filename": {
"type": "string",
"description": "Filename to use when reading from stdin - this will be used in source-maps, errors etc",
"default": "unknown",
"shorthand": "f"
},
"filenameRelative": {
"hidden": true,
"type": "string"
},
"inputSourceMap": {
"hidden": true
},
"moduleId": {
},
"highlightErrors": {
"description": "ANSI syntax highlight error messages"
"type": "boolean",
"default": true
},
"suppressDeprecationMessages": {
"type": "boolean",
"default": false,
"hidden": true
},
"resolveModuleSource": {
"hidden": true
},
"experimental": {
"description": "Enable all ES7+ transformers",
"shorthand": "e",
"type": "boolean",
"default": false
},
"playground": {
"description": "Enable all playground transformers",
"shorthand": "p",
"type": "boolean",
"default": false
},
"blacklist": {
"type": "transformerList",
"description": "Blacklist of transformers to NOT use",
"shorthand": "b"
},
"whitelist": {
"type": "transformerList",
"description": "Whitelist of transformers to ONLY use",
"shorthand": "l"
},
"optional": {
"type": "transformerList",
"description": "List of optional transformers to enable"
},
"modules": {
"type": "string",
"description": "Module formatter type to use [common]",
"default": "common",
"shorthand": "m"
},
"moduleIds": {
"type": "boolean",
"default": false,
"shorthand": "M"
},
"loose": {
"type": "transformerList",
"description": "List of transformers to enable loose mode ON",
"shorthand": "L"
},
"ignore": {
"type": "list"
},
"only": {
"type": "list"
},
"code": {
"hidden": true,
"default": true,
"type": "boolean"
},
"ast": {
"hidden": true,
"default": true,
"type": "boolean"
},
"comments": {
"type": "boolean",
"default": true
},
"compact": {
"type": "string",
"default": "auto"
},
"keepModuleIdExtensions": {
"type": "boolean",
"description": "Keep extensions when generating module ids",
"default": false,
"shorthand": "k"
},
"auxiliaryComment": {
"type": "string",
"default": "",
"shorthand": "a"
},
"externalHelpers": {
"type": "string",
"default": false,
"shorthand": "r"
},
"returnUsedHelpers": {
"type": "boolean",
"default": false,
"hidden": true
},
"sourceMap": {
"type": "string",
"default": false,
"shorthand": "s"
},
"sourceMapName": {
"type": "string"
},
"sourceFileName": {
"type": "string"
},
"sourceRoot": {
"type": "string"
},
"moduleRoot": {
"type": "string"
}
}

View File

@ -27,7 +27,7 @@ export default function (exports, opts) {
};
exports.JSXNamespacedName = function (node, parent, scope, file) {
throw file.errorWithNode(node, messages.get("JSXNamespacedTags"));
throw this.errorWithNode(messages.get("JSXNamespacedTags"));
};
exports.JSXMemberExpression = {

View File

@ -39,7 +39,7 @@ var visitor = {
var callback = self.specHandle;
if (self.isLoose) callback = self.looseHandle;
return callback.call(self, getThisReference, node, parent);
return callback.call(self, this, getThisReference);
}
};
@ -191,11 +191,12 @@ export default class ReplaceSupers {
* Description
*/
looseHandle(getThisReference: Function, node: Object, parent: Object) {
if (t.isIdentifier(node, { name: "super" })) {
looseHandle(path: TraversalPath, getThisReference: Function) {
var node = path.node;
if (path.isIdentifier({ name: "super" })) {
this.hasSuper = true;
return this.getLooseSuperProperty(node, parent);
} else if (t.isCallExpression(node)) {
return this.getLooseSuperProperty(node, path.parent);
} else if (path.isCallExpression()) {
var callee = node.callee;
if (!t.isMemberExpression(callee)) return;
if (callee.object.name !== "super") return;
@ -211,15 +212,18 @@ export default class ReplaceSupers {
* Description
*/
specHandle(getThisReference: Function, node: Object, parent: Object) {
specHandle(path: TraversalPath, getThisReference: Function) {
var methodNode = this.methodNode;
var property;
var computed;
var args;
var thisReference;
var parent = path.parent;
var node = path.node;
if (isIllegalBareSuper(node, parent)) {
throw this.file.errorWithNode(node, messages.get("classesIllegalBareSuper"));
throw path.errorWithNode(messages.get("classesIllegalBareSuper"));
}
if (t.isCallExpression(node)) {

View File

@ -4,7 +4,7 @@ import object from "../helpers/object";
import File from "./file";
import each from "lodash/collection/each";
export default function transform(code: code, opts?: Object) {
export default function transform(code: string, opts?: Object) {
var file = new File(opts);
return file.parse(code);
}

View File

@ -45,7 +45,7 @@ var importsVisitor = {
ImportDeclaration: {
enter(node, parent, scope, formatter) {
formatter.hasLocalImports = true;
extend(formatter.localImports, t.getBindingIdentifiers(node));
extend(formatter.localImports, this.getBindingIdentifiers());
formatter.bumpImportOccurences(node);
}
}
@ -54,11 +54,11 @@ var importsVisitor = {
var exportsVisitor = {
ExportDeclaration: {
enter(node, parent, scope, formatter) {
var declar = node.declaration;
var declar = this.get("declaration");
formatter.hasLocalImports = true;
if (declar && t.isStatement(declar)) {
extend(formatter.localExports, t.getBindingIdentifiers(declar));
if (declar.isStatement()) {
extend(formatter.localExports, declar.getBindingIdentifiers());
}
if (!node.default) {
@ -105,16 +105,16 @@ export default class DefaultFormatter {
}
getLocalExports() {
this.file.scope.traverse(this.file.ast, exportsVisitor, this);
this.file.path.traverse(exportsVisitor, this);
}
getLocalImports() {
this.file.scope.traverse(this.file.ast, importsVisitor, this);
this.file.path.traverse(importsVisitor, this);
}
remapAssignments() {
if (this.hasLocalImports) {
this.file.scope.traverse(this.file.ast, remapVisitor, this);
this.file.path.traverse(remapVisitor, this);
}
}

View File

@ -0,0 +1 @@
for (var KEY = 0; KEY < ARR.length; KEY++) BODY;

View File

@ -1,4 +1,5 @@
import includes from "lodash/collection/includes";
import traverse from "../traversal";
/**
* This class is responsible for traversing over the provided `File`s
@ -31,14 +32,14 @@ export default class TransformerPass {
var whitelist = opts.whitelist;
if (whitelist.length) return includes(whitelist, key);
// optional
if (transformer.optional && !includes(opts.optional, key)) return false;
// experimental
if (transformer.experimental && !opts.experimental) return false;
if (transformer.experimental && opts.experimental) return true;
// playground
if (transformer.playground && !opts.playground) return false;
if (transformer.playground && opts.playground) return true;
// optional
if (transformer.optional && !includes(opts.optional, key)) return false;
return true;
}
@ -59,7 +60,7 @@ export default class TransformerPass {
file.log.debug(`Running transformer ${this.transformer.key}`);
file.scope.traverse(file.ast, this.handlers, file);
traverse(file.ast, this.handlers, file.scope, file);
this.ran = true;
}

View File

@ -3,6 +3,7 @@ import isFunction from "lodash/lang/isFunction";
import traverse from "../traversal";
import isObject from "lodash/lang/isObject";
import assign from "lodash/object/assign";
import File from "./file";
import each from "lodash/collection/each";
/**
@ -66,6 +67,10 @@ export default class Transformer {
}
buildPass(file: File): TransformerPass {
if (!(file instanceof File)) {
throw new Error("Multiple versions of babel are interacting, this is either due to a version mismatch in a plugin or it was resolved incorrectly");
}
return new TransformerPass(file, this);
}
}

View File

@ -32,7 +32,7 @@ export function BlockStatement(node, parent, scope, file) {
var letRefs = node._letReferences;
if (!letRefs) return;
scope.traverse(node, visitor, {
this.traverse(visitor, {
letRefs: letRefs,
file: file
});

View File

@ -111,7 +111,7 @@ function traverseReplace(node, parent, scope, remaps) {
var letReferenceBlockVisitor = {
enter(node, parent, scope, state) {
if (this.isFunction()) {
scope.traverse(node, letReferenceFunctionVisitor, state);
this.traverse(letReferenceFunctionVisitor, state);
return this.skip();
}
}
@ -173,7 +173,7 @@ var loopVisitor = {
if (this.isLoop()) {
state.ignoreLabeless = true;
scope.traverse(node, loopVisitor, state);
this.traverse(loopVisitor, state);
state.ignoreLabeless = false;
}

View File

@ -15,7 +15,7 @@ export function ClassDeclaration(node, parent, scope, file) {
}
export function ClassExpression(node, parent, scope, file) {
return new ClassTransformer(node, parent, scope, file).run();
return new ClassTransformer(this, file).run();
}
var verifyConstructorVisitor = traverse.explode({
@ -33,11 +33,11 @@ var verifyConstructorVisitor = traverse.explode({
CallExpression: {
enter(node, parent, scope, state) {
if (t.isIdentifier(node.callee, { name: "super" })) {
if (this.get("callee").isIdentifier({ name: "super" })) {
state.hasBareSuper = true;
if (!state.hasSuper) {
throw state.file.errorWithNode(node, "super call is only allowed in derived constructor");
throw this.errorWithNode("super call is only allowed in derived constructor");
}
}
}
@ -46,7 +46,7 @@ var verifyConstructorVisitor = traverse.explode({
ThisExpression: {
enter(node, parent, scope, state) {
if (state.hasSuper && !state.hasBareSuper) {
throw state.file.errorWithNode(node, "'this' is not allowed before super()");
throw this.errorWithNode("'this' is not allowed before super()");
}
}
}
@ -58,10 +58,11 @@ class ClassTransformer {
* Description
*/
constructor(node: Object, parent: Object, scope: Scope, file: File) {
this.parent = parent;
this.scope = scope;
this.node = node;
constructor(path: TraversalPath, file: File) {
this.parent = path.parent;
this.scope = path.scope;
this.node = path.node;
this.path = path;
this.file = file;
this.hasInstanceMutators = false;
@ -71,11 +72,11 @@ class ClassTransformer {
this.staticMutatorMap = {};
this.hasConstructor = false;
this.className = node.id;
this.classRef = node.id || scope.generateUidIdentifier("class");
this.className = this.node.id;
this.classRef = this.node.id || this.scope.generateUidIdentifier("class");
this.superName = node.superClass || t.identifier("Function");
this.hasSuper = !!node.superClass;
this.superName = this.node.superClass || t.identifier("Function");
this.hasSuper = !!this.node.superClass;
this.isLoose = file.isLoose("es6.classes");
}
@ -174,11 +175,13 @@ class ClassTransformer {
var classBody = this.node.body.body;
var body = this.body;
var classBodyPaths = this.path.get("body").get("body");
for (var i = 0; i < classBody.length; i++) {
var node = classBody[i];
if (t.isMethodDefinition(node)) {
var isConstructor = (!node.computed && t.isIdentifier(node.key, { name: "constructor" })) || t.isLiteral(node.key, { value: "constructor" });
if (isConstructor) this.verifyConstructor(node);
if (isConstructor) this.verifyConstructor(classBodyPaths[i]);
var replaceSupers = new ReplaceSupers({
methodNode: node,
@ -205,8 +208,8 @@ class ClassTransformer {
}
}
// we have no constructor, we have a super, and the super doesn't appear to be falsy
if (!this.hasConstructor && this.hasSuper && t.evaluateTruthy(superName, this.scope) !== false) {
// we have no constructor, but we're a derived class
if (!this.hasConstructor && this.hasSuper) {
var helperName = "class-super-constructor-call";
if (this.isLoose) helperName += "-loose";
constructor.body.body.push(util.template(helperName, {
@ -249,19 +252,17 @@ class ClassTransformer {
* Description
*/
verifyConstructor(node: Object) {
return; // enable this for the next major
verifyConstructor(path: TraversalPath) {
var state = {
hasBareSuper: false,
hasSuper: this.hasSuper,
file: this.file
};
traverse(node, verifyConstructorVisitor, this.scope, state);
path.traverse(verifyConstructorVisitor, state);
if (!state.hasBareSuper && this.hasSuper) {
throw this.file.errorWithNode(node, "Derived constructor must call super()");
throw path.errorWithNode("Derived constructor must call super()");
}
}

View File

@ -37,7 +37,7 @@ var visitor = {
};
export function Scopable(node, parent, scope, file) {
scope.traverse(node, visitor, {
this.traverse(visitor, {
constants: scope.getAllBindingsOfKind("const"),
file: file
});

View File

@ -432,7 +432,7 @@ class DestructuringTransformer {
} else {
arrayRef = this.scope.generateUidBasedOnNode(arrayRef);
this.nodes.push(this.buildVariableDeclaration(arrayRef, toArray));
this.scope.assignTypeGeneric(arrayRef.name, "Array");
//this.getBinding(arrayRef.name).assignTypeGeneric("Array");
}
//

View File

@ -5,6 +5,10 @@ import * as t from "../../../types";
export var check = t.isForOfStatement;
export function ForOfStatement(node, parent, scope, file) {
if (this.get("right").isArrayExpression()) {
return _ForOfStatementArray.call(this, node, scope, file);
}
var callback = spec;
if (file.isLoose("es6.forOf")) callback = loose;
@ -29,13 +33,48 @@ export function ForOfStatement(node, parent, scope, file) {
t.inherits(loop, node);
// todo: find out why this is necessary? #538
loop._scopeInfo = node._scopeInfo;
if (build.replaceParent) this.parentPath.node = build.node;
return build.node;
}
export function _ForOfStatementArray(node, scope, file) {
var nodes = [];
var right = node.right;
if (!t.isIdentifier(right) || !scope.hasBinding(right.name)) {
var uid = scope.generateUidIdentifier("arr");
nodes.push(t.variableDeclaration("var", [
t.variableDeclarator(uid, right)
]));
right = uid;
}
var iterationKey = scope.generateUidIdentifier("i");
var loop = util.template("for-of-array", {
BODY: node.body,
KEY: iterationKey,
ARR: right
});
t.inherits(loop, node);
t.ensureBlock(loop);
var iterationValue = t.memberExpression(right, iterationKey, true);
var left = node.left;
if (t.isVariableDeclaration(left)) {
left.declarations[0].init = iterationValue;
loop.body.body.unshift(left);
} else {
loop.body.body.unshift(t.expressionStatement(t.assignmentExpression("=", left, iterationValue)));
}
nodes.push(loop);
return nodes;
}
var loose = function (node, parent, scope, file) {
var left = node.left;
var declar, id;

View File

@ -26,7 +26,7 @@ export function ImportDeclaration(node, parent, scope, file) {
export function ExportDeclaration(node, parent, scope, file) {
// flow type
if (t.isTypeAlias(node.declaration)) return;
if (this.get("declaration").isTypeAlias()) return;
var nodes = [];
var i;

View File

@ -49,41 +49,42 @@ exports.Function = function (node, parent, scope, file) {
body.push(defNode);
};
for (var i = 0; i < node.params.length; i++) {
var param = node.params[i];
var params = this.get("params");
for (var i = 0; i < params.length; i++) {
var param = params[i];
if (!t.isAssignmentPattern(param)) {
if (!t.isRestElement(param)) {
if (!param.isAssignmentPattern()) {
if (!param.isRestElement()) {
lastNonDefaultParam = i + 1;
}
if (!t.isIdentifier(param)) {
scope.traverse(param, iifeVisitor, state);
if (!param.isIdentifier()) {
param.traverse(iifeVisitor, state);
}
if (file.transformers["es6.blockScopingTDZ"].canRun() && t.isIdentifier(param)) {
pushDefNode(param, t.identifier("undefined"), i);
if (file.transformers["es6.blockScopingTDZ"].canRun() && param.isIdentifier()) {
pushDefNode(param.node, t.identifier("undefined"), i);
}
continue;
}
var left = param.left;
var right = param.right;
var left = param.get("left");
var right = param.get("right");
var placeholder = scope.generateUidIdentifier("x");
placeholder._isDefaultPlaceholder = true;
node.params[i] = placeholder;
if (!state.iife) {
if (t.isIdentifier(right) && scope.hasOwnBinding(right.name)) {
if (right.isIdentifier() && scope.hasOwnBinding(right.node.name)) {
state.iife = true;
} else {
scope.traverse(right, iifeVisitor, state);
right.traverse(iifeVisitor, state);
}
}
pushDefNode(left, right, i);
pushDefNode(left.node, right.node, i);
}
// we need to cut off all trailing default parameters

View File

@ -15,7 +15,7 @@ var memberExpressionOptimisationVisitor = {
// to the wrong function
if (this.isFunctionDeclaration() || this.isFunctionExpression()) {
state.noOptimise = true;
scope.traverse(node, memberExpressionOptimisationVisitor, state);
this.traverse(memberExpressionOptimisationVisitor, state);
state.noOptimise = false;
return this.skip();
}
@ -88,7 +88,7 @@ exports.Function = function (node, parent, scope, file) {
name: rest.name
};
scope.traverse(node, memberExpressionOptimisationVisitor, state);
this.traverse(memberExpressionOptimisationVisitor, state);
// we only have shorthands and there's no other references
if (state.canOptimise && state.candidates.length) {
@ -127,16 +127,14 @@ exports.Function = function (node, parent, scope, file) {
);
}
scope.assignTypeGeneric(rest.name, "Array");
var loop = util.template("rest", {
ARGUMENTS: argsId,
ARRAY_KEY: arrKey,
ARRAY_LEN: arrLen,
START: start,
ARRAY: rest,
KEY: key,
LEN: len,
START: start,
ARRAY: rest,
KEY: key,
LEN: len
});
loop._blockHoist = node.params.length + 1;
node.body.body.unshift(loop);

View File

@ -81,7 +81,7 @@ export function CallExpression(node, parent, scope) {
var callee = node.callee;
if (t.isMemberExpression(callee)) {
if (this.get("callee").isMemberExpression()) {
var temp = scope.generateTempBasedOnNode(callee.object);
if (temp) {
callee.object = t.assignmentExpression("=", temp, callee.object);
@ -101,7 +101,7 @@ export function NewExpression(node, parent, scope, file) {
var args = node.arguments;
if (!hasSpread(args)) return;
var nativeType = t.isIdentifier(node.callee) && includes(t.NATIVE_TYPE_NAMES, node.callee.name);
var nativeType = this.get("callee").isIdentifier() && includes(t.NATIVE_TYPE_NAMES, node.callee.name);
var nodes = build(args, scope);

View File

@ -7,7 +7,7 @@ export function UnaryExpression(node, parent, scope, file) {
if (node.operator === "typeof") {
var call = t.callExpression(file.addHelper("typeof"), [node.argument]);
if (t.isIdentifier(node.argument)) {
if (this.get("argument").isIdentifier()) {
var undefLiteral = t.literal("undefined");
return t.conditionalExpression(
t.binaryExpression("===", t.unaryExpression("typeof", node.argument), undefLiteral),

View File

@ -6,7 +6,7 @@ import map from "lodash/collection/map";
import * as t from "../../../types";
exports.Function = function (node, parent, scope, file) {
var tailCall = new TailCallTransformer(node, scope, file);
var tailCall = new TailCallTransformer(this, scope, file);
tailCall.run();
};
@ -18,11 +18,11 @@ function returnBlock(expr) {
var firstPass = {
enter(node, parent, scope, state) {
if (this.isIfStatement()) {
if (t.isReturnStatement(node.alternate)) {
if (this.get("alternate").isReturnStatement()) {
t.ensureBlock(node, "alternate");
}
if (t.isReturnStatement(node.consequent)) {
if (this.get("consequent").isReturnStatement()) {
t.ensureBlock(node, "consequent");
}
} else if (this.isReturnStatement()) {
@ -85,17 +85,18 @@ var thirdPass = {
};
class TailCallTransformer {
constructor(node, scope, file) {
constructor(path, scope, file) {
this.hasTailRecursion = false;
this.needsArguments = false;
this.setsArguments = false;
this.needsThis = false;
this.ownerId = node.id;
this.ownerId = path.node.id;
this.vars = [];
this.scope = scope;
this.path = path;
this.file = file;
this.node = node;
this.node = path.node;
}
getArgumentsId() {
@ -142,8 +143,8 @@ class TailCallTransformer {
hasDeopt() {
// check if the ownerId has been reassigned, if it has then it's not safe to
// perform optimisations
var ownerIdInfo = this.scope.getBindingInfo(this.ownerId.name);
return ownerIdInfo && ownerIdInfo.reassigned;
var ownerIdInfo = this.scope.getBinding(this.ownerId.name);
return ownerIdInfo && !ownerIdInfo.constant;
}
run() {
@ -156,7 +157,7 @@ class TailCallTransformer {
if (!ownerId) return;
// traverse the function and look for tail recursion
scope.traverse(node, firstPass, this);
this.path.traverse(firstPass, this);
if (!this.hasTailRecursion) return;
@ -167,10 +168,10 @@ class TailCallTransformer {
//
scope.traverse(node, secondPass, this);
this.path.traverse(secondPass, this);
if (!this.needsThis || !this.needsArguments) {
scope.traverse(node, thirdPass, this);
this.path.traverse(thirdPass, this);
}
var body = t.ensureBlock(node).body;

View File

@ -9,11 +9,11 @@ export function check(node) {
}
export function TaggedTemplateExpression(node, parent, scope, file) {
var args = [];
var quasi = node.quasi;
var args = [];
var strings = [];
var raw = [];
var raw = [];
for (var i = 0; i < quasi.quasis.length; i++) {
var elem = quasi.quasis[i];

View File

@ -4,6 +4,7 @@ import * as util from "../../../util";
import * as t from "../../../types";
export var experimental = true;
export var optional = true;
var container = function (parent, call, ret, file) {
if (t.isExpressionStatement(parent) && !file.isConsequenceExpressionStatement(parent)) {

View File

@ -4,6 +4,7 @@ import * as util from "../../../util";
import * as t from "../../../types";
export var experimental = true;
export var optional = true;
export function ComprehensionExpression(node, parent, scope, file) {
var callback = array;

View File

@ -4,6 +4,7 @@ import build from "../../helpers/build-binary-assignment-operator-transformer";
import * as t from "../../../types";
export var experimental = true;
export var optional = true;
var MATH_POW = t.memberExpression(t.identifier("Math"), t.identifier("pow"));

View File

@ -3,6 +3,7 @@
import * as t from "../../../types";
export var experimental = true;
export var optional = true;
export function manipulateOptions(opts) {
if (opts.whitelist.length) opts.whitelist.push("es6.destructuring");

View File

@ -20,7 +20,6 @@ export default {
"playground.objectGetterMemoization": require("./playground/object-getter-memoization"),
reactCompat: require("./other/react-compat"),
flow: require("./other/flow"),
react: require("./other/react"),
_modules: require("./internal/modules"),
@ -45,6 +44,7 @@ export default {
// needs to be before `_aliasFunction` due to define property closure
"es6.properties.computed": require("./es6/properties.computed"),
"optimisation.es6.forOf": require("./optimisation/flow.for-of"),
"es6.forOf": require("./es6/for-of"),
"es6.regex.sticky": require("./es6/regex.sticky"),
@ -110,5 +110,6 @@ export default {
"utility.inlineExpressions": require("./utility/inline-expressions"),
"utility.deadCodeElimination": require("./utility/dead-code-elimination"),
flow: require("./other/flow"),
_cleanUp: require("./internal/cleanup")
};

View File

@ -34,13 +34,13 @@ var functionVisitor = {
}
// traverse all child nodes of this function and find `arguments` and `this`
scope.traverse(node, functionChildrenVisitor, state);
this.traverse(functionChildrenVisitor, state);
return this.skip();
}
};
var go = function (getBody, node, scope) {
function aliasFunction(getBody, path, scope) {
var argumentsId;
var thisId;
@ -56,7 +56,7 @@ var go = function (getBody, node, scope) {
// traverse the function and find all alias functions so we can alias
// `arguments` and `this` if necessary
scope.traverse(node, functionVisitor, state);
path.traverse(functionVisitor, state);
var body;
@ -77,16 +77,16 @@ var go = function (getBody, node, scope) {
};
export function Program(node, parent, scope) {
go(function () {
aliasFunction(function () {
return node.body;
}, node, scope);
}, this, scope);
};
export function FunctionDeclaration(node, parent, scope) {
go(function () {
aliasFunction(function () {
t.ensureBlock(node);
return node.body.body;
}, node, scope);
}, this, scope);
}
export { FunctionDeclaration as FunctionExpression };

View File

@ -50,7 +50,12 @@ export function ExportDeclaration(node, parent, scope) {
return [getDeclar(), node];
}
} else {
if (t.isFunctionDeclaration(declar)) {
if (t.isClassDeclaration(declar)) {
// export class Foo {}
node.specifiers = [t.importSpecifier(declar.id, declar.id)];
node.declaration = null;
return [getDeclar(), node];
} else if (t.isFunctionDeclaration(declar)) {
// export function Foo() {}
node.specifiers = [t.importSpecifier(declar.id, declar.id)];
node.declaration = null;
@ -59,7 +64,7 @@ export function ExportDeclaration(node, parent, scope) {
} else if (t.isVariableDeclaration(declar)) {
// export var foo = "bar";
var specifiers = [];
var bindings = t.getBindingIdentifiers(declar);
var bindings = this.get("declaration").getBindingIdentifiers();
for (var key in bindings) {
var id = bindings[key];
specifiers.push(t.exportSpecifier(id, id));

View File

@ -0,0 +1,11 @@
import { _ForOfStatementArray } from "../es6/for-of";
import * as t from "../../../types";
export var check = t.isForOfStatement;
export var optional = true;
export function ForOfStatement(node, parent, scope, file) {
if (this.get("right").isTypeGeneric("Array")) {
return _ForOfStatementArray.call(this, node, scope, file);
}
}

View File

@ -29,5 +29,5 @@ export function ImportDeclaration(node) {
}
export function ExportDeclaration(node) {
if (t.isTypeAlias(node.declaration)) this.remove();
if (this.get("declaration").isTypeAlias()) this.remove();
}

View File

@ -4,7 +4,7 @@ import core from "core-js/library";
import has from "lodash/object/has";
import * as t from "../../../types";
var isSymboliterator = t.buildMatchMemberExpression("Symbol.iterator");
var isSymbolIterator = t.buildMatchMemberExpression("Symbol.iterator");
var coreHas = function (node) {
return node.name !== "_" && has(core, node.name);
@ -47,7 +47,7 @@ var astVisitor = {
if (!callee.computed) return false;
prop = callee.property;
if (!isSymboliterator(prop)) return false;
if (!isSymbolIterator(prop)) return false;
return util.template("corejs-iterator", {
CORE_ID: file.get("coreIdentifier"),
@ -59,7 +59,7 @@ var astVisitor = {
if (node.operator !== "in") return;
var left = node.left;
if (!isSymboliterator(left)) return;
if (!isSymbolIterator(left)) return;
return util.template("corejs-is-iterator", {
CORE_ID: file.get("coreIdentifier"),
@ -76,7 +76,7 @@ export function manipulateOptions(opts) {
}
export function Program(node, parent, scope, file) {
scope.traverse(node, astVisitor, file);
this.traverse(astVisitor, file);
}
export function pre(file) {

View File

@ -3,6 +3,7 @@ import build from "../../helpers/build-conditional-assignment-operator-transform
import * as t from "../../../types";
export var playground = true;
export var optional = true;
build(exports, {
is(node, file) {

View File

@ -2,6 +2,7 @@ import build from "../../helpers/build-conditional-assignment-operator-transform
import * as t from "../../../types";
export var playground = true;
export var optional = true;
build(exports, {
is(node) {

View File

@ -1,9 +1,10 @@
import * as t from "../../../types";
export var playground = true;
export var optional = true;
export function BindMemberExpression(node, parent, scope) {
console.error("Method binding is deprecated and will be removed in 5.0.0");
export function BindMemberExpression(node, parent, scope, file) {
file.log.deprecate("Method binding is deprecated and will be removed in 5.0.0");
var object = node.object;
var prop = node.property;
@ -26,8 +27,8 @@ export function BindMemberExpression(node, parent, scope) {
}
}
export function BindFunctionExpression(node, parent, scope) {
console.error("Method binding is deprecated and will be removed in 5.0.0");
export function BindFunctionExpression(node, parent, scope, file) {
file.log.deprecate("Method binding is deprecated and will be removed in 5.0.0");
var buildCall = function (args) {
var param = scope.generateUidIdentifier("val");

View File

@ -1,6 +1,7 @@
import * as t from "../../../types";
export var playground = true;
export var optional = true;
var visitor = {
enter(node, parent, scope, state) {
@ -20,10 +21,9 @@ export function MethodDefinition(node, parent, scope, file) {
if (node.kind !== "memo") return;
node.kind = "get";
console.error("Object getter memoization is deprecated and will be removed in 5.0.0");
file.log.deprecate("Object getter memoization is deprecated and will be removed in 5.0.0");
var value = node.value;
t.ensureBlock(value);
t.ensureBlock(node.value);
var key = node.key;
@ -36,7 +36,7 @@ export function MethodDefinition(node, parent, scope, file) {
file: file
};
scope.traverse(value, visitor, state);
this.get("value").traverse(visitor, state);
return node;
}

View File

@ -20,7 +20,7 @@ function toStatements(node) {
export var optional = true;
export function ConditionalExpression(node, parent, scope) {
var evaluateTest = t.evaluateTruthy(node.test, scope);
var evaluateTest = this.get("test").evaluateTruthy();
if (evaluateTest === true) {
return node.consequent;
} else if (evaluateTest === false) {
@ -32,9 +32,8 @@ export var IfStatement = {
exit(node, parent, scope) {
var consequent = node.consequent;
var alternate = node.alternate;
var test = node.test;
var evaluateTest = t.evaluateTruthy(test, scope);
var evaluateTest = this.get("test").evaluateTruthy();
// we can check if a test will be truthy 100% and if so then we can inline
// the consequent and completely ignore the alternate

View File

@ -6,7 +6,7 @@ var match = t.buildMatchMemberExpression("process.env");
export function MemberExpression(node) {
if (match(node.object)) {
var key = t.toComputedKey(node, node.property);
var key = this.toComputedKey();
if (t.isLiteral(key)) {
return t.valueToNode(process.env[key.value]);
}

View File

@ -3,7 +3,7 @@ import * as t from "../../../types";
export var optional = true;
export function Expression(node, parent, scope) {
var res = t.evaluate(node, scope);
var res = this.evaluate();
if (res.confident) return t.valueToNode(res.value);
}

View File

@ -1,11 +1,9 @@
import * as t from "../../../types";
var isConsole = t.buildMatchMemberExpression("console", true);
export var optional = true;
export function CallExpression(node, parent) {
if (isConsole(node.callee)) {
if (this.get("callee").matchesPattern("console", true)) {
if (t.isExpressionStatement(parent)) {
this.parentPath.remove();
} else {

View File

@ -14,7 +14,7 @@ function check(source, file) {
}
export function CallExpression(node, parent, scope, file) {
if (t.isIdentifier(node.callee, { name: "require" }) && node.arguments.length === 1) {
if (this.get("callee").isIdentifier({ name: "require" }) && node.arguments.length === 1) {
check(node.arguments[0], file);
}
}

View File

@ -0,0 +1,68 @@
import * as t from "../types";
export default class Binding {
constructor({ identifier, scope, path, kind }) {
this.identifier = identifier;
this.constant = true;
this.scope = scope;
this.path = path;
this.kind = kind;
}
/**
* Description
*/
setTypeAnnotation() {
var typeInfo = this.path.getTypeAnnotation();
this.typeAnnotationInferred = typeInfo.inferred;
this.typeAnnotation = typeInfo.annotation;
}
/**
* Description
*/
isTypeGeneric(): boolean {
return this.path.isTypeGeneric(...arguments);
}
/**
* Description
*/
assignTypeGeneric(type: Object, params?) {
var typeParams = null;
if (params) params = t.typeParameterInstantiation(params);
this.assignType(t.genericTypeAnnotation(t.identifier(type), typeParams));
}
/**
* Description
*/
assignType(type: Object) {
this.typeAnnotation = type;
}
/**
* Description
*/
reassign() {
this.constant = false;
if (this.typeAnnotationInferred) {
// destroy the inferred typeAnnotation
this.typeAnnotation = null;
}
}
/**
* Description
*/
isCompatibleWithType(newType): boolean {
return false;
}
}

View File

@ -1,10 +1,8 @@
module.exports = traverse;
import TraversalContext from "./context";
import includes from "lodash/collection/includes";
import * as t from "../types";
function traverse(parent, opts, scope, state) {
export default function traverse(parent, opts, scope, state, parentPath) {
if (!parent) return;
if (!opts.noScope && !scope) {
@ -20,10 +18,10 @@ function traverse(parent, opts, scope, state) {
// array of nodes
if (Array.isArray(parent)) {
for (var i = 0; i < parent.length; i++) {
traverse.node(parent[i], opts, scope, state);
traverse.node(parent[i], opts, scope, state, parentPath);
}
} else {
traverse.node(parent, opts, scope, state);
traverse.node(parent, opts, scope, state, parentPath);
}
}
@ -42,7 +40,6 @@ traverse.node = function (node, opts, scope, state, parentPath) {
function clearNode(node) {
node._declarations = null;
node.extendedRange = null;
node._scopeInfo = null;
node._paths = null;
node.tokens = null;
node.range = null;

View File

@ -1,226 +0,0 @@
import traverse from "./index";
import includes from "lodash/collection/includes";
import Scope from "./scope";
import * as t from "../types";
export default class TraversalPath {
constructor(parent, container) {
this.container = container;
this.parent = parent;
this.data = {};
}
static get(parentPath, context, parent, container, key) {
var targetNode = container[key];
var paths = container._paths ||= [];
var path;
for (var i = 0; i < paths.length; i++) {
var pathCheck = paths[i];
if (pathCheck.node === targetNode) {
path = pathCheck;
break;
}
}
if (!path) {
path = new TraversalPath(parent, container);
paths.push(path);
}
path.setContext(parentPath, context, key);
return path;
}
static getScope(node, parent, scope) {
var ourScope = scope;
// we're entering a new scope so let's construct it!
if (t.isScope(node, parent)) {
ourScope = new Scope(node, parent, scope);
}
return ourScope;
}
insertBefore(node) {
}
insertAfter(node) {
}
setData(key, val) {
return this.data[key] = val;
}
getData(key) {
return this.data[key];
}
setScope() {
this.scope = TraversalPath.getScope(this.node, this.parent, this.context.scope);
}
setContext(parentPath, context, key) {
this.shouldSkip = false;
this.shouldStop = false;
this.parentPath = parentPath || this.parentPath;
this.context = context;
this.state = context.state;
this.opts = context.opts;
this.key = key;
this.setScope();
}
remove() {
this._refresh(this.node, []);
this.container[this.key] = null;
this.flatten();
}
skip() {
this.shouldSkip = true;
}
stop() {
this.shouldStop = true;
this.shouldSkip = true;
}
flatten() {
this.context.flatten();
}
_refresh(oldNode, newNodes) {
// todo
}
refresh() {
var node = this.node;
this._refresh(node, [node]);
}
get node() {
return this.container[this.key];
}
set node(replacement) {
if (!replacement) return this.remove();
var oldNode = this.node;
var isArray = Array.isArray(replacement);
var replacements = isArray ? replacement : [replacement];
// inherit comments from original node to the first replacement node
var inheritTo = replacements[0];
if (inheritTo) t.inheritsComments(inheritTo, oldNode);
// replace the node
this.container[this.key] = replacement;
// potentially create new scope
this.setScope();
// refresh scope with new/removed bindings
this._refresh(oldNode, replacements);
var file = this.scope && this.scope.file;
if (file) {
for (var i = 0; i < replacements.length; i++) {
file.checkNode(replacements[i], this.scope);
}
}
// we're replacing a statement or block node with an array of statements so we better
// ensure that it's a block
if (isArray) {
if (includes(t.STATEMENT_OR_BLOCK_KEYS, this.key) && !t.isBlockStatement(this.container)) {
t.ensureBlock(this.container, this.key);
}
this.flatten();
// TODO: duplicate internal path metadata across the new node paths
}
}
call(key) {
var node = this.node;
if (!node) return;
var opts = this.opts;
var fn = opts[key] || opts;
if (opts[node.type]) fn = opts[node.type][key] || fn;
var replacement = fn.call(this, node, this.parent, this.scope, this.state);
if (replacement) {
this.node = replacement;
}
}
isBlacklisted() {
var blacklist = this.opts.blacklist;
return blacklist && blacklist.indexOf(this.node.type) > -1;
}
visit() {
if (this.isBlacklisted()) return false;
this.call("enter");
if (this.shouldSkip) {
return this.shouldStop;
}
var node = this.node;
var opts = this.opts;
if (node) {
if (Array.isArray(node)) {
// traverse over these replacement nodes we purposely don't call exitNode
// as the original node has been destroyed
for (var i = 0; i < node.length; i++) {
traverse.node(node[i], opts, this.scope, this.state, this);
}
} else {
traverse.node(node, opts, this.scope, this.state, this);
this.call("exit");
}
}
return this.shouldStop;
}
get(key) {
return TraversalPath.get(this, this.context, this.node, this.node, key);
}
isReferencedIdentifier(opts) {
return t.isReferencedIdentifier(this.node, this.parent, opts);
}
isReferenced() {
return t.isReferenced(this.node, this.parent);
}
isScope() {
return t.isScope(this.node, this.parent);
}
getBindingIdentifiers() {
return t.getBindingIdentifiers(this.node);
}
}
for (var i = 0; i < t.TYPES.length; i++) {
let type = t.TYPES[i];
let typeKey = `is${type}`;
TraversalPath.prototype[typeKey] = function (opts) {
return t[typeKey](this.node, opts);
};
}

View File

@ -0,0 +1,24 @@
import * as t from "../../types";
/**
* Description
*/
export function toComputedKey(): Object {
var node = this.node;
var key;
if (this.isMemberExpression()) {
key = node.property;
} else if (this.isProperty()) {
key = node.key;
} else {
throw new ReferenceError("todo");
}
if (!node.computed) {
if (t.isIdentifier(key)) key = t.literal(key.name);
}
return key;
}

View File

@ -1,5 +1,3 @@
import * as t from "./index";
/**
* Walk the input `node` and statically evaluate if it's truthy.
*
@ -18,15 +16,15 @@ import * as t from "./index";
*
*/
export function evaluateTruthy(node: Object, scope: Scope): boolean {
var res = evaluate(node, scope);
export function evaluateTruthy(): boolean {
var res = this.evaluate();
if (res.confident) return !!res.value;
}
/**
* Walk the input `node` and statically evaluate it.
*
* Returns an pbject in the form `{ confident, value }`. `confident` indicates
* Returns an object in the form `{ confident, value }`. `confident` indicates
* whether or not we had to drop out of evaluating the expression because of
* hitting an unknown node that we couldn't confidently find the value of.
*
@ -38,24 +36,27 @@ export function evaluateTruthy(node: Object, scope: Scope): boolean {
*
*/
export function evaluate(node: Object, scope: Scope): { confident: boolean; value: any } {
export function evaluate(): { confident: boolean; value: any } {
var confident = true;
var value = evaluate(node);
var value = evaluate(this);
if (!confident) value = undefined;
return {
confident: confident,
value: value
};
function evaluate(node) {
function evaluate(path) {
if (!confident) return;
if (t.isSequenceExpression(node)) {
return evaluate(node.expressions[node.expressions.length - 1]);
var node = path.node;
if (path.isSequenceExpression()) {
var exprs = path.get("expressions");
return evaluate(exprs[exprs.length - 1]);
}
if (t.isLiteral(node)) {
if (path.isLiteral()) {
if (node.regex && node.value === null) {
// we have a regex and we can't represent it natively
} else {
@ -63,24 +64,29 @@ export function evaluate(node: Object, scope: Scope): { confident: boolean; valu
}
}
if (t.isConditionalExpression(node)) {
if (evaluate(node.test)) {
return evaluate(node.consequent);
if (path.isConditionalExpression()) {
if (evaluate(path.get("test"))) {
return evaluate(path.get("consequent"));
} else {
return evaluate(node.alternate);
return evaluate(path.get("alternate"));
}
}
if (t.isIdentifier(node)) {
if (node.name === "undefined") {
return undefined;
if (path.isIdentifier({ name: "undefined" })) {
return undefined;
}
if (path.isIdentifier() || path.isMemberExpression()) {
path = path.resolve();
if (path) {
return evaluate(path);
} else {
return evaluate(scope.getImmutableBindingValue(node.name));
return confident = false;
}
}
if (t.isUnaryExpression(node, { prefix: true })) {
var arg = evaluate(node.argument);
if (path.isUnaryExpression({ prefix: true })) {
var arg = evaluate(path.get("argument"));
switch (node.operator) {
case "void": return undefined;
case "!": return !arg;
@ -89,13 +95,13 @@ export function evaluate(node: Object, scope: Scope): { confident: boolean; valu
}
}
if (t.isArrayExpression(node) || t.isObjectExpression(node)) {
if (path.isArrayExpression() || path.isObjectExpression()) {
// we could evaluate these but it's probably impractical and not very useful
}
if (t.isLogicalExpression(node)) {
let left = evaluate(node.left);
let right = evaluate(node.right);
if (path.isLogicalExpression()) {
let left = evaluate(path.get("left"));
let right = evaluate(path.get("right"));
switch (node.operator) {
case "||": return left || right;
@ -103,9 +109,9 @@ export function evaluate(node: Object, scope: Scope): { confident: boolean; valu
}
}
if (t.isBinaryExpression(node)) {
let left = evaluate(node.left);
let right = evaluate(node.right);
if (path.isBinaryExpression()) {
let left = evaluate(path.get("left"));
let right = evaluate(path.get("right"));
switch (node.operator) {
case "-": return left - right;

View File

@ -0,0 +1,464 @@
import isBoolean from "lodash/lang/isBoolean";
import isNumber from "lodash/lang/isNumber";
import isRegExp from "lodash/lang/isRegExp";
import isString from "lodash/lang/isString";
import traverse from "../index";
import includes from "lodash/collection/includes";
import assign from "lodash/object/assign";
import Scope from "../scope";
import * as t from "../../types";
export default class TraversalPath {
constructor(parent, container) {
this.container = container;
this.parent = parent;
this.data = {};
}
static get(parentPath: TraversalPath, context?: TraversalContext, parent, container, key, file?: File) {
var targetNode = container[key];
var paths = container._paths ||= [];
var path;
for (var i = 0; i < paths.length; i++) {
var pathCheck = paths[i];
if (pathCheck.node === targetNode) {
path = pathCheck;
break;
}
}
if (!path) {
path = new TraversalPath(parent, container);
paths.push(path);
}
path.setContext(parentPath, context, key, file);
return path;
}
static getScope(path: TraversalPath, scope: Scope, file?: File) {
var ourScope = scope;
// we're entering a new scope so let's construct it!
if (path.isScope()) {
ourScope = new Scope(path, scope, file);
}
return ourScope;
}
insertBefore(node) {
}
insertAfter(node) {
}
setData(key, val) {
return this.data[key] = val;
}
getData(key, def) {
var val = this.data[key];
if (!val && def) val = this.data[key] = def;
return val;
}
setScope(file?) {
this.scope = TraversalPath.getScope(this, this.context && this.context.scope, file);
}
setContext(parentPath, context, key, file?) {
this.shouldSkip = false;
this.shouldStop = false;
this.parentPath = parentPath || this.parentPath;
this.key = key;
if (context) {
this.context = context;
this.state = context.state;
this.opts = context.opts;
}
this.setScope(file);
}
remove() {
this._refresh(this.node, []);
this.container[this.key] = null;
this.flatten();
}
skip() {
this.shouldSkip = true;
}
stop() {
this.shouldStop = true;
this.shouldSkip = true;
}
flatten() {
this.context.flatten();
}
_refresh(oldNode, newNodes) {
// todo
}
refresh() {
var node = this.node;
this._refresh(node, [node]);
}
errorWithNode(msg, Error = SyntaxError) {
var loc = this.node.loc.start;
var err = new Error(`Line ${loc.line}: ${msg}`);
err.loc = loc;
return err;
}
get node() {
return this.container[this.key];
}
set node(replacement) {
if (!replacement) return this.remove();
var oldNode = this.node;
var isArray = Array.isArray(replacement);
var replacements = isArray ? replacement : [replacement];
// inherit comments from original node to the first replacement node
var inheritTo = replacements[0];
if (inheritTo) t.inheritsComments(inheritTo, oldNode);
// replace the node
this.container[this.key] = replacement;
// potentially create new scope
this.setScope();
var file = this.scope && this.scope.file;
if (file) {
for (var i = 0; i < replacements.length; i++) {
file.checkNode(replacements[i], this.scope);
}
}
// we're replacing a statement or block node with an array of statements so we better
// ensure that it's a block
if (isArray) {
if (includes(t.STATEMENT_OR_BLOCK_KEYS, this.key) && !t.isBlockStatement(this.container)) {
t.ensureBlock(this.container, this.key);
}
this.flatten();
// TODO: duplicate internal path metadata across the new node paths
}
}
call(key) {
var node = this.node;
if (!node) return;
var opts = this.opts;
var fn = opts[key] || opts;
if (opts[node.type]) fn = opts[node.type][key] || fn;
var replacement = fn.call(this, node, this.parent, this.scope, this.state);
if (replacement) {
this.node = replacement;
}
}
isBlacklisted(): boolean {
var blacklist = this.opts.blacklist;
return blacklist && blacklist.indexOf(this.node.type) > -1;
}
visit(): boolean {
if (this.isBlacklisted()) return false;
this.call("enter");
if (this.shouldSkip) {
return this.shouldStop;
}
var node = this.node;
var opts = this.opts;
if (node) {
if (Array.isArray(node)) {
// traverse over these replacement nodes we purposely don't call exitNode
// as the original node has been destroyed
for (var i = 0; i < node.length; i++) {
traverse.node(node[i], opts, this.scope, this.state, this);
}
} else {
traverse.node(node, opts, this.scope, this.state, this);
this.call("exit");
}
}
return this.shouldStop;
}
get(key) {
var node = this.node;
var container = node[key];
if (Array.isArray(container)) {
return container.map((_, i) => {
return TraversalPath.get(this, this.context, node, container, i);
});
} else {
return TraversalPath.get(this, this.context, node, node, key);
}
}
has(key): boolean {
return !!this.node[key];
}
is(key): boolean {
return this.has(key);
}
isnt(key): boolean {
return !this.has(key);
}
getTypeAnnotation(): {
inferred: boolean;
annotation: ?Object;
} {
if (this.typeInfo) {
return this.typeInfo;
}
var info = this.typeInfo = {
inferred: false,
annotation: null
};
var type = this.node.typeAnnotation;
if (!type) {
info.inferred = true;
type = this.inferType(this);
}
if (type) {
if (t.isTypeAnnotation(type)) type = type.typeAnnotation;
info.annotation = type;
}
return info;
}
resolve(): ?TraversalPath {
if (this.isVariableDeclarator()) {
if (this.get("id").isIdentifier()) {
return this.get("init").resolve();
} else {
// otherwise it's a request for a destructuring declarator and i'm not
// ready to resolve those just yet
}
} else if (this.isIdentifier()) {
var binding = this.scope.getBinding(this.node.name);
if (!binding || !binding.constant) return;
if (binding.path === this) {
return this;
} else {
return binding.path.resolve();
}
} else if (this.isMemberExpression()) {
// this is dangerous, as non-direct target assignments will mutate it's state
// making this resolution inaccurate
var targetKey = this.toComputedKey();
if (!t.isLiteral(targetKey)) return;
var targetName = targetKey.value;
var target = this.get("object").resolve();
if (!target || !target.isObjectExpression()) return;
var props = target.get("properties");
for (var i = 0; i < props.length; i++) {
var prop = props[i];
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 ||= key.isLiteral({ value: targetName });
if (match) return prop.get("value");
}
} else {
return this;
}
}
inferType(path: TraversalPath): ?Object {
path = path.resolve();
if (!path) return;
if (path.isRestElement() || path.parentPath.isRestElement() || path.isArrayExpression()) {
return t.genericTypeAnnotation(t.identifier("Array"));
}
if (path.parentPath.isTypeCastExpression()) {
return path.parentPath.node.typeAnnotation;
}
if (path.isTypeCastExpression()) {
return path.node.typeAnnotation;
}
if (path.isObjectExpression()) {
return t.genericTypeAnnotation(t.identifier("Object"));
}
if (path.isFunction()) {
return t.identifier("Function");
}
if (path.isLiteral()) {
var value = path.node.value;
if (isString(value)) return t.stringTypeAnnotation();
if (isNumber(value)) return t.numberTypeAnnotation();
if (isBoolean(value)) return t.booleanTypeAnnotation();
}
if (path.isCallExpression()) {
var callee = path.get("callee").resolve();
if (callee && callee.isFunction()) return callee.node.returnType;
}
}
isScope(): boolean {
return t.isScope(this.node, this.parent);
}
isReferencedIdentifier(opts): boolean {
return t.isReferencedIdentifier(this.node, this.parent, opts);
}
isReferenced(): boolean {
return t.isReferenced(this.node, this.parent);
}
isBlockScoped(): boolean {
return t.isBlockScoped(this.node);
}
isVar(): boolean {
return t.isVar(this.node);
}
isScope(): boolean {
return t.isScope(this.node, this.parent);
}
isTypeGeneric(genericName: string, opts = {}): boolean {
var typeInfo = this.getTypeAnnotation();
var type = typeInfo.annotation;
if (!type) return false;
if (type.inferred && opts.inference === false) {
return false;
}
if (!t.isGenericTypeAnnotation(type) || !t.isIdentifier(type.id, { name: genericName })) {
return false;
}
if (opts.requireTypeParameters && !type.typeParameters) {
return false;
}
return true;
}
getBindingIdentifiers() {
return t.getBindingIdentifiers(this.node);
}
traverse(opts, state) {
traverse(this.node, opts, this.scope, state, this);
}
/**
* Match the current node if it matches the provided `pattern`.
*
* For example, given the match `React.createClass` it would match the
* parsed nodes of `React.createClass` and `React["createClass"]`.
*/
matchesPattern(pattern: string, allowPartial?: boolean): boolean {
var parts = pattern.split(".");
// not a member expression
if (!this.isMemberExpression()) return false;
var search = [this.node];
var i = 0;
while (search.length) {
var node = search.shift();
if (allowPartial && i === parts.length) {
return true;
}
if (t.isIdentifier(node)) {
// this part doesn't match
if (parts[i] !== node.name) return false;
} else if (t.isLiteral(node)) {
// this part doesn't match
if (parts[i] !== node.value) return false;
} else if (t.isMemberExpression(node)) {
if (node.computed && !t.isLiteral(node.property)) {
// we can't deal with this
return false;
} else {
search.push(node.object);
search.push(node.property);
continue;
}
} else {
// we can't deal with this
return false;
}
// too many parts
if (++i > parts.length) {
return false;
}
}
return true;
}
}
assign(TraversalPath.prototype, require("./evaluation"));
assign(TraversalPath.prototype, require("./conversion"));
for (var i = 0; i < t.TYPES.length; i++) {
let type = t.TYPES[i];
let typeKey = `is${type}`;
TraversalPath.prototype[typeKey] = function (opts) {
return t[typeKey](this.node, opts);
};
}

View File

@ -2,6 +2,7 @@ import includes from "lodash/collection/includes";
import traverse from "./index";
import defaults from "lodash/object/defaults";
import * as messages from "../messages";
import Binding from "./binding";
import globals from "globals";
import flatten from "lodash/array/flatten";
import extend from "lodash/object/extend";
@ -12,27 +13,27 @@ import * as t from "../types";
var functionVariableVisitor = {
enter(node, parent, scope, state) {
if (t.isFor(node)) {
each(t.FOR_INIT_KEYS, function (key) {
var declar = node[key];
if (t.isVar(declar)) state.scope.registerBinding("var", declar);
each(t.FOR_INIT_KEYS, (key) => {
var declar = this.get(key);
if (declar.isVar()) state.scope.registerBinding("var", declar);
});
}
// this block is a function so we'll stop since none of the variables
// declared within are accessible
if (t.isFunction(node)) return this.skip();
if (this.isFunction()) return this.skip();
// function identifier doesn't belong to this scope
if (state.blockId && node === state.blockId) return;
// delegate block scope handling to the `blockVariableVisitor`
if (t.isBlockScoped(node)) return;
if (this.isBlockScoped()) return;
// this will be hit again once we traverse into it after this iteration
if (t.isExportDeclaration(node) && t.isDeclaration(node.declaration)) return;
if (this.isExportDeclaration() && t.isDeclaration(node.declaration)) return;
// we've ran into a declaration!
if (t.isDeclaration(node)) state.scope.registerDeclaration(node);
if (this.isDeclaration()) state.scope.registerDeclaration(this);
}
};
@ -42,16 +43,20 @@ var programReferenceVisitor = {
state.addGlobal(node);
} else if (t.isLabeledStatement(node)) {
state.addGlobal(node);
} else if (t.isAssignmentExpression(node) || t.isUpdateExpression(node) || (t.isUnaryExpression(node) && node.operator === "delete")) {
scope.registerBindingReassignment(node);
} else if (t.isAssignmentExpression(node)) {
scope.registerConstantViolation(this.get("left"), this.get("right"));
} else if (t.isUpdateExpression(node)) {
scope.registerConstantViolation(this.get("argument"), null);
} else if (t.isUnaryExpression(node) && node.operator === "delete") {
scope.registerConstantViolation(this.get("left"), null);
}
}
};
var blockVariableVisitor = {
enter(node, parent, scope, state) {
if (t.isFunctionDeclaration(node) || t.isBlockScoped(node)) {
state.registerDeclaration(node);
if (this.isFunctionDeclaration() || this.isBlockScoped()) {
state.registerDeclaration(this);
} else if (t.isScope(node, parent)) {
this.skip();
}
@ -65,12 +70,20 @@ export default class Scope {
* within.
*/
constructor(block: Object, parentBlock: Object, parent?: Scope, file?: File) {
constructor(path: TraversalPath, parent?: Scope, file?: File) {
var cached = path.getData("scope");
if (cached) {
return cached;
} else {
//path.setData("scope", this);
}
this.parent = parent;
this.file = parent ? parent.file : file;
this.parentBlock = parentBlock;
this.block = block;
this.parentBlock = path.parent;
this.block = path.node;
this.path = path;
this.crawl();
}
@ -104,9 +117,7 @@ export default class Scope {
*/
generateUidIdentifier(name: string) {
var id = t.identifier(this.generateUid(name));
this.getFunctionParent().registerBinding("uid", id);
return id;
return t.identifier(this.generateUid(name));
}
/**
@ -121,7 +132,8 @@ export default class Scope {
do {
uid = this._generateUid(name, i);
i++;
} while (this.hasBinding(uid) || this.hasGlobal(uid));
} while (this.hasBinding(uid) || this.hasGlobal(uid) || this.hasUid(uid));
this.getFunctionParent().uids[uid] = true;
return uid;
}
@ -131,6 +143,19 @@ export default class Scope {
return `_${id}`;
}
/**
* Description
*/
hasUid(name): boolean {
var scope = this;
do {
if (scope.uids[name]) return true;
scope = scope.parent;
} while (scope);
return false;
}
/*
* Description
*/
@ -209,7 +234,7 @@ export default class Scope {
rename(oldName: string, newName: string) {
newName ||= this.generateUidIdentifier(oldName).name;
var info = this.getBindingInfo(oldName);
var info = this.getBinding(oldName);
if (!info) return;
var binding = info.identifier;
@ -220,7 +245,7 @@ export default class Scope {
if (t.isReferencedIdentifier(node, parent) && node.name === oldName) {
node.name = newName;
} else if (t.isDeclaration(node)) {
var ids = t.getBindingIdentifiers(node);
var ids = this.getBindingIdentifiers();
for (var name in ids) {
if (name === oldName) ids[name].name = newName;
}
@ -238,102 +263,6 @@ export default class Scope {
binding.name = newName;
}
/**
* Description
*/
inferType(node: Object) {
var target;
if (t.isVariableDeclarator(node)) {
target = node.init;
}
if (t.isArrayExpression(target)) {
return t.genericTypeAnnotation(t.identifier("Array"));
}
if (t.isObjectExpression(target)) {
return;
}
if (t.isLiteral(target)) {
return;
}
if (t.isCallExpression(target) && t.isIdentifier(target.callee)) {
var funcInfo = this.getBindingInfo(target.callee.name);
if (funcInfo) {
var funcNode = funcInfo.node;
return !funcInfo.reassigned && t.isFunction(funcNode) && node.returnType;
}
}
if (t.isIdentifier(target)) {
return;
}
}
/**
* Description
*/
isTypeGeneric(name: string, genericName: string) {
var info = this.getBindingInfo(name);
if (!info) return false;
var type = info.typeAnnotation;
return t.isGenericTypeAnnotation(type) && t.isIdentifier(type.id, { name: genericName });
}
/**
* Description
*/
assignTypeGeneric(name: string, type: Object) {
this.assignType(name, t.genericTypeAnnotation(t.identifier(type)));
}
/**
* Description
*/
assignType(name: string, type: Object) {
var info = this.getBindingInfo(name);
if (!info) return;
info.typeAnnotation = type;
}
/**
* Description
*/
getTypeAnnotation(id: Object, node: Object): Object {
var info = {
annotation: null,
inferred: false
};
var type;
if (id.typeAnnotation) {
type = id.typeAnnotation;
}
if (!type) {
info.inferred = true;
type = this.inferType(node);
}
if (type) {
if (t.isTypeAnnotation(type)) type = type.typeAnnotation;
info.annotation = type;
}
return info;
}
/**
* Description
*/
@ -341,8 +270,9 @@ export default class Scope {
toArray(node: Object, i?: number) {
var file = this.file;
if (t.isIdentifier(node) && this.isTypeGeneric(node.name, "Array")) {
return node;
if (t.isIdentifier(node)) {
var binding = this.getBinding(node.name);
if (binding && binding.isTypeGeneric("Array", { inference: false })) return node;
}
if (t.isArrayExpression(node)) {
@ -368,33 +298,21 @@ export default class Scope {
* Description
*/
refreshDeclaration(node: Object) {
if (t.isBlockScoped(node)) {
this.getBlockParent().registerDeclaration(node);
} else if (t.isVariableDeclaration(node, { kind: "var" })) {
this.getFunctionParent().registerDeclaration(node);
} else if (node === this.block) {
this.recrawl();
}
}
/**
* Description
*/
registerDeclaration(node: Object) {
registerDeclaration(path: TraversalPath) {
var node = path.node;
if (t.isFunctionDeclaration(node)) {
this.registerBinding("hoisted", node);
this.registerBinding("hoisted", path);
} else if (t.isVariableDeclaration(node)) {
for (var i = 0; i < node.declarations.length; i++) {
this.registerBinding(node.kind, node.declarations[i]);
var declarations = path.get("declarations");
for (var i = 0; i < declarations.length; i++) {
this.registerBinding(node.kind, declarations[i]);
}
} else if (t.isClassDeclaration(node)) {
this.registerBinding("let", node);
this.registerBinding("let", path);
} else if (t.isImportDeclaration(node) || t.isExportDeclaration(node)) {
this.registerBinding("module", node);
this.registerBinding("module", path);
} else {
this.registerBinding("unknown", node);
this.registerBinding("unknown", path);
}
}
@ -402,18 +320,16 @@ export default class Scope {
* Description
*/
registerBindingReassignment(node: Object) {
var ids = t.getBindingIdentifiers(node);
registerConstantViolation(left: TraversalPath, right: TraversalPath) {
var ids = left.getBindingIdentifiers();
for (var name in ids) {
var info = this.getBindingInfo(name);
if (info) {
info.reassigned = true;
if (info.typeAnnotationInferred) {
// destroy the inferred typeAnnotation
info.typeAnnotation = null;
}
var binding = this.getBinding(name);
if (!binding) continue;
if (right) {
var rightType = right.typeAnnotation;
if (rightType && binding.isCompatibleWithType(rightType)) continue;
}
binding.reassign();
}
}
@ -421,27 +337,22 @@ export default class Scope {
* Description
*/
registerBinding(kind: string, node: Object) {
registerBinding(kind: string, path: TraversalPath) {
if (!kind) throw new ReferenceError("no `kind`");
var ids = t.getBindingIdentifiers(node);
var ids = path.getBindingIdentifiers();
for (var name in ids) {
var id = ids[name];
this.checkBlockScopedCollisions(kind, name, id);
var typeInfo = this.getTypeAnnotation(id, node);
this.bindings[name] = {
typeAnnotationInferred: typeInfo.inferred,
typeAnnotation: typeInfo.annotation,
reassigned: false,
identifier: id,
scope: this,
node: node,
kind: kind
};
this.bindings[name] = new Binding({
identifier: id,
scope: this,
path: path,
kind: kind
});
}
}
@ -472,7 +383,7 @@ export default class Scope {
*/
recrawl() {
this.block._scopeInfo = null;
this.path.setData("scopeInfo", null);
this.crawl();
}
@ -481,91 +392,88 @@ export default class Scope {
*/
crawl() {
var block = this.block;
var i;
var path = this.path;
//
var info = block._scopeInfo;
if (info) {
extend(this, info);
return;
}
var info = path.getData("scopeInfo");
if (info) return extend(this, info);
info = block._scopeInfo = {
info = path.setData("scopeInfo", {
bindings: object(),
globals: object()
};
globals: object(),
uids: object()
});
extend(this, info);
// ForStatement - left, init
if (t.isLoop(block)) {
for (i = 0; i < t.FOR_INIT_KEYS.length; i++) {
var node = block[t.FOR_INIT_KEYS[i]];
if (t.isBlockScoped(node)) this.registerBinding("let", node);
if (path.isLoop()) {
for (let i = 0; i < t.FOR_INIT_KEYS.length; i++) {
var node = path.get(t.FOR_INIT_KEYS[i]);
if (node.isBlockScoped()) this.registerBinding("let", node);
}
if (t.isBlockStatement(block.body)) {
block = block.body;
}
var body = path.get("body");
if (body.isBlockStatement()) path = path.get("body");
}
// FunctionExpression - id
if (t.isFunctionExpression(block) && block.id) {
if (!t.isProperty(this.parentBlock, { method: true })) {
this.registerBinding("var", block.id);
if (path.isFunctionExpression() && path.has("id")) {
if (!t.isProperty(path.parent, { method: true })) {
this.registerBinding("var", path.get("id"));
}
}
// Class
if (t.isClass(block) && block.id) {
this.registerBinding("var", block.id);
if (path.isClass() && path.has("id")) {
this.registerBinding("var", path.get("id"));
}
// Function - params, rest
if (t.isFunction(block)) {
for (i = 0; i < block.params.length; i++) {
this.registerBinding("param", block.params[i]);
if (path.isFunction()) {
var params = path.get("params");
for (let i = 0; i < params.length; i++) {
this.registerBinding("param", params[i]);
}
this.traverse(block.body, blockVariableVisitor, this);
this.traverse(path.get("body").node, blockVariableVisitor, this);
}
// Program, BlockStatement, Function - let variables
if (t.isBlockStatement(block) || t.isProgram(block)) {
this.traverse(block, blockVariableVisitor, this);
if (path.isBlockStatement() || path.isProgram()) {
this.traverse(path.node, blockVariableVisitor, this);
}
// CatchClause - param
if (t.isCatchClause(block)) {
this.registerBinding("let", block.param);
if (path.isCatchClause()) {
this.registerBinding("let", path.get("param"));
}
// ComprehensionExpression - blocks
if (t.isComprehensionExpression(block)) {
this.registerBinding("let", block);
if (path.isComprehensionExpression()) {
this.registerBinding("let", path);
}
// Program, Function - var variables
if (t.isProgram(block) || t.isFunction(block)) {
this.traverse(block, functionVariableVisitor, {
blockId: block.id,
if (path.isProgram() || path.isFunction()) {
this.traverse(path.node, functionVariableVisitor, {
blockId: path.get("id").node,
scope: this
});
}
// Program
if (t.isProgram(block)) {
this.traverse(block, programReferenceVisitor, this);
if (path.isProgram()) {
this.traverse(path.node, programReferenceVisitor, this);
}
}
@ -666,7 +574,7 @@ export default class Scope {
* Description
*/
getBindingInfo(name: string) {
getBinding(name: string) {
var scope = this;
do {
@ -688,7 +596,7 @@ export default class Scope {
*/
getBindingIdentifier(name: string) {
var info = this.getBindingInfo(name);
var info = this.getBinding(name);
return info && info.identifier;
}
@ -701,44 +609,6 @@ export default class Scope {
return binding && binding.identifier;
}
/**
* Description
*/
getOwnImmutableBindingValue(name: string) {
return this._immutableBindingInfoToValue(this.getOwnBindingInfo(name));
}
/**
* Description
*/
getImmutableBindingValue(name: string) {
return this._immutableBindingInfoToValue(this.getBindingInfo(name));
}
_immutableBindingInfoToValue(info) {
if (!info) return;
// can't guarantee this value is the same
if (info.reassigned) return;
var node = info.node;
if (t.isVariableDeclarator(node)) {
if (t.isIdentifier(node.id)) {
node = node.init;
} else {
// otherwise it's probably a destructuring like:
// var { foo } = "foo";
return;
}
}
if (t.isImmutable(node)) {
return node;
}
}
/**
* Description
*/
@ -781,7 +651,7 @@ export default class Scope {
*/
removeBinding(name: string) {
var info = this.getBindingInfo(name);
var info = this.getBinding(name);
if (info) info.scope.removeOwnBinding(name);
}
}

View File

@ -10,7 +10,7 @@ import * as t from "./index";
* Description
*/
export function toComputedKey(node: Object, key: Object = node.key): Object {
export function toComputedKey(node: Object, key: Object = node.key || node.property): Object {
if (!node.computed) {
if (t.isIdentifier(key)) key = t.literal(key.name);
}

View File

@ -24,15 +24,15 @@ function registerType(type: string, skipAliasCheck?: boolean) {
};
}
export var STATEMENT_OR_BLOCK_KEYS = ["consequent", "body", "alternate"];
export var NATIVE_TYPE_NAMES = ["Array", "Object", "Number", "Boolean", "Date", "Array", "String"];
export var FLATTENABLE_KEYS = ["body", "expressions"];
export var FOR_INIT_KEYS = ["left", "init"];
export var COMMENT_KEYS = ["leadingComments", "trailingComments"];
export const STATEMENT_OR_BLOCK_KEYS = ["consequent", "body", "alternate"];
export const NATIVE_TYPE_NAMES = ["Array", "Object", "Number", "Boolean", "Date", "Array", "String"];
export const FLATTENABLE_KEYS = ["body", "expressions"];
export const FOR_INIT_KEYS = ["left", "init"];
export const COMMENT_KEYS = ["leadingComments", "trailingComments"];
export var VISITOR_KEYS = require("./visitor-keys");
export var BUILDER_KEYS = require("./builder-keys");
export var ALIAS_KEYS = require("./alias-keys");
export const VISITOR_KEYS = require("./visitor-keys");
export const BUILDER_KEYS = require("./builder-keys");
export const ALIAS_KEYS = require("./alias-keys");
t.FLIPPED_ALIAS_KEYS = {};
@ -52,7 +52,7 @@ each(t.FLIPPED_ALIAS_KEYS, function (types, type) {
registerType(type, false);
});
export var TYPES = Object.keys(t.VISITOR_KEYS).concat(Object.keys(t.FLIPPED_ALIAS_KEYS));
export const TYPES = Object.keys(t.VISITOR_KEYS).concat(Object.keys(t.FLIPPED_ALIAS_KEYS));
/**
* Returns whether `node` is of given `type`.
@ -281,7 +281,6 @@ export function inheritsComments(child: Object, parent: Object): Object {
export function inherits(child: Object, parent: Object): Object {
child._declarations = parent._declarations;
child._scopeInfo = parent._scopeInfo;
child.range = parent.range;
child.start = parent.start;
child.loc = parent.loc;
@ -298,7 +297,6 @@ toFastProperties(t);
toFastProperties(t.VISITOR_KEYS);
exports.__esModule = true;
assign(t, require("./evaluators"));
assign(t, require("./retrievers"));
assign(t, require("./validators"));
assign(t, require("./converters"));

View File

@ -93,7 +93,7 @@
"TypeofTypeAnnotation": ["argument"],
"TypeAlias": ["id", "typeParameters", "right"],
"TypeAnnotation": ["typeAnnotation"],
"TypeCastExpression": ["expression"],
"TypeCastExpression": ["expression", "typeAnnotation"],
"TypeParameterDeclaration": ["params"],
"TypeParameterInstantiation": ["params"],
"ObjectTypeAnnotation": ["key", "value"],

View File

@ -4,6 +4,7 @@ import buildDebug from "debug/node";
import cloneDeep from "lodash/lang/cloneDeep";
import isBoolean from "lodash/lang/isBoolean";
import * as messages from "./messages";
import minimatch from "minimatch";
import contains from "lodash/collection/contains";
import traverse from "./traversal";
import isString from "lodash/lang/isString";
@ -43,16 +44,21 @@ export function list(val: string): Array<string> {
export function regexify(val: any): RegExp {
if (!val) return new RegExp(/.^/);
if (Array.isArray(val)) val = val.join("|");
if (isString(val)) return new RegExp(val);
if (isString(val)) return minimatch.makeRe(val, { nocase: true });
if (isRegExp(val)) return val;
throw new TypeError("illegal type for regexify");
}
export function arrayify(val: any): Array {
export function arrayify(val: any, mapFn?: Function): Array {
if (!val) return [];
if (isBoolean(val)) return [val];
if (isString(val)) return list(val);
if (Array.isArray(val)) return val;
if (isBoolean(val)) return arrayify([val], mapFn);
if (isString(val)) return arrayify(list(val), mapFn);
if (Array.isArray(val)) {
if (mapFn) val = val.map(mapFn);
return val;
}
throw new TypeError("illegal type for arrayify");
}

View File

@ -55,6 +55,7 @@ var run = function (task, done) {
var getOpts = function (self) {
return _.merge({
suppressDeprecationMessages: true,
filename: self.loc
}, opts);
};

View File

@ -1,4 +1,4 @@
for (let i of [1, 2, 3]) {
for (let i of nums) {
var x = 5;
fns.push(function () {
return i * x;

View File

@ -5,7 +5,7 @@ var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = [1, 2, 3][Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
for (var _iterator = nums[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var x;
(function () {
@ -30,4 +30,4 @@ try {
throw _iteratorError;
}
}
}
}

View File

@ -26,7 +26,9 @@ define(["exports"], function (exports) {
function foo7() {}
var foo8 = exports.foo8 = function foo8() {
var foo8 = function foo8() {
_classCallCheck(this, foo8);
};
});
exports.foo8 = foo8;
});

View File

@ -25,6 +25,8 @@ exports.foo6 = foo6;
function foo7() {}
var foo8 = exports.foo8 = function foo8() {
var foo8 = function foo8() {
_classCallCheck(this, foo8);
};
};
exports.foo8 = foo8;

View File

@ -34,11 +34,13 @@ System.register([], function (_export) {
_export("foo6", foo6);
foo8 = _export("foo8", function foo8() {
foo8 = function foo8() {
_classCallCheck(this, foo8);
});
};
_export("foo8", foo8);
_export("foo3", foo3 = 5);
}
};
});
});

View File

@ -32,7 +32,9 @@
function foo7() {}
var foo8 = exports.foo8 = function foo8() {
var foo8 = function foo8() {
_classCallCheck(this, foo8);
};
});
exports.foo8 = foo8;
});

View File

@ -1,2 +0,0 @@
var arr: Array = bar;
[...arr];

View File

@ -1,4 +0,0 @@
"use strict";
var arr = bar;
[].concat(arr);

View File

@ -1,5 +1,5 @@
function add() {
return [for (i of [1, 2, 3]) i * arguments[0]];
return [for (i of nums) i * arguments[0]];
}
add(5);

View File

@ -10,7 +10,7 @@ function add() {
var _iteratorError = undefined;
try {
for (var _iterator = [1, 2, 3][Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
for (var _iterator = nums[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var i = _step.value;
_ref.push(i * _arguments[0]);
@ -34,4 +34,4 @@ function add() {
})();
}
add(5);
add(5);

View File

@ -2,32 +2,15 @@
var arr = (function () {
var _arr = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
var _arr2 = [1, 2, 3];
try {
for (var _iterator = [1, 2, 3][Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var i = _step.value;
for (var _i = 0; _i < _arr2.length; _i++) {
var i = _arr2[_i];
if (i > 1) {
_arr.push(i * i);
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator["return"]) {
_iterator["return"]();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
if (i > 1) {
_arr.push(i * i);
}
}
return _arr;
})();
})();

View File

@ -2,30 +2,13 @@
var arr = (function () {
var _arr = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
var _arr2 = [1, 2, 3];
try {
for (var _iterator = [1, 2, 3][Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var i = _step.value;
for (var _i = 0; _i < _arr2.length; _i++) {
var i = _arr2[_i];
_arr.push(i * i);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator["return"]) {
_iterator["return"]();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
_arr.push(i * i);
}
return _arr;
})();
})();

View File

@ -1,5 +1,5 @@
function add() {
return [for (i of [1, 2, 3]) i * this.multiplier];
return [for (i of nums) i * this.multiplier];
}
add.call({ multiplier: 5 });

View File

@ -10,7 +10,7 @@ function add() {
var _iteratorError = undefined;
try {
for (var _iterator = [1, 2, 3][Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
for (var _iterator = nums[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var i = _step.value;
_ref.push(i * _this.multiplier);
@ -34,4 +34,4 @@ function add() {
})();
}
add.call({ multiplier: 5 });
add.call({ multiplier: 5 });

3
vendor/.babelrc vendored Normal file
View File

@ -0,0 +1,3 @@
{
"breakConfig": true
}