diff --git a/packages/babel-generator/src/buffer.js b/packages/babel-generator/src/buffer.js index 65b35ce16d..10a0a9a0c1 100644 --- a/packages/babel-generator/src/buffer.js +++ b/packages/babel-generator/src/buffer.js @@ -1,267 +1,113 @@ -import type Position from "./position"; -import repeat from "lodash/repeat"; +import Position from "./position"; +import type SourceMap from "./source-map"; import trimEnd from "lodash/trimEnd"; +const SPACES_RE = /^[ \t]+$/; + /** - * Buffer for collecting generated output. + * The Buffer class exists to manage the queue of tokens being pushed onto the output string + * in such a way that the final string buffer is treated as write-only until the final .get() + * call. This allows V8 to optimize the output efficiently by not requiring it to store the + * string in contiguous memory. */ export default class Buffer { - constructor(position: Position, format: Object) { - this.printedCommentStarts = {}; - this.parenPushNewlineState = null; - this.position = position; - this._indent = format.indent.base; - this.format = format; - this.buf = ""; - - // Maintaining a reference to the last char in the buffer is an optimization - // to make sure that v8 doesn't "flatten" the string more often than needed - // see https://github.com/babel/babel/pull/3283 for details. - this.last = ""; - - this.map = null; - this._sourcePosition = { - line: null, - column: null, - filename: null, - }; - this._endsWithWord = false; + constructor(map: ?SourceMap) { + this._map = map; } - printedCommentStarts: Object; - parenPushNewlineState: ?Object; - position: Position; - _indent: number; - format: Object; - buf: string; - last: string; + _map: SourceMap = null; + _buf: string = ""; + _last: string = ""; + _queue: Array = []; + + _position: Position = new Position; + _sourcePosition: Object = { + line: null, + column: null, + filename: null, + }; /** - * Description + * Get the final string output from the buffer, along with the sourcemap if one exists. */ - catchUp(node: Object) { - // 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"); - } - } - } + get(): Object { + this._flush(); - /** - * Get the current trimmed buffer. - */ - - get(): string { - return trimEnd(this.buf); - } - - /** - * Get the current indent. - */ - - getIndent(): string { - if (this.format.compact || this.format.concise) { - return ""; - } else { - return repeat(this.format.indent.style, this._indent); - } - } - - /** - * Get the current indent size. - */ - - indentSize(): number { - return this.getIndent().length; - } - - /** - * Increment indent size. - */ - - indent() { - this._indent++; - } - - /** - * Decrement indent size. - */ - - dedent() { - this._indent--; - } - - /** - * Add a semicolon to the buffer. - */ - - semicolon() { - this.token(";"); - } - - /** - * Add a right brace to the buffer. - */ - - rightBrace() { - this.newline(true); - if (this.format.minified && !this._lastPrintedIsEmptyStatement) { - this._removeLast(";"); - } - this.token("}"); - } - - /** - * Add a keyword to the buffer. - */ - - keyword(name: string) { - this.word(name); - this.space(); - } - - /** - * Add a space to the buffer unless it is compact. - */ - - space() { - if (this.format.compact) return; - - if (this.buf && !this.endsWith(" ") && !this.endsWith("\n")) { - this.push(" "); - } - } - - /** - * Writes a token that can't be safely parsed without taking whitespace into account. - */ - - word(str: string) { - if (this._endsWithWord) this.push(" "); - - this.push(str); - this._endsWithWord = true; - } - - /** - * Writes a simple token. - */ - - token(str: string) { - // space is mandatory to avoid outputting