diff --git a/src/common/ast-path.js b/src/common/ast-path.js index 50b35942b..50f05f6a0 100644 --- a/src/common/ast-path.js +++ b/src/common/ast-path.js @@ -139,6 +139,12 @@ class AstPath { } } + /** + * @template {(path: AstPath) => any} T + * @param {T} callback + * @param {number} [count=0] + * @returns {ReturnType} + */ callParent(callback, count = 0) { const stackIndex = this.#getNodeStackIndex(count + 1); const parentValues = this.stack.splice(stackIndex + 1); diff --git a/src/language-html/embed/attribute.js b/src/language-html/embed/attribute.js index 56b2378f5..75c771be4 100644 --- a/src/language-html/embed/attribute.js +++ b/src/language-html/embed/attribute.js @@ -8,7 +8,7 @@ import printVueAttribute from "./vue-attributes.js"; /** * @typedef {import("../../document/builders.js").Doc} Doc - * @typedef {import("../../common/ast-path.js")} AstPath + * @typedef {import("../../common/ast-path.js").default} AstPath */ function printAttribute(path, options) { diff --git a/src/language-html/embed/vue-attributes.js b/src/language-html/embed/vue-attributes.js index a86a81946..919557177 100644 --- a/src/language-html/embed/vue-attributes.js +++ b/src/language-html/embed/vue-attributes.js @@ -15,7 +15,7 @@ import { printVueVForDirective } from "./vue-v-for-directive.js"; /** * @typedef {import("../../document/builders.js").Doc} Doc - * @typedef {import("../../common/ast-path.js")} AstPath + * @typedef {import("../../common/ast-path.js").default} AstPath */ function printVueAttribute(path, options) { diff --git a/src/language-js/needs-parens.js b/src/language-js/needs-parens.js index 712d2cf94..fbdbece54 100644 --- a/src/language-js/needs-parens.js +++ b/src/language-js/needs-parens.js @@ -17,6 +17,14 @@ import { startsWithNoLookaheadToken, } from "./utils/index.js"; +/** + * @typedef {import("../common/ast-path.js").default} AstPath + */ + +/** + * @param {AstPath} path + * @returns {boolean} + */ function needsParens(path, options) { if (path.isRoot) { return false; @@ -1066,6 +1074,10 @@ const isStatement = createTypeCheckFunction([ "WithStatement", ]); +/** + * @param {AstPath} path + * @returns {boolean} + */ function isPathInForStatementInitializer(path) { let i = 0; let { node } = path; @@ -1093,6 +1105,10 @@ function endsWithRightBracket(node) { return isObjectOrRecordExpression(node); } +/** + * @param {AstPath} path + * @returns {boolean} + */ function isFollowedByRightBracket(path) { const { parent, key } = path; switch (parent.type) { @@ -1126,6 +1142,10 @@ function isFollowedByRightBracket(path) { return false; } +/** + * @param {AstPath} path + * @returns {boolean} + */ function shouldWrapFunctionForExportDefault(path, options) { const { node, parent } = path; @@ -1170,6 +1190,10 @@ new (a?.b)(); new (a?.())(); ``` */ +/** + * @param {AstPath} path + * @returns {boolean} + */ function shouldAddParenthesesToChainElement(path) { // Babel, this was implemented before #13735, can use `path.match` as estree does const { node, parent, grandparent, key } = path; diff --git a/src/language-js/print/call-expression.js b/src/language-js/print/call-expression.js index 7baac263d..3b4875dca 100644 --- a/src/language-js/print/call-expression.js +++ b/src/language-js/print/call-expression.js @@ -14,7 +14,7 @@ import printMemberChain from "./member-chain.js"; import { printFunctionTypeParameters, printOptionalToken } from "./misc.js"; function printCallExpression(path, options, print) { - const { node, parent } = path; + const { node } = path; const isNew = node.type === "NewExpression"; const isDynamicImport = node.type === "ImportExpression"; @@ -27,16 +27,13 @@ function printCallExpression(path, options, print) { if ( isTemplateLiteralSingleArg || // Dangling comments are not handled, all these special cases should have arguments #9668 - (args.length > 0 && - !isNew && - !isDynamicImport && - // We want to keep CommonJS- and AMD-style require calls, and AMD-style - // define calls, as a unit. - // e.g. `define(["some/lib"], (lib) => {` - (isCommonsJsOrAmdCall(node, parent) || - // Keep test declarations on a single line - // e.g. `it('long name', () => {` - isTestCall(node, parent))) + // We want to keep CommonJS- and AMD-style require calls, and AMD-style + // define calls, as a unit. + // e.g. `define(["some/lib"], (lib) => {` + isCommonsJsOrAmdModuleDefinition(path) || + // Keep test declarations on a single line + // e.g. `it('long name', () => {` + isTestCall(node, path.parent) ) { const printed = []; iterateCallArgumentsPath(path, () => { @@ -45,7 +42,7 @@ function printCallExpression(path, options, print) { if (!(isTemplateLiteralSingleArg && printed[0].label?.embed)) { return [ isNew ? "new " : "", - isDynamicImport ? printDynamicImportCallee(node) : print("callee"), + printCallee(path, print), optional, printFunctionTypeParameters(path, options, print), "(", @@ -72,7 +69,7 @@ function printCallExpression(path, options, print) { const contents = [ isNew ? "new " : "", - isDynamicImport ? printDynamicImportCallee(node) : print("callee"), + printCallee(path, print), optional, printFunctionTypeParameters(path, options, print), printCallArguments(path, options, print), @@ -87,32 +84,45 @@ function printCallExpression(path, options, print) { return contents; } -function printDynamicImportCallee(node) { - if (!node.phase) { - return "import"; +function printCallee(path, print) { + const { node } = path; + + if (node.type === "ImportExpression") { + return `import${node.phase ? `.${node.phase}` : ""}`; } - return `import.${node.phase}`; + + return print("callee"); } -function isCommonsJsOrAmdCall(node, parentNode) { +function isCommonsJsOrAmdModuleDefinition(path) { + const { node } = path; + + if (node.type !== "CallExpression" || node.optional) { + return false; + } + if (node.callee.type !== "Identifier") { return false; } + const args = getCallArguments(node); + + // AMD module if (node.callee.name === "require") { - const args = getCallArguments(node); return (args.length === 1 && isStringLiteral(args[0])) || args.length > 1; } - if (node.callee.name === "define") { - const args = getCallArguments(node); + // CommonJS module + if ( + node.callee.name === "define" && + path.parent.type === "ExpressionStatement" + ) { return ( - parentNode.type === "ExpressionStatement" && - (args.length === 1 || - (args.length === 2 && args[0].type === "ArrayExpression") || - (args.length === 3 && - isStringLiteral(args[0]) && - args[1].type === "ArrayExpression")) + args.length === 1 || + (args.length === 2 && args[0].type === "ArrayExpression") || + (args.length === 3 && + isStringLiteral(args[0]) && + args[1].type === "ArrayExpression") ); } diff --git a/src/language-js/print/type-parameters.js b/src/language-js/print/type-parameters.js index 5c41fba8a..17248ba97 100644 --- a/src/language-js/print/type-parameters.js +++ b/src/language-js/print/type-parameters.js @@ -26,6 +26,11 @@ import { shouldHugType, } from "./type-annotation.js"; +/** + * @typedef {import("../../document/builders.js").Doc} Doc + * @typedef {import("../../common/ast-path.js").default} AstPath + */ + const getTypeParametersGroupId = createGroupIdMapper("typeParameters"); // Keep comma if the file extension not `.ts` and @@ -42,6 +47,9 @@ function shouldForceTrailingComma(path, options, paramsKey) { ); } +/** + * @param {AstPath} path + */ function printTypeParameters(path, options, print, paramsKey) { const { node } = path; @@ -54,8 +62,7 @@ function printTypeParameters(path, options, print, paramsKey) { return print(paramsKey); } - const grandparent = path.getNode(2); - const isParameterInTestCall = grandparent && isTestCall(grandparent); + const isParameterInTestCall = isTestCall(path.grandparent); const isArrowFunctionVariable = path.match( (node) => @@ -123,7 +130,7 @@ function printTypeParameter(path, options, print) { const { node, parent } = path; /** - * @type {import("../../document/builders.js").Doc[]} + * @type {Doc[]} */ const parts = [node.type === "TSTypeParameter" && node.const ? "const " : ""]; diff --git a/src/language-js/utils/index.js b/src/language-js/utils/index.js index 394e3d112..51534145e 100644 --- a/src/language-js/utils/index.js +++ b/src/language-js/utils/index.js @@ -372,14 +372,14 @@ function isTestCallCallee(node) { // eg; `describe("some string", (done) => {})` function isTestCall(node, parent) { - if (node.type !== "CallExpression" || node.optional) { + if (node?.type !== "CallExpression" || node.optional) { return false; } const args = getCallArguments(node); if (args.length === 1) { - if (isAngularTestWrapper(node) && parent && isTestCall(parent)) { + if (isAngularTestWrapper(node) && isTestCall(parent)) { return isFunctionOrArrowExpression(args[0]); }