import find from "lodash/find"; import findLast from "lodash/findLast"; import isInteger from "lodash/isInteger"; import repeat from "lodash/repeat"; import Buffer from "./buffer"; import * as n from "./node"; import Whitespace from "./whitespace"; import * as t from "babel-types"; const SCIENTIFIC_NOTATION = /e/i; const ZERO_DECIMAL_INTEGER = /\.0+$/; const NON_DECIMAL_LITERAL = /^0[box]/; export type Format = { shouldPrintComment: (comment: string) => boolean; retainLines: boolean; retainFunctionParens: boolean; comments: boolean; auxiliaryCommentBefore: string; auxiliaryCommentAfter: string; compact: boolean | "auto"; minified: boolean; quotes: "single" | "double"; concise: boolean; indent: { adjustMultilineComment: boolean; style: string; base: number; } }; export default class Printer { constructor(format, map, tokens) { this.format = format || {}; this._buf = new Buffer(map); this._whitespace = tokens.length > 0 ? new Whitespace(tokens) : null; } format: Format; inForStatementInitCounter: number = 0; _buf: Buffer; _whitespace: Whitespace; _printStack: Array = []; _indent: number = 0; _insideAux: boolean = false; _printedCommentStarts: Object = {}; _parenPushNewlineState: ?Object = null; _printAuxAfterOnNextUserNode: boolean = false; _printedComments: WeakSet = new WeakSet(); _endsWithInteger = false; _endsWithWord = false; generate(ast) { this.print(ast); this._maybeAddAuxComment(); return this._buf.get(); } /** * Increment indent size. */ indent(): void { if (this.format.compact || this.format.concise) return; this._indent++; } /** * Decrement indent size. */ dedent(): void { if (this.format.compact || this.format.concise) return; this._indent--; } /** * Add a semicolon to the buffer. */ semicolon(force: boolean = false): void { this._maybeAddAuxComment(); this._append(";", !force /* queue */); } /** * Add a right brace to the buffer. */ rightBrace(): void { if (this.format.minified) { this._buf.removeLastSemicolon(); } this.token("}"); } /** * Add a space to the buffer unless it is compact. */ space(force: boolean = false): void { if (this.format.compact) return; if ((this._buf.hasContent() && !this.endsWith(" ") && !this.endsWith("\n")) || force) { this._space(); } } /** * Writes a token that can't be safely parsed without taking whitespace into account. */ word(str: string): void { if (this._endsWithWord) this._space(); this._maybeAddAuxComment(); this._append(str); this._endsWithWord = true; } /** * Writes a number token so that we can validate if it is an integer. */ number(str: string): void { this.word(str); // Integer tokens need special handling because they cannot have '.'s inserted // immediately after them. this._endsWithInteger = isInteger(+str) && !NON_DECIMAL_LITERAL.test(str) && !SCIENTIFIC_NOTATION.test(str) && !ZERO_DECIMAL_INTEGER.test(str) && str[str.length - 1] !== "."; } /** * Writes a simple token. */ token(str: string): void { // space is mandatory to avoid outputting