diff --git a/lib/6to5/transformation/helpers/name-method.js b/lib/6to5/transformation/helpers/name-method.js index 00d3c77a08..340bf7e27d 100644 --- a/lib/6to5/transformation/helpers/name-method.js +++ b/lib/6to5/transformation/helpers/name-method.js @@ -9,28 +9,31 @@ module.exports = function (node, file, scope) { var id = t.toIdentifier(key.value); key = t.identifier(id); - var selfReference = false; - var outerDeclar = scope.get(id, true); + var state = { + id: id, + selfReference: false, + outerDeclar: scope.get(id, true), + }; traverse(node, { - enter: function (node, parent, scope) { + enter: function (node, parent, scope, context, state) { // check if this node is an identifier that matches the same as our function id - if (!t.isIdentifier(node, { name: id })) return; + if (!t.isIdentifier(node, { name: state.id })) return; // check if this node is the one referenced if (!t.isReferenced(node, parent)) return; // check that we don't have a local variable declared as that removes the need // for the wrapper - var localDeclar = scope.get(id, true); - if (localDeclar !== outerDeclar) return; + var localDeclar = scope.get(state.id, true); + if (localDeclar !== state.outerDeclar) return; - selfReference = true; - this.stop(); + state.selfReference = true; + context.stop(); } - }, scope); + }, scope, state); - if (selfReference) { + if (state.selfReference) { node.value = util.template("property-method-assignment-wrapper", { FUNCTION: node.value, FUNCTION_ID: key, diff --git a/lib/6to5/transformation/helpers/remap-async-to-generator.js b/lib/6to5/transformation/helpers/remap-async-to-generator.js index 86a2c00ba7..7868e86ec7 100644 --- a/lib/6to5/transformation/helpers/remap-async-to-generator.js +++ b/lib/6to5/transformation/helpers/remap-async-to-generator.js @@ -6,8 +6,8 @@ module.exports = function (node, callId) { node.generator = true; traverse(node, { - enter: function (node) { - if (t.isFunction(node)) this.skip(); + enter: function (node, parent, scope, context) { + if (t.isFunction(node)) context.skip(); if (t.isAwaitExpression(node)) { node.type = "YieldExpression"; diff --git a/lib/6to5/transformation/modules/_default.js b/lib/6to5/transformation/modules/_default.js index 42e377b3a8..68040ac8b8 100644 --- a/lib/6to5/transformation/modules/_default.js +++ b/lib/6to5/transformation/modules/_default.js @@ -20,13 +20,13 @@ DefaultFormatter.prototype.getLocalExports = function () { var localExports = {}; traverse(this.file.ast, { - enter: function (node) { + enter: function (node, parent, scope, context, localExports) { var declar = node && node.declaration; if (t.isExportDeclaration(node) && declar && t.isStatement(declar)) { _.extend(localExports, t.getIds(declar, true)); } } - }); + }, null, localExports); return localExports; }; @@ -35,12 +35,12 @@ DefaultFormatter.prototype.getLocalImports = function () { var localImports = {}; traverse(this.file.ast, { - enter: function (node) { + enter: function (node, parent, scope, context, localImports) { if (t.isImportDeclaration(node)) { _.extend(localImports, t.getIds(node, true)); } } - }); + }, null, localImports); return localImports; }; @@ -62,7 +62,7 @@ DefaultFormatter.prototype.checkCollisions = function () { }; traverse(file.ast, { - enter: function (node) { + enter: function (node, parent, scope, context, check) { if (t.isAssignmentExpression(node)) { var left = node.left; @@ -75,7 +75,7 @@ DefaultFormatter.prototype.checkCollisions = function () { _.each(t.getIds(node, true), check); } } - }); + }, null, check); }; DefaultFormatter.prototype.remapExportAssignment = function (node) { @@ -100,9 +100,9 @@ DefaultFormatter.prototype.remapAssignments = function () { }; traverse(this.file.ast, { - enter: function (node, parent, scope) { + enter: function (node, parent, scope, context, isLocalReference) { if (t.isUpdateExpression(node) && isLocalReference(node.argument, scope)) { - this.skip(); + context.skip(); // expand to long file assignment expression var assign = t.assignmentExpression(node.operator[0] + "=", node.argument, t.literal(1)); @@ -130,11 +130,11 @@ DefaultFormatter.prototype.remapAssignments = function () { } if (t.isAssignmentExpression(node) && isLocalReference(node.left, scope)) { - this.skip(); + context.skip(); return self.remapExportAssignment(node); } } - }); + }, null, isLocalReference); }; DefaultFormatter.prototype.getModuleName = function () { diff --git a/lib/6to5/transformation/modules/system.js b/lib/6to5/transformation/modules/system.js index b70c8423a4..d0650a69c2 100644 --- a/lib/6to5/transformation/modules/system.js +++ b/lib/6to5/transformation/modules/system.js @@ -63,28 +63,31 @@ SystemFormatter.prototype.importSpecifier = function (specifier, node, nodes) { SystemFormatter.prototype.buildRunnerSetters = function (block, hoistDeclarators) { return t.arrayExpression(_.map(this.ids, function (uid, source) { - var nodes = []; + var state = { + nodes: [], + hoistDeclarators: hoistDeclarators + }; traverse(block, { - enter: function (node) { + enter: function (node, parent, scope, context, state) { if (node._importSource === source) { if (t.isVariableDeclaration(node)) { _.each(node.declarations, function (declar) { - hoistDeclarators.push(t.variableDeclarator(declar.id)); - nodes.push(t.expressionStatement( + state.hoistDeclarators.push(t.variableDeclarator(declar.id)); + state.nodes.push(t.expressionStatement( t.assignmentExpression("=", declar.id, declar.init) )); }); } else { - nodes.push(node); + state.nodes.push(node); } - this.remove(); + context.remove(); } } - }); + }, null, state); - return t.functionExpression(null, [uid], t.blockStatement(nodes)); + return t.functionExpression(null, [uid], t.blockStatement(state.nodes)); })); }; @@ -112,10 +115,10 @@ SystemFormatter.prototype.transform = function (ast) { // hoist up all variable declarations traverse(block, { - enter: function (node, parent, scope) { + enter: function (node, parent, scope, context, hoistDeclarators) { if (t.isFunction(node)) { // nothing inside is accessible - return this.skip(); + return context.skip(); } if (t.isVariableDeclaration(node)) { @@ -150,7 +153,8 @@ SystemFormatter.prototype.transform = function (ast) { return nodes; } } - }); + }, null, hoistDeclarators); + if (hoistDeclarators.length) { var hoistDeclar = t.variableDeclaration("var", hoistDeclarators); hoistDeclar._blockHoist = true; @@ -159,15 +163,15 @@ SystemFormatter.prototype.transform = function (ast) { // hoist up function declarations for circular references traverse(block, { - enter: function (node) { - if (t.isFunction(node)) this.skip(); + enter: function (node, parent, scope, context, handlerBody) { + if (t.isFunction(node)) context.skip(); if (t.isFunctionDeclaration(node) || node._blockHoist) { handlerBody.push(node); - this.remove(); + context.remove(); } } - }); + }, null, handlerBody); handlerBody.push(returnStatement); diff --git a/lib/6to5/transformation/transformer.js b/lib/6to5/transformation/transformer.js index f4de701b51..dbcd0f9d10 100644 --- a/lib/6to5/transformation/transformer.js +++ b/lib/6to5/transformation/transformer.js @@ -4,6 +4,25 @@ var traverse = require("../traverse"); var t = require("../types"); var _ = require("lodash"); +function noop() { } + +function enter(node, parent, scope, context, args) { + var fns = args[1][node.type]; + if (!fns) return; + return fns.enter(node, parent, scope, context, args[0]); +} + +function exit(node, parent, scope, context, args) { + var fns = args[1][node.type]; + if (!fns) return; + return fns.exit(node, parent, scope, context, args[0]); +} + +var traverseOpts = { + enter: enter, + exit: exit +}; + function Transformer(key, transformer, opts) { this.manipulateOptions = transformer.manipulateOptions; this.experimental = !!transformer.experimental; @@ -32,6 +51,9 @@ Transformer.prototype.normalise = function (transformer) { if (!_.isObject(fns)) return; + if (!fns.enter) fns.enter = noop; + if (!fns.exit) fns.exit = noop; + transformer[type] = fns; var aliases = t.FLIPPED_ALIAS_KEYS[type]; @@ -54,28 +76,8 @@ Transformer.prototype.astRun = function (file, key) { }; Transformer.prototype.transform = function (file) { - var transformer = this.transformer; - - var build = function (exit) { - return function (node, parent, scope) { - var fns = transformer[node.type]; - if (!fns) return; - - var fn = fns.enter; - if (exit) fn = fns.exit; - if (!fn) return; - - return fn.call(this, node, parent, file, scope); - }; - }; - this.astRun(file, "before"); - - traverse(file.ast, { - enter: build(), - exit: build(true) - }); - + traverse(file.ast, traverseOpts, null, [file, this.transformer]); this.astRun(file, "after"); }; diff --git a/lib/6to5/transformation/transformers/_alias-functions.js b/lib/6to5/transformation/transformers/_alias-functions.js index 5cf9003c84..bc52a45ee8 100644 --- a/lib/6to5/transformation/transformers/_alias-functions.js +++ b/lib/6to5/transformation/transformers/_alias-functions.js @@ -5,22 +5,23 @@ var go = function (getBody, node, file, scope) { var argumentsId; var thisId; - var getArgumentsId = function () { - return argumentsId = argumentsId || file.generateUidIdentifier("arguments", scope); - }; - - var getThisId = function () { - return thisId = thisId || file.generateUidIdentifier("this", scope); + var state = { + getArgumentsId: function () { + return argumentsId = argumentsId || file.generateUidIdentifier("arguments", scope); + }, + getThisId: function () { + return thisId = thisId || file.generateUidIdentifier("this", scope); + } }; // traverse the function and find all alias functions so we can alias // `arguments` and `this` if necessary traverse(node, { - enter: function (node) { + enter: function (node, parent, scope, context, state) { if (!node._aliasFunction) { if (t.isFunction(node)) { // stop traversal of this node as it'll be hit again by this transformer - return this.skip(); + return context.skip(); } else { return; } @@ -28,30 +29,30 @@ var go = function (getBody, node, file, scope) { // traverse all child nodes of this function and find `arguments` and `this` traverse(node, { - enter: function (node, parent) { + enter: function (node, parent, scope, context, state) { if (t.isFunction(node) && !node._aliasFunction) { - return this.skip(); + return context.skip(); } - if (node._ignoreAliasFunctions) return this.skip(); + if (node._ignoreAliasFunctions) return context.skip(); var getId; if (t.isIdentifier(node) && node.name === "arguments") { - getId = getArgumentsId; + getId = state.getArgumentsId; } else if (t.isThisExpression(node)) { - getId = getThisId; + getId = state.getThisId; } else { return; } if (t.isReferenced(node, parent)) return getId(); } - }); + }, null, state); - return this.skip(); + return context.skip(); } - }); + }, null, state); var body; @@ -71,14 +72,14 @@ var go = function (getBody, node, file, scope) { } }; -exports.Program = function (node, parent, file, scope) { +exports.Program = function (node, parent, scope, context, file) { go(function () { return node.body; }, node, file, scope); }; exports.FunctionDeclaration = -exports.FunctionExpression = function (node, parent, file, scope) { +exports.FunctionExpression = function (node, parent, scope, context, file) { go(function () { t.ensureBlock(node); return node.body.body; diff --git a/lib/6to5/transformation/transformers/es6-classes.js b/lib/6to5/transformation/transformers/es6-classes.js index dc079ca164..18f24c4031 100644 --- a/lib/6to5/transformation/transformers/es6-classes.js +++ b/lib/6to5/transformation/transformers/es6-classes.js @@ -3,11 +3,11 @@ var traverse = require("../../traverse"); var util = require("../../util"); var t = require("../../types"); -exports.ClassDeclaration = function (node, parent, file, scope) { +exports.ClassDeclaration = function (node, parent, scope, context, file) { return new Class(node, file, scope, true).run(); }; -exports.ClassExpression = function (node, parent, file, scope) { +exports.ClassExpression = function (node, parent, scope, context, file) { if (!node.id) { if (t.isProperty(parent) && parent.value === node && !parent.computed && t.isIdentifier(parent.key)) { // var o = { foo: class {} }; @@ -316,16 +316,16 @@ Class.prototype.replaceSuperReferences = function (methodNode) { function traverseLevel(node, topLevel) { traverse(node, { - enter: function (node, parent) { + enter: function (node, parent, scope, context) { if (t.isFunction(node) && !t.isArrowFunctionExpression(node)) { // we need to call traverseLevel again so we're context aware traverseLevel(node, false); - return this.skip(); + return context.skip(); } if (t.isProperty(node, { method: true }) || t.isMethodDefinition(node)) { // break on object methods - return this.skip(); + return context.skip(); } var getThisReference = function () { diff --git a/lib/6to5/transformation/transformers/es6-computed-property-names.js b/lib/6to5/transformation/transformers/es6-computed-property-names.js index dcfff3b827..3789ca1d23 100644 --- a/lib/6to5/transformation/transformers/es6-computed-property-names.js +++ b/lib/6to5/transformation/transformers/es6-computed-property-names.js @@ -1,6 +1,6 @@ var t = require("../../types"); -exports.ObjectExpression = function (node, parent, file, scope) { +exports.ObjectExpression = function (node, parent, scope, context, file) { var hasComputed = false; for (var i = 0; i < node.properties.length; i++) { diff --git a/lib/6to5/transformation/transformers/es6-constants.js b/lib/6to5/transformation/transformers/es6-constants.js index dc8e0ce49e..096eee9097 100644 --- a/lib/6to5/transformation/transformers/es6-constants.js +++ b/lib/6to5/transformation/transformers/es6-constants.js @@ -6,7 +6,7 @@ exports.Program = exports.BlockStatement = exports.ForInStatement = exports.ForOfStatement = -exports.ForStatement = function (node, parent, file) { +exports.ForStatement = function (node, parent, scope, context, file) { var hasConstants = false; var constants = {}; @@ -69,14 +69,19 @@ exports.ForStatement = function (node, parent, file) { if (!hasConstants) return; + var state = { + check: check, + getIds: getIds + }; + traverse(node, { - enter: function (child, parent, scope) { + enter: function (child, parent, scope, context, state) { if (child._ignoreConstant) return; if (t.isVariableDeclaration(child)) return; if (t.isVariableDeclarator(child) || t.isDeclaration(child) || t.isAssignmentExpression(child)) { - check(parent, getIds(child), scope); + state.check(parent, state.getIds(child), scope); } } - }); + }, null, state); }; diff --git a/lib/6to5/transformation/transformers/es6-default-parameters.js b/lib/6to5/transformation/transformers/es6-default-parameters.js index 0d99b6db8e..4b03d56295 100644 --- a/lib/6to5/transformation/transformers/es6-default-parameters.js +++ b/lib/6to5/transformation/transformers/es6-default-parameters.js @@ -2,7 +2,7 @@ var traverse = require("../../traverse"); var util = require("../../util"); var t = require("../../types"); -exports.Function = function (node, parent, file, scope) { +exports.Function = function (node, parent, scope, context, file) { if (!node.defaults || !node.defaults.length) return; t.ensureBlock(node); diff --git a/lib/6to5/transformation/transformers/es6-destructuring.js b/lib/6to5/transformation/transformers/es6-destructuring.js index 22d016b38f..bcd04ba4b2 100644 --- a/lib/6to5/transformation/transformers/es6-destructuring.js +++ b/lib/6to5/transformation/transformers/es6-destructuring.js @@ -148,7 +148,7 @@ var pushPattern = function (opts) { }; exports.ForInStatement = -exports.ForOfStatement = function (node, parent, file, scope) { +exports.ForOfStatement = function (node, parent, scope, context, file) { var declar = node.left; if (!t.isVariableDeclaration(declar)) return; @@ -174,7 +174,7 @@ exports.ForOfStatement = function (node, parent, file, scope) { block.body = nodes.concat(block.body); }; -exports.Function = function (node, parent, file, scope) { +exports.Function = function (node, parent, scope, context, file) { var nodes = []; var hasDestructuring = false; @@ -205,7 +205,7 @@ exports.Function = function (node, parent, file, scope) { block.body = nodes.concat(block.body); }; -exports.CatchClause = function (node, parent, file, scope) { +exports.CatchClause = function (node, parent, scope, context, file) { var pattern = node.param; if (!t.isPattern(pattern)) return; @@ -223,7 +223,7 @@ exports.CatchClause = function (node, parent, file, scope) { node.body.body = nodes.concat(node.body.body); }; -exports.ExpressionStatement = function (node, parent, file, scope) { +exports.ExpressionStatement = function (node, parent, scope, context, file) { var expr = node.expression; if (expr.type !== "AssignmentExpression") return; @@ -245,7 +245,7 @@ exports.ExpressionStatement = function (node, parent, file, scope) { return nodes; }; -exports.AssignmentExpression = function (node, parent, file, scope) { +exports.AssignmentExpression = function (node, parent, scope, context, file) { if (parent.type === "ExpressionStatement") return; if (!t.isPattern(node.left)) return; @@ -270,7 +270,7 @@ exports.AssignmentExpression = function (node, parent, file, scope) { return t.toSequenceExpression(nodes, scope); }; -exports.VariableDeclaration = function (node, parent, file, scope) { +exports.VariableDeclaration = function (node, parent, scope, context, file) { if (t.isForInStatement(parent) || t.isForOfStatement(parent)) return; var nodes = []; diff --git a/lib/6to5/transformation/transformers/es6-for-of.js b/lib/6to5/transformation/transformers/es6-for-of.js index 61357cfbad..9018e995af 100644 --- a/lib/6to5/transformation/transformers/es6-for-of.js +++ b/lib/6to5/transformation/transformers/es6-for-of.js @@ -1,11 +1,11 @@ var util = require("../../util"); var t = require("../../types"); -exports.ForOfStatement = function (node, parent, file, scope) { +exports.ForOfStatement = function (node, parent, scope, context, file) { var callback = spec; if (file.isLoose("forOf")) callback = loose; - var build = callback(node, parent, file, scope); + var build = callback(node, parent, scope, context, file); var declar = build.declar; var loop = build.loop; var block = loop.body; @@ -31,7 +31,7 @@ exports.ForOfStatement = function (node, parent, file, scope) { return loop; }; -var loose = function (node, parent, file, scope) { +var loose = function (node, parent, scope, context, file) { var left = node.left; var declar, id; @@ -63,7 +63,7 @@ var loose = function (node, parent, file, scope) { }; }; -var spec = function (node, parent, file, scope) { +var spec = function (node, parent, scope, context, file) { var left = node.left; var declar; diff --git a/lib/6to5/transformation/transformers/es6-let-scoping.js b/lib/6to5/transformation/transformers/es6-let-scoping.js index bdce7be036..7c589851cd 100644 --- a/lib/6to5/transformation/transformers/es6-let-scoping.js +++ b/lib/6to5/transformation/transformers/es6-let-scoping.js @@ -34,7 +34,7 @@ exports.VariableDeclaration = function (node, parent) { isLet(node, parent); }; -exports.Loop = function (node, parent, file, scope) { +exports.Loop = function (node, parent, scope, context, file) { var init = node.left || node.init; if (isLet(init, node)) { t.ensureBlock(node); @@ -46,7 +46,7 @@ exports.Loop = function (node, parent, file, scope) { node.label = parent.label; } - var letScoping = new LetScoping(node, node.body, parent, file, scope); + var letScoping = new LetScoping(node, node.body, parent, scope, file); letScoping.run(); if (node.label && !t.isLabeledStatement(parent)) { @@ -55,9 +55,9 @@ exports.Loop = function (node, parent, file, scope) { } }; -exports.BlockStatement = function (block, parent, file, scope) { +exports.BlockStatement = function (block, parent, scope, context, file) { if (!t.isLoop(parent)) { - var letScoping = new LetScoping(false, block, parent, file, scope); + var letScoping = new LetScoping(false, block, parent, scope, file); letScoping.run(); } }; @@ -68,11 +68,11 @@ exports.BlockStatement = function (block, parent, file, scope) { * @param {Boolean|Node} loopParent * @param {Node} block * @param {Node} parent - * @param {File} file * @param {Scope} scope + * @param {File} file */ -function LetScoping(loopParent, block, parent, file, scope) { +function LetScoping(loopParent, block, parent, scope, file) { this.loopParent = loopParent; this.parent = parent; this.scope = scope; @@ -167,7 +167,7 @@ LetScoping.prototype.remap = function () { if (!this.info.hasDuplicates) return; - var replace = function (node, parent, scope) { + var replace = function (node, parent, scope, context, replacements) { if (!t.isIdentifier(node)) return; if (!t.isReferenced(node, parent)) return; if (scope && scope.hasOwn(node.name)) return; @@ -176,7 +176,7 @@ LetScoping.prototype.remap = function () { var traverseReplace = function (node, parent) { replace(node, parent); - traverse(node, { enter: replace }); + traverse(node, { enter: replace }, null, replacements); }; var loopParent = this.loopParent; @@ -186,7 +186,7 @@ LetScoping.prototype.remap = function () { traverseReplace(loopParent.update, loopParent); } - traverse(block, { enter: replace }); + traverse(block, { enter: replace }, null, replacements); }; /** @@ -277,11 +277,11 @@ LetScoping.prototype.checkLoop = function () { }; traverse(this.block, { - enter: function (node, parent) { + enter: function (node, parent, scope, context) { var replace; if (t.isFunction(node) || t.isLoop(node)) { - return this.skip(); + return context.skip(); } if (node && !node.label) { @@ -305,7 +305,7 @@ LetScoping.prototype.checkLoop = function () { if (replace) return t.inherits(replace, node); } - }); + }, null, has); return has; }; @@ -316,9 +316,8 @@ LetScoping.prototype.checkLoop = function () { */ LetScoping.prototype.hoistVarDeclarations = function () { - var self = this; traverse(this.block, { - enter: function (node, parent) { + enter: function (node, parent, scope, context, self) { if (t.isForStatement(node)) { if (isVar(node.init, node)) { node.init = t.sequenceExpression(self.pushDeclar(node.init)); @@ -330,10 +329,10 @@ LetScoping.prototype.hoistVarDeclarations = function () { } else if (isVar(node, parent)) { return self.pushDeclar(node).map(t.expressionStatement); } else if (t.isFunction(node)) { - return this.skip(); + return context.skip(); } } - }); + }, null, this); }; /** @@ -359,13 +358,15 @@ LetScoping.prototype.getParams = function (params) { */ LetScoping.prototype.getLetReferences = function () { - var closurify = false; - var self = this; + var state = { + self: this, + closurify: false + }; // traverse through this block, stopping on functions and checking if they // contain any outside let references traverse(this.block, { - enter: function (node, parent, scope) { + enter: function (node, parent, scope, context, state) { if (t.isFunction(node)) { traverse(node, { enter: function (node, parent) { @@ -379,22 +380,22 @@ LetScoping.prototype.getLetReferences = function () { // to our let scope if (scope.hasOwn(node.name, true)) return; - closurify = true; + state.closurify = true; // this key doesn't appear just outside our scope - if (!_.contains(self.info.outsideKeys, node.name)) return; + if (!_.contains(state.self.info.outsideKeys, node.name)) return; // push this badboy - self.letReferences[node.name] = node; + state.self.letReferences[node.name] = node; } - }); + }, null, state); - return this.skip(); + return context.skip(); } } - }); + }, null, state); - return closurify; + return state.closurify; }; /** diff --git a/lib/6to5/transformation/transformers/es6-modules.js b/lib/6to5/transformation/transformers/es6-modules.js index 9d9631095b..a9b60473f2 100644 --- a/lib/6to5/transformation/transformers/es6-modules.js +++ b/lib/6to5/transformation/transformers/es6-modules.js @@ -6,7 +6,7 @@ exports.ast = { } }; -exports.ImportDeclaration = function (node, parent, file) { +exports.ImportDeclaration = function (node, parent, scope, context, file) { var nodes = []; if (node.specifiers.length) { @@ -26,7 +26,7 @@ exports.ImportDeclaration = function (node, parent, file) { return nodes; }; -exports.ExportDeclaration = function (node, parent, file) { +exports.ExportDeclaration = function (node, parent, scope, context, file) { var nodes = []; if (node.declaration) { diff --git a/lib/6to5/transformation/transformers/es6-property-method-assignment.js b/lib/6to5/transformation/transformers/es6-property-method-assignment.js index 30a54102a5..7237ef1c4f 100644 --- a/lib/6to5/transformation/transformers/es6-property-method-assignment.js +++ b/lib/6to5/transformation/transformers/es6-property-method-assignment.js @@ -2,7 +2,7 @@ var nameMethod = require("../helpers/name-method"); var util = require("../../util"); var t = require("../../types"); -exports.Property = function (node, parent, file, scope) { +exports.Property = function (node, parent, scope, context, file) { if (!node.method) return; node.method = false; diff --git a/lib/6to5/transformation/transformers/es6-rest-parameters.js b/lib/6to5/transformation/transformers/es6-rest-parameters.js index 5bfcf88601..f9ec265c9e 100644 --- a/lib/6to5/transformation/transformers/es6-rest-parameters.js +++ b/lib/6to5/transformation/transformers/es6-rest-parameters.js @@ -1,7 +1,7 @@ var util = require("../../util"); var t = require("../../types"); -exports.Function = function (node, parent, file) { +exports.Function = function (node, parent, scope, context, file) { if (!node.rest) return; var rest = node.rest; diff --git a/lib/6to5/transformation/transformers/es6-spread.js b/lib/6to5/transformation/transformers/es6-spread.js index 8547bfa550..4fa64b5e2b 100644 --- a/lib/6to5/transformation/transformers/es6-spread.js +++ b/lib/6to5/transformation/transformers/es6-spread.js @@ -40,7 +40,7 @@ var build = function (props, file) { return nodes; }; -exports.ArrayExpression = function (node, parent, file) { +exports.ArrayExpression = function (node, parent, scope, context, file) { var elements = node.elements; if (!hasSpread(elements)) return; @@ -55,7 +55,7 @@ exports.ArrayExpression = function (node, parent, file) { return t.callExpression(t.memberExpression(first, t.identifier("concat")), nodes); }; -exports.CallExpression = function (node, parent, file, scope) { +exports.CallExpression = function (node, parent, scope, context, file) { var args = node.arguments; if (!hasSpread(args)) return; @@ -95,7 +95,7 @@ exports.CallExpression = function (node, parent, file, scope) { node.arguments.unshift(contextLiteral); }; -exports.NewExpression = function (node, parent, file) { +exports.NewExpression = function (node, parent, scope, context, file) { var args = node.arguments; if (!hasSpread(args)) return; diff --git a/lib/6to5/transformation/transformers/es6-template-literals.js b/lib/6to5/transformation/transformers/es6-template-literals.js index 8ad54914f7..d2f360b104 100644 --- a/lib/6to5/transformation/transformers/es6-template-literals.js +++ b/lib/6to5/transformation/transformers/es6-template-literals.js @@ -4,7 +4,7 @@ var buildBinaryExpression = function (left, right) { return t.binaryExpression("+", left, right); }; -exports.TaggedTemplateExpression = function (node, parent, file) { +exports.TaggedTemplateExpression = function (node, parent, scope, context, file) { var args = []; var quasi = node.quasi; diff --git a/lib/6to5/transformation/transformers/es7-abstract-references.js b/lib/6to5/transformation/transformers/es7-abstract-references.js index 89bc7c05e4..39003b914c 100644 --- a/lib/6to5/transformation/transformers/es7-abstract-references.js +++ b/lib/6to5/transformation/transformers/es7-abstract-references.js @@ -21,7 +21,7 @@ var container = function (parent, call, ret) { } }; -exports.AssignmentExpression = function (node, parent, file, scope) { +exports.AssignmentExpression = function (node, parent, scope, context, file) { var left = node.left; if (!t.isVirtualPropertyExpression(left)) return; @@ -74,7 +74,7 @@ exports.UnaryExpression = function (node, parent) { return container(parent, call, t.literal(true)); }; -exports.CallExpression = function (node, parent, file, scope) { +exports.CallExpression = function (node, parent, scope, context, file) { var callee = node.callee; if (!t.isVirtualPropertyExpression(callee)) return; diff --git a/lib/6to5/transformation/transformers/es7-array-comprehension.js b/lib/6to5/transformation/transformers/es7-array-comprehension.js index 0a18a94e8a..e50b135d0b 100644 --- a/lib/6to5/transformation/transformers/es7-array-comprehension.js +++ b/lib/6to5/transformation/transformers/es7-array-comprehension.js @@ -5,7 +5,7 @@ var t = require("../../types"); exports.experimental = true; -var build = function (node, parent, file, scope) { +var build = function (node, parent, scope, context, file) { var uid = scope.generateUidBasedOnNode(parent, file); var container = util.template("array-comprehension-container", { @@ -34,8 +34,8 @@ var build = function (node, parent, file, scope) { return container; }; -exports.ComprehensionExpression = function (node, parent, file, scope) { +exports.ComprehensionExpression = function (node, parent, scope, context, file) { if (node.generator) return; - return build(node, parent, file, scope); + return build(node, parent, scope, context, file); }; diff --git a/lib/6to5/transformation/transformers/es7-object-spread.js b/lib/6to5/transformation/transformers/es7-object-spread.js index a9ffecaf03..083bbdc166 100644 --- a/lib/6to5/transformation/transformers/es7-object-spread.js +++ b/lib/6to5/transformation/transformers/es7-object-spread.js @@ -4,7 +4,7 @@ var t = require("../../types"); exports.experimental = true; -exports.ObjectExpression = function (node, parent, file) { +exports.ObjectExpression = function (node, parent, scope, context, file) { var hasSpread = false; var i; var prop; diff --git a/lib/6to5/transformation/transformers/optional-async-to-generator.js b/lib/6to5/transformation/transformers/optional-async-to-generator.js index 143445a9c2..d6f952783c 100644 --- a/lib/6to5/transformation/transformers/optional-async-to-generator.js +++ b/lib/6to5/transformation/transformers/optional-async-to-generator.js @@ -5,7 +5,7 @@ exports.optional = true; exports.manipulateOptions = bluebirdCoroutines.manipulateOptions; -exports.Function = function (node, parent, file) { +exports.Function = function (node, parent, scope, context, file) { if (!node.async || node.generator) return; return remapAsyncToGenerator(node, file.addHelper("async-to-generator")); diff --git a/lib/6to5/transformation/transformers/optional-bluebird-coroutines.js b/lib/6to5/transformation/transformers/optional-bluebird-coroutines.js index 5338fa1e1c..73b9db1fd7 100644 --- a/lib/6to5/transformation/transformers/optional-bluebird-coroutines.js +++ b/lib/6to5/transformation/transformers/optional-bluebird-coroutines.js @@ -9,7 +9,7 @@ exports.manipulateOptions = function (opts) { exports.optional = true; -exports.Function = function (node, parent, file) { +exports.Function = function (node, parent, scope, context, file) { if (!node.async || node.generator) return; return remapAsyncToGenerator( diff --git a/lib/6to5/transformation/transformers/optional-core-aliasing.js b/lib/6to5/transformation/transformers/optional-core-aliasing.js index 3610ae8f62..d563bb619a 100644 --- a/lib/6to5/transformation/transformers/optional-core-aliasing.js +++ b/lib/6to5/transformation/transformers/optional-core-aliasing.js @@ -26,7 +26,7 @@ exports.ast = { exit: function (ast, file) { traverse(ast, { - enter: function (node, parent) { + enter: function (node, parent, scope, context) { var prop; if (t.isMemberExpression(node) && t.isReferenced(node, parent)) { @@ -37,7 +37,7 @@ exports.ast = { if (!t.isReferenced(obj, node)) return; if (!node.computed && coreHas(obj) && _.has(core[obj.name], prop.name)) { - this.skip(); + context.skip(); return t.prependToMemberExpression(node, file._coreId); } } else if (t.isIdentifier(node) && !t.isMemberExpression(parent) && t.isReferenced(node, parent) && _.contains(ALIASABLE_CONSTRUCTORS, node.name)) { diff --git a/lib/6to5/transformation/transformers/optional-proto-to-assign.js b/lib/6to5/transformation/transformers/optional-proto-to-assign.js index e3f0a78022..73a9c8167d 100644 --- a/lib/6to5/transformation/transformers/optional-proto-to-assign.js +++ b/lib/6to5/transformation/transformers/optional-proto-to-assign.js @@ -17,7 +17,7 @@ var buildDefaultsCallExpression = function (expr, ref, file) { exports.optional = true; exports.secondPass = true; -exports.AssignmentExpression = function (node, parent, file, scope) { +exports.AssignmentExpression = function (node, parent, scope, context, file) { if (t.isExpressionStatement(parent)) return; if (!isProtoAssignmentExpression(node)) return; @@ -32,7 +32,7 @@ exports.AssignmentExpression = function (node, parent, file, scope) { return t.toSequenceExpression(nodes); }; -exports.ExpressionStatement = function (node, parent, file) { +exports.ExpressionStatement = function (node, parent, scope, context, file) { var expr = node.expression; if (!t.isAssignmentExpression(expr, { operator: "=" })) return; @@ -41,7 +41,7 @@ exports.ExpressionStatement = function (node, parent, file) { } }; -exports.ObjectExpression = function (node, parent, file) { +exports.ObjectExpression = function (node, parent, scope, context, file) { var proto; for (var i = 0; i < node.properties.length; i++) { diff --git a/lib/6to5/transformation/transformers/optional-typeof-symbol.js b/lib/6to5/transformation/transformers/optional-typeof-symbol.js index 36b8458936..1c1aed8cc1 100644 --- a/lib/6to5/transformation/transformers/optional-typeof-symbol.js +++ b/lib/6to5/transformation/transformers/optional-typeof-symbol.js @@ -2,8 +2,8 @@ var t = require("../../types"); exports.optional = true; -exports.UnaryExpression = function (node, parent, file) { - this.skip(); +exports.UnaryExpression = function (node, parent, scope, context, file) { + context.skip(); if (node.operator === "typeof") { var call = t.callExpression(file.addHelper("typeof"), [node.argument]); diff --git a/lib/6to5/transformation/transformers/playground-memoization-operator.js b/lib/6to5/transformation/transformers/playground-memoization-operator.js index 1f5fa4bb9f..1bd6d004ee 100644 --- a/lib/6to5/transformation/transformers/playground-memoization-operator.js +++ b/lib/6to5/transformation/transformers/playground-memoization-operator.js @@ -48,7 +48,7 @@ var buildAssignment = function (expr, obj, prop) { return t.assignmentExpression("=", buildAbsoluteRef(expr.left, obj, prop), expr.right); }; -exports.ExpressionStatement = function (node, parent, file, scope) { +exports.ExpressionStatement = function (node, parent, scope, context, file) { var expr = node.expression; if (!isMemo(expr)) return; @@ -66,7 +66,7 @@ exports.ExpressionStatement = function (node, parent, file, scope) { return nodes; }; -exports.AssignmentExpression = function (node, parent, file, scope) { +exports.AssignmentExpression = function (node, parent, scope, context, file) { if (t.isExpressionStatement(parent)) return; if (!isMemo(node)) return; diff --git a/lib/6to5/transformation/transformers/playground-method-binding.js b/lib/6to5/transformation/transformers/playground-method-binding.js index 6c6bd9e37b..4f3fe3bb51 100644 --- a/lib/6to5/transformation/transformers/playground-method-binding.js +++ b/lib/6to5/transformation/transformers/playground-method-binding.js @@ -1,6 +1,6 @@ var t = require("../../types"); -exports.BindMemberExpression = function (node, parent, file, scope) { +exports.BindMemberExpression = function (node, parent, scope, context, file) { var object = node.object; var prop = node.property; @@ -22,7 +22,7 @@ exports.BindMemberExpression = function (node, parent, file, scope) { } }; -exports.BindFunctionExpression = function (node, parent, file, scope) { +exports.BindFunctionExpression = function (node, parent, scope, context, file) { var buildCall = function (args) { var param = file.generateUidIdentifier("val", scope); return t.functionExpression(null, [param], t.blockStatement([ diff --git a/lib/6to5/transformation/transformers/playground-object-getter-memoization.js b/lib/6to5/transformation/transformers/playground-object-getter-memoization.js index 9c3f0bb6d0..08af63ea57 100644 --- a/lib/6to5/transformation/transformers/playground-object-getter-memoization.js +++ b/lib/6to5/transformation/transformers/playground-object-getter-memoization.js @@ -2,7 +2,7 @@ var traverse = require("../../traverse"); var t = require("../../types"); exports.Property = -exports.MethodDefinition = function (node, parent, file) { +exports.MethodDefinition = function (node, parent, scope, context, file) { if (node.kind !== "memo") return; node.kind = "get"; diff --git a/lib/6to5/transformation/transformers/react.js b/lib/6to5/transformation/transformers/react.js index 41b1ae4e14..79bca15c19 100644 --- a/lib/6to5/transformation/transformers/react.js +++ b/lib/6to5/transformation/transformers/react.js @@ -15,7 +15,7 @@ exports.XJSIdentifier = function (node) { } }; -exports.XJSNamespacedName = function (node, parent, file) { +exports.XJSNamespacedName = function (node, parent, scope, context, file) { throw file.errorWithNode(node, "Namespace tags are not supported. ReactJSX is not XML."); }; @@ -42,7 +42,7 @@ var isTag = function(tagName) { }; exports.XJSOpeningElement = { - exit: function (node, parent, file) { + exit: function (node, parent, scope, context, file) { var reactCompat = file.opts.reactCompat; var tagExpr = node.name; var args = []; diff --git a/lib/6to5/transformation/transformers/spec-no-for-in-of-assignment.js b/lib/6to5/transformation/transformers/spec-no-for-in-of-assignment.js index 5af12108f1..0b00e88f9d 100644 --- a/lib/6to5/transformation/transformers/spec-no-for-in-of-assignment.js +++ b/lib/6to5/transformation/transformers/spec-no-for-in-of-assignment.js @@ -1,7 +1,7 @@ var t = require("../../types"); exports.ForInStatement = -exports.ForOfStatement = function (node, parent, file) { +exports.ForOfStatement = function (node, parent, scope, context, file) { var left = node.left; if (t.isVariableDeclaration(left)) { var declar = left.declarations[0]; diff --git a/lib/6to5/transformation/transformers/spec-setters.js b/lib/6to5/transformation/transformers/spec-setters.js index 30b404ed1e..54d88827aa 100644 --- a/lib/6to5/transformation/transformers/spec-setters.js +++ b/lib/6to5/transformation/transformers/spec-setters.js @@ -1,5 +1,5 @@ exports.MethodDefinition = -exports.Property = function (node, parent, file) { +exports.Property = function (node, parent, scope, context, file) { if (node.kind === "set" && node.value.params.length !== 1) { throw file.errorWithNode(node.value, "Setters must have only one parameter"); } diff --git a/lib/6to5/traverse/index.js b/lib/6to5/traverse/index.js index 625905944d..7307a7665d 100644 --- a/lib/6to5/traverse/index.js +++ b/lib/6to5/traverse/index.js @@ -1,9 +1,13 @@ module.exports = traverse; +/* jshint maxparams:7 */ + var Scope = require("./scope"); var t = require("../types"); var _ = require("lodash"); +function noop() { } + function TraversalContext() { this.didSkip = false; this.didRemove = false; @@ -35,7 +39,7 @@ TraversalContext.prototype.reset = function () { this.didRemove = false; }; -TraversalContext.prototype.maybeReplace = function (result, obj, key, node) { +function replaceNode(obj, key, node, result) { var isArray = Array.isArray(result); // inherit comments from original node to the first replacement node @@ -44,7 +48,7 @@ TraversalContext.prototype.maybeReplace = function (result, obj, key, node) { if (inheritTo) t.inheritsComments(inheritTo, node); // replace the node - node = obj[key] = result; + obj[key] = result; // we're replacing a statement or block node with an array of statements so we better // ensure that it's a block @@ -53,11 +57,48 @@ TraversalContext.prototype.maybeReplace = function (result, obj, key, node) { } if (isArray) { - this.flatten(); + return true; } +} + +TraversalContext.prototype.enterNode = function (obj, key, node, enter, parent, scope, state) { + var result = enter(node, parent, scope, this, state); + var flatten = false; + + if (result) { + flatten = replaceNode(obj, key, node, result); + node = result; + + if (flatten) { + this.didFlatten = true; + } + } + + if (this.didRemove) { + obj[key] = null; + this.didFlatten = true; + } + + return node; }; -TraversalContext.prototype.visitNode = function (obj, key, opts, scope, parent) { +TraversalContext.prototype.exitNode = function (obj, key, node, exit, parent, scope, state) { + var result = exit(node, parent, scope, this, state); + var flatten = false; + + if (result) { + flatten = replaceNode(obj, key, node, result); + node = result; + + if (flatten) { + this.didFlatten = true; + } + } + + return node; +}; + +TraversalContext.prototype.visitNode = function (obj, key, opts, scope, parent, state) { this.reset(); var node = obj[key]; @@ -70,48 +111,23 @@ TraversalContext.prototype.visitNode = function (obj, key, opts, scope, parent) if (t.isScope(node)) ourScope = new Scope(node, scope); - var result; + node = this.enterNode(obj, key, node, opts.enter, parent, ourScope, state); - // enter - if (opts.enter) { - result = opts.enter.call(this, node, parent, ourScope); + if (this.didSkip) + return this.didStop; - if (result) { - this.maybeReplace(result, obj, key, node); - node = obj[key]; - } - - if (this.didRemove) { - node = obj[key] = null; - this.flatten(); - } - - // stop traversal - if (this.didSkip) - return this.didStop; - } - - // traverse node - traverseNode(node, opts, ourScope); - - // exit - if (opts.exit) { - result = opts.exit.call(this, node, parent, ourScope); - - if (result) { - this.maybeReplace(result, obj, key, node); - } - } + traverseNode(node, opts, ourScope, state); + this.exitNode(obj, key, node, opts.exit, parent, ourScope, state); return this.didStop; }; -TraversalContext.prototype.visit = function (node, key, opts, scope) { +TraversalContext.prototype.visit = function (node, key, opts, scope, state) { var nodes = node[key]; if (!nodes) return; if (!Array.isArray(nodes)) { - return this.visitNode(node, key, opts, scope, node); + return this.visitNode(node, key, opts, scope, node, state); } if (nodes.length === 0) { @@ -119,7 +135,7 @@ TraversalContext.prototype.visit = function (node, key, opts, scope) { } for (var k = 0; k < nodes.length; k++) { - if (nodes[k] && this.visitNode(nodes, k, opts, scope, node)) + if (nodes[k] && this.visitNode(nodes, k, opts, scope, node, state)) return true; } @@ -133,31 +149,33 @@ TraversalContext.prototype.visit = function (node, key, opts, scope) { } }; -function traverseNode(node, opts, scope) { +function traverseNode(node, opts, scope, state) { var keys = t.VISITOR_KEYS[node.type]; if (!keys) return; - opts = opts || {}; var context = new TraversalContext(); - for (var j = 0; j < keys.length; j++) { - if (context.visit(node, keys[j], opts, scope)) + if (context.visit(node, keys[j], opts, scope, state)) return; } } -function traverse(parent, opts, scope) { +function traverse(parent, opts, scope, state) { // falsy node if (!parent) return; + if (!opts) opts = {}; + if (!opts.enter) opts.enter = noop; + if (!opts.exit) opts.exit = noop; + // array of nodes if (!Array.isArray(parent)) { - traverseNode(parent, opts, scope); + traverseNode(parent, opts, scope, state); return; } for (var i = 0; i < parent.length; i++) { - traverseNode(parent[i], opts, scope); + traverseNode(parent[i], opts, scope, state); } } @@ -190,23 +208,25 @@ traverse.removeProperties = function (tree) { traverse.hasType = function (tree, type, blacklistTypes) { blacklistTypes = [].concat(blacklistTypes || []); - var has = false; - // the node we're searching in is blacklisted if (_.contains(blacklistTypes, tree.type)) return false; // the type we're looking for is the same as the passed node if (tree.type === type) return true; + var state = { + has: false + }; + traverse(tree, { blacklist: blacklistTypes, - enter: function (node) { + enter: function (node, parent, scope, context, state) { if (node.type === type) { - has = true; - this.skip(); + state.has = true; + context.skip(); } } - }); + }, null, state); - return has; + return state.has; }; diff --git a/lib/6to5/traverse/scope.js b/lib/6to5/traverse/scope.js index f02803c886..320728c059 100644 --- a/lib/6to5/traverse/scope.js +++ b/lib/6to5/traverse/scope.js @@ -159,8 +159,13 @@ Scope.prototype.getInfo = function () { // Program, Function - var variables if (t.isProgram(block) || t.isFunction(block)) { + var state = { + blockId: block.id, + add: add + }; + traverse(block, { - enter: function (node, parent, scope) { + enter: function (node, parent, scope, context, state) { if (t.isFor(node)) { _.each(FOR_KEYS, function (key) { var declar = node[key]; @@ -170,22 +175,22 @@ Scope.prototype.getInfo = function () { // 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 (t.isFunction(node)) return context.skip(); // function identifier doesn't belong to this scope - if (block.id && node === block.id) return; + if (state.blockId && node === state.blockId) return; if (t.isIdentifier(node) && t.isReferenced(node, parent) && !scope.has(node.name)) { - add(node, true); + state.add(node, true); } // we've ran into a declaration! // we'll let the BlockStatement scope deal with `let` declarations unless if (t.isDeclaration(node) && !t.isLet(node)) { - add(node); + state.add(node); } } - }, this); + }, this, state); } // Function - params, rest diff --git a/lib/6to5/types/index.js b/lib/6to5/types/index.js index 7984bcfba8..9155d41bd4 100644 --- a/lib/6to5/types/index.js +++ b/lib/6to5/types/index.js @@ -575,4 +575,5 @@ t.isSpecifierDefault = function (specifier) { return t.isIdentifier(specifier.id) && specifier.id.name === "default"; }; -toFastProperties(t); \ No newline at end of file +toFastProperties(t); +toFastProperties(t.VISITOR_KEYS); \ No newline at end of file diff --git a/lib/6to5/util.js b/lib/6to5/util.js index 66af587e4f..87ce7cebe5 100644 --- a/lib/6to5/util.js +++ b/lib/6to5/util.js @@ -142,12 +142,12 @@ exports.template = function (name, nodes, keepExpression) { if (!_.isEmpty(nodes)) { traverse(template, { - enter: function (node) { + enter: function (node, parent, scope, context, nodes) { if (t.isIdentifier(node) && _.has(nodes, node.name)) { return nodes[node.name]; } } - }); + }, null, nodes); } var node = template.body[0];