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); } diff --git a/packages/babel-generator/src/printer.js b/packages/babel-generator/src/printer.js index a8c843d82c..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) { @@ -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) { @@ -240,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) { 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;