import Buffer from "./buffer"; import * as n from "./node"; import * as t from "@babel/types"; import * as generatorFunctions from "./generators"; import type SourceMap from "./source-map"; 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; recordAndTupleSyntaxType: "bar" | "hash"; jsescOption; jsonCompatibleStrings?; }; class Printer { constructor(format: Format, map: SourceMap) { this.format = format; this._buf = new Buffer(map); } declare format: Format; inForStatementInitCounter: number = 0; declare _buf: Buffer; _printStack: Array = []; _indent: number = 0; _insideAux: boolean = false; _printedCommentStarts: any = {}; _parenPushNewlineState: any = 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