diff --git a/benchmark/babel-parser/many-empty-statements/attachComment-false.bench.mjs b/benchmark/babel-parser/many-empty-statements/attachComment-false.bench.mjs index 6dd79a42e6ab..9fe7943b6b9d 100644 --- a/benchmark/babel-parser/many-empty-statements/attachComment-false.bench.mjs +++ b/benchmark/babel-parser/many-empty-statements/attachComment-false.bench.mjs @@ -7,13 +7,14 @@ const suite = new Benchmark.Suite(); function benchCases(name, implementation, options) { for (const length of [256, 512, 1024, 2048]) { + const input = ";".repeat(length); suite.add(`${name} ${length} empty statement`, () => { - implementation.parse(";".repeat(length), options); + implementation.parse(input, options); }); } } -benchCases("baseline", baseline); +benchCases("baseline", baseline, { attachComment: true }); benchCases("current + attachComment: false", current, { attachComment: false }); suite.on("cycle", report).run(); diff --git a/benchmark/babel-parser/many-leading-trailing-comments/attachComment-false.bench.mjs b/benchmark/babel-parser/many-leading-trailing-comments/attachComment-false.bench.mjs new file mode 100644 index 000000000000..3866f375362f --- /dev/null +++ b/benchmark/babel-parser/many-leading-trailing-comments/attachComment-false.bench.mjs @@ -0,0 +1,28 @@ +import Benchmark from "benchmark"; +import baseline from "@babel-baseline/parser"; +import current from "../../lib/index.js"; +import { report } from "../util.mjs"; + +const suite = new Benchmark.Suite(); + +function createInput(length) { + return "\n// c\na".repeat(length); +} + +function benchCases(name, implementation, options) { + for (const length of [256, 512, 1024, 2048]) { + const input = createInput(length); + const { parse } = implementation; + suite.add( + `${name} ${length} leading comments + ${length - 1} trailing comments`, + () => { + parse(input, options); + } + ); + } +} + +benchCases("baseline", baseline, { attachComment: true }); +benchCases("current + attachComment: false", current, { attachComment: false }); + +suite.on("cycle", report).run(); diff --git a/benchmark/babel-parser/many-leading-trailing-comments/bench.mjs b/benchmark/babel-parser/many-leading-trailing-comments/bench.mjs index fe6d23352e5b..b9765096c1b1 100644 --- a/benchmark/babel-parser/many-leading-trailing-comments/bench.mjs +++ b/benchmark/babel-parser/many-leading-trailing-comments/bench.mjs @@ -7,7 +7,7 @@ const suite = new Benchmark.Suite(); function createInput(length) { return "\n// c\na".repeat(length); } -current.parse(createInput(256), {}); + function benchCases(name, implementation, options) { for (const length of [128, 256, 512, 1024]) { const input = createInput(length); diff --git a/eslint/babel-eslint-parser/src/worker/configuration.cjs b/eslint/babel-eslint-parser/src/worker/configuration.cjs index c34bfb8a6546..fe5386fd7f61 100644 --- a/eslint/babel-eslint-parser/src/worker/configuration.cjs +++ b/eslint/babel-eslint-parser/src/worker/configuration.cjs @@ -31,6 +31,8 @@ function normalizeParserOptions(options) { allowSuperOutsideMethod: true, ...options.babelOptions.parserOpts, plugins: getParserPlugins(options.babelOptions), + // skip comment attaching for parsing performance + attachComment: false, ranges: true, tokens: true, }, diff --git a/packages/babel-parser/src/options.js b/packages/babel-parser/src/options.js index eb4dd6789605..ce0fff102458 100755 --- a/packages/babel-parser/src/options.js +++ b/packages/babel-parser/src/options.js @@ -22,6 +22,7 @@ export type Options = { tokens: boolean, createParenthesizedExpressions: boolean, errorRecovery: boolean, + attachComment: boolean, }; export const defaultOptions: Options = { @@ -66,6 +67,11 @@ export const defaultOptions: Options = { // When enabled, errors are attached to the AST instead of being directly thrown. // Some errors will still throw, because @babel/parser can't always recover. errorRecovery: false, + // When enabled, comments will be attached to adjacent AST nodes as one of + // `leadingComments`, `trailingComments` and `innerComments`. The comment attachment + // is vital to preserve comments after transform. If you don't print AST back, + // consider set this option to `false` for performance + attachComment: true, }; // Interpret and default an options object diff --git a/packages/babel-parser/src/parser/node.js b/packages/babel-parser/src/parser/node.js index df41ff3fec7d..1d8a08b90a0d 100644 --- a/packages/babel-parser/src/parser/node.js +++ b/packages/babel-parser/src/parser/node.js @@ -9,7 +9,6 @@ import type { Comment, Node as NodeType, NodeBase } from "../types"; class Node implements NodeBase { constructor(parser: Parser, pos: number, loc: Position) { - this.type = ""; this.start = pos; this.end = 0; this.loc = new SourceLocation(loc); @@ -17,15 +16,15 @@ class Node implements NodeBase { if (parser?.filename) this.loc.filename = parser.filename; } - type: string; - start: number; - end: number; - loc: SourceLocation; - range: [number, number]; - leadingComments: Array; - trailingComments: Array; - innerComments: Array; - extra: { [key: string]: any }; + type: string = ""; + declare start: number; + declare end: number; + declare loc: SourceLocation; + declare range: [number, number]; + declare leadingComments: Array; + declare trailingComments: Array; + declare innerComments: Array; + declare extra: { [key: string]: any }; } const NodePrototype = Node.prototype; @@ -135,7 +134,7 @@ export class NodeUtils extends UtilParser { node.end = pos; node.loc.end = loc; if (this.options.ranges) node.range[1] = pos; - this.processComment(node); + if (this.options.attachComment) this.processComment(node); return node; } diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index 0894adf14337..d3cf76f376f1 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -403,7 +403,7 @@ export default class Tokenizer extends ParserErrors { const comment = this.skipBlockComment(); if (comment !== undefined) { this.addComment(comment); - comments.push(comment); + if (this.options.attachComment) comments.push(comment); } break; } @@ -412,7 +412,7 @@ export default class Tokenizer extends ParserErrors { const comment = this.skipLineComment(2); if (comment !== undefined) { this.addComment(comment); - comments.push(comment); + if (this.options.attachComment) comments.push(comment); } break; } @@ -436,7 +436,7 @@ export default class Tokenizer extends ParserErrors { const comment = this.skipLineComment(3); if (comment !== undefined) { this.addComment(comment); - comments.push(comment); + if (this.options.attachComment) comments.push(comment); } } else { break loop; @@ -452,7 +452,7 @@ export default class Tokenizer extends ParserErrors { const comment = this.skipLineComment(4); if (comment !== undefined) { this.addComment(comment); - comments.push(comment); + if (this.options.attachComment) comments.push(comment); } } else { break loop; diff --git a/packages/babel-parser/test/attachComment-false.js b/packages/babel-parser/test/attachComment-false.js new file mode 100644 index 000000000000..eaf467ee3a6e --- /dev/null +++ b/packages/babel-parser/test/attachComment-false.js @@ -0,0 +1,12 @@ +import path from "path"; +import { runFixtureTestsWithoutExactASTMatch } from "./helpers/runFixtureTests"; +import { parseExpression } from "../lib"; +import { fileURLToPath } from "url"; + +runFixtureTestsWithoutExactASTMatch( + path.join(path.dirname(fileURLToPath(import.meta.url)), "expressions"), + (input, options = {}) => { + options.attachComment = false; + return parseExpression(input, options); + }, +); diff --git a/packages/babel-parser/test/fixtures/comments/attachComment-false/array-expression-trailing-comma/input.js b/packages/babel-parser/test/fixtures/comments/attachComment-false/array-expression-trailing-comma/input.js new file mode 100644 index 000000000000..bc0802b8addd --- /dev/null +++ b/packages/babel-parser/test/fixtures/comments/attachComment-false/array-expression-trailing-comma/input.js @@ -0,0 +1,18 @@ +const nonTrailing = [ + "One", // One + // Two + "Two" // Three + // Four +] + +const trailingAfterComma = [ + "One", // One + // Two + "Two", // Three + // Four +] + +const trailingAfterArray = [ + "One", // One + // Two +] // Three diff --git a/packages/babel-parser/test/fixtures/comments/attachComment-false/array-expression-trailing-comma/output.json b/packages/babel-parser/test/fixtures/comments/attachComment-false/array-expression-trailing-comma/output.json new file mode 100644 index 000000000000..d8c2e190a265 --- /dev/null +++ b/packages/babel-parser/test/fixtures/comments/attachComment-false/array-expression-trailing-comma/output.json @@ -0,0 +1,187 @@ +{ + "type": "File", + "start":0,"end":229,"loc":{"start":{"line":1,"column":0},"end":{"line":18,"column":10}}, + "program": { + "type": "Program", + "start":0,"end":229,"loc":{"start":{"line":1,"column":0},"end":{"line":18,"column":10}}, + "sourceType": "script", + "interpreter": null, + "body": [ + { + "type": "VariableDeclaration", + "start":0,"end":76,"loc":{"start":{"line":1,"column":0},"end":{"line":6,"column":1}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":6,"end":76,"loc":{"start":{"line":1,"column":6},"end":{"line":6,"column":1}}, + "id": { + "type": "Identifier", + "start":6,"end":17,"loc":{"start":{"line":1,"column":6},"end":{"line":1,"column":17},"identifierName":"nonTrailing"}, + "name": "nonTrailing" + }, + "init": { + "type": "ArrayExpression", + "start":20,"end":76,"loc":{"start":{"line":1,"column":20},"end":{"line":6,"column":1}}, + "elements": [ + { + "type": "StringLiteral", + "start":24,"end":29,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":7}}, + "extra": { + "rawValue": "One", + "raw": "\"One\"" + }, + "value": "One" + }, + { + "type": "StringLiteral", + "start":50,"end":55,"loc":{"start":{"line":4,"column":2},"end":{"line":4,"column":7}}, + "extra": { + "rawValue": "Two", + "raw": "\"Two\"" + }, + "value": "Two" + } + ] + } + } + ], + "kind": "const" + }, + { + "type": "VariableDeclaration", + "start":78,"end":162,"loc":{"start":{"line":8,"column":0},"end":{"line":13,"column":1}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":84,"end":162,"loc":{"start":{"line":8,"column":6},"end":{"line":13,"column":1}}, + "id": { + "type": "Identifier", + "start":84,"end":102,"loc":{"start":{"line":8,"column":6},"end":{"line":8,"column":24},"identifierName":"trailingAfterComma"}, + "name": "trailingAfterComma" + }, + "init": { + "type": "ArrayExpression", + "start":105,"end":162,"loc":{"start":{"line":8,"column":27},"end":{"line":13,"column":1}}, + "extra": { + "trailingComma": 140 + }, + "elements": [ + { + "type": "StringLiteral", + "start":109,"end":114,"loc":{"start":{"line":9,"column":2},"end":{"line":9,"column":7}}, + "extra": { + "rawValue": "One", + "raw": "\"One\"" + }, + "value": "One" + }, + { + "type": "StringLiteral", + "start":135,"end":140,"loc":{"start":{"line":11,"column":2},"end":{"line":11,"column":7}}, + "extra": { + "rawValue": "Two", + "raw": "\"Two\"" + }, + "value": "Two" + } + ] + } + } + ], + "kind": "const" + }, + { + "type": "VariableDeclaration", + "start":164,"end":220,"loc":{"start":{"line":15,"column":0},"end":{"line":18,"column":1}}, + "declarations": [ + { + "type": "VariableDeclarator", + "start":170,"end":220,"loc":{"start":{"line":15,"column":6},"end":{"line":18,"column":1}}, + "id": { + "type": "Identifier", + "start":170,"end":188,"loc":{"start":{"line":15,"column":6},"end":{"line":15,"column":24},"identifierName":"trailingAfterArray"}, + "name": "trailingAfterArray" + }, + "init": { + "type": "ArrayExpression", + "start":191,"end":220,"loc":{"start":{"line":15,"column":27},"end":{"line":18,"column":1}}, + "extra": { + "trailingComma": 200 + }, + "elements": [ + { + "type": "StringLiteral", + "start":195,"end":200,"loc":{"start":{"line":16,"column":2},"end":{"line":16,"column":7}}, + "extra": { + "rawValue": "One", + "raw": "\"One\"" + }, + "value": "One" + } + ] + } + } + ], + "kind": "const" + } + ], + "directives": [] + }, + "comments": [ + { + "type": "CommentLine", + "value": " One", + "start":31,"end":37,"loc":{"start":{"line":2,"column":9},"end":{"line":2,"column":15}} + }, + { + "type": "CommentLine", + "value": " Two", + "start":41,"end":47,"loc":{"start":{"line":3,"column":3},"end":{"line":3,"column":9}} + }, + { + "type": "CommentLine", + "value": " Three", + "start":56,"end":64,"loc":{"start":{"line":4,"column":8},"end":{"line":4,"column":16}} + }, + { + "type": "CommentLine", + "value": " Four", + "start":67,"end":74,"loc":{"start":{"line":5,"column":2},"end":{"line":5,"column":9}} + }, + { + "type": "CommentLine", + "value": " One", + "start":116,"end":122,"loc":{"start":{"line":9,"column":9},"end":{"line":9,"column":15}} + }, + { + "type": "CommentLine", + "value": " Two", + "start":126,"end":132,"loc":{"start":{"line":10,"column":3},"end":{"line":10,"column":9}} + }, + { + "type": "CommentLine", + "value": " Three", + "start":142,"end":150,"loc":{"start":{"line":11,"column":9},"end":{"line":11,"column":17}} + }, + { + "type": "CommentLine", + "value": " Four", + "start":153,"end":160,"loc":{"start":{"line":12,"column":2},"end":{"line":12,"column":9}} + }, + { + "type": "CommentLine", + "value": " One", + "start":202,"end":208,"loc":{"start":{"line":16,"column":9},"end":{"line":16,"column":15}} + }, + { + "type": "CommentLine", + "value": " Two", + "start":212,"end":218,"loc":{"start":{"line":17,"column":3},"end":{"line":17,"column":9}} + }, + { + "type": "CommentLine", + "value": " Three", + "start":221,"end":229,"loc":{"start":{"line":18,"column":2},"end":{"line":18,"column":10}} + } + ] +} diff --git a/packages/babel-parser/test/fixtures/comments/attachComment-false/options.json b/packages/babel-parser/test/fixtures/comments/attachComment-false/options.json new file mode 100644 index 000000000000..e2f838582320 --- /dev/null +++ b/packages/babel-parser/test/fixtures/comments/attachComment-false/options.json @@ -0,0 +1,3 @@ +{ + "attachComment": false +}