Merge pull request #3584 from loganfsmyth/generator-refactor-cleanup2
babel-generator: More refactoring and optimizations
This commit is contained in:
commit
a1c2d4dd5d
@ -58,6 +58,9 @@ export default class Buffer {
|
||||
*/
|
||||
|
||||
queue(str: string): void {
|
||||
// Drop trailing spaces when a newline is inserted.
|
||||
if (str === "\n") while (this._queue.length > 0 && SPACES_RE.test(this._queue[0][0])) this._queue.shift();
|
||||
|
||||
const { line, column, filename } = this._sourcePosition;
|
||||
this._queue.unshift([str, line, column, filename]);
|
||||
}
|
||||
@ -86,10 +89,6 @@ export default class Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
removeTrailingSpaces(): void {
|
||||
while (this._queue.length > 0 && SPACES_RE.test(this._queue[0][0])) this._queue.shift();
|
||||
}
|
||||
|
||||
removeTrailingNewline(): void {
|
||||
if (this._queue.length > 0 && this._queue[0][0] === "\n") this._queue.shift();
|
||||
}
|
||||
@ -98,10 +97,23 @@ export default class Buffer {
|
||||
if (this._queue.length > 0 && this._queue[0][0] === ";") this._queue.shift();
|
||||
}
|
||||
|
||||
endsWith(str: string): boolean {
|
||||
endsWith(suffix: string): boolean {
|
||||
// Fast path to avoid iterating over this._queue.
|
||||
if (suffix.length === 1) {
|
||||
let last;
|
||||
if (this._queue.length > 0) {
|
||||
const str = this._queue[0][0];
|
||||
last = str[str.length - 1];
|
||||
} else {
|
||||
last = this._last;
|
||||
}
|
||||
|
||||
return last === suffix;
|
||||
}
|
||||
|
||||
const end = this._last + this._queue.reduce((acc, item) => item[0] + acc, "");
|
||||
if (str.length <= end.length) {
|
||||
return end.slice(-str.length) === str;
|
||||
if (suffix.length <= end.length) {
|
||||
return end.slice(-suffix.length) === suffix;
|
||||
}
|
||||
|
||||
// We assume that everything being matched is at most a single token plus some whitespace,
|
||||
@ -109,15 +121,6 @@ export default class Buffer {
|
||||
return false;
|
||||
}
|
||||
|
||||
getLast(): string {
|
||||
if (this._queue.length > 0) {
|
||||
const last = this._queue[0][0];
|
||||
return last[last.length - 1];
|
||||
}
|
||||
|
||||
return this._last;
|
||||
}
|
||||
|
||||
hasContent(): boolean {
|
||||
return this._queue.length > 0 || !!this._last;
|
||||
}
|
||||
|
||||
@ -21,9 +21,12 @@ export function BlockStatement(node: Object) {
|
||||
if (node.directives && node.directives.length) this.newline();
|
||||
|
||||
this.printSequence(node.body, node, { indent: true });
|
||||
if (!this.format.retainLines && !this.format.concise) this.removeTrailingNewline();
|
||||
this.removeTrailingNewline();
|
||||
|
||||
this.source("end", node.loc);
|
||||
|
||||
if (!this.endsWith("\n")) this.newline();
|
||||
|
||||
this.rightBrace();
|
||||
} else {
|
||||
this.source("end", node.loc);
|
||||
|
||||
@ -42,6 +42,8 @@ export function ClassBody(node: Object) {
|
||||
this.printSequence(node.body, node);
|
||||
this.dedent();
|
||||
|
||||
if (!this.endsWith("\n")) this.newline();
|
||||
|
||||
this.rightBrace();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
/* eslint max-len: 0 */
|
||||
|
||||
import isInteger from "lodash/isInteger";
|
||||
import isNumber from "lodash/isNumber";
|
||||
import * as t from "babel-types";
|
||||
import * as n from "../node";
|
||||
|
||||
const SCIENTIFIC_NOTATION = /e/i;
|
||||
const ZERO_DECIMAL_INTEGER = /\.0+$/;
|
||||
const NON_DECIMAL_LITERAL = /^0[box]/;
|
||||
|
||||
export function UnaryExpression(node: Object) {
|
||||
if (node.operator === "void" || node.operator === "delete" || node.operator === "typeof") {
|
||||
@ -89,15 +85,16 @@ export function Decorator(node: Object) {
|
||||
function commaSeparatorNewline() {
|
||||
this.token(",");
|
||||
this.newline();
|
||||
|
||||
if (!this.endsWith("\n")) this.space();
|
||||
}
|
||||
|
||||
export function CallExpression(node: Object) {
|
||||
this.print(node.callee, node);
|
||||
if (node.loc) this.printAuxAfterComment();
|
||||
|
||||
this.token("(");
|
||||
|
||||
let isPrettyCall = node._prettyCall && !this.format.retainLines && !this.format.compact;
|
||||
let isPrettyCall = node._prettyCall;
|
||||
|
||||
let separator;
|
||||
if (isPrettyCall) {
|
||||
@ -208,17 +205,6 @@ export function MemberExpression(node: Object) {
|
||||
this.print(node.property, node);
|
||||
this.token("]");
|
||||
} else {
|
||||
if (t.isNumericLiteral(node.object)) {
|
||||
let val = this.getPossibleRaw(node.object) || node.object.value;
|
||||
if (isInteger(+val) &&
|
||||
!NON_DECIMAL_LITERAL.test(val) &&
|
||||
!SCIENTIFIC_NOTATION.test(val) &&
|
||||
!ZERO_DECIMAL_INTEGER.test(val) &&
|
||||
!this.endsWith(".")) {
|
||||
this.token(".");
|
||||
}
|
||||
}
|
||||
|
||||
this.token(".");
|
||||
this.print(node.property, node);
|
||||
}
|
||||
|
||||
@ -251,6 +251,7 @@ export function ObjectTypeAnnotation(node: Object) {
|
||||
|
||||
this.printJoin(props, node, {
|
||||
indent: true,
|
||||
statement: true,
|
||||
iterator: () => {
|
||||
if (props.length !== 1) {
|
||||
this.semicolon();
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import * as t from "babel-types";
|
||||
|
||||
export function WithStatement(node: Object) {
|
||||
this.keyword("with");
|
||||
this.word("with");
|
||||
this.space();
|
||||
this.token("(");
|
||||
this.print(node.object, node);
|
||||
this.token(")");
|
||||
@ -9,7 +10,8 @@ export function WithStatement(node: Object) {
|
||||
}
|
||||
|
||||
export function IfStatement(node: Object) {
|
||||
this.keyword("if");
|
||||
this.word("if");
|
||||
this.space();
|
||||
this.token("(");
|
||||
this.print(node.test, node);
|
||||
this.token(")");
|
||||
@ -45,7 +47,8 @@ function getLastStatement(statement) {
|
||||
}
|
||||
|
||||
export function ForStatement(node: Object) {
|
||||
this.keyword("for");
|
||||
this.word("for");
|
||||
this.space();
|
||||
this.token("(");
|
||||
|
||||
this.inForStatementInitCounter++;
|
||||
@ -69,7 +72,8 @@ export function ForStatement(node: Object) {
|
||||
}
|
||||
|
||||
export function WhileStatement(node: Object) {
|
||||
this.keyword("while");
|
||||
this.word("while");
|
||||
this.space();
|
||||
this.token("(");
|
||||
this.print(node.test, node);
|
||||
this.token(")");
|
||||
@ -78,7 +82,8 @@ export function WhileStatement(node: Object) {
|
||||
|
||||
let buildForXStatement = function (op) {
|
||||
return function (node: Object) {
|
||||
this.keyword("for");
|
||||
this.word("for");
|
||||
this.space();
|
||||
this.token("(");
|
||||
this.print(node.left, node);
|
||||
this.space();
|
||||
@ -98,7 +103,8 @@ export function DoWhileStatement(node: Object) {
|
||||
this.space();
|
||||
this.print(node.body, node);
|
||||
this.space();
|
||||
this.keyword("while");
|
||||
this.word("while");
|
||||
this.space();
|
||||
this.token("(");
|
||||
this.print(node.test, node);
|
||||
this.token(")");
|
||||
@ -135,7 +141,8 @@ export function LabeledStatement(node: Object) {
|
||||
}
|
||||
|
||||
export function TryStatement(node: Object) {
|
||||
this.keyword("try");
|
||||
this.word("try");
|
||||
this.space();
|
||||
this.print(node.block, node);
|
||||
this.space();
|
||||
|
||||
@ -157,7 +164,8 @@ export function TryStatement(node: Object) {
|
||||
}
|
||||
|
||||
export function CatchClause(node: Object) {
|
||||
this.keyword("catch");
|
||||
this.word("catch");
|
||||
this.space();
|
||||
this.token("(");
|
||||
this.print(node.param, node);
|
||||
this.token(")");
|
||||
@ -166,7 +174,8 @@ export function CatchClause(node: Object) {
|
||||
}
|
||||
|
||||
export function SwitchStatement(node: Object) {
|
||||
this.keyword("switch");
|
||||
this.word("switch");
|
||||
this.space();
|
||||
this.token("(");
|
||||
this.print(node.discriminant, node);
|
||||
this.token(")");
|
||||
@ -209,14 +218,14 @@ function variableDeclarationIdent() {
|
||||
// "let " or "var " indentation.
|
||||
this.token(",");
|
||||
this.newline();
|
||||
for (let i = 0; i < 4; i++) this.space(true);
|
||||
if (this.endsWith("\n")) for (let i = 0; i < 4; i++) this.space(true);
|
||||
}
|
||||
|
||||
function constDeclarationIdent() {
|
||||
// "const " indentation.
|
||||
this.token(",");
|
||||
this.newline();
|
||||
for (let i = 0; i < 6; i++) this.space(true);
|
||||
if (this.endsWith("\n")) for (let i = 0; i < 6; i++) this.space(true);
|
||||
}
|
||||
|
||||
export function VariableDeclaration(node: Object, parent: Object) {
|
||||
@ -247,7 +256,7 @@ export function VariableDeclaration(node: Object, parent: Object) {
|
||||
//
|
||||
|
||||
let separator;
|
||||
if (!this.format.compact && !this.format.concise && hasInits && !this.format.retainLines) {
|
||||
if (hasInits) {
|
||||
separator = node.kind === "const" ? constDeclarationIdent : variableDeclarationIdent;
|
||||
}
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@ export function ObjectExpression(node: Object) {
|
||||
|
||||
if (props.length) {
|
||||
this.space();
|
||||
this.printList(props, node, { indent: true });
|
||||
this.printList(props, node, { indent: true, statement: true });
|
||||
this.space();
|
||||
}
|
||||
|
||||
@ -124,12 +124,8 @@ export function NullLiteral() {
|
||||
|
||||
export function NumericLiteral(node: Object) {
|
||||
let raw = this.getPossibleRaw(node);
|
||||
if (raw != null) {
|
||||
this.word(raw);
|
||||
return;
|
||||
}
|
||||
|
||||
this.word(node.value + "");
|
||||
this.number(raw == null ? node.value + "" : raw);
|
||||
}
|
||||
|
||||
export function StringLiteral(node: Object, parent: Object) {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import detectIndent from "detect-indent";
|
||||
import Whitespace from "./whitespace";
|
||||
import SourceMap from "./source-map";
|
||||
import * as messages from "babel-messages";
|
||||
import Printer from "./printer";
|
||||
import type {Format} from "./printer";
|
||||
|
||||
/**
|
||||
* Babel's code generator, turns an ast into code, maintaining sourcemaps,
|
||||
@ -14,119 +14,15 @@ class Generator extends Printer {
|
||||
opts = opts || {};
|
||||
|
||||
const tokens = ast.tokens || [];
|
||||
let format = Generator.normalizeOptions(code, opts, tokens);
|
||||
let format = normalizeOptions(code, opts, tokens);
|
||||
let map = opts.sourceMaps ? new SourceMap(opts, code) : null;
|
||||
super(format, map, tokens);
|
||||
|
||||
let map = opts.sourceMaps ? new SourceMap(opts, code) : null;
|
||||
|
||||
super(format, map);
|
||||
|
||||
this.ast = ast;
|
||||
|
||||
this._whitespace = tokens.length > 0 ? new Whitespace(tokens) : null;
|
||||
this.ast = ast;
|
||||
}
|
||||
|
||||
format: {
|
||||
shouldPrintComment: (comment: string) => boolean;
|
||||
retainLines: boolean;
|
||||
comments: boolean;
|
||||
auxiliaryCommentBefore: string;
|
||||
auxiliaryCommentAfter: string;
|
||||
compact: boolean | "auto";
|
||||
minified: boolean;
|
||||
quotes: "single" | "double";
|
||||
concise: boolean;
|
||||
indent: {
|
||||
adjustMultilineComment: boolean;
|
||||
style: string;
|
||||
base: number;
|
||||
}
|
||||
};
|
||||
|
||||
_whitespace: Whitespace;
|
||||
ast: Object;
|
||||
|
||||
/**
|
||||
* Normalize generator options, setting defaults.
|
||||
*
|
||||
* - Detects code indentation.
|
||||
* - If `opts.compact = "auto"` and the code is over 100KB, `compact` will be set to `true`.
|
||||
*/
|
||||
|
||||
static normalizeOptions(code, opts, tokens) {
|
||||
let style = " ";
|
||||
if (code && typeof code === "string") {
|
||||
let indent = detectIndent(code).indent;
|
||||
if (indent && indent !== " ") style = indent;
|
||||
}
|
||||
|
||||
let format = {
|
||||
auxiliaryCommentBefore: opts.auxiliaryCommentBefore,
|
||||
auxiliaryCommentAfter: opts.auxiliaryCommentAfter,
|
||||
shouldPrintComment: opts.shouldPrintComment,
|
||||
retainLines: opts.retainLines,
|
||||
comments: opts.comments == null || opts.comments,
|
||||
compact: opts.compact,
|
||||
minified: opts.minified,
|
||||
concise: opts.concise,
|
||||
quotes: opts.quotes || Generator.findCommonStringDelimiter(code, tokens),
|
||||
indent: {
|
||||
adjustMultilineComment: true,
|
||||
style: style,
|
||||
base: 0
|
||||
}
|
||||
};
|
||||
|
||||
if (format.minified) {
|
||||
format.compact = true;
|
||||
}
|
||||
|
||||
if (format.compact === "auto") {
|
||||
format.compact = code.length > 100000; // 100KB
|
||||
|
||||
if (format.compact) {
|
||||
console.error("[BABEL] " + messages.get("codeGeneratorDeopt", opts.filename, "100KB"));
|
||||
}
|
||||
}
|
||||
|
||||
if (format.compact) {
|
||||
format.indent.adjustMultilineComment = false;
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if input code uses more single or double quotes.
|
||||
*/
|
||||
static findCommonStringDelimiter(code, tokens) {
|
||||
let occurences = {
|
||||
single: 0,
|
||||
double: 0
|
||||
};
|
||||
|
||||
let checked = 0;
|
||||
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
let token = tokens[i];
|
||||
if (token.type.label !== "string") continue;
|
||||
|
||||
let raw = code.slice(token.start, token.end);
|
||||
if (raw[0] === "'") {
|
||||
occurences.single++;
|
||||
} else {
|
||||
occurences.double++;
|
||||
}
|
||||
|
||||
checked++;
|
||||
if (checked >= 3) break;
|
||||
}
|
||||
if (occurences.single > occurences.double) {
|
||||
return "single";
|
||||
} else {
|
||||
return "double";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate code and sourcemap from ast.
|
||||
*
|
||||
@ -134,13 +30,96 @@ class Generator extends Printer {
|
||||
*/
|
||||
|
||||
generate() {
|
||||
this.print(this.ast);
|
||||
this.printAuxAfterComment();
|
||||
|
||||
return this._buf.get();
|
||||
return super.generate(this.ast);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize generator options, setting defaults.
|
||||
*
|
||||
* - Detects code indentation.
|
||||
* - If `opts.compact = "auto"` and the code is over 100KB, `compact` will be set to `true`.
|
||||
*/
|
||||
|
||||
function normalizeOptions(code, opts, tokens): Format {
|
||||
let style = " ";
|
||||
if (code && typeof code === "string") {
|
||||
let indent = detectIndent(code).indent;
|
||||
if (indent && indent !== " ") style = indent;
|
||||
}
|
||||
|
||||
let format = {
|
||||
auxiliaryCommentBefore: opts.auxiliaryCommentBefore,
|
||||
auxiliaryCommentAfter: opts.auxiliaryCommentAfter,
|
||||
shouldPrintComment: opts.shouldPrintComment,
|
||||
retainLines: opts.retainLines,
|
||||
comments: opts.comments == null || opts.comments,
|
||||
compact: opts.compact,
|
||||
minified: opts.minified,
|
||||
concise: opts.concise,
|
||||
quotes: opts.quotes || findCommonStringDelimiter(code, tokens),
|
||||
indent: {
|
||||
adjustMultilineComment: true,
|
||||
style: style,
|
||||
base: 0
|
||||
}
|
||||
};
|
||||
|
||||
if (format.minified) {
|
||||
format.compact = true;
|
||||
|
||||
format.shouldPrintComment = format.shouldPrintComment || (() => format.comments);
|
||||
} else {
|
||||
format.shouldPrintComment = format.shouldPrintComment || ((value) => format.comments ||
|
||||
(value.indexOf("@license") >= 0 || value.indexOf("@preserve") >= 0));
|
||||
}
|
||||
|
||||
if (format.compact === "auto") {
|
||||
format.compact = code.length > 100000; // 100KB
|
||||
|
||||
if (format.compact) {
|
||||
console.error("[BABEL] " + messages.get("codeGeneratorDeopt", opts.filename, "100KB"));
|
||||
}
|
||||
}
|
||||
|
||||
if (format.compact) {
|
||||
format.indent.adjustMultilineComment = false;
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if input code uses more single or double quotes.
|
||||
*/
|
||||
function findCommonStringDelimiter(code, tokens) {
|
||||
let occurences = {
|
||||
single: 0,
|
||||
double: 0
|
||||
};
|
||||
|
||||
let checked = 0;
|
||||
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
let token = tokens[i];
|
||||
if (token.type.label !== "string") continue;
|
||||
|
||||
let raw = code.slice(token.start, token.end);
|
||||
if (raw[0] === "'") {
|
||||
occurences.single++;
|
||||
} else {
|
||||
occurences.double++;
|
||||
}
|
||||
|
||||
checked++;
|
||||
if (checked >= 3) break;
|
||||
}
|
||||
if (occurences.single > occurences.double) {
|
||||
return "single";
|
||||
} else {
|
||||
return "double";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We originally exported the Generator class above, but to make it extra clear that it is a private API,
|
||||
|
||||
@ -53,11 +53,6 @@ function isOrHasCallExpression(node) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function isUserWhitespacable(node) {
|
||||
return t.isUserWhitespacable(node);
|
||||
}
|
||||
|
||||
export function needsWhitespace(node, parent, type) {
|
||||
if (!node) return 0;
|
||||
|
||||
|
||||
@ -1,25 +1,63 @@
|
||||
/* eslint max-len: 0 */
|
||||
|
||||
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;
|
||||
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) {
|
||||
constructor(format, map, tokens) {
|
||||
this.format = format || {};
|
||||
this._buf = new Buffer(map);
|
||||
this.insideAux = false;
|
||||
this._printAuxAfterOnNextUserNode = false;
|
||||
this._printStack = [];
|
||||
this._printedCommentStarts = {};
|
||||
this._parenPushNewlineState = null;
|
||||
this._indent = 0;
|
||||
this.inForStatementInitCounter = 0;
|
||||
this._whitespace = tokens.length > 0 ? new Whitespace(tokens) : null;
|
||||
}
|
||||
|
||||
_printedCommentStarts: Object;
|
||||
_parenPushNewlineState: ?Object;
|
||||
format: Format;
|
||||
inForStatementInitCounter: number = 0;
|
||||
|
||||
_buf: Buffer;
|
||||
_whitespace: Whitespace;
|
||||
_printStack: Array<Node> = [];
|
||||
_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.
|
||||
@ -46,6 +84,7 @@ export default class Printer {
|
||||
*/
|
||||
|
||||
semicolon(force: boolean = false): void {
|
||||
this._maybeAddAuxComment();
|
||||
this._append(";", !force /* queue */);
|
||||
}
|
||||
|
||||
@ -54,23 +93,12 @@ export default class Printer {
|
||||
*/
|
||||
|
||||
rightBrace(): void {
|
||||
if (!this.endsWith("\n")) this.newline();
|
||||
|
||||
if (this.format.minified) {
|
||||
this._buf.removeLastSemicolon();
|
||||
}
|
||||
this.token("}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a keyword to the buffer.
|
||||
*/
|
||||
|
||||
keyword(name: string): void {
|
||||
this.word(name);
|
||||
this.space();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a space to the buffer unless it is compact.
|
||||
*/
|
||||
@ -90,27 +118,48 @@ export default class Printer {
|
||||
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 {
|
||||
const last = this._buf.getLast();
|
||||
// space is mandatory to avoid outputting <!--
|
||||
// http://javascript.spec.whatwg.org/#comment-syntax
|
||||
if ((str === "--" && last === "!") ||
|
||||
if ((str === "--" && this.endsWith("!")) ||
|
||||
|
||||
// Need spaces for operators of the same kind to avoid: `a+++b`
|
||||
(str[0] === "+" && last === "+") ||
|
||||
(str[0] === "-" && last === "-")) {
|
||||
(str[0] === "+" && this.endsWith("+")) ||
|
||||
(str[0] === "-" && this.endsWith("-")) ||
|
||||
|
||||
// Needs spaces to avoid changing '34' to '34.', which would still be a valid number.
|
||||
(str[0] === "." && this._endsWithInteger)) {
|
||||
this._space();
|
||||
}
|
||||
|
||||
this._maybeAddAuxComment();
|
||||
this._append(str);
|
||||
}
|
||||
|
||||
@ -135,7 +184,6 @@ export default class Printer {
|
||||
if (this.endsWith("{\n") || this.endsWith(":\n")) i--;
|
||||
if (i <= 0) return;
|
||||
|
||||
this._buf.removeTrailingSpaces();
|
||||
for (let j = 0; j < i; j++) {
|
||||
this._newline();
|
||||
}
|
||||
@ -177,6 +225,7 @@ export default class Printer {
|
||||
else this._buf.append(str);
|
||||
|
||||
this._endsWithWord = false;
|
||||
this._endsWithInteger = false;
|
||||
}
|
||||
|
||||
_maybeIndent(str: string): void {
|
||||
@ -211,7 +260,9 @@ export default class Printer {
|
||||
// catch up to this nodes newline if we're behind
|
||||
const pos = loc ? loc[prop] : null;
|
||||
if (pos && pos.line !== null) {
|
||||
while (this._buf.getCurrentLine() < pos.line) {
|
||||
const count = pos.line - this._buf.getCurrentLine();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
this._newline();
|
||||
}
|
||||
}
|
||||
@ -259,7 +310,7 @@ export default class Printer {
|
||||
}
|
||||
}
|
||||
|
||||
print(node, parent, opts = {}) {
|
||||
print(node, parent) {
|
||||
if (!node) return;
|
||||
|
||||
let oldConcise = this.format.concise;
|
||||
@ -274,38 +325,34 @@ export default class Printer {
|
||||
|
||||
this._printStack.push(node);
|
||||
|
||||
let oldInAux = this.insideAux;
|
||||
this.insideAux = !node.loc;
|
||||
if (!this.insideAux) this.printAuxAfterComment();
|
||||
else if (!oldInAux) this._printAuxBeforeComment();
|
||||
let oldInAux = this._insideAux;
|
||||
this._insideAux = !node.loc;
|
||||
this._maybeAddAuxComment(this._insideAux && !oldInAux);
|
||||
|
||||
let needsParens = n.needsParens(node, parent, this._printStack);
|
||||
if (needsParens) this.token("(");
|
||||
|
||||
this._printLeadingComments(node, parent);
|
||||
|
||||
this._printNewline(true, node, parent, opts);
|
||||
|
||||
let loc = (t.isProgram(node) || t.isFile(node)) ? null : node.loc;
|
||||
this.withSource("start", loc, () => {
|
||||
this[node.type](node, parent);
|
||||
});
|
||||
|
||||
// Check again if any of our children may have left an aux comment on the stack
|
||||
if (!this.insideAux) this.printAuxAfterComment();
|
||||
|
||||
this._printTrailingComments(node, parent);
|
||||
|
||||
if (needsParens) this.token(")");
|
||||
|
||||
// end
|
||||
this._printStack.pop();
|
||||
if (opts.after) opts.after();
|
||||
|
||||
this.format.concise = oldConcise;
|
||||
this.insideAux = oldInAux;
|
||||
this._insideAux = oldInAux;
|
||||
}
|
||||
|
||||
this._printNewline(false, node, parent, opts);
|
||||
_maybeAddAuxComment(enteredPositionlessNode) {
|
||||
if (enteredPositionlessNode) this._printAuxBeforeComment();
|
||||
if (!this._insideAux) this._printAuxAfterComment();
|
||||
}
|
||||
|
||||
_printAuxBeforeComment() {
|
||||
@ -321,7 +368,7 @@ export default class Printer {
|
||||
}
|
||||
}
|
||||
|
||||
printAuxAfterComment() {
|
||||
_printAuxAfterComment() {
|
||||
if (!this._printAuxAfterOnNextUserNode) return;
|
||||
this._printAuxAfterOnNextUserNode = false;
|
||||
|
||||
@ -346,32 +393,29 @@ export default class Printer {
|
||||
printJoin(nodes: ?Array, parent: Object, opts = {}) {
|
||||
if (!nodes || !nodes.length) return;
|
||||
|
||||
let len = nodes.length;
|
||||
let node, i;
|
||||
|
||||
if (opts.indent) this.indent();
|
||||
|
||||
let printOpts = {
|
||||
statement: opts.statement,
|
||||
const newlineOpts = {
|
||||
addNewlines: opts.addNewlines,
|
||||
after: () => {
|
||||
if (opts.iterator) {
|
||||
opts.iterator(node, i);
|
||||
}
|
||||
|
||||
if (opts.separator && parent.loc) {
|
||||
this.printAuxAfterComment();
|
||||
}
|
||||
|
||||
if (opts.separator && i < len - 1) {
|
||||
opts.separator.call(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (i = 0; i < nodes.length; i++) {
|
||||
node = nodes[i];
|
||||
this.print(node, parent, printOpts);
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i];
|
||||
if (!node) continue;
|
||||
|
||||
if (opts.statement) this._printNewline(true, node, parent, newlineOpts);
|
||||
|
||||
this.print(node, parent);
|
||||
|
||||
if (opts.iterator) {
|
||||
opts.iterator(node, i);
|
||||
}
|
||||
|
||||
if (opts.separator && i < nodes.length - 1) {
|
||||
opts.separator.call(this);
|
||||
}
|
||||
|
||||
if (opts.statement) this._printNewline(false, node, parent, newlineOpts);
|
||||
}
|
||||
|
||||
if (opts.indent) this.dedent();
|
||||
@ -426,10 +470,6 @@ export default class Printer {
|
||||
// Fast path since 'this.newline' does nothing when not tracking lines.
|
||||
if (this.format.retainLines || this.format.compact) return;
|
||||
|
||||
if (!opts.statement && !n.isUserWhitespacable(node, parent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fast path for concise since 'this.newline' just inserts a space when
|
||||
// concise formatting is in use.
|
||||
if (this.format.concise) {
|
||||
@ -442,9 +482,17 @@ export default class Printer {
|
||||
if (node.start != null && !node._ignoreUserWhitespace && this._whitespace) {
|
||||
// user node
|
||||
if (leading) {
|
||||
lines = this._whitespace.getNewlinesBefore(node);
|
||||
const comments = node.leadingComments;
|
||||
const comment = comments && find(comments, (comment) =>
|
||||
!!comment.loc && this.format.shouldPrintComment(comment.value));
|
||||
|
||||
lines = this._whitespace.getNewlinesBefore(comment || node);
|
||||
} else {
|
||||
lines = this._whitespace.getNewlinesAfter(node);
|
||||
const comments = node.trailingComments;
|
||||
const comment = comments && findLast(comments, (comment) =>
|
||||
!!comment.loc && this.format.shouldPrintComment(comment.value));
|
||||
|
||||
lines = this._whitespace.getNewlinesAfter(comment || node);
|
||||
}
|
||||
} else {
|
||||
// generated node
|
||||
@ -468,65 +516,48 @@ export default class Printer {
|
||||
return (node && (leading ? node.leadingComments : node.trailingComments)) || [];
|
||||
}
|
||||
|
||||
_shouldPrintComment(comment) {
|
||||
if (this.format.shouldPrintComment) {
|
||||
return this.format.shouldPrintComment(comment.value);
|
||||
} else {
|
||||
if (!this.format.minified &&
|
||||
(comment.value.indexOf("@license") >= 0 || comment.value.indexOf("@preserve") >= 0)) {
|
||||
return true;
|
||||
} else {
|
||||
return this.format.comments;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_printComment(comment) {
|
||||
if (!this._shouldPrintComment(comment)) return;
|
||||
if (!this.format.shouldPrintComment(comment.value)) return;
|
||||
|
||||
// Some plugins use this to mark comments as removed using the AST-root 'comments' property,
|
||||
// where they can't manually mutate the AST node comment lists.
|
||||
if (comment.ignore) return;
|
||||
comment.ignore = true;
|
||||
|
||||
if (this._printedComments.has(comment)) return;
|
||||
this._printedComments.add(comment);
|
||||
|
||||
if (comment.start != null) {
|
||||
if (this._printedCommentStarts[comment.start]) return;
|
||||
this._printedCommentStarts[comment.start] = true;
|
||||
}
|
||||
|
||||
// Exclude comments from source mappings since they will only clutter things.
|
||||
// whitespace before
|
||||
this.newline(this._whitespace ? this._whitespace.getNewlinesBefore(comment) : 0);
|
||||
|
||||
if (!this.endsWith("[") && !this.endsWith("{")) this.space();
|
||||
|
||||
let val = comment.type === "CommentLine" ? `//${comment.value}\n` : `/*${comment.value}*/`;
|
||||
|
||||
//
|
||||
if (comment.type === "CommentBlock" && this.format.indent.adjustMultilineComment) {
|
||||
let offset = comment.loc && comment.loc.start.column;
|
||||
if (offset) {
|
||||
let newlineRegex = new RegExp("\\n\\s{1," + offset + "}", "g");
|
||||
val = val.replace(newlineRegex, "\n");
|
||||
}
|
||||
|
||||
let indentSize = Math.max(this._getIndent().length, this._buf.getCurrentColumn());
|
||||
val = val.replace(/\n(?!$)/g, `\n${repeat(" ", indentSize)}`);
|
||||
}
|
||||
|
||||
this.withSource("start", comment.loc, () => {
|
||||
// whitespace before
|
||||
this.newline(this._whitespace ? this._whitespace.getNewlinesBefore(comment) : 0);
|
||||
|
||||
if (!this.endsWith("[") && !this.endsWith("{")) this.space();
|
||||
|
||||
let val = comment.type === "CommentLine" ? `//${comment.value}` : `/*${comment.value}*/`;
|
||||
|
||||
//
|
||||
if (comment.type === "CommentBlock" && this.format.indent.adjustMultilineComment) {
|
||||
let offset = comment.loc && comment.loc.start.column;
|
||||
if (offset) {
|
||||
let newlineRegex = new RegExp("\\n\\s{1," + offset + "}", "g");
|
||||
val = val.replace(newlineRegex, "\n");
|
||||
}
|
||||
|
||||
let indentSize = Math.max(this._getIndent().length, this._buf.getCurrentColumn());
|
||||
val = val.replace(/\n(?!$)/g, `\n${repeat(" ", indentSize)}`);
|
||||
}
|
||||
|
||||
// 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) &&
|
||||
comment.type === "CommentLine") {
|
||||
val += "\n";
|
||||
}
|
||||
|
||||
//
|
||||
this.token(val);
|
||||
|
||||
// whitespace after
|
||||
this.newline((this._whitespace ? this._whitespace.getNewlinesAfter(comment) : 0) ||
|
||||
(comment.type === "CommentLine" ? 1 : 0));
|
||||
this._append(val);
|
||||
});
|
||||
|
||||
// whitespace after
|
||||
this.newline((this._whitespace ? this._whitespace.getNewlinesAfter(comment) : 0) +
|
||||
// Subtract one to account for the line force-added above.
|
||||
(comment.type === "CommentLine" ? -1 : 0));
|
||||
}
|
||||
|
||||
_printComments(comments?: Array<Object>) {
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
function test() {}
|
||||
|
||||
// Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com>
|
||||
|
||||
@ -6,6 +6,6 @@ function foo(l) {
|
||||
|
||||
function foo() {
|
||||
return (
|
||||
1 && 2 ||
|
||||
1 && 2 ||
|
||||
3);
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ function foo() {
|
||||
}
|
||||
|
||||
function foo() {
|
||||
return(
|
||||
return (
|
||||
// foobar
|
||||
"bar"
|
||||
);
|
||||
|
||||
@ -4,20 +4,18 @@ Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.test = undefined;
|
||||
/*after*/
|
||||
/*before*/require("foo"); /*after*/
|
||||
|
||||
/*before*/require("foo-bar"); /*after*/
|
||||
/*after*/ /*before*/require("foo") /*after*/;
|
||||
|
||||
/*before*/require("./directory/foo-bar"); /*after*/
|
||||
/*before*/require("foo-bar") /*after*/;
|
||||
|
||||
/*before*/require("./directory/foo-bar") /*after*/;
|
||||
|
||||
var /*before*/_foo = require("foo2") /*after*/;
|
||||
|
||||
/*before*/
|
||||
var _foo2 = babelHelpers.interopRequireDefault(_foo);
|
||||
/*before*/var _foo2 = babelHelpers.interopRequireDefault(_foo);
|
||||
|
||||
/*after*/
|
||||
var /*before*/_foo3 = require("foo3") /*after*/;
|
||||
/*after*/var /*before*/_foo3 = require("foo3") /*after*/;
|
||||
|
||||
/*before*/var /*after*/foo2 = babelHelpers.interopRequireWildcard(_foo3);
|
||||
|
||||
@ -31,5 +29,5 @@ var test = /*before*/exports. /*after*/test = 5;
|
||||
/*before*/(0, _foo4.bar) /*after*/( /*before*/_foo2.default /*after*/, /*before*/_foo5.foo /*after*/);
|
||||
|
||||
/* my comment */
|
||||
/*before*/_foo5.foo; /*after*/
|
||||
/*before*/_foo2.default; /*after*/
|
||||
/*before*/_foo5.foo /*after*/;
|
||||
/*before*/_foo2.default /*after*/;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user