diff --git a/src/babel/transformation/helpers/build-react-transformer.js b/src/babel/transformation/helpers/build-react-transformer.js index 8b9e4897b0..46a9bdfef7 100644 --- a/src/babel/transformation/helpers/build-react-transformer.js +++ b/src/babel/transformation/helpers/build-react-transformer.js @@ -17,7 +17,7 @@ export default function (exports, opts) { }; exports.JSXIdentifier = function (node, parent) { - if (node.name === "this" && t.isReferenced(node, parent)) { + if (node.name === "this" && this.isReferenced()) { return t.thisExpression(); } else if (esutils.keyword.isIdentifierNameES6(node.name)) { node.type = "Identifier"; diff --git a/src/babel/transformation/helpers/remap-async-to-generator.js b/src/babel/transformation/helpers/remap-async-to-generator.js index 93c5d320dc..32b2240925 100644 --- a/src/babel/transformation/helpers/remap-async-to-generator.js +++ b/src/babel/transformation/helpers/remap-async-to-generator.js @@ -19,7 +19,7 @@ var awaitVisitor = { var referenceVisitor = { enter(node, parent, scope, state) { var name = state.id.name; - if (t.isReferencedIdentifier(node, parent, { name: name }) && scope.bindingIdentifierEquals(name, state.id)) { + if (this.isReferencedIdentifier({ name: name }) && scope.bindingIdentifierEquals(name, state.id)) { return state.ref = state.ref || scope.generateUidIdentifier(name); } } diff --git a/src/babel/transformation/transformers/es6/parameters.default.js b/src/babel/transformation/transformers/es6/parameters.default.js index 2a6d20a312..56345ee839 100644 --- a/src/babel/transformation/transformers/es6/parameters.default.js +++ b/src/babel/transformation/transformers/es6/parameters.default.js @@ -1,5 +1,6 @@ import callDelegate from "../../helpers/call-delegate"; import * as util from "../../../util"; +import traverse from "../../../traversal"; import * as t from "../../../types"; export function shouldVisit(node) { @@ -13,16 +14,15 @@ var hasDefaults = function (node) { return false; }; -var iifeVisitor = { - enter(node, parent, scope, state) { - if (!this.isReferencedIdentifier()) return; +var iifeVisitor = traverse.explode({ + ReferencedIdentifier(node, parent, scope, state) { if (!state.scope.hasOwnBinding(node.name)) return; if (state.scope.bindingIdentifierEquals(node.name, node)) return; state.iife = true; this.stop(); } -}; +}); exports.Function = function (node, parent, scope, file) { if (!hasDefaults(node)) return; diff --git a/src/babel/transformation/transformers/es6/spec.block-scoping.js b/src/babel/transformation/transformers/es6/spec.block-scoping.js index 811ee576ab..43699b2b1b 100644 --- a/src/babel/transformation/transformers/es6/spec.block-scoping.js +++ b/src/babel/transformation/transformers/es6/spec.block-scoping.js @@ -1,8 +1,8 @@ +import traverse from "../../../traversal"; import * as t from "../../../types"; -var visitor = { - enter(node, parent, scope, state) { - if (!this.isReferencedIdentifier()) return; +var visitor = traverse.explode({ + ReferencedIdentifier(node, parent, scope, state) { if (t.isFor(parent) && parent.left === node) return; var declared = state.letRefs[node.name]; @@ -25,7 +25,7 @@ var visitor = { return t.logicalExpression("&&", assert, node); } } -}; +}); export var metadata = { optional: true diff --git a/src/babel/transformation/transformers/es6/tail-call.js b/src/babel/transformation/transformers/es6/tail-call.js index 91aac9ae4b..495d88591a 100644 --- a/src/babel/transformation/transformers/es6/tail-call.js +++ b/src/babel/transformation/transformers/es6/tail-call.js @@ -1,6 +1,7 @@ import reduceRight from "lodash/collection/reduceRight"; import * as messages from "../../../messages"; import flatten from "lodash/array/flatten"; +import traverse from "../../../traversal"; import * as util from "../../../util"; import map from "lodash/collection/map"; import * as t from "../../../types"; @@ -16,54 +17,61 @@ function returnBlock(expr) { } // looks for and replaces tail recursion calls -var firstPass = { +var firstPass = traverse.explode({ enter(node, parent, scope, state) { - if (this.isReturnStatement()) { - this.skip(); - return state.subTransform(node.argument); - } else if (t.isTryStatement(parent)) { + if (t.isTryStatement(parent)) { if (node === parent.block) { this.skip(); } else if (parent.finalizer && node !== parent.finalizer) { this.skip(); } - } else if (this.isFunction()) { - this.skip(); - } else if (this.isVariableDeclaration()) { - this.skip(); - state.vars.push(node); } + }, + + ReturnStatement(node, parent, scope, state) { + this.skip(); + return state.subTransform(node.argument); + }, + + Function(node, parent, scope, state) { + this.skip(); + }, + + VariableDeclaration(node, parent, scope, state) { + this.skip(); + state.vars.push(node); } -}; +}); // hoists up function declarations, replaces `this` and `arguments` and marks // them as needed -var secondPass = { - enter(node, parent, scope, state) { - if (this.isThisExpression()) { - state.needsThis = true; - return state.getThisId(); - } else if (this.isReferencedIdentifier({ name: "arguments" })) { - state.needsArguments = true; - return state.getArgumentsId(); - } else if (this.isFunction()) { - this.skip(); - if (this.isFunctionDeclaration()) { - node = t.variableDeclaration("var", [ - t.variableDeclarator(node.id, t.toExpression(node)) - ]); - node._blockHoist = 2; - return node; - } +var secondPass = traverse.explode({ + ThisExpression(node, parent, scope, state) { + state.needsThis = true; + return state.getThisId(); + }, + + ReferencedIdentifier(node, parent, scope, state) { + if (node.name !== "arguments") return; + state.needsArguments = true; + return state.getArgumentsId(); + }, + + Function(node, parent, scope, state) { + this.skip(); + if (this.isFunctionDeclaration()) { + node = t.variableDeclaration("var", [ + t.variableDeclarator(node.id, t.toExpression(node)) + ]); + node._blockHoist = 2; + return node; } } -}; +}); // optimizes recursion by removing `this` and `arguments` if they aren't used -var thirdPass = { - enter(node, parent, scope, state) { - if (!this.isExpressionStatement()) return; - +var thirdPass = traverse.explode({ + ExpressionStatement(node, parent, scope, state) { var expr = node.expression; if (!t.isAssignmentExpression(expr)) return; @@ -75,7 +83,7 @@ var thirdPass = { }); } } -}; +}); class TailCallTransformer { constructor(path, scope, file) { diff --git a/src/babel/traversal/binding.js b/src/babel/traversal/binding.js index 86fe6c5ab6..2e538ad87a 100644 --- a/src/babel/traversal/binding.js +++ b/src/babel/traversal/binding.js @@ -15,7 +15,7 @@ export default class Binding { * Description */ - setTypeAnnotation() { + setTypeAnnotation() { var typeInfo = this.path.getTypeAnnotation(); this.typeAnnotationInferred = typeInfo.inferred; this.typeAnnotation = typeInfo.annotation; diff --git a/src/babel/traversal/path/index.js b/src/babel/traversal/path/index.js index 0fc9937999..8970437f56 100644 --- a/src/babel/traversal/path/index.js +++ b/src/babel/traversal/path/index.js @@ -1,10 +1,12 @@ import PathHoister from "./hoister"; +import * as virtualTypes from "./virtual-types"; 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 codeFrame from "../../helpers/code-frame"; import parse from "../../helpers/parse"; +import { explode } from "../visitors"; import traverse from "../index"; import includes from "lodash/collection/includes"; import assign from "lodash/object/assign"; @@ -12,33 +14,33 @@ import extend from "lodash/object/extend"; import Scope from "../scope"; import * as t from "../../types"; -var hoistVariablesVisitor = { - enter(node, parent, scope) { - if (this.isFunction()) { - return this.skip(); - } +var hoistVariablesVisitor = explode({ + Function() { + this.skip(); + }, + + VariableDeclaration(node, parent, scope) { + if (node.kind !== "var") return; - if (this.isVariableDeclaration() && node.kind === "var") { var bindings = this.getBindingIdentifiers(); - for (var key in bindings) { - scope.push({ id: bindings[key] }); - } - - var exprs = []; - - for (var i = 0; i < node.declarations.length; i++) { - var declar = node.declarations[i]; - if (declar.init) { - exprs.push(t.expressionStatement( - t.assignmentExpression("=", declar.id, declar.init) - )); - } - } - - return exprs; + for (var key in bindings) { + scope.push({ id: bindings[key] }); } + + var exprs = []; + + for (var i = 0; i < node.declarations.length; i++) { + var declar = node.declarations[i]; + if (declar.init) { + exprs.push(t.expressionStatement( + t.assignmentExpression("=", declar.id, declar.init) + )); + } + } + + return exprs; } -}; +}); export default class TraversalPath { constructor(parent, container) { @@ -938,46 +940,6 @@ export default class TraversalPath { } } - /** - * Description - */ - - isScope(): boolean { - return t.isScope(this.node, this.parent); - } - - /** - * Description - */ - - isReferencedIdentifier(opts): boolean { - return t.isReferencedIdentifier(this.node, this.parent, opts); - } - - /** - * Description - */ - - isReferenced(): boolean { - return t.isReferenced(this.node, this.parent); - } - - /** - * Description - */ - - isBlockScoped(): boolean { - return t.isBlockScoped(this.node); - } - - /** - * Description - */ - - isVar(): boolean { - return t.isVar(this.node); - } - /** * Description */ @@ -1096,8 +1058,15 @@ export default class TraversalPath { assign(TraversalPath.prototype, require("./evaluation")); assign(TraversalPath.prototype, require("./conversion")); -for (var i = 0; i < t.TYPES.length; i++) { - let type = t.TYPES[i]; +for (let type in virtualTypes) { + if (type[0] === "_") continue; + + TraversalPath.prototype[`is${type}`] = function (opts) { + return virtualTypes[type].checkPath(this, opts); + }; +} + +for (let type of (t.TYPES: Array)) { let typeKey = `is${type}`; TraversalPath.prototype[typeKey] = function (opts) { return t[typeKey](this.node, opts); diff --git a/src/babel/traversal/path/virtual-types.js b/src/babel/traversal/path/virtual-types.js new file mode 100644 index 0000000000..60459a567f --- /dev/null +++ b/src/babel/traversal/path/virtual-types.js @@ -0,0 +1,34 @@ +import * as t from "../../types"; + +export var ReferencedIdentifier = { + type: "Identifier", + checkPath(path, opts) { + return t.isReferencedIdentifier(path.node, path.parent, opts); + } +}; + +export var Scope = { + type: "Scopable", + checkPath(path) { + return t.isScope(path.node, path.parent); + } +}; + +export var Referenced = { + checkPath(path) { + return t.isReferenced(path.node, path.parent); + } +}; + +export var BlockScoped = { + checkPath(path) { + return t.isBlockScoped(path.node); + } +}; + +export var Var = { + type: "VariableDeclaration", + checkPath(path) { + return t.isVar(path.node); + } +}; diff --git a/src/babel/traversal/scope.js b/src/babel/traversal/scope.js index dc007577d3..a70d63132a 100644 --- a/src/babel/traversal/scope.js +++ b/src/babel/traversal/scope.js @@ -65,33 +65,34 @@ var programReferenceVisitor = explode({ } }); -var blockVariableVisitor = { +var blockVariableVisitor = explode({ + Scope() { + this.skip(); + }, + enter(node, parent, scope, state) { if (this.isFunctionDeclaration() || this.isBlockScoped()) { state.registerDeclaration(this); } - if (this.isScope()) { - this.skip(); - } } -}; +}); var renameVisitor = explode({ - Identifier(node, parent, scope, state) { - if (this.isReferenced() && node.name === state.oldName) { - if (this.parentPath.isProperty() && this.key === "key" && parent.shorthand) { - var value = t.identifier(state.newName);; + ReferencedIdentifier(node, parent, scope, state) { + if (node.name !== state.oldName) return; - if (parent.value === state.binding) { - state.info.identifier = state.binding = value; - } + if (this.parentPath.isProperty() && this.key === "key" && parent.shorthand) { + var value = t.identifier(state.newName);; - parent.shorthand = false; - parent.value = value; - parent.key = t.identifier(state.oldName); - } else { - node.name = state.newName; + if (parent.value === state.binding) { + state.info.identifier = state.binding = value; } + + parent.shorthand = false; + parent.value = value; + parent.key = t.identifier(state.oldName); + } else { + node.name = state.newName; } }, diff --git a/src/babel/traversal/visitors/index.js b/src/babel/traversal/visitors.js similarity index 73% rename from src/babel/traversal/visitors/index.js rename to src/babel/traversal/visitors.js index 8ab9e0e55d..4bc805f62f 100644 --- a/src/babel/traversal/visitors/index.js +++ b/src/babel/traversal/visitors.js @@ -1,20 +1,56 @@ -import * as typeWrappers from "./wrappers"; -import * as messages from "../../messages"; -import * as t from "../../types"; +import * as virtualTypes from "./path/virtual-types"; +import * as messages from "../messages"; +import * as t from "../types"; export function explode(visitor, mergeConflicts) { // make sure there's no __esModule type since this is because we're using loose mode // and it sets __esModule to be enumerable on all modules :( delete visitor.__esModule; + // ensure visitors are objects for (let nodeType in visitor) { if (shouldIgnoreKey(nodeType)) continue; var fns = visitor[nodeType]; if (typeof fns === "function") { - visitor[nodeType] = fns = { enter: fns }; + visitor[nodeType] = { enter: fns }; } + } + + // add type wrappers + for (let nodeType in visitor) { + if (shouldIgnoreKey(nodeType)) continue; + + var wrapper = virtualTypes[nodeType]; + if (!wrapper) continue; + + // wrap all the functions + var fns = visitor[nodeType]; + for (var type in fns) { + fns[type] = wrapCheck(wrapper, fns[type]); + } + + // clear it from the visitor + delete visitor[nodeType]; + + if (wrapper.type) { + // merge the visitor if necessary or just put it back in + if (visitor[wrapper.type]) { + merge(visitor[wrapper.type], fns); + } else { + visitor[wrapper.type] = fns; + } + } else { + merge(visitor, fns); + } + } + + // add aliases + for (let nodeType in visitor) { + if (shouldIgnoreKey(nodeType)) continue; + + var fns = visitor[nodeType]; var aliases = t.FLIPPED_ALIAS_KEYS[nodeType]; if (!aliases) continue; @@ -34,30 +70,6 @@ export function explode(visitor, mergeConflicts) { } } - // handle type wrappers - for (let nodeType in visitor) { - if (shouldIgnoreKey(nodeType)) continue; - - var wrapper = typeWrappers[nodeType]; - if (!wrapper) continue; - - // wrap all the functions - var fns = visitor[nodeType]; - for (var type in fns) { - fns[type] = wrapper.wrap(fns[type]); - } - - // clear it from the visitor - delete visitor[nodeType]; - - // merge the visitor if necessary or just put it back in - if (visitor[wrapper.type]) { - merge(visitor[wrapper.type], fns); - } else { - visitor[wrapper.type] = fns; - } - } - return visitor; } @@ -75,7 +87,7 @@ export function verify(visitor) { for (var nodeType in visitor) { if (shouldIgnoreKey(nodeType)) continue; - if (!t.VISITOR_KEYS[nodeType]) { + if (t.TYPES.indexOf(nodeType) < 0) { throw new Error(messages.get("traverseVerifyNodeType", nodeType)); } @@ -94,6 +106,14 @@ export function verify(visitor) { visitor._verified = true; } +function wrapCheck(wrapper, fn) { + return function () { + if (wrapper.checkPath(this)) { + return fn.apply(this, arguments); + } + }; +} + function shouldIgnoreKey(key) { // internal/hidden key if (key[0] === "_") return true; diff --git a/src/babel/traversal/visitors/wrappers.js b/src/babel/traversal/visitors/wrappers.js deleted file mode 100644 index c57a5147ef..0000000000 --- a/src/babel/traversal/visitors/wrappers.js +++ /dev/null @@ -1,10 +0,0 @@ -export var ReferencedIdentifier = { - type: "Identifier", - wrap(fn) { - return function () { - if (this.isReferencedIdentifier()) { - return fn.apply(this, arguments); - } - }; - } -}