From edb53db4b1ec326d3f0f4fb683f56c83beefeba4 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 2 Dec 2025 06:49:28 +0800 Subject: [PATCH] Move code (#18399) --- src/language-js/print/await-expression.js | 44 +++ src/language-js/print/block.js | 2 +- src/language-js/print/estree.js | 323 ++---------------- src/language-js/print/for-statement.js | 47 +++ src/language-js/print/if-statement.js | 59 ++++ src/language-js/print/sequence-expression.js | 40 +++ .../{statement.js => statement-sequence.js} | 0 src/language-js/print/switch-statement.js | 75 ++++ src/language-js/print/try-statement.js | 45 +++ src/language-js/print/variable-declaration.js | 41 +++ src/language-js/print/while-statement.js | 28 ++ 11 files changed, 410 insertions(+), 294 deletions(-) create mode 100644 src/language-js/print/await-expression.js create mode 100644 src/language-js/print/for-statement.js create mode 100644 src/language-js/print/if-statement.js create mode 100644 src/language-js/print/sequence-expression.js rename src/language-js/print/{statement.js => statement-sequence.js} (100%) create mode 100644 src/language-js/print/switch-statement.js create mode 100644 src/language-js/print/try-statement.js create mode 100644 src/language-js/print/variable-declaration.js create mode 100644 src/language-js/print/while-statement.js diff --git a/src/language-js/print/await-expression.js b/src/language-js/print/await-expression.js new file mode 100644 index 000000000..0b6c41987 --- /dev/null +++ b/src/language-js/print/await-expression.js @@ -0,0 +1,44 @@ +import { group, indent, softline } from "../../document/index.js"; +import { + isCallExpression, + isMemberExpression, + startsWithNoLookaheadToken, +} from "../utilities/index.js"; + +/** + * @import AstPath from "../../common/ast-path.js" + * @import {Doc} from "../../document/index.js" + */ + +function printAwaitExpression(path, options, print) { + const { node } = path; + /** @type{Doc[]} */ + let parts = ["await"]; + if (node.argument) { + parts.push(" ", print("argument")); + const { parent } = path; + if ( + (isCallExpression(parent) && parent.callee === node) || + (isMemberExpression(parent) && parent.object === node) + ) { + parts = [indent([softline, ...parts]), softline]; + // avoid printing `await (await` on one line + const parentAwaitOrBlock = path.findAncestor( + (node) => + node.type === "AwaitExpression" || node.type === "BlockStatement", + ); + if ( + parentAwaitOrBlock?.type !== "AwaitExpression" || + !startsWithNoLookaheadToken( + parentAwaitOrBlock.argument, + (leftmostNode) => leftmostNode === node, + ) + ) { + return group(parts); + } + } + } + return parts; +} + +export { printAwaitExpression }; diff --git a/src/language-js/print/block.js b/src/language-js/print/block.js index d5b83521d..3c3c4fce7 100644 --- a/src/language-js/print/block.js +++ b/src/language-js/print/block.js @@ -6,7 +6,7 @@ import { hasComment, isNextLineEmpty, } from "../utilities/index.js"; -import { printStatementSequence } from "./statement.js"; +import { printStatementSequence } from "./statement-sequence.js"; /** @import {Doc} from "../../document/index.js" */ diff --git a/src/language-js/print/estree.js b/src/language-js/print/estree.js index ddf9bf1c5..6a9bbb0f3 100644 --- a/src/language-js/print/estree.js +++ b/src/language-js/print/estree.js @@ -1,38 +1,28 @@ import { group, hardline, - ifBreak, indent, - join, - line, replaceEndOfLine, softline, } from "../../document/index.js"; import { printDanglingComments } from "../../main/comments/print.js"; -import hasNewline from "../../utilities/has-newline.js"; import UnexpectedNodeError from "../../utilities/unexpected-node-error.js"; -import { locEnd, locStart } from "../loc.js"; import { CommentCheckFlags, hasComment, isArrayExpression, - isCallExpression, isLiteral, isMeaningfulEmptyStatement, - isMemberExpression, isMethod, - isNextLineEmpty, isObjectExpression, - needsHardlineAfterDanglingComment, - startsWithNoLookaheadToken, } from "../utilities/index.js"; -import isBlockComment from "../utilities/is-block-comment.js"; import { printArray } from "./array.js"; import { printArrowFunction } from "./arrow-function.js"; import { printAssignmentExpression, printVariableDeclarator, } from "./assignment.js"; +import { printAwaitExpression } from "./await-expression.js"; import { printBinaryishExpression } from "./binaryish.js"; import { printBindExpression } from "./bind-expression.js"; import { printBlock } from "./block.js"; @@ -44,6 +34,7 @@ import { printClassProperty, } from "./class.js"; import { printExpressionStatement } from "./expression-statement.js"; +import { printForStatement } from "./for-statement.js"; import { printFunction, printMethod, @@ -51,11 +42,11 @@ import { printThrowStatement, } from "./function.js"; import { printHtmlBinding } from "./html-binding.js"; +import { printIfStatement } from "./if-statement.js"; import { printLiteral } from "./literal.js"; import { printMemberExpression } from "./member.js"; import { adjustClause, - printDeclareToken, printDefiniteToken, printOptionalToken, } from "./misc.js"; @@ -67,13 +58,20 @@ import { import { printObject } from "./object.js"; import { printProperty } from "./property.js"; import { printRestElement, printSpreadElement } from "./rest-element.js"; -import { printStatementSequence } from "./statement.js"; +import { printSequenceExpression } from "./sequence-expression.js"; +import { printSwitchCase, printSwitchStatement } from "./switch-statement.js"; import { printTaggedTemplateExpression, printTemplateLiteral, } from "./template-literal.js"; import { printTernary } from "./ternary.js"; +import { printCatchClause, printTryStatement } from "./try-statement.js"; import { printTypeAnnotationProperty } from "./type-annotation.js"; +import { printVariableDeclaration } from "./variable-declaration.js"; +import { + printDoWhileStatement, + printWhileStatement, +} from "./while-statement.js"; /** * @import AstPath from "../../common/ast-path.js" @@ -165,35 +163,8 @@ function printEstree(path, options, print, args) { `yield${node.delegate ? "*" : ""}`, node.argument ? [" ", print("argument")] : "", ]; - case "AwaitExpression": { - /** @type{Doc[]} */ - let parts = ["await"]; - if (node.argument) { - parts.push(" ", print("argument")); - const { parent } = path; - if ( - (isCallExpression(parent) && parent.callee === node) || - (isMemberExpression(parent) && parent.object === node) - ) { - parts = [indent([softline, ...parts]), softline]; - // avoid printing `await (await` on one line - const parentAwaitOrBlock = path.findAncestor( - (node) => - node.type === "AwaitExpression" || node.type === "BlockStatement", - ); - if ( - parentAwaitOrBlock?.type !== "AwaitExpression" || - !startsWithNoLookaheadToken( - parentAwaitOrBlock.argument, - (leftmostNode) => leftmostNode === node, - ) - ) { - return group(parts); - } - } - } - return parts; - } + case "AwaitExpression": + return printAwaitExpression(path, options, print); case "ExportDefaultDeclaration": case "ExportNamedDeclaration": @@ -246,39 +217,9 @@ function printEstree(path, options, print, args) { case "ArrayExpression": case "ArrayPattern": return printArray(path, options, print); - case "SequenceExpression": { - const { parent } = path; - if ( - parent.type === "ExpressionStatement" || - parent.type === "ForStatement" - ) { - // For ExpressionStatements and for-loop heads, which are among - // the few places a SequenceExpression appears unparenthesized, we want - // to indent expressions after the first. - const parts = []; - path.each(({ isFirst }) => { - if (isFirst) { - parts.push(print()); - } else { - parts.push(",", indent([line, print()])); - } - }, "expressions"); - return group(parts); - } + case "SequenceExpression": + return printSequenceExpression(path, options, print); - const parts = join([",", line], path.map(print, "expressions")); - - if ( - ((parent.type === "ReturnStatement" || - parent.type === "ThrowStatement") && - path.key === "argument") || - (parent.type === "ArrowFunctionExpression" && path.key === "body") - ) { - return group(ifBreak([indent([softline, parts]), softline], parts)); - } - - return group(parts); - } case "ThisExpression": return "this"; case "Super": @@ -312,46 +253,8 @@ function printEstree(path, options, print, args) { ]; case "ConditionalExpression": return printTernary(path, options, print, args); - case "VariableDeclaration": { - const printed = path.map(print, "declarations"); - - // We generally want to terminate all variable declarations with a - // semicolon, except when they in the () part of for loops. - const parentNode = path.parent; - - const isParentForLoop = - parentNode.type === "ForStatement" || - parentNode.type === "ForInStatement" || - parentNode.type === "ForOfStatement"; - - const hasValue = node.declarations.some((decl) => decl.init); - - let firstVariable; - if (printed.length === 1 && !hasComment(node.declarations[0])) { - firstVariable = printed[0]; - } else if (printed.length > 0) { - // Indent first var to comply with eslint one-var rule - firstVariable = indent(printed[0]); - } - - return group([ - printDeclareToken(path), - node.kind, - firstVariable ? [" ", firstVariable] : "", - indent( - printed - .slice(1) - .map((p) => [ - ",", - hasValue && !isParentForLoop ? hardline : line, - p, - ]), - ), - options.semi && !(isParentForLoop && parentNode.body !== node) - ? ";" - : "", - ]); - } + case "VariableDeclaration": + return printVariableDeclaration(path, options, print); case "WithStatement": return group([ "with (", @@ -359,89 +262,15 @@ function printEstree(path, options, print, args) { ")", adjustClause(node.body, print("body")), ]); - case "IfStatement": { - const consequent = adjustClause(node.consequent, print("consequent")); - const opening = group([ - "if (", - group([indent([softline, print("test")]), softline]), - ")", - consequent, - ]); - /** @type{Doc[]} */ - const parts = [opening]; - - if (node.alternate) { - const commentOnOwnLine = - hasComment( - node.consequent, - CommentCheckFlags.Trailing | CommentCheckFlags.Line, - ) || needsHardlineAfterDanglingComment(node); - const elseOnSameLine = - node.consequent.type === "BlockStatement" && !commentOnOwnLine; - parts.push(elseOnSameLine ? " " : hardline); - - if (hasComment(node, CommentCheckFlags.Dangling)) { - parts.push( - printDanglingComments(path, options), - commentOnOwnLine ? hardline : " ", - ); - } - - parts.push( - "else", - group( - adjustClause( - node.alternate, - print("alternate"), - node.alternate.type === "IfStatement", - ), - ), - ); - } - - return parts; - } - case "ForStatement": { - const body = adjustClause(node.body, print("body")); - - // We want to keep dangling comments above the loop to stay consistent. - // Any comment positioned between the for statement and the parentheses - // is going to be printed before the statement. - const dangling = printDanglingComments(path, options); - const printedComments = dangling ? [dangling, softline] : ""; - - if (!node.init && !node.test && !node.update) { - return [printedComments, group(["for (;;)", body])]; - } - - return [ - printedComments, - group([ - "for (", - group([ - indent([ - softline, - print("init"), - ";", - line, - print("test"), - ";", - node.update ? [line, print("update")] : ifBreak("", line), - ]), - softline, - ]), - ")", - body, - ]), - ]; - } + case "IfStatement": + return printIfStatement(path, options, print); + case "ForStatement": + return printForStatement(path, options, print); case "WhileStatement": - return group([ - "while (", - group([indent([softline, print("test")]), softline]), - ")", - adjustClause(node.body, print("body")), - ]); + return printWhileStatement(path, options, print); + case "DoWhileStatement": + return printDoWhileStatement(path, options, print); + case "ForInStatement": return group([ "for (", @@ -464,19 +293,6 @@ function printEstree(path, options, print, args) { adjustClause(node.body, print("body")), ]); - case "DoWhileStatement": { - const clause = adjustClause(node.body, print("body")); - const doBody = group(["do", clause]); - - return [ - doBody, - node.body.type === "BlockStatement" ? " " : hardline, - "while (", - group([indent([softline, print("test")]), softline]), - ")", - options.semi ? ";" : "", - ]; - } case "DoExpression": return [node.async ? "async " : "", "do ", print("body")]; case "BreakStatement": @@ -493,93 +309,14 @@ function printEstree(path, options, print, args) { print("body"), ]; case "TryStatement": - return [ - "try ", - print("block"), - node.handler ? [" ", print("handler")] : "", - node.finalizer ? [" finally ", print("finalizer")] : "", - ]; + return printTryStatement(path, options, print); case "CatchClause": - if (node.param) { - const parameterHasComments = hasComment( - node.param, - (comment) => - !isBlockComment(comment) || - (comment.leading && - hasNewline(options.originalText, locEnd(comment))) || - (comment.trailing && - hasNewline(options.originalText, locStart(comment), { - backwards: true, - })), - ); - const param = print("param"); - - return [ - "catch ", - parameterHasComments - ? ["(", indent([softline, param]), softline, ") "] - : ["(", param, ") "], - print("body"), - ]; - } - - return ["catch ", print("body")]; + return printCatchClause(path, options, print); // Note: ignoring n.lexical because it has no printing consequences. case "SwitchStatement": - return [ - group([ - "switch (", - indent([softline, print("discriminant")]), - softline, - ")", - ]), - " {", - node.cases.length > 0 - ? indent([ - hardline, - join( - hardline, - path.map( - ({ node, isLast }) => [ - print(), - !isLast && isNextLineEmpty(node, options) ? hardline : "", - ], - "cases", - ), - ), - ]) - : "", - hardline, - "}", - ]; - case "SwitchCase": { - const parts = []; - if (node.test) { - parts.push("case ", print("test"), ":"); - } else { - parts.push("default:"); - } - - if (hasComment(node, CommentCheckFlags.Dangling)) { - parts.push(" ", printDanglingComments(path, options)); - } - - const consequent = node.consequent.filter( - (node) => node.type !== "EmptyStatement", - ); - - if (consequent.length > 0) { - const cons = printStatementSequence(path, options, print, "consequent"); - - parts.push( - consequent.length === 1 && consequent[0].type === "BlockStatement" - ? [" ", cons] - : indent([hardline, cons]), - ); - } - - return parts; - } + return printSwitchStatement(path, options, print); + case "SwitchCase": + return printSwitchCase(path, options, print); // JSX extensions below. case "DebuggerStatement": return ["debugger", options.semi ? ";" : ""]; diff --git a/src/language-js/print/for-statement.js b/src/language-js/print/for-statement.js new file mode 100644 index 000000000..e0a474747 --- /dev/null +++ b/src/language-js/print/for-statement.js @@ -0,0 +1,47 @@ +import { + group, + ifBreak, + indent, + line, + softline, +} from "../../document/index.js"; +import { printDanglingComments } from "../../main/comments/print.js"; +import { adjustClause } from "./misc.js"; + +function printForStatement(path, options, print) { + const { node } = path; + const body = adjustClause(node.body, print("body")); + + // We want to keep dangling comments above the loop to stay consistent. + // Any comment positioned between the for statement and the parentheses + // is going to be printed before the statement. + const dangling = printDanglingComments(path, options); + const printedComments = dangling ? [dangling, softline] : ""; + + if (!node.init && !node.test && !node.update) { + return [printedComments, group(["for (;;)", body])]; + } + + return [ + printedComments, + group([ + "for (", + group([ + indent([ + softline, + print("init"), + ";", + line, + print("test"), + ";", + node.update ? [line, print("update")] : ifBreak("", line), + ]), + softline, + ]), + ")", + body, + ]), + ]; +} + +export { printForStatement }; diff --git a/src/language-js/print/if-statement.js b/src/language-js/print/if-statement.js new file mode 100644 index 000000000..87c282333 --- /dev/null +++ b/src/language-js/print/if-statement.js @@ -0,0 +1,59 @@ +import { group, hardline, indent, softline } from "../../document/index.js"; +import { printDanglingComments } from "../../main/comments/print.js"; +import { + CommentCheckFlags, + hasComment, + needsHardlineAfterDanglingComment, +} from "../utilities/index.js"; +import { adjustClause } from "./misc.js"; + +/** + * @import AstPath from "../../common/ast-path.js" + * @import {Doc} from "../../document/index.js" + */ + +function printIfStatement(path, options, print) { + const { node } = path; + const consequent = adjustClause(node.consequent, print("consequent")); + const opening = group([ + "if (", + group([indent([softline, print("test")]), softline]), + ")", + consequent, + ]); + /** @type{Doc[]} */ + const parts = [opening]; + + if (node.alternate) { + const commentOnOwnLine = + hasComment( + node.consequent, + CommentCheckFlags.Trailing | CommentCheckFlags.Line, + ) || needsHardlineAfterDanglingComment(node); + const elseOnSameLine = + node.consequent.type === "BlockStatement" && !commentOnOwnLine; + parts.push(elseOnSameLine ? " " : hardline); + + if (hasComment(node, CommentCheckFlags.Dangling)) { + parts.push( + printDanglingComments(path, options), + commentOnOwnLine ? hardline : " ", + ); + } + + parts.push( + "else", + group( + adjustClause( + node.alternate, + print("alternate"), + node.alternate.type === "IfStatement", + ), + ), + ); + } + + return parts; +} + +export { printIfStatement }; diff --git a/src/language-js/print/sequence-expression.js b/src/language-js/print/sequence-expression.js new file mode 100644 index 000000000..4e22f1be9 --- /dev/null +++ b/src/language-js/print/sequence-expression.js @@ -0,0 +1,40 @@ +import { + group, + ifBreak, + indent, + join, + line, + softline, +} from "../../document/index.js"; + +function printSequenceExpression(path, options, print) { + const { parent } = path; + if (parent.type === "ExpressionStatement" || parent.type === "ForStatement") { + // For ExpressionStatements and for-loop heads, which are among + // the few places a SequenceExpression appears unparenthesized, we want + // to indent expressions after the first. + const parts = []; + path.each(({ isFirst }) => { + if (isFirst) { + parts.push(print()); + } else { + parts.push(",", indent([line, print()])); + } + }, "expressions"); + return group(parts); + } + + const parts = join([",", line], path.map(print, "expressions")); + + if ( + ((parent.type === "ReturnStatement" || parent.type === "ThrowStatement") && + path.key === "argument") || + (parent.type === "ArrowFunctionExpression" && path.key === "body") + ) { + return group(ifBreak([indent([softline, parts]), softline], parts)); + } + + return group(parts); +} + +export { printSequenceExpression }; diff --git a/src/language-js/print/statement.js b/src/language-js/print/statement-sequence.js similarity index 100% rename from src/language-js/print/statement.js rename to src/language-js/print/statement-sequence.js diff --git a/src/language-js/print/switch-statement.js b/src/language-js/print/switch-statement.js new file mode 100644 index 000000000..cf2081dac --- /dev/null +++ b/src/language-js/print/switch-statement.js @@ -0,0 +1,75 @@ +import { + group, + hardline, + indent, + join, + softline, +} from "../../document/index.js"; +import { printDanglingComments } from "../../main/comments/print.js"; +import { + CommentCheckFlags, + hasComment, + isNextLineEmpty, +} from "../utilities/index.js"; +import { printStatementSequence } from "./statement-sequence.js"; + +function printSwitchStatement(path, options, print) { + return [ + group([ + "switch (", + indent([softline, print("discriminant")]), + softline, + ")", + ]), + " {", + path.node.cases.length > 0 + ? indent([ + hardline, + join( + hardline, + path.map( + ({ node, isLast }) => [ + print(), + !isLast && isNextLineEmpty(node, options) ? hardline : "", + ], + "cases", + ), + ), + ]) + : "", + hardline, + "}", + ]; +} + +function printSwitchCase(path, options, print) { + const { node } = path; + const parts = []; + if (node.test) { + parts.push("case ", print("test"), ":"); + } else { + parts.push("default:"); + } + + if (hasComment(node, CommentCheckFlags.Dangling)) { + parts.push(" ", printDanglingComments(path, options)); + } + + const consequent = node.consequent.filter( + (node) => node.type !== "EmptyStatement", + ); + + if (consequent.length > 0) { + const cons = printStatementSequence(path, options, print, "consequent"); + + parts.push( + consequent.length === 1 && consequent[0].type === "BlockStatement" + ? [" ", cons] + : indent([hardline, cons]), + ); + } + + return parts; +} + +export { printSwitchCase, printSwitchStatement }; diff --git a/src/language-js/print/try-statement.js b/src/language-js/print/try-statement.js new file mode 100644 index 000000000..c1af19e35 --- /dev/null +++ b/src/language-js/print/try-statement.js @@ -0,0 +1,45 @@ +import { indent, softline } from "../../document/index.js"; +import hasNewline from "../../utilities/has-newline.js"; +import { locEnd, locStart } from "../loc.js"; +import { hasComment } from "../utilities/index.js"; +import isBlockComment from "../utilities/is-block-comment.js"; + +function printTryStatement(path, options, print) { + const { node } = path; + return [ + "try ", + print("block"), + node.handler ? [" ", print("handler")] : "", + node.finalizer ? [" finally ", print("finalizer")] : "", + ]; +} + +function printCatchClause(path, options, print) { + const { node } = path; + if (node.param) { + const parameterHasComments = hasComment( + node.param, + (comment) => + !isBlockComment(comment) || + (comment.leading && + hasNewline(options.originalText, locEnd(comment))) || + (comment.trailing && + hasNewline(options.originalText, locStart(comment), { + backwards: true, + })), + ); + const param = print("param"); + + return [ + "catch ", + parameterHasComments + ? ["(", indent([softline, param]), softline, ") "] + : ["(", param, ") "], + print("body"), + ]; + } + + return ["catch ", print("body")]; +} + +export { printCatchClause, printTryStatement }; diff --git a/src/language-js/print/variable-declaration.js b/src/language-js/print/variable-declaration.js new file mode 100644 index 000000000..aa58642bc --- /dev/null +++ b/src/language-js/print/variable-declaration.js @@ -0,0 +1,41 @@ +import { group, hardline, indent, line } from "../../document/index.js"; +import { hasComment } from "../utilities/index.js"; +import { printDeclareToken } from "./misc.js"; + +function printVariableDeclaration(path, options, print) { + const { node } = path; + const printed = path.map(print, "declarations"); + + // We generally want to terminate all variable declarations with a + // semicolon, except when they in the () part of for loops. + const parentNode = path.parent; + + const isParentForLoop = + parentNode.type === "ForStatement" || + parentNode.type === "ForInStatement" || + parentNode.type === "ForOfStatement"; + + const hasValue = node.declarations.some((decl) => decl.init); + + let firstVariable; + if (printed.length === 1 && !hasComment(node.declarations[0])) { + firstVariable = printed[0]; + } else if (printed.length > 0) { + // Indent first var to comply with eslint one-var rule + firstVariable = indent(printed[0]); + } + + return group([ + printDeclareToken(path), + node.kind, + firstVariable ? [" ", firstVariable] : "", + indent( + printed + .slice(1) + .map((p) => [",", hasValue && !isParentForLoop ? hardline : line, p]), + ), + options.semi && !(isParentForLoop && parentNode.body !== node) ? ";" : "", + ]); +} + +export { printVariableDeclaration }; diff --git a/src/language-js/print/while-statement.js b/src/language-js/print/while-statement.js new file mode 100644 index 000000000..a5d3a9fdb --- /dev/null +++ b/src/language-js/print/while-statement.js @@ -0,0 +1,28 @@ +import { group, hardline, indent, softline } from "../../document/index.js"; +import { adjustClause } from "./misc.js"; + +function printWhileStatement(path, options, print) { + return group([ + "while (", + group([indent([softline, print("test")]), softline]), + ")", + adjustClause(path.node.body, print("body")), + ]); +} + +function printDoWhileStatement(path, options, print) { + const { body } = path.node; + const clause = adjustClause(body, print("body")); + const doBody = group(["do", clause]); + + return [ + doBody, + body.type === "BlockStatement" ? " " : hardline, + "while (", + group([indent([softline, print("test")]), softline]), + ")", + options.semi ? ";" : "", + ]; +} + +export { printDoWhileStatement, printWhileStatement };