From e7796b45c99503548670436033a5e095d27d07a7 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Sun, 17 Apr 2016 00:49:36 -0700 Subject: [PATCH 1/4] Add a fast path for checking for exact node types. In my unscientific tests locally, this look the time for generating 200 files down from 11.8 to 8.3 seconds. --- packages/babel-types/src/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/babel-types/src/index.js b/packages/babel-types/src/index.js index ea4f785c12..e7e63eb126 100644 --- a/packages/babel-types/src/index.js +++ b/packages/babel-types/src/index.js @@ -96,6 +96,10 @@ export function is(type: string, node: Object, opts?: Object): boolean { export function isType(nodeType: string, targetType: string): boolean { if (nodeType === targetType) return true; + // This is a fast-path. If the test above failed, but an alias key is found, then the + // targetType was a primary node type, so there's no need to check the aliases. + if (t.ALIAS_KEYS[targetType]) return false; + let aliases: ?Array = t.FLIPPED_ALIAS_KEYS[targetType]; if (aliases) { if (aliases[0] === nodeType) return true; From dc92a16e7f83fe2d07f7ab2f798676bd975943da Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Sun, 17 Apr 2016 01:49:50 -0700 Subject: [PATCH 2/4] Fast path for newline processing. --- packages/babel-generator/src/printer.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/babel-generator/src/printer.js b/packages/babel-generator/src/printer.js index a8c843d82c..d4c8e5e0da 100644 --- a/packages/babel-generator/src/printer.js +++ b/packages/babel-generator/src/printer.js @@ -211,10 +211,20 @@ export default class Printer extends Buffer { } _printNewline(leading, node, parent, opts) { + // Fast path since 'this.newline' does nothing when not tracking lines. + if (this.format.retainLines || this.format.compact) return; + if (!opts.statement && !n.isUserWhitespacable(node, parent)) { return; } + // Fast path for concise since 'this.newline' just inserts a space when + // concise formatting is in use. + if (this.format.concise) { + this.space(); + return; + } + let lines = 0; if (node.start != null && !node._ignoreUserWhitespace && this.tokens.length) { From eb455dff85bcf7071d60be470d32fa8365d53711 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Sun, 17 Apr 2016 14:43:57 -0700 Subject: [PATCH 3/4] Pre-expand type aliases for fast lookup. --- packages/babel-generator/src/node/index.js | 48 +++++++++++++++------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/packages/babel-generator/src/node/index.js b/packages/babel-generator/src/node/index.js index 3d035b8442..4f1d48c42b 100644 --- a/packages/babel-generator/src/node/index.js +++ b/packages/babel-generator/src/node/index.js @@ -2,22 +2,42 @@ import whitespace from "./whitespace"; import * as parens from "./parentheses"; import * as t from "babel-types"; -function find(obj, node, parent, printStack) { - if (!obj) return; - let result; +function expandAliases(obj) { + let newObj = {}; - let types = Object.keys(obj); - for (let i = 0; i < types.length; i++) { - let type = types[i]; + function add(type, func) { + let fn = newObj[type]; + newObj[type] = fn ? function(node, parent, stack) { + let result = fn(node, parent, stack); - if (t.is(type, node)) { - let fn = obj[type]; - result = fn(node, parent, printStack); - if (result != null) break; + return result == null ? func(node, parent, stack) : result; + } : func; + } + + for (let type of Object.keys(obj)) { + + let aliases = t.FLIPPED_ALIAS_KEYS[type]; + if (aliases) { + for (let alias of aliases) { + add(alias, obj[type]); + } + } else { + add(type, obj[type]); } } - return result; + return newObj; +} + +// Rather than using `t.is` on each object property, we pre-expand any type aliases +// into concrete types so that the 'find' call below can be as fast as possible. +let expandedParens = expandAliases(parens); +let expandedWhitespaceNodes = expandAliases(whitespace.nodes); +let expandedWhitespaceList = expandAliases(whitespace.list); + +function find(obj, node, parent, printStack) { + let fn = obj[node.type]; + return fn ? fn(node, parent, printStack) : null; } function isOrHasCallExpression(node) { @@ -45,10 +65,10 @@ export function needsWhitespace(node, parent, type) { node = node.expression; } - let linesInfo = find(whitespace.nodes, node, parent); + let linesInfo = find(expandedWhitespaceNodes, node, parent); if (!linesInfo) { - let items = find(whitespace.list, node, parent); + let items = find(expandedWhitespaceList, node, parent); if (items) { for (let i = 0; i < items.length; i++) { linesInfo = needsWhitespace(items[i], node, type); @@ -75,5 +95,5 @@ export function needsParens(node, parent, printStack) { if (isOrHasCallExpression(node)) return true; } - return find(parens, node, parent, printStack); + return find(expandedParens, node, parent, printStack); } From ca8556e36f584b7e694af27132a6bf8920083d52 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Sun, 17 Apr 2016 15:11:12 -0700 Subject: [PATCH 4/4] Let function optimize better by using a boolean. --- packages/babel-generator/src/printer.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/babel-generator/src/printer.js b/packages/babel-generator/src/printer.js index d4c8e5e0da..6b24eeacc2 100644 --- a/packages/babel-generator/src/printer.js +++ b/packages/babel-generator/src/printer.js @@ -182,11 +182,11 @@ export default class Printer extends Buffer { } printTrailingComments(node, parent) { - this.printComments(this.getComments("trailingComments", node, parent)); + this.printComments(this.getComments(false, node, parent)); } printLeadingComments(node, parent) { - this.printComments(this.getComments("leadingComments", node, parent)); + this.printComments(this.getComments(true, node, parent)); } printInnerComments(node, indent = true) { @@ -250,8 +250,10 @@ export default class Printer extends Buffer { this.newline(lines); } - getComments(key, node) { - return (node && node[key]) || []; + getComments(leading, node) { + // Note, we use a boolean flag here instead of passing in the attribute name as it is faster + // because this is called extremely frequently. + return (node && (leading ? node.leadingComments : node.trailingComments)) || []; } shouldPrintComment(comment) {