import Buffer from "./buffer"; import * as n from "./node"; import * as t from "@babel/types"; import * as generatorFunctions from "./generators"; const SCIENTIFIC_NOTATION = /e/i; const ZERO_DECIMAL_INTEGER = /\.0+$/; const NON_DECIMAL_LITERAL = /^0[box]/; const PURE_ANNOTATION_RE = /^\s*[@#]__PURE__\s*$/; export type Format = { shouldPrintComment: (comment: string) => boolean, retainLines: boolean, retainFunctionParens: boolean, comments: boolean, auxiliaryCommentBefore: string, auxiliaryCommentAfter: string, compact: boolean | "auto", minified: boolean, concise: boolean, indent: { adjustMultilineComment: boolean, style: string, base: number, }, decoratorsBeforeExport: boolean, }; export default class Printer { constructor(format, map) { this.format = format || {}; this._buf = new Buffer(map); } format: Format; inForStatementInitCounter: number = 0; _buf: Buffer; _printStack: Array = []; _indent: number = 0; _insideAux: boolean = false; _printedCommentStarts: Object = {}; _parenPushNewlineState: ?Object = null; _noLineTerminator: boolean = false; _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 { // prevent concatenating words and creating // comment out of division and regex if (this._endsWithWord || (this.endsWith("/") && str.indexOf("/") === 0)) { 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 = Number.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