Improve stripComments (#18774)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
fisker Cheungandautofix-ci[bot] authored and GitHub committed 2026-01-29 09:39:41 +08:00
1 parent e8bcedaacd
commit 696134156c
19 files changed
+127 -104

No files matched your search

+1
View File
@@ -0,0 +1 @@
export const commentsPropertyInOptions = Symbol.for("comments");
+6 -19
View File
@@ -13,7 +13,6 @@ import { getCallArguments } from "../utilities/call-arguments.js";
import { isInsideCallOrNewExpressionParentheses } from "../utilities/call-or-new-expression-parentheses.js";
import { createTypeCheckFunction } from "../utilities/create-type-check-function.js";
import { getFunctionParameters } from "../utilities/function-parameters.js";
import getTextWithoutComments from "../utilities/get-text-without-comments.js";
import { isBlockComment } from "../utilities/is-block-comment.js";
import { isLineComment } from "../utilities/is-line-comment.js";
import { isMethod } from "../utilities/is-method.js";
@@ -29,6 +28,7 @@ import {
isMemberExpression,
isUnionType,
} from "../utilities/node-types.js";
import { stripComments } from "../utilities/strip-comments.js";
/**
@import {Node, Comment, NodeMap} from "../types/estree.js";
@@ -235,7 +235,7 @@ function handleIfStatementComments({
) {
const start = locStart(enclosingNode);
const end = locEnd(enclosingNode);
const ifStatementTxt = getTextWithoutComments(options, start, end);
const ifStatementTxt = stripComments(options).slice(start, end);
let elseTokenIndex = getNextNonSpaceNonCommentCharacterIndex(
ifStatementTxt,
locEnd(enclosingNode.consequent) - start,
@@ -646,17 +646,10 @@ function isInArgumentOrParameterParentheses(node, comment, options) {
return false;
}
const nodeText = getTextWithoutComments(options, nodeStart, nodeEnd);
const text = stripComments(options);
return (
nodeText
.slice(0, locStart(comment) - nodeStart)
.trimEnd()
.endsWith("(") &&
nodeText
.slice(locEnd(comment) - nodeStart)
.trimStart()
.startsWith(")")
text.slice(0, locStart(comment)).trimEnd().endsWith("(") &&
text.slice(locEnd(comment)).trimStart().startsWith(")")
);
}
@@ -1022,13 +1015,7 @@ function handleIgnoreComments({ comment, enclosingNode, followingNode }) {
@param {Comment} comment
*/
function isBeforeMappedTypeOpeningBracket(node, comment, options) {
const start = locStart(node);
const textAfter = getTextWithoutComments(
options,
start + 1,
locStart(node.key),
);
const bracketIndex = start + 1 + textAfter.indexOf("[");
const bracketIndex = stripComments(options).indexOf("[", locStart(node));
return locEnd(comment) < bracketIndex;
}
+2 -4
View File
@@ -50,11 +50,9 @@ function locEnd(node) {
node.type === "ExportDefaultDeclaration" ||
node.type === "ExportNamedDeclaration" ||
node.type === "ExportAllDeclaration") &&
// @ts-expect-error -- Added in postprocess.js
node.__end
node.__contentEnd
) {
// @ts-expect-error -- safe
return node.__end;
return node.__contentEnd;
}
return locEndWithFullText(node);
+1 -1
View File
@@ -29,7 +29,7 @@ const ignoredProperties = new Set([
"trailingComma",
"docblock",
// Prettier
"__end",
"__contentEnd",
]);
const removeTemplateElementsValue = (node) => {
+27 -12
View File
@@ -1,14 +1,19 @@
import * as assert from "#universal/assert";
import { commentsPropertyInOptions } from "../../../constants.js";
import { locEnd, locEndWithFullText, locStart } from "../../loc.js";
import { createTypeCheckFunction } from "../../utilities/create-type-check-function.js";
import { getRaw } from "../../utilities/get-raw.js";
import getTextWithoutComments from "../../utilities/get-text-without-comments.js";
import { isBlockComment } from "../../utilities/is-block-comment.js";
import { isLineComment } from "../../utilities/is-line-comment.js";
import { isTypeCastComment } from "../../utilities/is-type-cast-comment.js";
import { stripComments } from "../../utilities/strip-comments.js";
import { mergeNestledJsdocComments } from "./merge-nestled-jsdoc-comments.js";
import visitNode from "./visit-node.js";
/**
@import {Node, Comment} from "../../types/estree.js"
*/
const isNodeWithRaw = createTypeCheckFunction([
// Babel
"RegExpLiteral",
@@ -172,7 +177,7 @@ function postprocess(ast, options) {
case "ExportDefaultDeclaration":
case "ExportNamedDeclaration":
case "ExportAllDeclaration":
addEnd(node, { comments, text });
addNodeContentEnd(node, { comments, text });
break;
}
},
@@ -261,16 +266,26 @@ function assertRaw(node, text) {
assert.equal(raw, text.slice(locStart(node), locEnd(node)));
}
function addEnd(node, { comments, text: originalText }) {
const start = locStart(node);
const end = locEndWithFullText(node);
const text = getTextWithoutComments(
{ [Symbol.for("comments")]: comments, originalText },
start,
end,
);
const cleaned = (text.at(-1) === ";" ? text.slice(0, -1) : text).trimEnd();
node.__end = end - (text.length - cleaned.length);
/**
@param {Node} node
@param {{comments: Comment[], text: string}} param1
*/
function addNodeContentEnd(node, { comments, text: originalText }) {
let end = locEndWithFullText(node);
if (originalText[end - 1] !== ";") {
return;
}
const text = stripComments({
[commentsPropertyInOptions]: comments,
originalText,
});
end -= 1;
const textBeforeSemicolon = text.slice(locStart(node), end);
const cleaned = textBeforeSemicolon.trimEnd();
node.__contentEnd = end - (textBeforeSemicolon.length - cleaned.length);
}
export default postprocess;
+1 -1
View File
@@ -16,7 +16,7 @@ function printIgnored(path, options /* , print*/) {
node.type === "ExportDefaultDeclaration" ||
node.type === "ExportNamedDeclaration" ||
node.type === "ExportAllDeclaration") &&
node.__end))
node.__contentEnd))
) {
text += ";";
} else if (shouldExpressionStatementPrintLeadingSemicolon(path, options)) {
+6 -8
View File
@@ -11,8 +11,8 @@ import hasNewline from "../../utilities/has-newline.js";
import hasNewlineInRange from "../../utilities/has-newline-in-range.js";
import { locEnd, locStart } from "../loc.js";
import { CommentCheckFlags, getComments } from "../utilities/comments.js";
import getTextWithoutComments from "../utilities/get-text-without-comments.js";
import { isLineComment } from "../utilities/is-line-comment.js";
import { stripComments } from "../utilities/strip-comments.js";
import { printClassMemberSemicolon } from "./class.js";
/**
@@ -70,13 +70,11 @@ function printTypeScriptMappedType(path, options, print) {
// Break after `{` like `printObject`
let shouldBreak = false;
if (options.objectWrap === "preserve") {
const start = locStart(node);
const textAfter = getTextWithoutComments(
options,
start + 1,
locStart(node.key),
);
const nextTokenIndex = start + 1 + textAfter.search(/\S/);
const text = stripComments(options);
// Skip `{`
const start = locStart(node) + 1;
const textAfter = text.slice(start);
const nextTokenIndex = start + textAfter.search(/\S/);
if (hasNewlineInRange(options.originalText, start, nextTokenIndex)) {
shouldBreak = true;
}
+8 -8
View File
@@ -13,10 +13,10 @@ import UnexpectedNodeError from "../../utilities/unexpected-node-error.js";
import { locEnd, locStart } from "../loc.js";
import { CommentCheckFlags, hasComment } from "../utilities/comments.js";
import { createTypeCheckFunction } from "../utilities/create-type-check-function.js";
import getTextWithoutComments from "../utilities/get-text-without-comments.js";
import { isShorthandSpecifier } from "../utilities/is-shorthand-specifier.js";
import { needsHardlineAfterDanglingComment } from "../utilities/needs-hardline-after-dangling-comment.js";
import { isStringLiteral } from "../utilities/node-types.js";
import { stripComments } from "../utilities/strip-comments.js";
import { printDecoratorsBeforeExport } from "./decorators.js";
import { printDeclareToken, printTrailingComma } from "./miscellaneous.js";
import { printObject } from "./object.js";
@@ -239,8 +239,7 @@ function shouldPrintSpecifiers(node, options) {
return true;
}
const text = getTextWithoutComments(
options,
const text = stripComments(options).slice(
locStart(node),
locStart(node.source),
);
@@ -249,11 +248,12 @@ function shouldPrintSpecifiers(node, options) {
}
function getImportAttributesKeyword(node, options) {
const textBetweenSourceAndAttributes = getTextWithoutComments(
options,
locEnd(node.source),
node.attributes?.[0] ? locStart(node.attributes[0]) : locEnd(node),
).trimStart();
const textBetweenSourceAndAttributes = stripComments(options)
.slice(
locEnd(node.source),
node.attributes?.[0] ? locStart(node.attributes[0]) : locEnd(node),
)
.trimStart();
if (textBetweenSourceAndAttributes.startsWith("assert")) {
return "assert";
+8 -7
View File
@@ -17,9 +17,9 @@ import {
hasComment,
} from "../utilities/comments.js";
import { createTypeCheckFunction } from "../utilities/create-type-check-function.js";
import getTextWithoutComments from "../utilities/get-text-without-comments.js";
import { isNextLineEmpty } from "../utilities/is-next-line-empty.js";
import { isObjectType } from "../utilities/node-types.js";
import { stripComments } from "../utilities/strip-comments.js";
import { shouldHugTheOnlyParameter } from "./function-parameters.js";
import {
printDanglingCommentsInList,
@@ -196,17 +196,18 @@ function printObject(path, options, print) {
function hasNewLineAfterOpeningBrace(node, firstProperty, options) {
const text = options.originalText;
let openingBraceIndex = locStart(node);
const firstPropertyStart = locStart(firstProperty);
let openingBraceIndex = locStart(node);
if (isPrintingImportAttributes(node)) {
const start = locStart(node);
const textBeforeAttributes = getTextWithoutComments(
options,
start,
openingBraceIndex = stripComments(options).lastIndexOf(
"{",
firstPropertyStart,
);
openingBraceIndex = start + textBeforeAttributes.lastIndexOf("{");
/* c8 ignore next 3 */
if (process.env.NODE_ENV !== "production") {
assert.ok(openingBraceIndex !== -1);
}
}
/* c8 ignore next 3 */
+1
View File
@@ -12,6 +12,7 @@ type PrettierNodeAdditionalProperties = {
};
comments?: Comment[];
prettierIgnore?: boolean;
__contentEnd?: number;
};
type PrettierCommentAdditionalProperties = {
@@ -1,5 +1,5 @@
import { locEnd, locStart } from "../loc.js";
import getTextWithoutComments from "./get-text-without-comments.js";
import { stripComments } from "./strip-comments.js";
/**
@import {Node, Comment, NodeMap} from "../types/estree.js"
@@ -52,11 +52,11 @@ function getCallOrNewExpressionOpeningParenthesisIndex(
return;
}
const text = stripComments(options);
const start = locEnd(
callOrNewExpression.typeArguments ?? callOrNewExpression.callee,
);
const text = getTextWithoutComments(options, start, closingParenthesisIndex);
const openingParenthesisIndex = text.indexOf("(");
const openingParenthesisIndex = text.indexOf("(", start);
/* c8 ignore next 6 */
if (openingParenthesisIndex === -1) {
@@ -68,7 +68,7 @@ function getCallOrNewExpressionOpeningParenthesisIndex(
return;
}
return start + openingParenthesisIndex;
return openingParenthesisIndex;
}
/**
@@ -1,31 +0,0 @@
import replaceNonLineBreaksWithSpace from "../../utilities/replace-non-line-breaks-with-space.js";
import { locEnd, locStart } from "../loc.js";
function getTextWithoutComments(options, start, end) {
let text = options.originalText.slice(start, end);
for (const comment of options[Symbol.for("comments")]) {
const commentStart = locStart(comment);
// Comments are sorted, we can escape if the comment is after the range
if (commentStart >= end) {
break;
}
const commentEnd = locEnd(comment);
if (commentEnd <= start) {
continue;
}
const startIndex = Math.max(commentStart - start, 0);
const endIndex = commentEnd - start;
text =
text.slice(0, startIndex) +
replaceNonLineBreaksWithSpace(text.slice(startIndex, endIndex)) +
text.slice(endIndex);
}
return text;
}
export default getTextWithoutComments;
@@ -0,0 +1,48 @@
import { commentsPropertyInOptions } from "../../constants.js";
import replaceNonLineBreaksWithSpace from "../../utilities/replace-non-line-breaks-with-space.js";
import { locEnd, locStart } from "../loc.js";
/**
@import {Node, Comment} from "../types/estree.js"
@typedef {Comment[]} Comments
@typedef {{
originalText: string,
[commentsPropertyInOptions]: Comments
}} Options
*/
/**
@param {string} text
@param {Comments} comments
@returns {string}
*/
function stripCommentsFromText(text, comments) {
for (const comment of comments) {
const start = locStart(comment);
const end = locEnd(comment);
text =
text.slice(0, start) +
replaceNonLineBreaksWithSpace(text.slice(start, end)) +
text.slice(end);
}
return text;
}
/** @type {WeakMap<Comment[], string>} */
const cache = new WeakMap();
/**
@param {Options} options
@returns {string}
*/
function stripComments(options) {
const comments = options[commentsPropertyInOptions];
if (!cache.has(comments)) {
cache.set(comments, stripCommentsFromText(options.originalText, comments));
}
return cache.get(comments);
}
export { stripComments };
+2 -1
View File
@@ -1,4 +1,5 @@
import AstPath from "../common/ast-path.js";
import { commentsPropertyInOptions } from "../constants.js";
import { cursor, inheritLabel } from "../document/index.js";
import isNonEmptyArray from "../utilities/is-non-empty-array.js";
import isObject from "../utilities/is-object.js";
@@ -144,7 +145,7 @@ function callPluginPrintFunction(path, options, printPath, args, embeds) {
async function prepareToPrint(ast, options) {
const comments = ast.comments ?? [];
options[Symbol.for("comments")] = comments;
options[commentsPropertyInOptions] = comments;
// For JS printer to ignore attached comments
options[Symbol.for("printedComments")] = new Set();
+2 -1
View File
@@ -1,3 +1,4 @@
import { commentsPropertyInOptions } from "../../constants.js";
import {
breakParent,
hardline,
@@ -255,7 +256,7 @@ function printComments(path, doc, options, printOptions) {
function ensureAllCommentsPrinted(options) {
const {
[Symbol.for("comments")]: comments,
[commentsPropertyInOptions]: comments,
[Symbol.for("printedComments")]: printedComments,
} = options;
+2 -1
View File
@@ -5,6 +5,7 @@ import {
guessEndOfLine,
normalizeEndOfLine,
} from "../common/end-of-line.js";
import { commentsPropertyInOptions } from "../constants.js";
import {
addAlignmentToDoc,
hardline,
@@ -69,7 +70,7 @@ async function coreFormat(originalText, opts, addAlignmentSize = 0) {
trimmed + convertEndOfLineOptionToCharacter(opts.endOfLine);
}
const comments = opts[Symbol.for("comments")];
const comments = opts[commentsPropertyInOptions];
if (opts.cursorOffset >= 0) {
// Roughly, our logic for preserving the user's cursor position is as
+3 -1
View File
@@ -1,7 +1,9 @@
import { commentsPropertyInOptions } from "../constants.js";
function printIgnored(path, options, printPath, args) {
const {
originalText,
[Symbol.for("comments")]: comments,
[commentsPropertyInOptions]: comments,
locStart,
locEnd,
[Symbol.for("printedComments")]: printedComments,
@@ -405,7 +405,7 @@ function HelloWorld(x) {
// eslint-disable-next-line
x.a |
x.b
).call(null);
).call(null)
}
================================================================================
@@ -1531,7 +1531,7 @@ matrix(
1, 0, 0,
0, 1, 0,
0, 0, 1
);
)
\`\`\`
will be transformed to:
@@ -1544,7 +1544,7 @@ matrix(
1, 0, 0,
0, 1, 0,
0, 0, 1
);
)
\`\`\`
## Options
@@ -3503,7 +3503,7 @@ matrix(
1, 0, 0,
0, 1, 0,
0, 0, 1
);
)
\`\`\`
will be transformed to:
@@ -3516,7 +3516,7 @@ matrix(
1, 0, 0,
0, 1, 0,
0, 0, 1
);
)
\`\`\`
## Options