Merge pull request #518 from gaearon/perf-v8
RFC: V8-specific performance impovements
This commit is contained in:
commit
ddb1c718f1
14
lib/6to5/to-fast-properties.js
Normal file
14
lib/6to5/to-fast-properties.js
Normal file
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* A trick from Bluebird to force V8 to use fast properties for an object.
|
||||
* Read more: http://stackoverflow.com/questions/24987896/
|
||||
*
|
||||
* Use %HasFastProperties(obj) and --allow-natives-syntax to check whether
|
||||
* a particular object already has fast properties.
|
||||
*/
|
||||
module.exports = function toFastProperties(obj) {
|
||||
/*jshint -W027*/
|
||||
function f() {}
|
||||
f.prototype = obj;
|
||||
return f;
|
||||
eval(obj);
|
||||
};
|
||||
@ -2,6 +2,24 @@ var traverse = require("../../traverse");
|
||||
var util = require("../../util");
|
||||
var t = require("../../types");
|
||||
|
||||
var traverser = {
|
||||
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: 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(state.id, true);
|
||||
if (localDeclar !== state.outerDeclar) return;
|
||||
|
||||
state.selfReference = true;
|
||||
context.stop();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = function (node, file, scope) {
|
||||
var key = t.toComputedKey(node, node.key);
|
||||
if (!t.isLiteral(key)) return node; // we can't set a function id with this
|
||||
@ -9,28 +27,15 @@ 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) {
|
||||
// check if this node is an identifier that matches the same as our function id
|
||||
if (!t.isIdentifier(node, { name: id })) return;
|
||||
traverse(node, traverser, scope, state);
|
||||
|
||||
// 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;
|
||||
|
||||
selfReference = true;
|
||||
this.stop();
|
||||
}
|
||||
}, scope);
|
||||
|
||||
if (selfReference) {
|
||||
if (state.selfReference) {
|
||||
node.value = util.template("property-method-assignment-wrapper", {
|
||||
FUNCTION: node.value,
|
||||
FUNCTION_ID: key,
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -16,35 +16,51 @@ function DefaultFormatter(file) {
|
||||
//this.checkCollisions();
|
||||
}
|
||||
|
||||
var exportsTraverser = {
|
||||
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));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
DefaultFormatter.prototype.getLocalExports = function () {
|
||||
var localExports = {};
|
||||
|
||||
traverse(this.file.ast, {
|
||||
enter: function (node) {
|
||||
var declar = node && node.declaration;
|
||||
if (t.isExportDeclaration(node) && declar && t.isStatement(declar)) {
|
||||
_.extend(localExports, t.getIds(declar, true));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
traverse(this.file.ast, exportsTraverser, null, localExports);
|
||||
return localExports;
|
||||
};
|
||||
|
||||
var importsTraverser = {
|
||||
enter: function (node, parent, scope, context, localImports) {
|
||||
if (t.isImportDeclaration(node)) {
|
||||
_.extend(localImports, t.getIds(node, true));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
DefaultFormatter.prototype.getLocalImports = function () {
|
||||
var localImports = {};
|
||||
|
||||
traverse(this.file.ast, {
|
||||
enter: function (node) {
|
||||
if (t.isImportDeclaration(node)) {
|
||||
_.extend(localImports, t.getIds(node, true));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
traverse(this.file.ast, importsTraverser, null, localImports);
|
||||
return localImports;
|
||||
};
|
||||
|
||||
var collissionsTraverser = {
|
||||
enter: function (node, parent, scope, context, check) {
|
||||
if (t.isAssignmentExpression(node)) {
|
||||
|
||||
var left = node.left;
|
||||
if (t.isMemberExpression(left)) {
|
||||
while (left.object) left = left.object;
|
||||
}
|
||||
|
||||
check(left);
|
||||
} else if (t.isDeclaration(node)) {
|
||||
_.each(t.getIds(node, true), check);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
DefaultFormatter.prototype.checkCollisions = function () {
|
||||
// todo: all check export collissions
|
||||
|
||||
@ -61,21 +77,7 @@ DefaultFormatter.prototype.checkCollisions = function () {
|
||||
}
|
||||
};
|
||||
|
||||
traverse(file.ast, {
|
||||
enter: function (node) {
|
||||
if (t.isAssignmentExpression(node)) {
|
||||
|
||||
var left = node.left;
|
||||
if (t.isMemberExpression(left)) {
|
||||
while (left.object) left = left.object;
|
||||
}
|
||||
|
||||
check(left);
|
||||
} else if (t.isDeclaration(node)) {
|
||||
_.each(t.getIds(node, true), check);
|
||||
}
|
||||
}
|
||||
});
|
||||
traverse(file.ast, collissionsTraverser, null, check);
|
||||
};
|
||||
|
||||
DefaultFormatter.prototype.remapExportAssignment = function (node) {
|
||||
@ -100,9 +102,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 +132,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 () {
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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");
|
||||
};
|
||||
|
||||
|
||||
@ -1,57 +1,62 @@
|
||||
var traverse = require("../../traverse");
|
||||
var t = require("../../types");
|
||||
|
||||
var functionChildrenTraverser = {
|
||||
enter: function (node, parent, scope, context, state) {
|
||||
if (t.isFunction(node) && !node._aliasFunction) {
|
||||
return context.skip();
|
||||
}
|
||||
|
||||
if (node._ignoreAliasFunctions) return context.skip();
|
||||
|
||||
var getId;
|
||||
|
||||
if (t.isIdentifier(node) && node.name === "arguments") {
|
||||
getId = state.getArgumentsId;
|
||||
} else if (t.isThisExpression(node)) {
|
||||
getId = state.getThisId;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (t.isReferenced(node, parent)) return getId();
|
||||
}
|
||||
};
|
||||
|
||||
var functionTraverser = {
|
||||
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 context.skip();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// traverse all child nodes of this function and find `arguments` and `this`
|
||||
traverse(node, functionChildrenTraverser, null, state);
|
||||
|
||||
return context.skip();
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
if (!node._aliasFunction) {
|
||||
if (t.isFunction(node)) {
|
||||
// stop traversal of this node as it'll be hit again by this transformer
|
||||
return this.skip();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// traverse all child nodes of this function and find `arguments` and `this`
|
||||
traverse(node, {
|
||||
enter: function (node, parent) {
|
||||
if (t.isFunction(node) && !node._aliasFunction) {
|
||||
return this.skip();
|
||||
}
|
||||
|
||||
if (node._ignoreAliasFunctions) return this.skip();
|
||||
|
||||
var getId;
|
||||
|
||||
if (t.isIdentifier(node) && node.name === "arguments") {
|
||||
getId = getArgumentsId;
|
||||
} else if (t.isThisExpression(node)) {
|
||||
getId = getThisId;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (t.isReferenced(node, parent)) return getId();
|
||||
}
|
||||
});
|
||||
|
||||
return this.skip();
|
||||
}
|
||||
});
|
||||
traverse(node, functionTraverser, null, state);
|
||||
|
||||
var body;
|
||||
|
||||
@ -71,14 +76,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;
|
||||
|
||||
@ -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 () {
|
||||
|
||||
@ -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++) {
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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 = [];
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
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;
|
||||
delete node.rest;
|
||||
node.rest = null;
|
||||
|
||||
t.ensureBlock(node);
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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"));
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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)) {
|
||||
|
||||
@ -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++) {
|
||||
|
||||
@ -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]);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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([
|
||||
|
||||
@ -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";
|
||||
|
||||
|
||||
@ -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 isCompatTag = 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 = [];
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
module.exports = traverse;
|
||||
|
||||
/* jshint maxparams:7 */
|
||||
|
||||
var Scope = require("./scope");
|
||||
var t = require("../types");
|
||||
var _ = require("lodash");
|
||||
|
||||
function TraversalContext(previousContext) {
|
||||
function noop() { }
|
||||
|
||||
function TraversalContext() {
|
||||
this.didSkip = false;
|
||||
this.didRemove = false;
|
||||
this.didStop = false;
|
||||
this.didFlatten = previousContext ? previousContext.didFlatten : false;
|
||||
this.didFlatten = false;
|
||||
}
|
||||
|
||||
TraversalContext.prototype.flatten = function () {
|
||||
@ -17,7 +21,7 @@ TraversalContext.prototype.flatten = function () {
|
||||
|
||||
TraversalContext.prototype.remove = function () {
|
||||
this.didRemove = true;
|
||||
this.skip();
|
||||
this.didSkip = true;
|
||||
};
|
||||
|
||||
TraversalContext.prototype.skip = function () {
|
||||
@ -26,13 +30,16 @@ TraversalContext.prototype.skip = function () {
|
||||
|
||||
TraversalContext.prototype.stop = function () {
|
||||
this.didStop = true;
|
||||
this.skip();
|
||||
this.didSkip = true;
|
||||
};
|
||||
|
||||
TraversalContext.prototype.maybeReplace = function (result, obj, key, node) {
|
||||
if (result === false) return node;
|
||||
if (result == null) return node;
|
||||
TraversalContext.prototype.reset = function () {
|
||||
this.didSkip = false;
|
||||
this.didStop = false;
|
||||
this.didRemove = false;
|
||||
};
|
||||
|
||||
function replaceNode(obj, key, node, result) {
|
||||
var isArray = Array.isArray(result);
|
||||
|
||||
// inherit comments from original node to the first replacement node
|
||||
@ -41,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
|
||||
@ -50,104 +57,139 @@ 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.visit = function (obj, key, opts, scope, parent) {
|
||||
var node = obj[key];
|
||||
if (!node) return;
|
||||
TraversalContext.prototype.exitNode = function (obj, key, node, exit, parent, scope, state) {
|
||||
var result = exit(node, parent, scope, this, state);
|
||||
var flatten = false;
|
||||
|
||||
// type is blacklisted
|
||||
if (opts.blacklist && opts.blacklist.indexOf(node.type) > -1) return;
|
||||
if (result) {
|
||||
flatten = replaceNode(obj, key, node, result);
|
||||
node = result;
|
||||
|
||||
var result;
|
||||
var ourScope = scope;
|
||||
if (t.isScope(node)) ourScope = new Scope(node, scope);
|
||||
|
||||
// enter
|
||||
if (opts.enter) {
|
||||
result = opts.enter.call(this, node, parent, ourScope);
|
||||
node = this.maybeReplace(result, obj, key, node);
|
||||
|
||||
if (this.didRemove) {
|
||||
obj[key] = null;
|
||||
this.flatten();
|
||||
if (flatten) {
|
||||
this.didFlatten = true;
|
||||
}
|
||||
|
||||
// stop traversal
|
||||
if (this.didSkip) return;
|
||||
}
|
||||
|
||||
// traverse node
|
||||
traverse(node, opts, ourScope);
|
||||
|
||||
// exit
|
||||
if (opts.exit) {
|
||||
result = opts.exit.call(this, node, parent, ourScope);
|
||||
node = this.maybeReplace(result, obj, key, node);
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
function traverse(parent, opts, scope) {
|
||||
// falsy node
|
||||
if (!parent) return;
|
||||
TraversalContext.prototype.visitNode = function (obj, key, opts, scope, parent, state) {
|
||||
this.reset();
|
||||
|
||||
// array of nodes
|
||||
if (Array.isArray(parent)) {
|
||||
for (var i = 0; i < parent.length; i++)
|
||||
traverse(parent[i], opts, scope);
|
||||
var node = obj[key];
|
||||
|
||||
// type is blacklisted
|
||||
if (opts.blacklist && opts.blacklist.indexOf(node.type) > -1)
|
||||
return;
|
||||
|
||||
var ourScope = scope;
|
||||
if (t.isScope(node))
|
||||
ourScope = new Scope(node, scope);
|
||||
|
||||
node = this.enterNode(obj, key, node, opts.enter, parent, ourScope, state);
|
||||
|
||||
if (this.didSkip)
|
||||
return this.didStop;
|
||||
|
||||
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, state) {
|
||||
var nodes = node[key];
|
||||
if (!nodes) return;
|
||||
|
||||
if (!Array.isArray(nodes)) {
|
||||
return this.visitNode(node, key, opts, scope, node, state);
|
||||
}
|
||||
|
||||
if (nodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// unknown node type to traverse
|
||||
var keys = t.VISITOR_KEYS[parent.type];
|
||||
for (var k = 0; k < nodes.length; k++) {
|
||||
if (nodes[k] && this.visitNode(nodes, k, opts, scope, node, state))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.didFlatten) {
|
||||
node[key] = _.flatten(node[key]);
|
||||
|
||||
if (key === "body") {
|
||||
// we can safely compact this
|
||||
node[key] = _.compact(node[key]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function traverseNode(node, opts, scope, state) {
|
||||
var keys = t.VISITOR_KEYS[node.type];
|
||||
if (!keys) return;
|
||||
|
||||
opts = opts || {};
|
||||
var context = null;
|
||||
|
||||
var context = new TraversalContext();
|
||||
for (var j = 0; j < keys.length; j++) {
|
||||
var key = keys[j];
|
||||
var nodes = parent[key];
|
||||
if (!nodes) continue;
|
||||
if (context.visit(node, keys[j], opts, scope, state))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(nodes)) {
|
||||
for (var k = 0; k < nodes.length; k++) {
|
||||
context = new TraversalContext(context);
|
||||
context.visit(nodes, k, opts, scope, parent);
|
||||
if (context.didStop) return;
|
||||
}
|
||||
function traverse(parent, opts, scope, state) {
|
||||
// falsy node
|
||||
if (!parent) return;
|
||||
|
||||
if (context && context.didFlatten) {
|
||||
parent[key] = _.flatten(parent[key]);
|
||||
if (!opts) opts = {};
|
||||
if (!opts.enter) opts.enter = noop;
|
||||
if (!opts.exit) opts.exit = noop;
|
||||
|
||||
if (key === "body") {
|
||||
// we can safely compact this
|
||||
parent[key] = _.compact(parent[key]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
context = new TraversalContext(context);
|
||||
context.visit(parent, key, opts, scope, parent);
|
||||
if (context.didStop) return;
|
||||
}
|
||||
// array of nodes
|
||||
if (!Array.isArray(parent)) {
|
||||
traverseNode(parent, opts, scope, state);
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < parent.length; i++) {
|
||||
traverseNode(parent[i], opts, scope, state);
|
||||
}
|
||||
}
|
||||
|
||||
traverse.removeProperties = function (tree) {
|
||||
var clear = function (node) {
|
||||
delete node._declarations;
|
||||
delete node.extendedRange;
|
||||
delete node._scopeInfo;
|
||||
delete node.tokens;
|
||||
delete node.range;
|
||||
delete node.start;
|
||||
delete node.end;
|
||||
delete node.loc;
|
||||
delete node.raw;
|
||||
node._declarations = null;
|
||||
node.extendedRange = null;
|
||||
node._scopeInfo = null;
|
||||
node.tokens = null;
|
||||
node.range = null;
|
||||
node.start = null;
|
||||
node.end = null;
|
||||
node.loc = null;
|
||||
node.raw = null;
|
||||
|
||||
clearComments(node.trailingComments);
|
||||
clearComments(node.leadingComments);
|
||||
@ -166,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;
|
||||
};
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
var esutils = require("esutils");
|
||||
var _ = require("lodash");
|
||||
var esutils = require("esutils");
|
||||
var _ = require("lodash");
|
||||
var toFastProperties = require("../to-fast-properties");
|
||||
|
||||
var t = exports;
|
||||
|
||||
@ -573,3 +574,6 @@ t.getSpecifierName = function (specifier) {
|
||||
t.isSpecifierDefault = function (specifier) {
|
||||
return t.isIdentifier(specifier.id) && specifier.id.name === "default";
|
||||
};
|
||||
|
||||
toFastProperties(t);
|
||||
toFastProperties(t.VISITOR_KEYS);
|
||||
@ -129,6 +129,14 @@ exports.buildDefineProperties = function (mutatorMap) {
|
||||
return objExpr;
|
||||
};
|
||||
|
||||
var templateTraverser = {
|
||||
enter: function (node, parent, scope, context, nodes) {
|
||||
if (t.isIdentifier(node) && _.has(nodes, node.name)) {
|
||||
return nodes[node.name];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.template = function (name, nodes, keepExpression) {
|
||||
var template = exports.templates[name];
|
||||
if (!template) throw new ReferenceError("unknown template " + name);
|
||||
@ -141,13 +149,7 @@ exports.template = function (name, nodes, keepExpression) {
|
||||
template = _.cloneDeep(template);
|
||||
|
||||
if (!_.isEmpty(nodes)) {
|
||||
traverse(template, {
|
||||
enter: function (node) {
|
||||
if (t.isIdentifier(node) && _.has(nodes, node.name)) {
|
||||
return nodes[node.name];
|
||||
}
|
||||
}
|
||||
});
|
||||
traverse(template, templateTraverser, null, nodes);
|
||||
}
|
||||
|
||||
var node = template.body[0];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user