From efea2c19f269ff9298fe3de26083ab1c428f4a3e Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Thu, 14 Apr 2016 22:30:31 -0700 Subject: [PATCH 01/22] Add more tests for minified output. --- .../test/fixtures/minified/arrow-functions/actual.js | 8 ++++++++ .../fixtures/minified/arrow-functions/expected.js | 1 + .../fixtures/minified/block-statements/actual.js | 12 ++++++++++++ .../fixtures/minified/block-statements/expected.js | 1 + .../test/fixtures/minified/modules/actual.js | 7 +++++++ .../test/fixtures/minified/modules/expected.js | 1 + 6 files changed, 30 insertions(+) create mode 100644 packages/babel-generator/test/fixtures/minified/arrow-functions/actual.js create mode 100644 packages/babel-generator/test/fixtures/minified/arrow-functions/expected.js create mode 100644 packages/babel-generator/test/fixtures/minified/block-statements/actual.js create mode 100644 packages/babel-generator/test/fixtures/minified/block-statements/expected.js create mode 100644 packages/babel-generator/test/fixtures/minified/modules/actual.js create mode 100644 packages/babel-generator/test/fixtures/minified/modules/expected.js diff --git a/packages/babel-generator/test/fixtures/minified/arrow-functions/actual.js b/packages/babel-generator/test/fixtures/minified/arrow-functions/actual.js new file mode 100644 index 0000000000..7865b8b80b --- /dev/null +++ b/packages/babel-generator/test/fixtures/minified/arrow-functions/actual.js @@ -0,0 +1,8 @@ +var foo = (arg1, arg2) => { + arg1; + arg2; +}; +var foo2 = (arg1, arg2) => { + arg1; +}; +var foo3 = arg1 => arg1; diff --git a/packages/babel-generator/test/fixtures/minified/arrow-functions/expected.js b/packages/babel-generator/test/fixtures/minified/arrow-functions/expected.js new file mode 100644 index 0000000000..fb62b97907 --- /dev/null +++ b/packages/babel-generator/test/fixtures/minified/arrow-functions/expected.js @@ -0,0 +1 @@ +var foo=(arg1,arg2) => {arg1;arg2};var foo2=(arg1,arg2) => {arg1};var foo3=arg1 => arg1; diff --git a/packages/babel-generator/test/fixtures/minified/block-statements/actual.js b/packages/babel-generator/test/fixtures/minified/block-statements/actual.js new file mode 100644 index 0000000000..d22dd7cf2f --- /dev/null +++ b/packages/babel-generator/test/fixtures/minified/block-statements/actual.js @@ -0,0 +1,12 @@ +if (true) { + foo; + bar2; +} else { + foo; + bar2; +} + +function fn () { + foo; + bar2; +} diff --git a/packages/babel-generator/test/fixtures/minified/block-statements/expected.js b/packages/babel-generator/test/fixtures/minified/block-statements/expected.js new file mode 100644 index 0000000000..6ce5278348 --- /dev/null +++ b/packages/babel-generator/test/fixtures/minified/block-statements/expected.js @@ -0,0 +1 @@ +if(true){foo;bar2}else {foo;bar2}function fn(){foo;bar2} diff --git a/packages/babel-generator/test/fixtures/minified/modules/actual.js b/packages/babel-generator/test/fixtures/minified/modules/actual.js new file mode 100644 index 0000000000..4a4072d108 --- /dev/null +++ b/packages/babel-generator/test/fixtures/minified/modules/actual.js @@ -0,0 +1,7 @@ +import * as foo from "foo"; +import {foo as bar, foo2 as bar2} from "foo"; +import {foo2} from "foo"; + +export * from "foo"; +export {foo as bar} from "foo"; +export {foo} from "foo"; diff --git a/packages/babel-generator/test/fixtures/minified/modules/expected.js b/packages/babel-generator/test/fixtures/minified/modules/expected.js new file mode 100644 index 0000000000..1c03cdd973 --- /dev/null +++ b/packages/babel-generator/test/fixtures/minified/modules/expected.js @@ -0,0 +1 @@ +import * as foo from "foo";import {foo as bar, foo2 as bar2} from "foo";import {foo2} from "foo";export * from "foo";export {foo as bar} from "foo";export {foo} from "foo"; From d042ddfdda7b39919f058604a4a15863bee040c8 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Tue, 12 Apr 2016 21:16:34 -0700 Subject: [PATCH 02/22] Clarify that the code generator class is not public. --- packages/babel-generator/src/index.js | 25 ++++++++++++++++++++----- packages/babel-generator/test/index.js | 5 +++-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/packages/babel-generator/src/index.js b/packages/babel-generator/src/index.js index 9c4e08672d..4254d8bdf9 100644 --- a/packages/babel-generator/src/index.js +++ b/packages/babel-generator/src/index.js @@ -10,13 +10,13 @@ import Printer from "./printer"; * user preferences, and valid output. */ -export class CodeGenerator extends Printer { +class Generator extends Printer { constructor(ast, opts, code) { opts = opts || {}; let comments = ast.comments || []; let tokens = ast.tokens || []; - let format = CodeGenerator.normalizeOptions(code, opts, tokens); + let format = Generator.normalizeOptions(code, opts, tokens); let position = new Position; @@ -66,7 +66,6 @@ export class CodeGenerator extends Printer { * * - Detects code indentation. * - If `opts.compact = "auto"` and the code is over 100KB, `compact` will be set to `true`. - */ static normalizeOptions(code, opts, tokens) { @@ -85,7 +84,7 @@ export class CodeGenerator extends Printer { compact: opts.compact, minified: opts.minified, concise: opts.concise, - quotes: opts.quotes || CodeGenerator.findCommonStringDelimiter(code, tokens), + quotes: opts.quotes || Generator.findCommonStringDelimiter(code, tokens), indent: { adjustMultilineComment: true, style: style, @@ -161,7 +160,23 @@ export class CodeGenerator extends Printer { } } + +/** + * We originally exported the Generator class above, but to make it extra clear that it is a private API, + * we have moved that to an internal class instance and simplified the interface to the two public methods + * that we wish to support. + */ + +export class CodeGenerator { + constructor(ast, opts, code) { + this._generator = new Generator(ast, opts, code); + } + generate() { + return this._generator.generate(); + } +} + export default function (ast: Object, opts: Object, code: string): Object { - let gen = new CodeGenerator(ast, opts, code); + let gen = new Generator(ast, opts, code); return gen.generate(); } diff --git a/packages/babel-generator/test/index.js b/packages/babel-generator/test/index.js index 163bfadcb5..718d9ffbd1 100644 --- a/packages/babel-generator/test/index.js +++ b/packages/babel-generator/test/index.js @@ -1,4 +1,5 @@ var Whitespace = require("../lib/whitespace"); +var Printer = require("../lib/printer"); var generate = require("../lib"); var assert = require("assert"); var parse = require("babylon").parse; @@ -9,10 +10,10 @@ var _ = require("lodash"); suite("generation", function () { test("completeness", function () { _.each(t.VISITOR_KEYS, function (keys, type) { - assert.ok(!!generate.CodeGenerator.prototype[type], type + " should exist"); + assert.ok(!!Printer.prototype[type], type + " should exist"); }); - _.each(generate.CodeGenerator.prototype, function (fn, type) { + _.each(Printer.prototype, function (fn, type) { if (!/[A-Z]/.test(type[0])) return; assert.ok(t.VISITOR_KEYS[type], type + " should not exist"); }); From c1ba5e6ac83cbfa90f7e0ba2c183b98e0131d0ab Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Tue, 12 Apr 2016 23:46:06 -0700 Subject: [PATCH 03/22] Remove unused force param. --- packages/babel-generator/src/buffer.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/babel-generator/src/buffer.js b/packages/babel-generator/src/buffer.js index 6b6e20d4d0..ebd1be5fda 100644 --- a/packages/babel-generator/src/buffer.js +++ b/packages/babel-generator/src/buffer.js @@ -131,13 +131,13 @@ export default class Buffer { } /** - * Add a space to the buffer unless it is compact (override with force). + * Add a space to the buffer unless it is compact. */ - space(force?: boolean) { - if (!force && this.format.compact) return; + space() { + if (this.format.compact) return; - if (force || this.buf && !this.isLast(" ") && !this.isLast("\n")) { + if (this.buf && !this.isLast(" ") && !this.isLast("\n")) { this.push(" "); } } From 67763085ab735da1ff38b5294b9e2eb02384763a Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Sun, 1 May 2016 11:45:11 -0700 Subject: [PATCH 04/22] Be consistent with semicolons. --- packages/babel-generator/src/generators/statements.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/babel-generator/src/generators/statements.js b/packages/babel-generator/src/generators/statements.js index 1f54dc988e..ea17353229 100644 --- a/packages/babel-generator/src/generators/statements.js +++ b/packages/babel-generator/src/generators/statements.js @@ -100,7 +100,8 @@ export function DoWhileStatement(node: Object) { this.keyword("while"); this.push("("); this.print(node.test, node); - this.push(");"); + this.push(")"); + this.semicolon(); } function buildLabelStatement(prefix, key = "label") { @@ -200,7 +201,8 @@ export function SwitchCase(node: Object) { } export function DebuggerStatement() { - this.push("debugger;"); + this.push("debugger"); + this.semicolon(); } export function VariableDeclaration(node: Object, parent: Object) { From 0be2f1cf48d02ac4cd683c911bd7e331dd89deac Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Sun, 1 May 2016 11:45:41 -0700 Subject: [PATCH 05/22] Remove unnecessary ensureSemicolon. --- packages/babel-generator/src/buffer.js | 8 -------- packages/babel-generator/src/generators/modules.js | 6 +++--- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/packages/babel-generator/src/buffer.js b/packages/babel-generator/src/buffer.js index ebd1be5fda..6a27cf66d0 100644 --- a/packages/babel-generator/src/buffer.js +++ b/packages/babel-generator/src/buffer.js @@ -101,14 +101,6 @@ export default class Buffer { this.push(";"); } - /** - * Ensure last character is a semicolon. - */ - - ensureSemicolon() { - if (!this.isLast(";")) this.semicolon(); - } - /** * Add a right brace to the buffer. */ diff --git a/packages/babel-generator/src/generators/modules.js b/packages/babel-generator/src/generators/modules.js index 31b54da80d..db31cbf6a1 100644 --- a/packages/babel-generator/src/generators/modules.js +++ b/packages/babel-generator/src/generators/modules.js @@ -54,7 +54,7 @@ function ExportDeclaration(node: Object) { if (node.declaration) { let declar = node.declaration; this.print(declar, node); - if (t.isStatement(declar) || t.isFunction(declar) || t.isClass(declar)) return; + if (!(t.isStatement(declar) || t.isFunction(declar) || t.isClass(declar))) this.semicolon(); } else { if (node.exportKind === "type") { this.push("type "); @@ -91,9 +91,9 @@ function ExportDeclaration(node: Object) { this.push(" from "); this.print(node.source, node); } - } - this.ensureSemicolon(); + this.semicolon(); + } } export function ImportDeclaration(node: Object) { From 61e3b1d314f30b2eb450c9b64235f9d0bab1e09f Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Sun, 17 Apr 2016 13:52:32 -0700 Subject: [PATCH 06/22] Remove incorrect function/class check. --- packages/babel-generator/src/generators/modules.js | 2 +- .../test/fixtures/general/non-block-arrow-func/expected.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-generator/src/generators/modules.js b/packages/babel-generator/src/generators/modules.js index db31cbf6a1..825bf8d56b 100644 --- a/packages/babel-generator/src/generators/modules.js +++ b/packages/babel-generator/src/generators/modules.js @@ -54,7 +54,7 @@ function ExportDeclaration(node: Object) { if (node.declaration) { let declar = node.declaration; this.print(declar, node); - if (!(t.isStatement(declar) || t.isFunction(declar) || t.isClass(declar))) this.semicolon(); + if (!t.isStatement(declar)) this.semicolon(); } else { if (node.exportKind === "type") { this.push("type "); diff --git a/packages/babel-plugin-transform-class-properties/test/fixtures/general/non-block-arrow-func/expected.js b/packages/babel-plugin-transform-class-properties/test/fixtures/general/non-block-arrow-func/expected.js index a1e381f8a8..37da88e80f 100644 --- a/packages/babel-plugin-transform-class-properties/test/fixtures/general/non-block-arrow-func/expected.js +++ b/packages/babel-plugin-transform-class-properties/test/fixtures/general/non-block-arrow-func/expected.js @@ -17,4 +17,4 @@ export default (param => { prop1: 'prop1', prop2: 'prop2' }, _temp; -}) +}); From 9ddf9aabfc1e756cbc9ce3433f4d35b41591a846 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Sun, 17 Apr 2016 15:13:34 -0700 Subject: [PATCH 07/22] Don't track position when sourcemaps are off. --- packages/babel-generator/src/buffer.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/babel-generator/src/buffer.js b/packages/babel-generator/src/buffer.js index 6a27cf66d0..3158a7c3e4 100644 --- a/packages/babel-generator/src/buffer.js +++ b/packages/babel-generator/src/buffer.js @@ -249,6 +249,8 @@ export default class Buffer { */ withSource(prop: string, loc: Location, cb: () => void) { + if (!this.opts.sourceMaps) return cb(); + // Use the call stack to manage a stack of "source location" data. let originalLine = this._sourcePosition.line; let originalColumn = this._sourcePosition.column; @@ -310,7 +312,7 @@ export default class Buffer { } // If there the line is ending, adding a new mapping marker is redundant - if (str[0] !== "\n") this.map.mark(this._sourcePosition); + if (this.opts.sourceMaps && str[0] !== "\n") this.map.mark(this._sourcePosition); // this.position.push(str); From 2f790889ebc513e99eacef5bd062e557be8a0c49 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Fri, 15 Apr 2016 21:54:19 -0700 Subject: [PATCH 08/22] Make sure to roll back the generated position markers when trimming spaces. --- packages/babel-generator/src/buffer.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/babel-generator/src/buffer.js b/packages/babel-generator/src/buffer.js index 3158a7c3e4..8ce4b55608 100644 --- a/packages/babel-generator/src/buffer.js +++ b/packages/babel-generator/src/buffer.js @@ -224,8 +224,10 @@ export default class Buffer { _removeSpacesAfterLastNewline() { let lastNewlineIndex = this.buf.lastIndexOf("\n"); if (lastNewlineIndex >= 0 && this.get().length <= lastNewlineIndex) { + let toRemove = this.buf.slice(lastNewlineIndex + 1); this.buf = this.buf.substring(0, lastNewlineIndex + 1); this.last = "\n"; + this.position.unshift(toRemove); } } From 680fcc16d798c6b83dbc3c938e8b25b1dc30ba99 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Wed, 13 Apr 2016 02:06:38 -0700 Subject: [PATCH 09/22] Standardize on .endsWith --- packages/babel-generator/src/buffer.js | 26 ++++--------------- .../src/generators/statements.js | 2 +- packages/babel-generator/src/printer.js | 2 +- 3 files changed, 7 insertions(+), 23 deletions(-) diff --git a/packages/babel-generator/src/buffer.js b/packages/babel-generator/src/buffer.js index 8ce4b55608..9e162b6bec 100644 --- a/packages/babel-generator/src/buffer.js +++ b/packages/babel-generator/src/buffer.js @@ -129,7 +129,7 @@ export default class Buffer { space() { if (this.format.compact) return; - if (this.buf && !this.isLast(" ") && !this.isLast("\n")) { + if (this.buf && !this.endsWith(" ") && !this.endsWith("\n")) { this.push(" "); } } @@ -144,7 +144,7 @@ export default class Buffer { } _removeLast(cha: string) { - if (!this._isLast(cha)) return; + if (!this.endsWith(cha)) return; this.buf = this.buf.slice(0, -1); this.last = this.buf[this.buf.length - 1]; this.position.unshift(cha); @@ -280,7 +280,7 @@ export default class Buffer { str = str.replace(/\n/g, `\n${indent}`); // we've got a newline before us so prepend on the indentation - if (this.isLast("\n")) this._push(indent); + if (this.endsWith("\n")) this._push(indent); } this._push(str); @@ -327,6 +327,8 @@ export default class Buffer { */ endsWith(str: string): boolean { + if (Array.isArray(str)) return str.some((s) => this.endsWith(s)); + if (str.length === 1) { return this.last === str; } else { @@ -334,22 +336,4 @@ export default class Buffer { } } - /** - * Test if a character is last in the buffer. - */ - - isLast(cha: string): boolean { - if (this.format.compact) return false; - return this._isLast(cha); - } - - _isLast(cha: string): boolean { - let last = this.last; - - if (Array.isArray(cha)) { - return cha.indexOf(last) >= 0; - } else { - return cha === last; - } - } } diff --git a/packages/babel-generator/src/generators/statements.js b/packages/babel-generator/src/generators/statements.js index ea17353229..c4d4e95faa 100644 --- a/packages/babel-generator/src/generators/statements.js +++ b/packages/babel-generator/src/generators/statements.js @@ -34,7 +34,7 @@ export function IfStatement(node: Object) { } if (node.alternate) { - if (this.isLast("}")) this.space(); + if (this.endsWith("}")) this.space(); this.push("else "); this.printAndIndentOnComments(node.alternate, node); } diff --git a/packages/babel-generator/src/printer.js b/packages/babel-generator/src/printer.js index 0e8884b181..026fa4fba4 100644 --- a/packages/babel-generator/src/printer.js +++ b/packages/babel-generator/src/printer.js @@ -290,7 +290,7 @@ export default class Printer extends Buffer { let column = this.position.column; let val = this.generateComment(comment); - if (column && !this.isLast(["\n", " ", "[", "{"])) { + if (column && !this.endsWith(["\n", " ", "[", "{"])) { this._push(" "); column++; } From 710f151d31d027256daae64b07851868d7adc00b Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Sun, 17 Apr 2016 21:43:36 -0700 Subject: [PATCH 10/22] Use the standard space helper for comment space. --- packages/babel-generator/src/printer.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/babel-generator/src/printer.js b/packages/babel-generator/src/printer.js index 026fa4fba4..488c045f32 100644 --- a/packages/babel-generator/src/printer.js +++ b/packages/babel-generator/src/printer.js @@ -287,14 +287,11 @@ export default class Printer extends Buffer { // whitespace before this.newline(this.whitespace.getNewlinesBefore(comment)); + if (!this.endsWith(["[", "{"])) this.space(); + let column = this.position.column; let val = this.generateComment(comment); - if (column && !this.endsWith(["\n", " ", "[", "{"])) { - this._push(" "); - column++; - } - // if (comment.type === "CommentBlock" && this.format.indent.adjustMultilineComment) { let offset = comment.loc && comment.loc.start.column; From 528128f62d949edca0a564e8641d85ef3a8032cf Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Sun, 24 Apr 2016 20:46:24 -0700 Subject: [PATCH 11/22] Avoid looking for raw values for all nodes. --- .../babel-generator/src/generators/base.js | 4 +--- .../babel-generator/src/generators/flow.js | 9 ++++---- .../babel-generator/src/generators/types.js | 22 ++++++++++++++----- packages/babel-generator/src/printer.js | 22 +++---------------- 4 files changed, 25 insertions(+), 32 deletions(-) diff --git a/packages/babel-generator/src/generators/base.js b/packages/babel-generator/src/generators/base.js index 18607c615f..c5063741a2 100644 --- a/packages/babel-generator/src/generators/base.js +++ b/packages/babel-generator/src/generators/base.js @@ -38,6 +38,4 @@ export function Directive(node: Object) { this.semicolon(); } -export function DirectiveLiteral(node: Object) { - this.push(this._stringLiteral(node.value)); -} +export { StringLiteral as DirectiveLiteral } from "./types"; diff --git a/packages/babel-generator/src/generators/flow.js b/packages/babel-generator/src/generators/flow.js index 15f39923ca..10645fab0a 100644 --- a/packages/babel-generator/src/generators/flow.js +++ b/packages/babel-generator/src/generators/flow.js @@ -140,16 +140,15 @@ export function NullableTypeAnnotation(node: Object) { this.print(node.typeAnnotation, node); } -export { NumericLiteral as NumericLiteralTypeAnnotation } from "./types"; +export { + NumericLiteral as NumericLiteralTypeAnnotation, + StringLiteral as StringLiteralTypeAnnotation, +} from "./types"; export function NumberTypeAnnotation() { this.push("number"); } -export function StringLiteralTypeAnnotation(node: Object) { - this.push(this._stringLiteral(node.value)); -} - export function StringTypeAnnotation() { this.push("string"); } diff --git a/packages/babel-generator/src/generators/types.js b/packages/babel-generator/src/generators/types.js index 5eb8e1148d..731a2f17cb 100644 --- a/packages/babel-generator/src/generators/types.js +++ b/packages/babel-generator/src/generators/types.js @@ -123,15 +123,27 @@ export function NullLiteral() { } export function NumericLiteral(node: Object) { + let raw = this.getPossibleRaw(node); + if (raw != null) { + // Write an empty string to add indentation on just this first time. + this.push(""); + this.push(raw, true /* noIndent */); + return; + } + this.push(node.value + ""); } export function StringLiteral(node: Object, parent: Object) { - this.push(this._stringLiteral(node.value, parent)); -} + let raw = this.getPossibleRaw(node); + if (raw != null) { + // Write an empty string to add indentation on just this first time. + this.push(""); + this.push(raw, true /* noIndent */); + return; + } -export function _stringLiteral(val: string, parent: Object): string { - val = JSON.stringify(val); + let val = JSON.stringify(node.value); // escape illegal js but valid json unicode characters val = val.replace(/[\u000A\u000D\u2028\u2029]/g, function (c) { @@ -152,5 +164,5 @@ export function _stringLiteral(val: string, parent: Object): string { val = `'${val}'`; } - return val; + return this.push(val); } diff --git a/packages/babel-generator/src/printer.js b/packages/babel-generator/src/printer.js index 488c045f32..cfe34c6964 100644 --- a/packages/babel-generator/src/printer.js +++ b/packages/babel-generator/src/printer.js @@ -53,7 +53,7 @@ export default class Printer extends Buffer { let loc = (t.isProgram(node) || t.isFile(node)) ? null : node.loc; this.withSource("start", loc, () => { - this._print(node, parent); + this[node.type](node, parent); }); // Check again if any of our children may have left an aux comment on the stack @@ -96,30 +96,14 @@ export default class Printer extends Buffer { } getPossibleRaw(node) { + if (this.format.minified) return; + let extra = node.extra; if (extra && extra.raw != null && extra.rawValue != null && node.value === extra.rawValue) { return extra.raw; } } - _print(node, parent) { - // In minified mode we need to produce as little bytes as needed - // and need to make sure that string quoting is consistent. - // That means we have to always reprint as opposed to getting - // the raw value. - if (!this.format.minified) { - let extra = this.getPossibleRaw(node); - if (extra) { - this.push(""); - this._push(extra); - return; - } - } - - let printMethod = this[node.type]; - printMethod.call(this, node, parent); - } - printJoin(nodes: ?Array, parent: Object, opts = {}) { if (!nodes || !nodes.length) return; From 315c07541980e4f96309d7e44c17a6ca538241c0 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Sun, 24 Apr 2016 22:55:36 -0700 Subject: [PATCH 12/22] Remove the unnecessary push/_push division. --- packages/babel-generator/src/buffer.js | 16 ++++------------ .../src/generators/template-literals.js | 6 +++--- packages/babel-generator/src/printer.js | 2 +- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/packages/babel-generator/src/buffer.js b/packages/babel-generator/src/buffer.js index 9e162b6bec..7c0c52efd1 100644 --- a/packages/babel-generator/src/buffer.js +++ b/packages/babel-generator/src/buffer.js @@ -44,7 +44,7 @@ export default class Buffer { // catch up to this nodes newline if we're behind if (node.loc && this.format.retainLines && this.buf) { while (this.position.line < node.loc.start.line) { - this._push("\n"); + this.push("\n"); } } } @@ -214,7 +214,7 @@ export default class Buffer { this.removeLast(" "); this._removeSpacesAfterLastNewline(); - this._push(repeat("\n", i)); + this.push(repeat("\n", i), true /* noIndent */); } /** @@ -280,17 +280,9 @@ export default class Buffer { str = str.replace(/\n/g, `\n${indent}`); // we've got a newline before us so prepend on the indentation - if (this.endsWith("\n")) this._push(indent); + if (this.endsWith("\n")) this.push(indent, true /* noIndent */); } - this._push(str); - } - - /** - * Push a string to the buffer. - */ - - _push(str: string): void { // see startTerminatorless() instance method let parenPushNewlineState = this.parenPushNewlineState; if (parenPushNewlineState) { @@ -304,7 +296,7 @@ export default class Buffer { if (cha === "\n" || cha === "/") { // we're going to break this terminator expression so we need to add a parentheses - this._push("("); + this.push("(", true /* noIndent */); this.indent(); parenPushNewlineState.printed = true; } diff --git a/packages/babel-generator/src/generators/template-literals.js b/packages/babel-generator/src/generators/template-literals.js index 2b817284ae..0005510143 100644 --- a/packages/babel-generator/src/generators/template-literals.js +++ b/packages/babel-generator/src/generators/template-literals.js @@ -4,7 +4,7 @@ export function TaggedTemplateExpression(node: Object) { } export function TemplateElement(node: Object) { - this._push(node.value.raw); + this.push(node.value.raw, true/* noIndent */); } export function TemplateLiteral(node: Object) { @@ -16,11 +16,11 @@ export function TemplateLiteral(node: Object) { this.print(quasis[i], node); if (i + 1 < quasis.length) { - this._push("${ "); + this.push("${ ", true /* noIndent */); this.print(node.expressions[i], node); this.push(" }"); } } - this._push("`"); + this.push("`", true /* noIndent */); } diff --git a/packages/babel-generator/src/printer.js b/packages/babel-generator/src/printer.js index cfe34c6964..c46a6bc9c9 100644 --- a/packages/babel-generator/src/printer.js +++ b/packages/babel-generator/src/printer.js @@ -300,7 +300,7 @@ export default class Printer extends Buffer { } // - this._push(val); + this.push(val, true /* noIndent */); // whitespace after this.newline(this.whitespace.getNewlinesAfter(comment)); From 3e1a661eb62de1cb95d30ce2f25944b2b8266281 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Mon, 25 Apr 2016 19:59:14 -0700 Subject: [PATCH 13/22] Use the general list handlers and the default separator. --- packages/babel-generator/src/generators/classes.js | 8 ++++---- packages/babel-generator/src/generators/flow.js | 10 ++++------ packages/babel-generator/src/generators/modules.js | 4 ++-- packages/babel-generator/src/generators/types.js | 4 ++-- .../test/fixtures/minified/modules/expected.js | 2 +- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/babel-generator/src/generators/classes.js b/packages/babel-generator/src/generators/classes.js index e6c499186c..9d4e5cf24f 100644 --- a/packages/babel-generator/src/generators/classes.js +++ b/packages/babel-generator/src/generators/classes.js @@ -1,5 +1,5 @@ export function ClassDeclaration(node: Object) { - this.printJoin(node.decorators, node, { separator: "" }); + this.printJoin(node.decorators, node); this.push("class"); if (node.id) { @@ -17,7 +17,7 @@ export function ClassDeclaration(node: Object) { if (node.implements) { this.push(" implements "); - this.printJoin(node.implements, node, { separator: ", " }); + this.printList(node.implements, node); } this.space(); @@ -43,7 +43,7 @@ export function ClassBody(node: Object) { } export function ClassProperty(node: Object) { - this.printJoin(node.decorators, node, { separator: "" }); + this.printJoin(node.decorators, node); if (node.static) this.push("static "); this.print(node.key, node); @@ -58,7 +58,7 @@ export function ClassProperty(node: Object) { } export function ClassMethod(node: Object) { - this.printJoin(node.decorators, node, { separator: "" }); + this.printJoin(node.decorators, node); if (node.static) { this.push("static "); diff --git a/packages/babel-generator/src/generators/flow.js b/packages/babel-generator/src/generators/flow.js index 10645fab0a..2c52a5a6e1 100644 --- a/packages/babel-generator/src/generators/flow.js +++ b/packages/babel-generator/src/generators/flow.js @@ -112,11 +112,11 @@ export function _interfaceish(node: Object) { this.print(node.typeParameters, node); if (node.extends.length) { this.push(" extends "); - this.printJoin(node.extends, node, { separator: ", " }); + this.printList(node.extends, node); } if (node.mixins && node.mixins.length) { this.push(" mixins "); - this.printJoin(node.mixins, node, { separator: ", " }); + this.printList(node.mixins, node); } this.space(); this.print(node.body, node); @@ -159,7 +159,7 @@ export function ThisTypeAnnotation() { export function TupleTypeAnnotation(node: Object) { this.push("["); - this.printJoin(node.types, node, { separator: ", " }); + this.printList(node.types, node); this.push("]"); } @@ -209,8 +209,7 @@ export function TypeParameter(node: Object) { export function TypeParameterInstantiation(node: Object) { this.push("<"); - this.printJoin(node.params, node, { - separator: ", ", + this.printList(node.params, node, { iterator: (node: Object) => { this.print(node.typeAnnotation, node); } @@ -228,7 +227,6 @@ export function ObjectTypeAnnotation(node: Object) { this.space(); this.printJoin(props, node, { - separator: false, indent: true, iterator: () => { if (props.length !== 1) { diff --git a/packages/babel-generator/src/generators/modules.js b/packages/babel-generator/src/generators/modules.js index 825bf8d56b..904e7093bf 100644 --- a/packages/babel-generator/src/generators/modules.js +++ b/packages/babel-generator/src/generators/modules.js @@ -81,7 +81,7 @@ function ExportDeclaration(node: Object) { this.push("{"); if (specifiers.length) { this.space(); - this.printJoin(specifiers, node, { separator: ", " }); + this.printList(specifiers, node); this.space(); } this.push("}"); @@ -121,7 +121,7 @@ export function ImportDeclaration(node: Object) { if (specifiers.length) { this.push("{"); this.space(); - this.printJoin(specifiers, node, { separator: ", " }); + this.printList(specifiers, node); this.space(); this.push("}"); } diff --git a/packages/babel-generator/src/generators/types.js b/packages/babel-generator/src/generators/types.js index 731a2f17cb..ba0f4cb49c 100644 --- a/packages/babel-generator/src/generators/types.js +++ b/packages/babel-generator/src/generators/types.js @@ -48,12 +48,12 @@ export function ObjectExpression(node: Object) { export { ObjectExpression as ObjectPattern }; export function ObjectMethod(node: Object) { - this.printJoin(node.decorators, node, { separator: "" }); + this.printJoin(node.decorators, node); this._method(node); } export function ObjectProperty(node: Object) { - this.printJoin(node.decorators, node, { separator: "" }); + this.printJoin(node.decorators, node); if (node.computed) { this.push("["); diff --git a/packages/babel-generator/test/fixtures/minified/modules/expected.js b/packages/babel-generator/test/fixtures/minified/modules/expected.js index 1c03cdd973..1ce58c20ad 100644 --- a/packages/babel-generator/test/fixtures/minified/modules/expected.js +++ b/packages/babel-generator/test/fixtures/minified/modules/expected.js @@ -1 +1 @@ -import * as foo from "foo";import {foo as bar, foo2 as bar2} from "foo";import {foo2} from "foo";export * from "foo";export {foo as bar} from "foo";export {foo} from "foo"; +import * as foo from "foo";import {foo as bar,foo2 as bar2} from "foo";import {foo2} from "foo";export * from "foo";export {foo as bar} from "foo";export {foo} from "foo"; From 8336aa52e832cd6cb0f850ced2916a9e976f9fb8 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Mon, 25 Apr 2016 22:13:06 -0700 Subject: [PATCH 14/22] Split all of the separators into functions. --- .../src/generators/expressions.js | 6 +++++- packages/babel-generator/src/generators/flow.js | 12 ++++++++++-- packages/babel-generator/src/generators/jsx.js | 6 +++++- .../src/generators/statements.js | 17 +++++++++++++---- packages/babel-generator/src/printer.js | 10 +++++++--- 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/packages/babel-generator/src/generators/expressions.js b/packages/babel-generator/src/generators/expressions.js index 3aa3679bb0..9c42733ca9 100644 --- a/packages/babel-generator/src/generators/expressions.js +++ b/packages/babel-generator/src/generators/expressions.js @@ -91,6 +91,10 @@ export function Decorator(node: Object) { this.newline(); } +function commaSeparatorNewline() { + this.push(",\n"); +} + export function CallExpression(node: Object) { this.print(node.callee, node); if (node.loc) this.printAuxAfterComment(); @@ -101,7 +105,7 @@ export function CallExpression(node: Object) { let separator; if (isPrettyCall) { - separator = ",\n"; + separator = commaSeparatorNewline; this.newline(); this.indent(); } diff --git a/packages/babel-generator/src/generators/flow.js b/packages/babel-generator/src/generators/flow.js index 2c52a5a6e1..ed461d8640 100644 --- a/packages/babel-generator/src/generators/flow.js +++ b/packages/babel-generator/src/generators/flow.js @@ -127,8 +127,12 @@ export function InterfaceDeclaration(node: Object) { this._interfaceish(node); } +function andSeparator() { + this.push(" & "); +} + export function IntersectionTypeAnnotation(node: Object) { - this.printJoin(node.types, node, { separator: " & " }); + this.printJoin(node.types, node, { separator: andSeparator }); } export function MixedTypeAnnotation() { @@ -277,8 +281,12 @@ export function QualifiedTypeIdentifier(node: Object) { this.print(node.id, node); } +function orSeparator() { + this.push(" | "); +} + export function UnionTypeAnnotation(node: Object) { - this.printJoin(node.types, node, { separator: " | " }); + this.printJoin(node.types, node, { separator: orSeparator }); } export function TypeCastExpression(node: Object) { diff --git a/packages/babel-generator/src/generators/jsx.js b/packages/babel-generator/src/generators/jsx.js index 7a926bb512..1850e0abf4 100644 --- a/packages/babel-generator/src/generators/jsx.js +++ b/packages/babel-generator/src/generators/jsx.js @@ -52,12 +52,16 @@ export function JSXElement(node: Object) { this.print(node.closingElement, node); } +function spaceSeparator() { + this.push(" "); +} + export function JSXOpeningElement(node: Object) { this.push("<"); this.print(node.name, node); if (node.attributes.length > 0) { this.push(" "); - this.printJoin(node.attributes, node, { separator: " " }); + this.printJoin(node.attributes, node, { separator: spaceSeparator }); } this.push(node.selfClosing ? " />" : ">"); } diff --git a/packages/babel-generator/src/generators/statements.js b/packages/babel-generator/src/generators/statements.js index c4d4e95faa..caea594d93 100644 --- a/packages/babel-generator/src/generators/statements.js +++ b/packages/babel-generator/src/generators/statements.js @@ -1,4 +1,3 @@ -import repeat from "lodash/repeat"; import * as t from "babel-types"; const NON_ALPHABETIC_UNARY_OPERATORS = t.UPDATE_OPERATORS.concat(t.NUMBER_UNARY_OPERATORS).concat(["!"]); @@ -205,6 +204,16 @@ export function DebuggerStatement() { this.semicolon(); } +function variableDeclarationIdent() { + // "let " or "var " indentation. + this.push(",\n "); +} + +function constDeclarationIdent() { + // "const " indentation. + this.push(",\n "); +} + export function VariableDeclaration(node: Object, parent: Object) { this.push(node.kind + " "); @@ -231,14 +240,14 @@ export function VariableDeclaration(node: Object, parent: Object) { // bar = "foo"; // - let sep; + let separator; if (!this.format.compact && !this.format.concise && hasInits && !this.format.retainLines) { - sep = `,\n${repeat(" ", node.kind.length + 1)}`; + separator = node.kind === "const" ? constDeclarationIdent : variableDeclarationIdent; } // - this.printList(node.declarations, node, { separator: sep }); + this.printList(node.declarations, node, { separator }); if (t.isFor(parent)) { // don't give semicolons to these nodes since they'll be inserted in the parent generator diff --git a/packages/babel-generator/src/printer.js b/packages/babel-generator/src/printer.js index c46a6bc9c9..e3772e8987 100644 --- a/packages/babel-generator/src/printer.js +++ b/packages/babel-generator/src/printer.js @@ -125,7 +125,7 @@ export default class Printer extends Buffer { } if (opts.separator && i < len - 1) { - this.push(opts.separator); + opts.separator.call(this); } } }; @@ -187,8 +187,7 @@ export default class Printer extends Buffer { printList(items, parent, opts = {}) { if (opts.separator == null) { - opts.separator = ","; - if (!this.format.compact) opts.separator += " "; + opts.separator = commaSeparator; } return this.printJoin(items, parent, opts); @@ -316,6 +315,11 @@ export default class Printer extends Buffer { } } +function commaSeparator() { + this.push(","); + this.space(); +} + for (let generator of [ require("./generators/template-literals"), require("./generators/expressions"), From f56670065727c9451d1aad9150afbca41163d417 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Mon, 25 Apr 2016 22:20:31 -0700 Subject: [PATCH 15/22] Avoid processing indentation for every pushed string. --- packages/babel-generator/src/buffer.js | 8 +------- packages/babel-generator/src/generators/statements.js | 6 ++++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/babel-generator/src/buffer.js b/packages/babel-generator/src/buffer.js index 7c0c52efd1..bbbf423a8c 100644 --- a/packages/babel-generator/src/buffer.js +++ b/packages/babel-generator/src/buffer.js @@ -273,14 +273,8 @@ export default class Buffer { push(str: string, noIndent?: boolean) { if (!this.format.compact && this._indent && !noIndent && str !== "\n") { - // we have an indent level and we aren't pushing a newline - let indent = this.getIndent(); - - // replace all newlines with newlines with the indentation - str = str.replace(/\n/g, `\n${indent}`); - // we've got a newline before us so prepend on the indentation - if (this.endsWith("\n")) this.push(indent, true /* noIndent */); + if (this.endsWith("\n")) this.push(this.getIndent(), true /* noIndent */); } // see startTerminatorless() instance method diff --git a/packages/babel-generator/src/generators/statements.js b/packages/babel-generator/src/generators/statements.js index caea594d93..7bcc5a57da 100644 --- a/packages/babel-generator/src/generators/statements.js +++ b/packages/babel-generator/src/generators/statements.js @@ -206,12 +206,14 @@ export function DebuggerStatement() { function variableDeclarationIdent() { // "let " or "var " indentation. - this.push(",\n "); + this.push(",\n"); + this.push(" "); } function constDeclarationIdent() { // "const " indentation. - this.push(",\n "); + this.push(",\n"); + this.push(" "); } export function VariableDeclaration(node: Object, parent: Object) { From 22e2c882693a67a00882b14f9422a31138684c32 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Mon, 25 Apr 2016 22:47:00 -0700 Subject: [PATCH 16/22] Drop the unneeded noIndent function param. --- packages/babel-generator/src/buffer.js | 11 +++++------ packages/babel-generator/src/generators/jsx.js | 2 +- .../src/generators/template-literals.js | 17 +++++++++-------- .../babel-generator/src/generators/types.js | 8 ++------ packages/babel-generator/src/printer.js | 6 +----- 5 files changed, 18 insertions(+), 26 deletions(-) diff --git a/packages/babel-generator/src/buffer.js b/packages/babel-generator/src/buffer.js index bbbf423a8c..a87185c1a2 100644 --- a/packages/babel-generator/src/buffer.js +++ b/packages/babel-generator/src/buffer.js @@ -213,8 +213,7 @@ export default class Buffer { } this.removeLast(" "); - this._removeSpacesAfterLastNewline(); - this.push(repeat("\n", i), true /* noIndent */); + this.push(repeat("\n", i)); } /** @@ -271,10 +270,10 @@ export default class Buffer { * Push a string to the buffer, maintaining indentation and newlines. */ - push(str: string, noIndent?: boolean) { - if (!this.format.compact && this._indent && !noIndent && str !== "\n") { + push(str: string) { + if (!this.format.compact && this._indent && str[0] !== "\n") { // we've got a newline before us so prepend on the indentation - if (this.endsWith("\n")) this.push(this.getIndent(), true /* noIndent */); + if (this.endsWith("\n")) str = this.getIndent() + str; } // see startTerminatorless() instance method @@ -290,7 +289,7 @@ export default class Buffer { if (cha === "\n" || cha === "/") { // we're going to break this terminator expression so we need to add a parentheses - this.push("(", true /* noIndent */); + str = "(" + str; this.indent(); parenPushNewlineState.printed = true; } diff --git a/packages/babel-generator/src/generators/jsx.js b/packages/babel-generator/src/generators/jsx.js index 1850e0abf4..3dbb72a4ef 100644 --- a/packages/babel-generator/src/generators/jsx.js +++ b/packages/babel-generator/src/generators/jsx.js @@ -35,7 +35,7 @@ export function JSXExpressionContainer(node: Object) { } export function JSXText(node: Object) { - this.push(node.value, true); + this.push(node.value); } export function JSXElement(node: Object) { diff --git a/packages/babel-generator/src/generators/template-literals.js b/packages/babel-generator/src/generators/template-literals.js index 0005510143..ac42c0770d 100644 --- a/packages/babel-generator/src/generators/template-literals.js +++ b/packages/babel-generator/src/generators/template-literals.js @@ -3,24 +3,25 @@ export function TaggedTemplateExpression(node: Object) { this.print(node.quasi, node); } -export function TemplateElement(node: Object) { - this.push(node.value.raw, true/* noIndent */); +export function TemplateElement(node: Object, parent: Object) { + const isFirst = parent.quasis[0] === node; + const isLast = parent.quasis[parent.quasis.length - 1] === node; + + let value = (isFirst ? "`" : "}") + node.value.raw + (isLast ? "`" : "${"); + + if (!isFirst) this.push(" "); + this.push(value); + if (!isLast) this.push(" "); } export function TemplateLiteral(node: Object) { - this.push("`"); - let quasis = node.quasis; for (let i = 0; i < quasis.length; i++) { this.print(quasis[i], node); if (i + 1 < quasis.length) { - this.push("${ ", true /* noIndent */); this.print(node.expressions[i], node); - this.push(" }"); } } - - this.push("`", true /* noIndent */); } diff --git a/packages/babel-generator/src/generators/types.js b/packages/babel-generator/src/generators/types.js index ba0f4cb49c..092f73fcae 100644 --- a/packages/babel-generator/src/generators/types.js +++ b/packages/babel-generator/src/generators/types.js @@ -125,9 +125,7 @@ export function NullLiteral() { export function NumericLiteral(node: Object) { let raw = this.getPossibleRaw(node); if (raw != null) { - // Write an empty string to add indentation on just this first time. - this.push(""); - this.push(raw, true /* noIndent */); + this.push(raw); return; } @@ -137,9 +135,7 @@ export function NumericLiteral(node: Object) { export function StringLiteral(node: Object, parent: Object) { let raw = this.getPossibleRaw(node); if (raw != null) { - // Write an empty string to add indentation on just this first time. - this.push(""); - this.push(raw, true /* noIndent */); + this.push(raw); return; } diff --git a/packages/babel-generator/src/printer.js b/packages/babel-generator/src/printer.js index e3772e8987..77f3808941 100644 --- a/packages/babel-generator/src/printer.js +++ b/packages/babel-generator/src/printer.js @@ -287,10 +287,6 @@ export default class Printer extends Buffer { val = val.replace(/\n/g, `\n${repeat(" ", indent)}`); } - if (column === 0) { - val = this.getIndent() + val; - } - // force a newline for line comments when retainLines is set in case the next printed node // doesn't catch up if ((this.format.compact || this.format.concise || this.format.retainLines) && @@ -299,7 +295,7 @@ export default class Printer extends Buffer { } // - this.push(val, true /* noIndent */); + this.push(val); // whitespace after this.newline(this.whitespace.getNewlinesAfter(comment)); From e702e672331ad4c3130ccb62f6f06d82db65bab5 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Sat, 30 Apr 2016 13:19:21 -0700 Subject: [PATCH 17/22] Split all of the pushes into individual tokens. --- packages/babel-generator/src/buffer.js | 5 +- .../babel-generator/src/generators/classes.js | 19 ++++-- .../src/generators/expressions.js | 6 +- .../babel-generator/src/generators/flow.js | 66 ++++++++++++++----- .../babel-generator/src/generators/jsx.js | 10 ++- .../babel-generator/src/generators/methods.js | 22 +++++-- .../babel-generator/src/generators/modules.js | 61 ++++++++++++----- .../src/generators/statements.js | 35 ++++++---- 8 files changed, 165 insertions(+), 59 deletions(-) diff --git a/packages/babel-generator/src/buffer.js b/packages/babel-generator/src/buffer.js index a87185c1a2..e9eaeb4dcd 100644 --- a/packages/babel-generator/src/buffer.js +++ b/packages/babel-generator/src/buffer.js @@ -213,7 +213,10 @@ export default class Buffer { } this.removeLast(" "); - this.push(repeat("\n", i)); + this._removeSpacesAfterLastNewline(); + for (let j = 0; j < i; j++) { + this.push("\n"); + } } /** diff --git a/packages/babel-generator/src/generators/classes.js b/packages/babel-generator/src/generators/classes.js index 9d4e5cf24f..5c418ff97c 100644 --- a/packages/babel-generator/src/generators/classes.js +++ b/packages/babel-generator/src/generators/classes.js @@ -10,13 +10,17 @@ export function ClassDeclaration(node: Object) { this.print(node.typeParameters, node); if (node.superClass) { - this.push(" extends "); + this.push(" "); + this.push("extends"); + this.push(" "); this.print(node.superClass, node); this.print(node.superTypeParameters, node); } if (node.implements) { - this.push(" implements "); + this.push(" "); + this.push("implements"); + this.push(" "); this.printList(node.implements, node); } @@ -45,7 +49,10 @@ export function ClassBody(node: Object) { export function ClassProperty(node: Object) { this.printJoin(node.decorators, node); - if (node.static) this.push("static "); + if (node.static) { + this.push("static"); + this.push(" "); + } this.print(node.key, node); this.print(node.typeAnnotation, node); if (node.value) { @@ -61,11 +68,13 @@ export function ClassMethod(node: Object) { this.printJoin(node.decorators, node); if (node.static) { - this.push("static "); + this.push("static"); + this.push(" "); } if (node.kind === "constructorCall") { - this.push("call "); + this.push("call"); + this.push(" "); } this._method(node); diff --git a/packages/babel-generator/src/generators/expressions.js b/packages/babel-generator/src/generators/expressions.js index 9c42733ca9..b663e905b2 100644 --- a/packages/babel-generator/src/generators/expressions.js +++ b/packages/babel-generator/src/generators/expressions.js @@ -61,7 +61,8 @@ export function ConditionalExpression(node: Object) { } export function NewExpression(node: Object, parent: Object) { - this.push("new "); + this.push("new"); + this.push(" "); this.print(node.callee, node); if (node.arguments.length === 0 && this.format.minified && !t.isCallExpression(parent, { callee: node }) && @@ -92,7 +93,8 @@ export function Decorator(node: Object) { } function commaSeparatorNewline() { - this.push(",\n"); + this.push(","); + this.push("\n"); } export function CallExpression(node: Object) { diff --git a/packages/babel-generator/src/generators/flow.js b/packages/babel-generator/src/generators/flow.js index ed461d8640..d1df30a1aa 100644 --- a/packages/babel-generator/src/generators/flow.js +++ b/packages/babel-generator/src/generators/flow.js @@ -25,36 +25,50 @@ export function NullLiteralTypeAnnotation() { } export function DeclareClass(node: Object) { - this.push("declare class "); + this.push("declare"); + this.push(" "); + this.push("class"); + this.push(" "); this._interfaceish(node); } export function DeclareFunction(node: Object) { - this.push("declare function "); + this.push("declare"); + this.push(" "); + this.push("function"); + this.push(" "); this.print(node.id, node); this.print(node.id.typeAnnotation.typeAnnotation, node); this.semicolon(); } export function DeclareInterface(node: Object) { - this.push("declare "); + this.push("declare"); + this.push(" "); this.InterfaceDeclaration(node); } export function DeclareModule(node: Object) { - this.push("declare module "); + this.push("declare"); + this.push(" "); + this.push("module"); + this.push(" "); this.print(node.id, node); this.space(); this.print(node.body, node); } export function DeclareTypeAlias(node: Object) { - this.push("declare "); + this.push("declare"); + this.push(" "); this.TypeAlias(node); } export function DeclareVariable(node: Object) { - this.push("declare var "); + this.push("declare"); + this.push(" "); + this.push("var"); + this.push(" "); this.print(node.id, node); this.print(node.id.typeAnnotation, node); this.semicolon(); @@ -111,11 +125,15 @@ export function _interfaceish(node: Object) { this.print(node.id, node); this.print(node.typeParameters, node); if (node.extends.length) { - this.push(" extends "); + this.push(" "); + this.push("extends"); + this.push(" "); this.printList(node.extends, node); } if (node.mixins && node.mixins.length) { - this.push(" mixins "); + this.push(" "); + this.push("mixins"); + this.push(" "); this.printList(node.mixins, node); } this.space(); @@ -123,12 +141,15 @@ export function _interfaceish(node: Object) { } export function InterfaceDeclaration(node: Object) { - this.push("interface "); + this.push("interface"); + this.push(" "); this._interfaceish(node); } function andSeparator() { - this.push(" & "); + this.push(" "); + this.push("&"); + this.push(" "); } export function IntersectionTypeAnnotation(node: Object) { @@ -168,12 +189,14 @@ export function TupleTypeAnnotation(node: Object) { } export function TypeofTypeAnnotation(node: Object) { - this.push("typeof "); + this.push("typeof"); + this.push(" "); this.print(node.argument, node); } export function TypeAlias(node: Object) { - this.push("type "); + this.push("type"); + this.push(" "); this.print(node.id, node); this.print(node.typeParameters, node); this.space(); @@ -247,12 +270,18 @@ export function ObjectTypeAnnotation(node: Object) { } export function ObjectTypeCallProperty(node: Object) { - if (node.static) this.push("static "); + if (node.static) { + this.push("static"); + this.push(" "); + } this.print(node.value, node); } export function ObjectTypeIndexer(node: Object) { - if (node.static) this.push("static "); + if (node.static) { + this.push("static"); + this.push(" "); + } this.push("["); this.print(node.id, node); this.push(":"); @@ -265,7 +294,10 @@ export function ObjectTypeIndexer(node: Object) { } export function ObjectTypeProperty(node: Object) { - if (node.static) this.push("static "); + if (node.static) { + this.push("static"); + this.push(" "); + } this.print(node.key, node); if (node.optional) this.push("?"); if (!t.isFunctionTypeAnnotation(node.value)) { @@ -282,7 +314,9 @@ export function QualifiedTypeIdentifier(node: Object) { } function orSeparator() { - this.push(" | "); + this.push(" "); + this.push("|"); + this.push(" "); } export function UnionTypeAnnotation(node: Object) { diff --git a/packages/babel-generator/src/generators/jsx.js b/packages/babel-generator/src/generators/jsx.js index 3dbb72a4ef..4ae406547b 100644 --- a/packages/babel-generator/src/generators/jsx.js +++ b/packages/babel-generator/src/generators/jsx.js @@ -23,7 +23,8 @@ export function JSXMemberExpression(node: Object) { } export function JSXSpreadAttribute(node: Object) { - this.push("{..."); + this.push("{"); + this.push("..."); this.print(node.argument, node); this.push("}"); } @@ -63,7 +64,12 @@ export function JSXOpeningElement(node: Object) { this.push(" "); this.printJoin(node.attributes, node, { separator: spaceSeparator }); } - this.push(node.selfClosing ? " />" : ">"); + if (node.selfClosing) { + this.push(" "); + this.push("/>"); + } else { + this.push(">"); + } } export function JSXClosingElement(node: Object) { diff --git a/packages/babel-generator/src/generators/methods.js b/packages/babel-generator/src/generators/methods.js index 76552736e4..328d45def3 100644 --- a/packages/babel-generator/src/generators/methods.js +++ b/packages/babel-generator/src/generators/methods.js @@ -27,10 +27,14 @@ export function _method(node: Object) { } if (kind === "get" || kind === "set") { - this.push(kind + " "); + this.push(kind); + this.push(" "); } - if (node.async) this.push("async "); + if (node.async) { + this.push("async"); + this.push(" "); + } if (node.computed) { this.push("["); @@ -46,7 +50,10 @@ export function _method(node: Object) { } export function FunctionExpression(node: Object) { - if (node.async) this.push("async "); + if (node.async) { + this.push("async"); + this.push(" "); + } this.push("function"); if (node.generator) this.push("*"); @@ -65,7 +72,10 @@ export function FunctionExpression(node: Object) { export { FunctionExpression as FunctionDeclaration }; export function ArrowFunctionExpression(node: Object) { - if (node.async) this.push("async "); + if (node.async) { + this.push("async"); + this.push(" "); + } if (node.params.length === 1 && t.isIdentifier(node.params[0])) { this.print(node.params[0], node); @@ -73,7 +83,9 @@ export function ArrowFunctionExpression(node: Object) { this._params(node); } - this.push(" => "); + this.push(" "); + this.push("=>"); + this.push(" "); this.print(node.body, node); } diff --git a/packages/babel-generator/src/generators/modules.js b/packages/babel-generator/src/generators/modules.js index 904e7093bf..8dc9a5ff83 100644 --- a/packages/babel-generator/src/generators/modules.js +++ b/packages/babel-generator/src/generators/modules.js @@ -3,7 +3,9 @@ import * as t from "babel-types"; export function ImportSpecifier(node: Object) { this.print(node.imported, node); if (node.local && node.local.name !== node.imported.name) { - this.push(" as "); + this.push(" "); + this.push("as"); + this.push(" "); this.print(node.local, node); } } @@ -19,34 +21,49 @@ export function ExportDefaultSpecifier(node: Object) { export function ExportSpecifier(node: Object) { this.print(node.local, node); if (node.exported && node.local.name !== node.exported.name) { - this.push(" as "); + this.push(" "); + this.push("as"); + this.push(" "); this.print(node.exported, node); } } export function ExportNamespaceSpecifier(node: Object) { - this.push("* as "); + this.push("*"); + this.push(" "); + this.push("as"); + this.push(" "); this.print(node.exported, node); } export function ExportAllDeclaration(node: Object) { - this.push("export *"); + this.push("export"); + this.push(" "); + this.push("*"); if (node.exported) { - this.push(" as "); + this.push(" "); + this.push("as"); + this.push(" "); this.print(node.exported, node); } - this.push(" from "); + this.push(" "); + this.push("from"); + this.push(" "); this.print(node.source, node); this.semicolon(); } export function ExportNamedDeclaration() { - this.push("export "); + this.push("export"); + this.push(" "); ExportDeclaration.apply(this, arguments); } export function ExportDefaultDeclaration() { - this.push("export default "); + this.push("export"); + this.push(" "); + this.push("default"); + this.push(" "); ExportDeclaration.apply(this, arguments); } @@ -57,7 +74,8 @@ function ExportDeclaration(node: Object) { if (!t.isStatement(declar)) this.semicolon(); } else { if (node.exportKind === "type") { - this.push("type "); + this.push("type"); + this.push(" "); } let specifiers = node.specifiers.slice(0); @@ -70,7 +88,8 @@ function ExportDeclaration(node: Object) { hasSpecial = true; this.print(specifiers.shift(), node); if (specifiers.length) { - this.push(", "); + this.push(","); + this.push(" "); } } else { break; @@ -88,7 +107,9 @@ function ExportDeclaration(node: Object) { } if (node.source) { - this.push(" from "); + this.push(" "); + this.push("from"); + this.push(" "); this.print(node.source, node); } @@ -97,10 +118,12 @@ function ExportDeclaration(node: Object) { } export function ImportDeclaration(node: Object) { - this.push("import "); + this.push("import"); + this.push(" "); if (node.importKind === "type" || node.importKind === "typeof") { - this.push(node.importKind + " "); + this.push(node.importKind); + this.push(" "); } let specifiers = node.specifiers.slice(0); @@ -111,7 +134,8 @@ export function ImportDeclaration(node: Object) { if (t.isImportDefaultSpecifier(first) || t.isImportNamespaceSpecifier(first)) { this.print(specifiers.shift(), node); if (specifiers.length) { - this.push(", "); + this.push(","); + this.push(" "); } } else { break; @@ -126,7 +150,9 @@ export function ImportDeclaration(node: Object) { this.push("}"); } - this.push(" from "); + this.push(" "); + this.push("from"); + this.push(" "); } this.print(node.source, node); @@ -134,6 +160,9 @@ export function ImportDeclaration(node: Object) { } export function ImportNamespaceSpecifier(node: Object) { - this.push("* as "); + this.push("*"); + this.push(" "); + this.push("as"); + this.push(" "); this.print(node.local, node); } diff --git a/packages/babel-generator/src/generators/statements.js b/packages/babel-generator/src/generators/statements.js index 7bcc5a57da..1f2ab63f3f 100644 --- a/packages/babel-generator/src/generators/statements.js +++ b/packages/babel-generator/src/generators/statements.js @@ -34,7 +34,8 @@ export function IfStatement(node: Object) { if (node.alternate) { if (this.endsWith("}")) this.space(); - this.push("else "); + this.push("else"); + this.push(" "); this.printAndIndentOnComments(node.alternate, node); } } @@ -82,7 +83,9 @@ let buildForXStatement = function (op) { this.keyword("for"); this.push("("); this.print(node.left, node); - this.push(` ${op} `); + this.push(" "); + this.push(op); + this.push(" "); this.print(node.right, node); this.push(")"); this.printBlock(node); @@ -93,7 +96,8 @@ export let ForInStatement = buildForXStatement("in"); export let ForOfStatement = buildForXStatement("of"); export function DoWhileStatement(node: Object) { - this.push("do "); + this.push("do"); + this.push(" "); this.print(node.body, node); this.space(); this.keyword("while"); @@ -132,7 +136,8 @@ export let ThrowStatement = buildLabelStatement("throw", "argument"); export function LabeledStatement(node: Object) { this.print(node.label, node); - this.push(": "); + this.push(":"); + this.push(" "); this.print(node.body, node); } @@ -152,7 +157,8 @@ export function TryStatement(node: Object) { if (node.finalizer) { this.space(); - this.push("finally "); + this.push("finally"); + this.push(" "); this.print(node.finalizer, node); } } @@ -186,11 +192,13 @@ export function SwitchStatement(node: Object) { export function SwitchCase(node: Object) { if (node.test) { - this.push("case "); + this.push("case"); + this.push(" "); this.print(node.test, node); this.push(":"); } else { - this.push("default:"); + this.push("default"); + this.push(":"); } if (node.consequent.length) { @@ -206,18 +214,21 @@ export function DebuggerStatement() { function variableDeclarationIdent() { // "let " or "var " indentation. - this.push(",\n"); - this.push(" "); + this.push(","); + this.push("\n"); + for (let i = 0; i < 4; i++) this.push(" "); } function constDeclarationIdent() { // "const " indentation. - this.push(",\n"); - this.push(" "); + this.push(","); + this.push("\n"); + for (let i = 0; i < 6; i++) this.push(" "); } export function VariableDeclaration(node: Object, parent: Object) { - this.push(node.kind + " "); + this.push(node.kind); + this.push(" "); let hasInits = false; // don't add whitespace to loop heads From 4286cb4f2a2f2c3b72bda482beb816181542da32 Mon Sep 17 00:00:00 2001 From: Logan Smyth Date: Sat, 30 Apr 2016 13:45:29 -0700 Subject: [PATCH 18/22] Explicitly label standalone tokens and words. --- packages/babel-generator/src/buffer.js | 25 +++- .../babel-generator/src/generators/base.js | 4 +- .../babel-generator/src/generators/classes.js | 18 +-- .../src/generators/expressions.js | 66 +++++---- .../babel-generator/src/generators/flow.js | 126 +++++++++--------- .../babel-generator/src/generators/jsx.js | 30 ++--- .../babel-generator/src/generators/methods.js | 26 ++-- .../babel-generator/src/generators/modules.js | 48 +++---- .../src/generators/statements.js | 74 +++++----- .../src/generators/template-literals.js | 2 +- .../babel-generator/src/generators/types.js | 40 +++--- packages/babel-generator/src/printer.js | 6 +- 12 files changed, 245 insertions(+), 220 deletions(-) diff --git a/packages/babel-generator/src/buffer.js b/packages/babel-generator/src/buffer.js index e9eaeb4dcd..bd9dc55fac 100644 --- a/packages/babel-generator/src/buffer.js +++ b/packages/babel-generator/src/buffer.js @@ -98,7 +98,7 @@ export default class Buffer { */ semicolon() { - this.push(";"); + this.token(";"); } /** @@ -110,7 +110,7 @@ export default class Buffer { if (this.format.minified && !this._lastPrintedIsEmptyStatement) { this._removeLast(";"); } - this.push("}"); + this.token("}"); } /** @@ -118,7 +118,7 @@ export default class Buffer { */ keyword(name: string) { - this.push(name); + this.word(name); this.space(); } @@ -134,6 +134,23 @@ export default class Buffer { } } + /** + * Writes a token that can't be safely parsed without taking whitespace into account. + */ + + word(str: string) { + this.push(str); + } + + /** + * Writes a simple token. + */ + + token(str: string) { + this.push(str); + } + + /** * Remove the last character. */ @@ -180,7 +197,7 @@ export default class Buffer { if (state.printed) { this.dedent(); this.newline(); - this.push(")"); + this.token(")"); } } diff --git a/packages/babel-generator/src/generators/base.js b/packages/babel-generator/src/generators/base.js index c5063741a2..6765bc762e 100644 --- a/packages/babel-generator/src/generators/base.js +++ b/packages/babel-generator/src/generators/base.js @@ -12,7 +12,7 @@ export function Program(node: Object) { } export function BlockStatement(node: Object) { - this.push("{"); + this.token("{"); this.printInnerComments(node); if (node.body.length) { this.newline(); @@ -27,7 +27,7 @@ export function BlockStatement(node: Object) { this.rightBrace(); } else { this.source("end", node.loc); - this.push("}"); + this.token("}"); } } diff --git a/packages/babel-generator/src/generators/classes.js b/packages/babel-generator/src/generators/classes.js index 5c418ff97c..a7f6675baa 100644 --- a/packages/babel-generator/src/generators/classes.js +++ b/packages/babel-generator/src/generators/classes.js @@ -1,6 +1,6 @@ export function ClassDeclaration(node: Object) { this.printJoin(node.decorators, node); - this.push("class"); + this.word("class"); if (node.id) { this.push(" "); @@ -11,7 +11,7 @@ export function ClassDeclaration(node: Object) { if (node.superClass) { this.push(" "); - this.push("extends"); + this.word("extends"); this.push(" "); this.print(node.superClass, node); this.print(node.superTypeParameters, node); @@ -19,7 +19,7 @@ export function ClassDeclaration(node: Object) { if (node.implements) { this.push(" "); - this.push("implements"); + this.word("implements"); this.push(" "); this.printList(node.implements, node); } @@ -31,10 +31,10 @@ export function ClassDeclaration(node: Object) { export { ClassDeclaration as ClassExpression }; export function ClassBody(node: Object) { - this.push("{"); + this.token("{"); this.printInnerComments(node); if (node.body.length === 0) { - this.push("}"); + this.token("}"); } else { this.newline(); @@ -50,14 +50,14 @@ export function ClassProperty(node: Object) { this.printJoin(node.decorators, node); if (node.static) { - this.push("static"); + this.word("static"); this.push(" "); } this.print(node.key, node); this.print(node.typeAnnotation, node); if (node.value) { this.space(); - this.push("="); + this.token("="); this.space(); this.print(node.value, node); } @@ -68,12 +68,12 @@ export function ClassMethod(node: Object) { this.printJoin(node.decorators, node); if (node.static) { - this.push("static"); + this.word("static"); this.push(" "); } if (node.kind === "constructorCall") { - this.push("call"); + this.word("call"); this.push(" "); } diff --git a/packages/babel-generator/src/generators/expressions.js b/packages/babel-generator/src/generators/expressions.js index b663e905b2..a9cbce45f4 100644 --- a/packages/babel-generator/src/generators/expressions.js +++ b/packages/babel-generator/src/generators/expressions.js @@ -21,47 +21,51 @@ export function UnaryExpression(node: Object) { needsSpace = false; } - this.push(node.operator); + if (node.operator === "void" || node.operator === "delete" || node.operator === "typeof") { + this.word(node.operator); + } else { + this.token(node.operator); + } if (needsSpace) this.push(" "); this.print(node.argument, node); } export function DoExpression(node: Object) { - this.push("do"); + this.word("do"); this.space(); this.print(node.body, node); } export function ParenthesizedExpression(node: Object) { - this.push("("); + this.token("("); this.print(node.expression, node); - this.push(")"); + this.token(")"); } export function UpdateExpression(node: Object) { if (node.prefix) { - this.push(node.operator); + this.token(node.operator); this.print(node.argument, node); } else { this.print(node.argument, node); - this.push(node.operator); + this.token(node.operator); } } export function ConditionalExpression(node: Object) { this.print(node.test, node); this.space(); - this.push("?"); + this.token("?"); this.space(); this.print(node.consequent, node); this.space(); - this.push(":"); + this.token(":"); this.space(); this.print(node.alternate, node); } export function NewExpression(node: Object, parent: Object) { - this.push("new"); + this.word("new"); this.push(" "); this.print(node.callee, node); if (node.arguments.length === 0 && this.format.minified && @@ -69,9 +73,9 @@ export function NewExpression(node: Object, parent: Object) { !t.isMemberExpression(parent) && !t.isNewExpression(parent)) return; - this.push("("); + this.token("("); this.printList(node.arguments, node); - this.push(")"); + this.token(")"); } export function SequenceExpression(node: Object) { @@ -79,21 +83,21 @@ export function SequenceExpression(node: Object) { } export function ThisExpression() { - this.push("this"); + this.word("this"); } export function Super() { - this.push("super"); + this.word("super"); } export function Decorator(node: Object) { - this.push("@"); + this.token("@"); this.print(node.expression, node); this.newline(); } function commaSeparatorNewline() { - this.push(","); + this.token(","); this.push("\n"); } @@ -101,7 +105,7 @@ export function CallExpression(node: Object) { this.print(node.callee, node); if (node.loc) this.printAuxAfterComment(); - this.push("("); + this.token("("); let isPrettyCall = node._prettyCall && !this.format.retainLines && !this.format.compact; @@ -119,15 +123,15 @@ export function CallExpression(node: Object) { this.dedent(); } - this.push(")"); + this.token(")"); } function buildYieldAwait(keyword: string) { return function (node: Object) { - this.push(keyword); + this.word(keyword); if (node.delegate) { - this.push("*"); + this.token("*"); } if (node.argument) { @@ -155,7 +159,7 @@ export function ExpressionStatement(node: Object) { export function AssignmentPattern(node: Object) { this.print(node.left, node); this.space(); - this.push("="); + this.token("="); this.space(); this.print(node.right, node); } @@ -167,7 +171,7 @@ export function AssignmentExpression(node: Object, parent: Object) { !n.needsParens(node, parent); if (parens) { - this.push("("); + this.token("("); } this.print(node.left, node); @@ -175,7 +179,11 @@ export function AssignmentExpression(node: Object, parent: Object) { let spaces = !this.format.compact || node.operator === "in" || node.operator === "instanceof"; if (spaces) this.push(" "); - this.push(node.operator); + if (node.operator === "in" || node.operator === "instanceof") { + this.word(node.operator); + } else { + this.token(node.operator); + } if (!spaces) { // space is mandatory to avoid outputting