merge internal transformers into single traversal pass

This commit is contained in:
Sebastian McKenzie
2015-05-07 15:53:22 +01:00
parent 8a4a76000d
commit 6f664ca64e
48 changed files with 243 additions and 417 deletions

View File

@@ -4,6 +4,7 @@ import moduleFormatters from "../modules";
import PluginManager from "./plugin-manager";
import shebangRegex from "shebang-regex";
import TraversalPath 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";
@@ -26,19 +27,6 @@ import path from "path";
import each from "lodash/collection/each";
import * as t from "../../types";
var checkTransformerVisitor = {
exit(node, parent, scope, state) {
checkPath(state.stack, this);
}
};
function checkPath(stack, path) {
each(stack, function (pass) {
if (pass.shouldRun || pass.ran) return;
pass.checkPath(path);
});
}
export default class File {
constructor(opts = {}, pipeline) {
this.dynamicImportTypes = {};
@@ -234,7 +222,46 @@ export default class File {
stack = beforePlugins.concat(stack, afterPlugins);
// register
this.transformerStack = stack.concat(secondaryStack);
this.transformerStack = this.mergeStack(stack.concat(secondaryStack));
}
mergeStack(_stack) {
var stack = [];
var ignore = [];
for (let pass of (_stack: Array)) {
// been merged
if (ignore.indexOf(pass) >= 0) continue;
var category = pass.transformer.metadata.category;
// can't merge
if (!pass.canTransform() || !category) {
stack.push(pass);
continue;
}
var mergeStack = [];
for (let pass of (_stack: Array)) {
if (pass.transformer.metadata.category === category) {
mergeStack.push(pass);
ignore.push(pass);
}
}
var visitors = [];
for (let pass of (mergeStack: Array)) {
visitors.push(pass.handlers);
}
var visitor = traverse.visitors.merge(visitors);
var mergeTransformer = new Transformer(category, visitor);
//console.log(mergeTransformer);
stack.push(mergeTransformer.buildPass(this));
}
return stack;
}
set(key: string, val): any {
@@ -357,23 +384,6 @@ export default class File {
return err;
}
checkPath(path) {
if (Array.isArray(path)) {
for (var i = 0; i < path.length; i++) {
this.checkPath(path[i]);
}
return;
}
var stack = this.transformerStack;
checkPath(stack, path);
path.traverse(checkTransformerVisitor, {
stack: stack
});
}
mergeSourceMap(map: Object) {
var opts = this.opts;
@@ -461,10 +471,6 @@ export default class File {
this._addAst(ast);
this.log.debug("End set AST");
this.log.debug("Start prepass");
this.checkPath(this.path);
this.log.debug("End prepass");
this.log.debug("Start module formatter init");
var modFormatter = this.moduleFormatter = this.getModuleFormatter(this.opts.modules);
if (modFormatter.init && this.transformers["es6.modules"].canTransform()) {

View File

@@ -10,10 +10,6 @@ export default function (exports, opts) {
return t.assignmentExpression("=", left, right);
};
exports.shouldVisit = function (node) {
return node.operator && (node.operator === opts.operator || node.operator === opts.operator + "=");
};
exports.ExpressionStatement = function (node, parent, scope, file) {
// hit the `AssignmentExpression` one below
if (this.isCompletionRecord()) return;

View File

@@ -10,12 +10,6 @@ import * as react from "./react";
import * as t from "../../types";
export default function (exports, opts) {
exports.shouldVisit = function (node) {
if (t.isJSX(node)) return true;
if (react.isCreateClass(node)) return true;
return false;
};
exports.JSXIdentifier = function (node, parent) {
if (node.name === "this" && this.isReferenced()) {
return t.thisExpression();

View File

@@ -5,6 +5,13 @@ var pipeline = new Pipeline;
//
import transformers from "./transformers";
for (var key in transformers) {
var transformer = transformers[key];
var metadata = transformer.metadata = transformer.metadata || {};
metadata.category = metadata.category || "builtin";
}
pipeline.addTransformers(transformers);
//

View File

@@ -8,27 +8,18 @@ import traverse from "../traversal";
export default class TransformerPass {
constructor(file: File, transformer: Transformer) {
this.shouldTransform = !transformer.shouldVisit;
this.transformer = transformer;
this.handlers = transformer.handlers;
this.skipKey = transformer.skipKey;
this.file = file;
this.ran = false;
this.transformer = transformer;
this.handlers = transformer.handlers;
this.skipKey = transformer.skipKey;
this.file = file;
this.ran = false;
}
canTransform(): boolean {
return this.file.pipeline.canTransform(this.transformer, this.file.opts);
}
checkPath(path: TraversalPath): boolean {
if (this.shouldTransform || this.ran) return;
this.shouldTransform = this.transformer.shouldVisit(path.node);
}
transform() {
if (!this.shouldTransform) return;
var file = this.file;
file.log.debug(`Start transformer ${this.transformer.key}`);

View File

@@ -25,7 +25,6 @@ export default class Transformer {
};
this.manipulateOptions = take("manipulateOptions");
this.shouldVisit = take("shouldVisit");
this.metadata = take("metadata") || {};
this.parser = take("parser");
this.post = take("post");
@@ -41,18 +40,6 @@ export default class Transformer {
this.handlers = this.normalize(transformer);
this.key = transformerKey;
//
if (!this.shouldVisit && !this.handlers.enter && !this.handlers.exit) {
var types = Object.keys(this.handlers);
this.shouldVisit = function (node) {
for (var i = 0; i < types.length; i++) {
if (node.type === types[i]) return true;
}
return false;
};
}
}
normalize(transformer: Object): Object {

View File

@@ -1,28 +1,26 @@
import * as defineMap from "../../helpers/define-map";
import * as t from "../../../types";
export function shouldVisit(node) {
return t.isProperty(node) && (node.kind === "get" || node.kind === "set");
}
export var ObjectExpression = {
exit(node, parent, scope, file) {
var mutatorMap = {};
var hasAny = false;
export function ObjectExpression(node, parent, scope, file) {
var mutatorMap = {};
var hasAny = false;
node.properties = node.properties.filter(function (prop) {
if (prop.kind === "get" || prop.kind === "set") {
hasAny = true;
defineMap.push(mutatorMap, prop, prop.kind, file);
return false;
} else {
return true;
}
});
node.properties = node.properties.filter(function (prop) {
if (prop.kind === "get" || prop.kind === "set") {
hasAny = true;
defineMap.push(mutatorMap, prop, prop.kind, file);
return false;
} else {
return true;
}
});
if (!hasAny) return;
if (!hasAny) return;
return t.callExpression(
t.memberExpression(t.identifier("Object"), t.identifier("defineProperties")),
[node, defineMap.toDefineObject(mutatorMap)]
);
}
return t.callExpression(
t.memberExpression(t.identifier("Object"), t.identifier("defineProperties")),
[node, defineMap.toDefineObject(mutatorMap)]
);
}
};

View File

@@ -1,7 +1,5 @@
import * as t from "../../../types";
export var shouldVisit = t.isArrowFunctionExpression;
export function ArrowFunctionExpression(node) {
t.ensureBlock(node);

View File

@@ -37,10 +37,6 @@ function standardizeLets(declars) {
}
}
export function shouldVisit(node) {
return t.isVariableDeclaration(node) && (node.kind === "let" || node.kind === "const");
}
export function VariableDeclaration(node, parent, scope, file) {
if (!isLet(node, parent)) return;

View File

@@ -11,17 +11,17 @@ import * as t from "../../../types";
const PROPERTY_COLLISION_METHOD_NAME = "__initializeProperties";
export var shouldVisit = t.isClass;
export function ClassDeclaration(node, parent, scope, file) {
return t.variableDeclaration("let", [
t.variableDeclarator(node.id, t.toExpression(node))
]);
}
export function ClassExpression(node, parent, scope, file) {
return new ClassTransformer(this, file).run();
}
export var ClassExpression = {
exit(node, parent, scope, file) {
return new ClassTransformer(this, file).run();
}
};
var collectPropertyReferencesVisitor = {
Identifier: {

View File

@@ -1,10 +1,6 @@
import * as messages from "../../../messages";
import * as t from "../../../types";
export function shouldVisit(node) {
return t.isVariableDeclaration(node, { kind: "const" }) || t.isImportDeclaration(node);
}
var visitor = {
enter(node, parent, scope, state) {
if (this.isAssignmentExpression() || this.isUpdateExpression()) {

View File

@@ -1,8 +1,6 @@
import * as messages from "../../../messages";
import * as t from "../../../types";
export var shouldVisit = t.isPattern;
export function ForOfStatement(node, parent, scope, file) {
var left = node.left;
@@ -83,7 +81,6 @@ exports.Function = function (node, parent, scope, file) {
var block = node.body;
block.body = nodes.concat(block.body);
this.checkSelf();
};
export function CatchClause(node, parent, scope, file) {
@@ -104,8 +101,6 @@ export function CatchClause(node, parent, scope, file) {
destructuring.init(pattern, ref);
node.body.body = nodes.concat(node.body.body);
this.checkSelf();
}
export function ExpressionStatement(node, parent, scope, file) {

View File

@@ -2,8 +2,6 @@ import * as messages from "../../../messages";
import * as util from "../../../util";
import * as t from "../../../types";
export var shouldVisit = t.isForOfStatement;
export function ForOfStatement(node, parent, scope, file) {
if (this.get("right").isArrayExpression()) {
return _ForOfStatementArray.call(this, node, scope, file);

View File

@@ -1,7 +1,5 @@
import * as t from "../../../types";
export { shouldVisit } from "../internal/modules";
function keepBlockHoist(node, nodes) {
if (node._blockHoist) {
for (let i = 0; i < nodes.length; i++) {

View File

@@ -1,8 +1,6 @@
import ReplaceSupers from "../../helpers/replace-supers";
import * as t from "../../../types";
export var shouldVisit = t.isSuper;
function Property(path, node, scope, getObjectRef, file) {
if (!node.method) return;

View File

@@ -3,10 +3,6 @@ import * as util from "../../../util";
import traverse from "../../../traversal";
import * as t from "../../../types";
export function shouldVisit(node) {
return t.isFunction(node) && hasDefaults(node);
}
var hasDefaults = function (node) {
for (var i = 0; i < node.params.length; i++) {
if (!t.isIdentifier(node.params[i])) return true;
@@ -96,6 +92,4 @@ exports.Function = function (node, parent, scope, file) {
} else {
node.body.body = body.concat(node.body.body);
}
this.checkSelf();
};

View File

@@ -2,8 +2,6 @@ import isNumber from "lodash/lang/isNumber";
import * as util from "../../../util";
import * as t from "../../../types";
export var shouldVisit = t.isRestElement;
var memberExpressionOptimisationVisitor = {
enter(node, parent, scope, state) {
// check if this scope has a local binding that will shadow the rest parameter
@@ -96,7 +94,6 @@ exports.Function = function (node, parent, scope, file) {
candidate.replaceWith(argsId);
optimizeMemberExpression(candidate.parent, node.params.length);
}
this.checkSelf();
return;
}
@@ -138,5 +135,4 @@ exports.Function = function (node, parent, scope, file) {
});
loop._blockHoist = node.params.length + 1;
node.body.body.unshift(loop);
this.checkSelf();
};

View File

@@ -63,42 +63,40 @@ function spec(node, body, objId, initProps, file) {
}
}
export function shouldVisit(node) {
return t.isProperty(node) && node.computed;
}
export var ObjectExpression = {
exit(node, parent, scope, file) {
var hasComputed = false;
export function ObjectExpression(node, parent, scope, file) {
var hasComputed = false;
for (var i = 0; i < node.properties.length; i++) {
hasComputed = t.isProperty(node.properties[i], { computed: true, kind: "init" });
if (hasComputed) break;
}
for (var i = 0; i < node.properties.length; i++) {
hasComputed = t.isProperty(node.properties[i], { computed: true, kind: "init" });
if (hasComputed) break;
if (!hasComputed) return;
var initProps = [];
var objId = scope.generateUidBasedOnNode(parent);
//
var body = [];
//
var callback = spec;
if (file.isLoose("es6.properties.computed")) callback = loose;
var result = callback(node, body, objId, initProps, file);
if (result) return result;
//
body.unshift(t.variableDeclaration("var", [
t.variableDeclarator(objId, t.objectExpression(initProps))
]));
body.push(t.expressionStatement(objId));
return body;
}
if (!hasComputed) return;
var initProps = [];
var objId = scope.generateUidBasedOnNode(parent);
//
var body = [];
//
var callback = spec;
if (file.isLoose("es6.properties.computed")) callback = loose;
var result = callback(node, body, objId, initProps, file);
if (result) return result;
//
body.unshift(t.variableDeclaration("var", [
t.variableDeclarator(objId, t.objectExpression(initProps))
]));
body.push(t.expressionStatement(objId));
return body;
}
};

View File

@@ -1,9 +1,5 @@
import * as t from "../../../types";
export function shouldVisit(node) {
return t.isProperty(node) && (node.method || node.shorthand);
}
export function Property(node) {
if (node.method) {
node.method = false;

View File

@@ -1,10 +1,6 @@
import * as regex from "../../helpers/regex";
import * as t from "../../../types";
export function shouldVisit(node) {
return regex.is(node, "y");
}
export function Literal(node) {
if (!regex.is(node, "y")) return;
return t.newExpression(t.identifier("RegExp"), [

View File

@@ -1,10 +1,6 @@
import rewritePattern from "regexpu/rewrite-pattern";
import * as regex from "../../helpers/regex";
export function shouldVisit(node) {
return regex.is(node, "u");
}
export function Literal(node) {
if (!regex.is(node, "u")) return;
node.regex.pattern = rewritePattern(node.regex.pattern, node.regex.flags);

View File

@@ -44,8 +44,6 @@ function build(props, scope) {
return nodes;
}
export var shouldVisit = t.isSpreadElement;
export function ArrayExpression(node, parent, scope) {
var elements = node.elements;
if (!hasSpread(elements)) return;

View File

@@ -4,10 +4,6 @@ var buildBinaryExpression = function (left, right) {
return t.binaryExpression("+", left, right);
};
export function shouldVisit(node) {
return t.isTemplateLiteral(node) || t.isTaggedTemplateExpression(node);
}
export function TaggedTemplateExpression(node, parent, scope, file) {
var quasi = node.quasi;
var args = [];

View File

@@ -1,7 +1,3 @@
export var metadata = {
stage: 1
};
export function shouldVisit() {
return false;
}

View File

@@ -1,7 +1,3 @@
export var metadata = {
stage: 0
};
export function shouldVisit() {
return false;
}

View File

@@ -7,10 +7,6 @@ export var metadata = {
stage: 1
};
export function shouldVisit(node) {
return !!node.decorators;
}
export function ObjectExpression(node, parent, scope, file) {
var hasDecorators = false;
for (var i = 0; i < node.properties.length; i++) {

View File

@@ -5,8 +5,6 @@ export var metadata = {
stage: 0
};
export var shouldVisit = t.isDoExpression;
export function DoExpression(node) {
var body = node.body.body;
if (body.length) {

View File

@@ -6,10 +6,6 @@ export var metadata = {
stage: 1
};
export function shouldVisit(node) {
return t.isExportDefaultSpecifier(node) || t.isExportNamespaceSpecifier(node);
}
function build(node, nodes, scope) {
var first = node.specifiers[0];
if (!t.isExportNamespaceSpecifier(first) && !t.isExportDefaultSpecifier(first)) return;

View File

@@ -1,7 +1,3 @@
export var metadata = {
stage: 1
};
export function shouldVisit() {
return false;
}

View File

@@ -1,124 +1,66 @@
export default {
"utility.removeDebugger": require("./utility/remove-debugger"),
"utility.removeConsole": require("./utility/remove-console"),
"utility.inlineEnvironmentVariables": require("./utility/inline-environment-variables"),
"utility.inlineExpressions": require("./utility/inline-expressions"),
"minification.deadCodeElimination": require("./minification/dead-code-elimination"),
_modules: require("./internal/modules"),
"es7.classProperties": require("./es7/class-properties"),
"es7.trailingFunctionCommas": require("./es7/trailing-function-commas"),
"es7.asyncFunctions": require("./es7/async-functions"),
"es7.decorators": require("./es7/decorators"),
strict: require("./other/strict"),
_validation: require("./internal/validation"),
"validation.undeclaredVariableCheck": require("./validation/undeclared-variable-check"),
"validation.react": require("./validation/react"),
// this goes at the start so we only transform the original user code
"spec.functionName": require("./spec/function-name"),
// needs to be before `_shadowFunctions`
"es6.arrowFunctions": require("./es6/arrow-functions"),
"spec.blockScopedFunctions": require("./spec/block-scoped-functions"),
"optimisation.react.constantElements": require("./optimisation/react.constant-elements"),
"optimisation.react.inlineElements": require("./optimisation/react.inline-elements"),
reactCompat: require("./other/react-compat"),
react: require("./other/react"),
// needs to be before `regenerator` due to generator comprehensions
// needs to be before `_shadowFunctions`
"es7.comprehensions": require("./es7/comprehensions"),
"es6.classes": require("./es6/classes"),
asyncToGenerator: require("./other/async-to-generator"),
bluebirdCoroutines: require("./other/bluebird-coroutines"),
"es6.objectSuper": require("./es6/object-super"),
"es7.objectRestSpread": require("./es7/object-rest-spread"),
"es7.exponentiationOperator": require("./es7/exponentiation-operator"),
"es6.spec.templateLiterals": require("./es6/spec.template-literals"),
"es6.templateLiterals": require("./es6/template-literals"),
"es5.properties.mutators": require("./es5/properties.mutators"),
"es6.properties.shorthand": require("./es6/properties.shorthand"),
// needs to be before `_shadowFunctions` due to define property closure
"es6.properties.computed": require("./es6/properties.computed"),
"optimisation.flow.forOf": require("./optimisation/flow.for-of"),
"es6.forOf": require("./es6/for-of"),
"es6.regex.sticky": require("./es6/regex.sticky"),
"es6.regex.unicode": require("./es6/regex.unicode"),
"es6.constants": require("./es6/constants"),
// needs to be before `es6.parameters.default` as default parameters will destroy the rest param
"es6.parameters.rest": require("./es6/parameters.rest"),
// needs to be after `es6.parameters.rest` as we use `toArray` and avoid turning an already known array into one
"es6.spread": require("./es6/spread"),
// needs to be before `es6.blockScoping` as default parameters have a TDZ
"es6.parameters.default": require("./es6/parameters.default"),
// needs to be before `es6.blockScoping` as let variables may be produced
"es6.destructuring": require("./es6/destructuring"),
// needs to be before `_shadowFunctions` due to block scopes sometimes being wrapped in a
// closure
"es6.blockScoping": require("./es6/block-scoping"),
// needs to be after `es6.blockScoping` due to needing `letReferences` set on blocks
"es6.spec.blockScoping": require("./es6/spec.block-scoping"),
// needs to be after `es6.parameters.*` and `es6.blockScoping` due to needing pure
// identifiers in parameters and variable declarators
"es6.tailCall": require("./es6/tail-call"),
regenerator: require("./other/regenerator"),
// needs to be after `regenerator` due to needing `regeneratorRuntime` references
// needs to be after `es6.forOf` due to needing `Symbol.iterator` references
// needs to be before `es6.modules` due to dynamic imports
runtime: require("./other/runtime"),
// needs to be before `_blockHoist` due to function hoisting etc
runtime: require("./other/runtime"),
"es7.exportExtensions": require("./es7/export-extensions"),
"es6.modules": require("./es6/modules"),
_blockHoist: require("./internal/block-hoist"),
"spec.protoToAssign": require("./spec/proto-to-assign"),
_shadowFunctions: require("./internal/shadow-functions"),
"es7.doExpressions": require("./es7/do-expressions"),
"es6.spec.symbols": require("./es6/spec.symbols"),
ludicrous: require("./other/ludicrous"),
"spec.undefinedToVoid": require("./spec/undefined-to-void"),
_strict: require("./internal/strict"),
_moduleFormatter: require("./internal/module-formatter"),
"es3.propertyLiterals": require("./es3/property-literals"),
"es3.memberExpressionLiterals": require("./es3/member-expression-literals"),
"minification.memberExpressionLiterals": require("./minification/member-expression-literals"),
"minification.propertyLiterals": require("./minification/property-literals"),
jscript: require("./other/jscript"),
flow: require("./other/flow")
flow: require("./other/flow"),
_hoistDirectives: require("./internal/hoist-directives"),
_blockHoist: require("./internal/block-hoist")
};

View File

@@ -0,0 +1,16 @@
import * as t from "../../../types";
export var BlockStatement = {
exit(node) {
for (var i = 0; i < node.body.length; i++) {
var bodyNode = node.body[i];
if (t.isExpressionStatement(bodyNode) && t.isLiteral(bodyNode.expression)) {
bodyNode._blockHoist = Infinity;
} else {
return;
}
}
}
};
export { BlockStatement as Program };

View File

@@ -1,8 +1,6 @@
import * as strict from "../../helpers/strict";
export function Program(program, parent, scope, file) {
this.stop();
strict.wrap(program, function () {
program.body = file.dynamicImports.concat(program.body);
});

View File

@@ -6,10 +6,6 @@
import * as t from "../../../types";
export function shouldVisit(node) {
return t.isImportDeclaration(node) || t.isExportDeclaration(node);
}
export function ImportDeclaration(node, parent, scope, file) {
if (node.source) {
node.source.value = file.resolveModuleSource(node.source.value);

View File

@@ -82,21 +82,24 @@ function aliasFunction(getBody, path, scope) {
}
};
export function shouldVisit(node) {
return true;
}
// todo: on all `this` and `arguments`, walk UP the tree instead of
// crawling the entire function tree
export function Program(node, parent, scope) {
aliasFunction(function () {
return node.body;
}, this, scope);
export var Program = {
exit(node, parent, scope) {
aliasFunction(function () {
return node.body;
}, this, scope);
}
};
export function FunctionDeclaration(node, parent, scope) {
aliasFunction(function () {
t.ensureBlock(node);
return node.body.body;
}, this, scope);
}
export var FunctionDeclaration = {
exit(node, parent, scope) {
aliasFunction(function () {
t.ensureBlock(node);
return node.body.body;
}, this, scope);
}
};
export { FunctionDeclaration as FunctionExpression };

View File

@@ -1,19 +0,0 @@
import * as t from "../../../types";
export function Program(program, parent, scope, file) {
if (file.transformers.strict.canTransform()) {
var directive = file.get("existingStrictDirective");
if (!directive) {
directive = t.expressionStatement(t.literal("use strict"));
var first = program.body[0];
if (first) {
directive.leadingComments = first.leadingComments;
first.leadingComments = [];
}
}
this.unshiftContainer("body", [directive]);
}
this.stop();
}

View File

@@ -1,10 +1,6 @@
import * as messages from "../../../messages";
import * as t from "../../../types";
export var metadata = {
readOnly: true
};
export function ForOfStatement(node, parent, scope, file) {
var left = node.left;
if (t.isVariableDeclaration(left)) {
@@ -43,16 +39,3 @@ export function Property(node, parent, scope, file) {
}
}
}
export function BlockStatement(node) {
for (var i = 0; i < node.body.length; i++) {
var bodyNode = node.body[i];
if (t.isExpressionStatement(bodyNode) && t.isLiteral(bodyNode.expression)) {
bodyNode._blockHoist = Infinity;
} else {
return;
}
}
}
export { BlockStatement as Program };

View File

@@ -1,7 +1,6 @@
import { _ForOfStatementArray } from "../es6/for-of";
import * as t from "../../../types";
export var shouldVisit = t.isForOfStatement;
export var metadata = {
optional: true
};

View File

@@ -1,9 +1,5 @@
import * as t from "../../../types";
export function shouldVisit(node) {
return node.isType || node.optional || node.implements || node.typeAnnotation || t.isFlow(node);
}
export function Flow(node) {
this.remove();
}

View File

@@ -1,14 +1,8 @@
import regenerator from "regenerator";
import * as t from "../../../types";
export function shouldVisit(node) {
return t.isFunction(node) && (node.async || node.generator);
}
export var Program = {
enter(ast) {
exit(ast) {
regenerator.transform(ast);
this.stop();
this.checkSelf();
}
};

View File

@@ -1,22 +1,33 @@
import * as messages from "../../../messages";
import * as t from "../../../types";
export function Program(program, parent, scope, file) {
var first = program.body[0];
if (t.isExpressionStatement(first) && t.isLiteral(first.expression, { value: "use strict" })) {
file.set("existingStrictDirective", program.body.shift());
const THIS_BREAK_KEYS = ["FunctionExpression", "FunctionDeclaration", "ClassExpression", "ClassDeclaration"];
export var Program = {
enter(program, parent, scope, file) {
var first = program.body[0];
var directive;
if (t.isExpressionStatement(first) && t.isLiteral(first.expression, { value: "use strict" })) {
directive = first;
} else {
directive = t.expressionStatement(t.literal("use strict"));
this.unshiftContainer("body", directive);
if (first) {
directive.leadingComments = first.leadingComments;
first.leadingComments = [];
}
}
directive._blockHoist = Infinity;
}
}
export function FunctionExpression() {
this.skip();
}
export { FunctionExpression as FunctionDeclaration };
export { FunctionExpression as Class };
export function ThisExpression() {
return t.identifier("undefined");
if (!this.findParent(function (node) {
return !node.shadow && THIS_BREAK_KEYS.indexOf(node.type) >= 0;
})) {
return t.identifier("undefined");
}
}
export function CallExpression(node, parent, scope, file) {

View File

@@ -23,21 +23,6 @@ function statementList(key, path, file) {
}
}
export function shouldVisit(node) {
var body;
if (node.type === "SwitchCase") {
body = node.consequent;
} else if (node.type === "BlockStatement") {
body = node.body;
}
if (body) {
for (var i = 0; i < body.length; i++) {
if (body[i].type === "FunctionDeclaration") return true;
}
}
return false;
}
export function BlockStatement(node, parent, scope, file) {
if ((t.isFunction(parent) && parent.body === node) || t.isExportDeclaration(parent)) {
return;

View File

@@ -1,14 +1,6 @@
import * as messages from "../../../messages";
import * as t from "../../../types";
export var metadata = {
readOnly: true
};
export function shouldVisit(node) {
return t.isModuleDeclaration(node) || (t.isCallExpression(node) && t.isIdentifier(node.callee, { name: "require" }));
}
// check if the input Literal `source` is an alternate casing of "react"
function check(source, file) {
if (t.isLiteral(source)) {

View File

@@ -2,8 +2,7 @@ import levenshtein from "leven";
import * as messages from "../../../messages";
export var metadata = {
optional: true,
readOnly: true
optional: true
};
export function Identifier(node, parent, scope, file) {

View File

@@ -1,5 +1,5 @@
import TraversalContext from "./context";
import { explode, verify } from "./visitors";
import * as visitors from "./visitors";
import * as messages from "../messages";
import includes from "lodash/collection/includes";
import * as t from "../types";
@@ -14,7 +14,7 @@ export default function traverse(parent, opts, scope, state, parentPath) {
}
if (!opts) opts = {};
verify(opts);
visitors.verify(opts);
// array of nodes
if (Array.isArray(parent)) {
@@ -26,8 +26,9 @@ export default function traverse(parent, opts, scope, state, parentPath) {
}
}
traverse.verify = verify;
traverse.explode = explode;
traverse.visitors = visitors;
traverse.verify = visitors.verify;
traverse.explode = visitors.explode;
traverse.node = function (node, opts, scope, state, parentPath) {
var keys = t.VISITOR_KEYS[node.type];

View File

@@ -107,12 +107,43 @@ export default class TraversalPath {
return ancestry;
}
/**
* Description
*/
inType(types) {
if (!Array.isArray(types)) types = [types];
var path = this;
while (path) {
for (var type of (types: Array)) {
if (path.node.type === type) return true;
}
path = path.parentPath;
}
return false;
}
/**
* Description
*/
findParent(callback) {
var path = this;
while (path) {
if (callback(path.node)) return path.node;
path = path.parentPath;
}
return null;
}
/**
* Description
*/
queueNode(path) {
if (this.context) {
if (this.context && this.context.queue) {
this.context.queue.push(path);
}
}
@@ -136,7 +167,6 @@ export default class TraversalPath {
} else if (this.isStatementOrBlock()) {
if (this.node) nodes.push(this.node);
this.container[this.key] = t.blockStatement(nodes);
this.checkSelf();
} else {
throw new Error("We don't know what to do with this node type. We were previously a Statement but we can't fit in here?");
}
@@ -163,8 +193,6 @@ export default class TraversalPath {
paths.push(TraversalPath.get(this, null, node, this.container, to));
}
}
this.checkPaths(paths);
}
_containerInsertBefore(nodes) {
@@ -234,7 +262,6 @@ export default class TraversalPath {
} else if (this.isStatementOrBlock()) {
if (this.node) nodes.unshift(this.node);
this.container[this.key] = t.blockStatement(nodes);
this.checkSelf();
} else {
throw new Error("We don't know what to do with this node type. We were previously a Statement but we can't fit in here?");
}
@@ -528,7 +555,7 @@ export default class TraversalPath {
}
if (this.node === replacement) {
return this.checkSelf();
return;
}
// normalise inserting an entire AST
@@ -572,26 +599,6 @@ export default class TraversalPath {
// potentially create new scope
this.setScope();
this.checkSelf();
}
/**
* Description
*/
checkSelf() {
this.checkPaths(this);
}
/**
* Description
*/
checkPaths(paths) {
var scope = this.scope;
var file = scope && scope.file;
if (file) file.checkPath(paths);
}
/**
@@ -691,9 +698,13 @@ export default class TraversalPath {
// call the function with the params (node, parent, scope, state)
var replacement = fn.call(this, node, this.parent, this.scope, this.state);
if (replacement) this.replaceWith(replacement, true);
if (replacement) {
this.replaceWith(replacement, true);
this.queueNode(this);
break;
}
if (this.shouldStop) break;
if (this.shouldStop || this.removed) break;
}
}

View File

@@ -36,12 +36,12 @@ export function explode(visitor, mergeConflicts) {
if (wrapper.type) {
// merge the visitor if necessary or just put it back in
if (visitor[wrapper.type]) {
merge(visitor[wrapper.type], fns);
mergePair(visitor[wrapper.type], fns);
} else {
visitor[wrapper.type] = fns;
}
} else {
merge(visitor, fns);
mergePair(visitor, fns);
}
}
@@ -61,7 +61,7 @@ export function explode(visitor, mergeConflicts) {
var existing = visitor[alias];
if (existing) {
if (mergeConflicts) {
merge(existing, fns);
mergePair(existing, fns);
}
} else {
visitor[alias] = fns;
@@ -105,6 +105,19 @@ export function verify(visitor) {
visitor._verified = true;
}
export function merge(visitors) {
var rootVisitor = {};
for (var visitor of (visitors: Array)) {
for (var type in visitor) {
var nodeVisitor = rootVisitor[type] = rootVisitor[type] || {};
mergePair(nodeVisitor, visitor[type]);
}
}
return rootVisitor;
}
function ensureEntranceObjects(obj) {
for (let key in obj) {
if (shouldIgnoreKey(key)) continue;
@@ -135,7 +148,7 @@ function addSelector(visitor, selector, fns) {
};
}
merge(visitor, fns);
mergePair(visitor, fns);
}
function wrapCheck(wrapper, fn) {
@@ -159,7 +172,7 @@ function shouldIgnoreKey(key) {
return false;
}
function merge(dest, src) {
function mergePair(dest, src) {
for (var key in src) {
dest[key] = (dest[key] || []).concat(src[key]);
}