i made the javascripts faster with a transformer prepass to check what transformers actually have to be ran

This commit is contained in:
Sebastian McKenzie
2015-02-04 12:56:34 +11:00
parent ffc9244f88
commit 7d950cd60a
30 changed files with 223 additions and 95 deletions

View File

@@ -9,6 +9,7 @@ var transform = require("./transformation");
var generate = require("./generation");
var defaults = require("lodash/object/defaults");
var contains = require("lodash/collection/contains");
var traverse = require("./traverse");
var clone = require("./helpers/clone");
var Scope = require("./traverse/scope");
var util = require("./util");
@@ -352,6 +353,8 @@ File.prototype.transform = function (ast) {
modFormatter.init();
}
this.checkNode(ast);
var astRun = function (key) {
each(self.transformerStack, function (pass) {
pass.astRun(key);
@@ -367,6 +370,30 @@ File.prototype.transform = function (ast) {
astRun("exit");
};
var checkTransformerVisitor = {
enter: function (node, parent, scope, context, state) {
state.check(node, scope);
}
};
File.prototype.checkNode = function (node, scope) {
var self = this;
scope = scope || this.scope;
var check = function (node, scope) {
each(self.transformerStack, function (pass) {
if (pass.shouldRun) return;
pass.checkNode(node, scope);
});
};
check(node, scope);
traverse(node, checkTransformerVisitor, scope, {
check: check
});
};
File.prototype.generate = function () {
var opts = this.opts;
var ast = this.ast;

View File

@@ -94,8 +94,10 @@ exports.Literal = function (node) {
});
this.push(val);
} else if (type === "boolean" || type === "number") {
this.push(JSON.stringify(val));
} else if (type === "number") {
this.push(val + "");
} else if (type === "boolean" ) {
this.push(val ? "true" : "false");
} else if (node.regex) {
this.push("/" + node.regex.pattern + "/" + node.regex.flags);
} else if (val === null) {

View File

@@ -11,11 +11,14 @@ var contains = require("lodash/collection/contains");
function TransformerPass(file, transformer) {
this.transformer = transformer;
this.shouldRun = !transformer.check;
this.handlers = transformer.handlers;
this.file = file;
}
TransformerPass.prototype.astRun = function (key) {
if (!this.shouldRun) return;
var handlers = this.handlers;
var file = this.file;
@@ -29,23 +32,39 @@ TransformerPass.prototype.canRun = function () {
var opts = this.file.opts;
var key = transformer.key;
// internal
if (key[0] === "_") return true;
// blacklist
var blacklist = opts.blacklist;
if (blacklist.length && contains(blacklist, key)) return false;
// whitelist
var whitelist = opts.whitelist;
if (whitelist.length && !contains(whitelist, key)) return false;
// optional
if (transformer.optional && !contains(opts.optional, key)) return false;
// experimental
if (transformer.experimental && !opts.experimental) return false;
// playground
if (transformer.playground && !opts.playground) return false;
return true;
};
TransformerPass.prototype.checkNode = function (node) {
var check = this.transformer.check;
if (check) {
return this.shouldRun = check(node);
} else {
return true;
}
};
var transformVisitor = {
enter: function (node, parent, scope, context, state) {
var fns = state.handlers[node.type];
@@ -61,6 +80,8 @@ var transformVisitor = {
};
TransformerPass.prototype.transform = function () {
if (!this.shouldRun) return;
var file = this.file;
util.debug(file.opts.filename + ": Running transformer " + this.transformer.key);

View File

@@ -16,13 +16,16 @@ var each = require("lodash/collection/each");
function Transformer(key, transformer, opts) {
this.manipulateOptions = transformer.manipulateOptions;
this.experimental = !!transformer.experimental;
this.playground = !!transformer.playground;
this.secondPass = !!transformer.secondPass;
this.optional = !!transformer.optional;
this.handlers = this.normalize(transformer);
this.opts = opts || {};
this.key = key;
this.check = transformer.check;
this.experimental = !!transformer.experimental;
this.playground = !!transformer.playground;
this.secondPass = !!transformer.secondPass;
this.optional = !!transformer.optional;
this.handlers = this.normalize(transformer);
this.opts = opts || {};
this.key = key;
}
Transformer.prototype.normalize = function (transformer) {

View File

@@ -3,6 +3,10 @@
var defineMap = require("../../helpers/define-map");
var t = require("../../../types");
exports.check = function (node) {
return t.isProperty(node) && (node.kind === "get" || node.kind === "set");
};
exports.ObjectExpression = function (node) {
var mutatorMap = {};
var hasAny = false;

View File

@@ -2,6 +2,8 @@
var t = require("../../../types");
exports.check = t.isArrowFunctionExpression;
exports.ArrowFunctionExpression = function (node) {
t.ensureBlock(node);

View File

@@ -7,6 +7,10 @@ var t = require("../../../types");
var values = require("lodash/object/values");
var extend = require("lodash/object/extend");
exports.check = function (node) {
return t.isVariableDeclaration(node) && (node.kind === "let" || node.kind === "const");
};
var isLet = function (node, parent) {
if (!t.isVariableDeclaration(node)) return false;
if (node._let) return true;

View File

@@ -6,6 +6,8 @@ var defineMap = require("../../helpers/define-map");
var util = require("../../../util");
var t = require("../../../types");
exports.check = t.isClass;
exports.ClassDeclaration = function (node, parent, scope, context, file) {
return new Class(node, file, scope, true).run();
};

View File

@@ -3,6 +3,10 @@
var traverse = require("../../../traverse");
var t = require("../../../types");
exports.check = function (node) {
return t.isVariableDeclaration(node, { kind: "const" });
};
var visitor = {
enter: function (node, parent, scope, context, state) {
if (t.isAssignmentExpression(node) || t.isUpdateExpression(node)) {

View File

@@ -4,6 +4,8 @@
var t = require("../../../types");
exports.check = t.isPattern;
var buildVariableAssign = function (opts, id, init) {
var op = opts.operator;
if (t.isMemberExpression(id)) op = "=";

View File

@@ -3,6 +3,8 @@
var util = require("../../../util");
var t = require("../../../types");
exports.check = t.isForOfStatement;
exports.ForOfStatement = function (node, parent, scope, context, file) {
var callback = spec;
if (file.isLoose("es6.forOf")) callback = loose;

View File

@@ -2,6 +2,8 @@
var t = require("../../../types");
exports.check = require("../internal/modules").check;
exports.ImportDeclaration = function (node, parent, scope, context, file) {
var nodes = [];

View File

@@ -4,6 +4,10 @@ var traverse = require("../../../traverse");
var util = require("../../../util");
var t = require("../../../types");
exports.check = function (node) {
return t.isFunction(node) && hasDefaults(node);
};
var hasDefaults = function (node) {
for (var i = 0; i < node.params.length; i++) {
if (t.isAssignmentPattern(node.params[i])) return true;

View File

@@ -3,6 +3,8 @@
var util = require("../../../util");
var t = require("../../../types");
exports.check = t.isRestElement;
var hasRest = function (node) {
return t.isRestElement(node.params[node.params.length - 1]);
};

View File

@@ -2,6 +2,10 @@
var t = require("../../../types");
exports.check = function (node) {
return t.isProperty(node) && node.computed;
};
exports.ObjectExpression = function (node, parent, scope, context, file) {
var hasComputed = false;

View File

@@ -4,6 +4,10 @@ var nameMethod = require("../../helpers/name-method");
var t = require("../../../types");
var clone = require("lodash/lang/clone");
exports.check = function (node) {
return t.isProperty(node) && (node.method || node.shorthand);
};
exports.Property = function (node, parent, scope, context, file) {
if (node.method) {
node.method = false;

View File

@@ -1,7 +1,9 @@
"use strict";
var t = require("../../../types");
var contains = require("lodash/collection/contains");
var t = require("../../../types");
exports.check = t.isSpreadElement;
var getSpreadLiteral = function (spread, file) {
return file.toArray(spread.argument);

View File

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

View File

@@ -2,6 +2,11 @@
var rewritePattern = require("regexpu/rewrite-pattern");
var pull = require("lodash/array/pull");
var t = require("../../../types");
exports.check = function (node) {
return t.isLiteral(node) && node.regex && node.regex.flags.indexOf("u") >= 0;
};
exports.Literal = function (node) {
var regex = node.regex;

View File

@@ -15,6 +15,10 @@ var resolveModuleSource = function (node, parent, scope, context, file) {
}
};
exports.check = function (node) {
return t.isImportDeclaration(node) || t.isExportDeclaration(node);
};
exports.ImportDeclaration = resolveModuleSource;
exports.ExportDeclaration = function (node, parent, scope) {

View File

@@ -10,6 +10,12 @@ var esutils = require("esutils");
var react = require("../../helpers/react");
var t = require("../../../types");
exports.check = function (node) {
if (t.isJSX(node)) return true;
if (react.isCreateClass(node)) return true;
return false;
};
exports.JSXIdentifier = function (node, parent) {
if (node.name === "this" && t.isReferenced(node, parent)) {
return t.thisExpression();

View File

@@ -1,6 +1,11 @@
"use strict";
var regenerator = require("regenerator-6to5");
var t = require("../../../types");
exports.check = function (node) {
return t.isFunction(node) && (node.async || node.generator);
};
exports.ast = {
before: function (ast, file) {

View File

@@ -23,6 +23,7 @@ exports.Property =
exports.MethodDefinition = function (node, parent, scope, context, file) {
if (node.kind !== "memo") return;
node.kind = "get";
file.checkNode(node, scope);
var value = node.value;
t.ensureBlock(value);

View File

@@ -10,11 +10,12 @@ var contains = require("lodash/collection/contains");
var flatten = require("lodash/array/flatten");
var compact = require("lodash/array/compact");
function TraversalContext() {
function TraversalContext(scope) {
this.shouldFlatten = false;
this.shouldRemove = false;
this.shouldSkip = false;
this.shouldStop = false;
this.scope = scope;
}
TraversalContext.prototype.flatten = function () {
@@ -44,20 +45,31 @@ TraversalContext.prototype.reset = function () {
TraversalContext.prototype.maybeRemove = function (obj, key) {
if (this.shouldRemove) {
obj[key] = null;
this.shouldFlatten = true;
this.flatten();
}
};
function replaceNode(obj, key, node, result) {
var isArray = Array.isArray(result);
TraversalContext.prototype.replaceNode = function (obj, key, node, replacement, scope) {
var isArray = Array.isArray(replacement);
// inherit comments from original node to the first replacement node
var inheritTo = result;
if (isArray) inheritTo = result[0];
var inheritTo = replacement;
if (isArray) inheritTo = replacement[0];
if (inheritTo) t.inheritsComments(inheritTo, node);
// replace the node
obj[key] = result;
obj[key] = replacement;
var file = this.scope && this.scope.file;
if (file) {
if (isArray) {
for (var i = 0; i < replacement.length; i++) {
file.checkNode(replacement[i], scope);
}
} else {
file.checkNode(replacement, scope);
}
}
// we're replacing a statement or block node with an array of statements so we better
// ensure that it's a block
@@ -66,39 +78,16 @@ function replaceNode(obj, key, node, result) {
}
if (isArray) {
return true;
this.flatten();
}
}
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.shouldFlatten = true;
}
}
this.maybeRemove(obj, key);
return node;
};
TraversalContext.prototype.exitNode = function (obj, key, node, exit, parent, scope, state) {
var result = exit(node, parent, scope, this, state);
var flatten = false;
TraversalContext.prototype.call = function (fn, obj, key, node, parent, scope, state) {
var replacement = fn(node, parent, scope, this, state);
if (result) {
flatten = replaceNode(obj, key, node, result);
node = result;
if (flatten) {
this.shouldFlatten = true;
}
if (replacement) {
this.replaceNode(obj, key, node, replacement, scope);
node = replacement;
}
this.maybeRemove(obj, key);
@@ -122,7 +111,7 @@ TraversalContext.prototype.visitNode = function (obj, key, opts, scope, parent,
ourScope = new Scope(node, parent, scope);
}
node = this.enterNode(obj, key, node, opts.enter, parent, ourScope, state);
node = this.call(opts.enter, obj, key, node, parent, ourScope, state);
if (this.shouldSkip) {
return this.shouldStop;
@@ -136,7 +125,7 @@ TraversalContext.prototype.visitNode = function (obj, key, opts, scope, parent,
}
} else {
traverseNode(node, opts, ourScope, state);
this.exitNode(obj, key, node, opts.exit, parent, ourScope, state);
this.call(opts.exit, obj, key, node, parent, ourScope, state);
}
return this.shouldStop;
@@ -174,7 +163,7 @@ function traverseNode(node, opts, scope, state) {
var keys = t.VISITOR_KEYS[node.type];
if (!keys) return;
var context = new TraversalContext();
var context = new TraversalContext(scope);
for (var i = 0; i < keys.length; i++) {
if (context.visit(node, keys[i], opts, scope, state)) {
return;

View File

@@ -35,6 +35,18 @@ function Scope(block, parentBlock, parent, file) {
Scope.defaultDeclarations = flatten([globals.builtin, globals.browser, globals.node].map(Object.keys));
/**
* Description
*
* @param {Object} node
* @param {Object} opts
* @param [state]
*/
Scope.prototype.traverse = function (node, opts, state) {
traverse(node, opts, this, state);
};
/**
* Description
*

View File

@@ -44,28 +44,39 @@
"ArrayPattern": ["Pattern"],
"AssignmentPattern": ["Pattern"],
"Property": ["UserWhitespacable"],
"Property": ["UserWhitespacable"],
"JSXElement": ["UserWhitespacable", "Expression"],
"ArrayExpression": ["Expression"],
"AssignmentExpression": ["Expression"],
"AwaitExpression": ["Expression"],
"BindFunctionExpression": ["Expression"],
"BindMemberExpression": ["Expression"],
"CallExpression": ["Expression"],
"ComprehensionExpression": ["Expression", "Scope"],
"ConditionalExpression": ["Expression"],
"Identifier": ["Expression"],
"Literal": ["Expression"],
"MemberExpression": ["Expression"],
"NewExpression": ["Expression"],
"ObjectExpression": ["Expression"],
"SequenceExpression": ["Expression"],
"TaggedTemplateExpression": ["Expression"],
"ThisExpression": ["Expression"],
"UpdateExpression": ["Expression"],
"ArrayExpression": ["Expression"],
"AssignmentExpression": ["Expression"],
"AwaitExpression": ["Expression"],
"BindFunctionExpression": ["Expression"],
"BindMemberExpression": ["Expression"],
"CallExpression": ["Expression"],
"ComprehensionExpression": ["Expression", "Scope"],
"ConditionalExpression": ["Expression"],
"Identifier": ["Expression"],
"Literal": ["Expression"],
"MemberExpression": ["Expression"],
"NewExpression": ["Expression"],
"ObjectExpression": ["Expression"],
"SequenceExpression": ["Expression"],
"TaggedTemplateExpression": ["Expression"],
"ThisExpression": ["Expression"],
"UpdateExpression": ["Expression"],
"VirtualPropertyExpression": ["Expression"],
"JSXEmptyExpression": ["Expression"],
"JSXMemberExpression": ["Expression"],
"YieldExpression": ["Expression"]
"JSXEmptyExpression": ["Expression"],
"JSXMemberExpression": ["Expression"],
"YieldExpression": ["Expression"],
"JSXAttribute": ["JSX"],
"JSXClosingElement": ["JSX"],
"JSXElement": ["JSX"],
"JSXEmptyExpression": ["JSX"],
"JSXExpressionContainer": ["JSX"],
"JSXIdentifier": ["JSX"],
"JSXMemberExpression": ["JSX"],
"JSXNamespacedName": ["JSX"],
"JSXOpeningElement": ["JSX"],
"JSXSpreadAttribute": ["JSX"]
}

View File

@@ -138,7 +138,7 @@ exports.parse = function (opts, code, callback) {
var ast = acorn.parse(code, {
allowImportExportEverywhere: opts.allowImportExportEverywhere,
allowReturnOutsideFunction: true,
allowReturnOutsideFunction: !opts._anal,
ecmaVersion: opts.experimental ? 7 : 6,
playground: opts.playground,
strictMode: opts.strictMode,