import { hasDescendant } from "../../utilities/ast.js"; import getStringWidth from "../../utilities/get-string-width.js"; import hasNewline from "../../utilities/has-newline.js"; import isNextLineEmptyAfterIndex from "../../utilities/is-next-line-empty.js"; import isNonEmptyArray from "../../utilities/is-non-empty-array.js"; import isObject from "../../utilities/is-object.js"; import printString from "../../utilities/print-string.js"; import { hasSameLocStart, locEnd, locStart } from "../loc.js"; import getVisitorKeys from "../traverse/get-visitor-keys.js"; import createTypeCheckFunction from "./create-type-check-function.js"; import getRaw from "./get-raw.js"; import isBlockComment from "./is-block-comment.js"; import isFlowKeywordType from "./is-flow-keyword-type.js"; import isLineComment from "./is-line-comment.js"; import isNodeMatches from "./is-node-matches.js"; import isTsKeywordType from "./is-ts-keyword-type.js"; /** @import * as Estree from "../types/estree.js" @import AstPath from "../../common/ast-path.js" */ /** * @param {Estree.Node} node * @param {(node: Estree.Node) => boolean} predicate * @returns {boolean} */ function hasNode(node, predicate) { return predicate(node) || hasDescendant(node, { getVisitorKeys, predicate }); } /** * @param {Estree.Node} node * @returns {boolean} */ function hasNakedLeftSide(node) { return ( node.type === "AssignmentExpression" || node.type === "BinaryExpression" || node.type === "LogicalExpression" || node.type === "NGPipeExpression" || node.type === "ConditionalExpression" || isCallExpression(node) || isMemberExpression(node) || node.type === "SequenceExpression" || node.type === "TaggedTemplateExpression" || node.type === "BindExpression" || (node.type === "UpdateExpression" && !node.prefix) || isBinaryCastExpression(node) || node.type === "TSNonNullExpression" || node.type === "ChainExpression" ); } function getLeftSide(node) { if (node.expressions) { return node.expressions[0]; } return ( node.left ?? node.test ?? node.callee ?? node.object ?? node.tag ?? node.argument ?? node.expression ); } function getLeftSidePathName(node) { if (node.expressions) { return ["expressions", 0]; } if (node.left) { return ["left"]; } if (node.test) { return ["test"]; } if (node.object) { return ["object"]; } if (node.callee) { return ["callee"]; } if (node.tag) { return ["tag"]; } if (node.argument) { return ["argument"]; } if (node.expression) { return ["expression"]; } throw new Error("Unexpected node has no left side."); } const isExportDeclaration = createTypeCheckFunction([ "ExportDefaultDeclaration", "DeclareExportDeclaration", "ExportNamedDeclaration", "ExportAllDeclaration", "DeclareExportAllDeclaration", ]); // These two functions exists because we used support `recordAndTuple` // Feel free to check `node.type` instead // https://github.com/prettier/prettier/pull/17363 const isArrayExpression = createTypeCheckFunction(["ArrayExpression"]); const isObjectExpression = createTypeCheckFunction(["ObjectExpression"]); /** * @param {Estree.Node} node * @returns {boolean} */ function isNullishCoalescing(node) { return node.type === "LogicalExpression" && node.operator === "??"; } /** * @param {Estree.Node} node * @returns {boolean} */ function isNumericLiteral(node) { return ( node.type === "NumericLiteral" || (node.type === "Literal" && typeof node.value === "number") ); } function isBooleanLiteral(node) { return ( node.type === "BooleanLiteral" || (node.type === "Literal" && typeof node.value === "boolean") ); } function isSignedNumericLiteral(node) { return ( node.type === "UnaryExpression" && (node.operator === "+" || node.operator === "-") && isNumericLiteral(node.argument) ); } /** * @param {Estree.Node} node * @returns {boolean} */ function isStringLiteral(node) { return Boolean( node && (node.type === "StringLiteral" || (node.type === "Literal" && typeof node.value === "string")), ); } function isRegExpLiteral(node) { return ( node.type === "RegExpLiteral" || (node.type === "Literal" && Boolean(node.regex)) ); } const isLiteral = createTypeCheckFunction([ "Literal", "BooleanLiteral", "BigIntLiteral", // Babel, flow use `BigIntLiteral` too "DirectiveLiteral", "NullLiteral", "NumericLiteral", "RegExpLiteral", "StringLiteral", ]); const isSingleWordType = createTypeCheckFunction([ "Identifier", "ThisExpression", "Super", "PrivateName", "PrivateIdentifier", ]); const isObjectType = createTypeCheckFunction([ "ObjectTypeAnnotation", "TSTypeLiteral", "TSMappedType", ]); const isFunctionOrArrowExpression = createTypeCheckFunction([ "FunctionExpression", "ArrowFunctionExpression", ]); /** * @param {Estree.Node} node * @returns {boolean} */ function isFunctionOrArrowExpressionWithBody(node) { return ( node.type === "FunctionExpression" || (node.type === "ArrowFunctionExpression" && node.body.type === "BlockStatement") ); } /** * Note: `inject` is used in AngularJS 1.x, `async` and `fakeAsync` in * Angular 2+, although `async` is deprecated and replaced by `waitForAsync` * since Angular 12. * * example: https://docs.angularjs.org/guide/unit-testing#using-beforeall- * * @param {Estree.CallExpression} node * @returns {boolean} */ function isAngularTestWrapper(node) { return ( isCallExpression(node) && node.callee.type === "Identifier" && ["async", "inject", "fakeAsync", "waitForAsync"].includes(node.callee.name) ); } const isJsxElement = createTypeCheckFunction(["JSXElement", "JSXFragment"]); function isMethod(node) { return ( (node.method && node.kind === "init") || node.kind === "get" || node.kind === "set" ); } /** * @param {Estree.Node} node * @returns {boolean} */ function isFlowObjectTypePropertyAFunction(node) { return ( (node.type === "ObjectTypeProperty" || node.type === "ObjectTypeInternalSlot") && !node.static && !node.method && // @ts-expect-error -- exists on `ObjectTypeProperty` but not `ObjectTypeInternalSlot` node.kind !== "get" && // @ts-expect-error -- exists on `ObjectTypeProperty` but not `ObjectTypeInternalSlot` node.kind !== "set" && node.value.type === "FunctionTypeAnnotation" ); } // Hack to differentiate between the following two which have the same ast // declare function f(a): void; // var f: (a) => void; function isTypeAnnotationAFunction(node) { return ( (node.type === "TypeAnnotation" || node.type === "TSTypeAnnotation") && node.typeAnnotation.type === "FunctionTypeAnnotation" && !node.static && !hasSameLocStart(node, node.typeAnnotation) ); } const isBinaryish = createTypeCheckFunction([ "BinaryExpression", "LogicalExpression", "NGPipeExpression", ]); /** * @param {Estree.Node} node * @returns {boolean} */ function isMemberish(node) { return ( isMemberExpression(node) || (node.type === "BindExpression" && Boolean(node.object)) ); } const isSimpleTypeAnnotation = createTypeCheckFunction([ "TSThisType", // literals "NullLiteralTypeAnnotation", "BooleanLiteralTypeAnnotation", "StringLiteralTypeAnnotation", "BigIntLiteralTypeAnnotation", "NumberLiteralTypeAnnotation", "TSLiteralType", "TSTemplateLiteralType", ]); /** * @param {Estree.Node} node * @returns {boolean} */ function isSimpleType(node) { return ( isTsKeywordType(node) || isFlowKeywordType(node) || isSimpleTypeAnnotation(node) || (node.type === "GenericTypeAnnotation" && !node.typeParameters) || (node.type === "TSTypeReference" && !node.typeArguments) ); } /** * @param {*} node * @returns {boolean} */ function isUnitTestSetupIdentifier(node) { return ( node.type === "Identifier" && (node.name === "beforeEach" || node.name === "beforeAll" || node.name === "afterEach" || node.name === "afterAll") ); } const testCallCalleePatterns = [ "it", "it.only", "it.skip", "describe", "describe.only", "describe.skip", "test", "test.only", "test.skip", "test.fixme", "test.step", "test.describe", "test.describe.only", "test.describe.skip", "test.describe.fixme", "test.describe.parallel", "test.describe.parallel.only", "test.describe.serial", "test.describe.serial.only", "skip", "xit", "xdescribe", "xtest", "fit", "fdescribe", "ftest", ]; function isTestCallCallee(node) { return isNodeMatches(node, testCallCalleePatterns); } // eg; `describe("some string", (done) => {})` function isTestCall(node, parent) { if (node?.type !== "CallExpression" || node.optional) { return false; } const args = getCallArguments(node); if (args.length === 1) { if (isAngularTestWrapper(node) && isTestCall(parent)) { return isFunctionOrArrowExpression(args[0]); } if (isUnitTestSetupIdentifier(node.callee)) { return isAngularTestWrapper(args[0]); } } else if ( (args.length === 2 || args.length === 3) && (args[0].type === "TemplateLiteral" || isStringLiteral(args[0])) && isTestCallCallee(node.callee) ) { // it("name", () => { ... }, 2500) if (args[2] && !isNumericLiteral(args[2])) { return false; } return ( (args.length === 2 ? isFunctionOrArrowExpression(args[1]) : isFunctionOrArrowExpressionWithBody(args[1]) && getFunctionParameters(args[1]).length <= 1) || isAngularTestWrapper(args[1]) ); } return false; } /** @return {(node: Estree.Node) => boolean} */ const skipChainExpression = (fn) => (node) => { if (node?.type === "ChainExpression") { node = node.expression; } return fn(node); }; const isCallExpression = skipChainExpression( createTypeCheckFunction(["CallExpression", "OptionalCallExpression"]), ); const isMemberExpression = skipChainExpression( createTypeCheckFunction(["MemberExpression", "OptionalMemberExpression"]), ); /** * Attempts to gauge the rough complexity of a node, for example * to detect deeply-nested booleans, call expressions with lots of arguments, etc. */ function isSimpleExpressionByNodeCount(node, maxInnerNodeCount = 5) { const count = getExpressionInnerNodeCount(node, maxInnerNodeCount); return count <= maxInnerNodeCount; } function getExpressionInnerNodeCount(node, maxCount) { let count = 0; for (const k in node) { const prop = node[k]; if (isObject(prop) && typeof prop.type === "string") { count++; count += getExpressionInnerNodeCount(prop, maxCount - count); } // Bail early to protect against bad performance. if (count > maxCount) { return count; } } return count; } const LONE_SHORT_ARGUMENT_THRESHOLD_RATE = 0.25; function isLoneShortArgument(node, options) { const { printWidth } = options; if (hasComment(node)) { return false; } const threshold = printWidth * LONE_SHORT_ARGUMENT_THRESHOLD_RATE; if ( node.type === "ThisExpression" || (node.type === "Identifier" && node.name.length <= threshold) || (isSignedNumericLiteral(node) && !hasComment(node.argument)) ) { return true; } const regexpPattern = (node.type === "Literal" && "regex" in node && node.regex.pattern) || (node.type === "RegExpLiteral" && node.pattern); if (regexpPattern) { return regexpPattern.length <= threshold; } if (isStringLiteral(node)) { return printString(getRaw(node), options).length <= threshold; } if (node.type === "TemplateLiteral") { return ( node.expressions.length === 0 && node.quasis[0].value.raw.length <= threshold && !node.quasis[0].value.raw.includes("\n") ); } if (node.type === "UnaryExpression") { return isLoneShortArgument(node.argument, { printWidth }); } if ( node.type === "CallExpression" && node.arguments.length === 0 && node.callee.type === "Identifier" ) { return node.callee.name.length <= threshold - 2; } return isLiteral(node); } /** * @param {string} text * @param {Estree.Node} node * @returns {boolean} */ function hasLeadingOwnLineComment(text, node) { if (isJsxElement(node)) { return hasNodeIgnoreComment(node); } return hasComment(node, CommentCheckFlags.Leading, (comment) => hasNewline(text, locEnd(comment)), ); } /** * @param {Estree.TemplateLiteral} template * @returns {boolean} */ function templateLiteralHasNewLines(template) { return template.quasis.some((quasi) => quasi.value.raw.includes("\n")); } /** * @param {Estree.TemplateLiteral | Estree.TaggedTemplateExpression} node * @param {string} text * @returns {boolean} */ function isTemplateOnItsOwnLine(node, text) { return ( ((node.type === "TemplateLiteral" && templateLiteralHasNewLines(node)) || (node.type === "TaggedTemplateExpression" && templateLiteralHasNewLines(node.quasi))) && !hasNewline(text, locStart(node), { backwards: true }) ); } /** * @param {Estree.Node} node * @returns {boolean} */ function needsHardlineAfterDanglingComment(node) { if (!hasComment(node)) { return false; } const lastDanglingComment = getComments(node, CommentCheckFlags.Dangling).at( -1, ); return lastDanglingComment && !isBlockComment(lastDanglingComment); } // Logic to check for args with multiple anonymous functions. For instance, // the following call should be split on multiple lines for readability: // source.pipe(map((x) => x + x), filter((x) => x % 2 === 0)) function isFunctionCompositionArgs(args) { if (args.length <= 1) { return false; } let count = 0; for (const arg of args) { if (isFunctionOrArrowExpression(arg)) { count += 1; if (count > 1) { return true; } } else if (isCallExpression(arg)) { for (const childArg of getCallArguments(arg)) { if (isFunctionOrArrowExpression(childArg)) { return true; } } } } return false; } // Logic to determine if a call is a “long curried function call”. // See https://github.com/prettier/prettier/issues/1420. // // `connect(a, b, c)(d)` // In the above call expression, the second call is the parent node and the // first call is the current node. /** * @param {AstPath} path * @returns {boolean} */ function isLongCurriedCallExpression(path) { const { node, parent, key } = path; return ( key === "callee" && isCallExpression(node) && isCallExpression(parent) && parent.arguments.length > 0 && node.arguments.length > parent.arguments.length ); } const simpleCallArgumentUnaryOperators = new Set(["!", "-", "+", "~"]); /** * @param {any} node * @param {number} depth * @returns {boolean} */ function isSimpleCallArgument(node, depth = 2) { if (depth <= 0) { return false; } if (node.type === "ChainExpression" || node.type === "TSNonNullExpression") { return isSimpleCallArgument(node.expression, depth); } const isChildSimple = (child) => isSimpleCallArgument(child, depth - 1); if (isRegExpLiteral(node)) { return getStringWidth(node.pattern ?? node.regex.pattern) <= 5; } if ( isLiteral(node) || isSingleWordType(node) || node.type === "ArgumentPlaceholder" ) { return true; } if (node.type === "TemplateLiteral") { return ( node.quasis.every((element) => !element.value.raw.includes("\n")) && node.expressions.every(isChildSimple) ); } if (isObjectExpression(node)) { return node.properties.every( (p) => !p.computed && (p.shorthand || (p.value && isChildSimple(p.value))), ); } if (isArrayExpression(node)) { return node.elements.every((x) => x === null || isChildSimple(x)); } if (isCallLikeExpression(node)) { if ( node.type === "ImportExpression" || isSimpleCallArgument(node.callee, depth) ) { const args = getCallArguments(node); return args.length <= depth && args.every(isChildSimple); } return false; } if (isMemberExpression(node)) { return ( isSimpleCallArgument(node.object, depth) && isSimpleCallArgument(node.property, depth) ); } if ( (node.type === "UnaryExpression" && simpleCallArgumentUnaryOperators.has(node.operator)) || node.type === "UpdateExpression" ) { return isSimpleCallArgument(node.argument, depth); } return false; } /** * @param {any} options * @param {("es5" | "all")} [level] * @returns {boolean} */ function shouldPrintComma(options, level = "es5") { return ( (options.trailingComma === "es5" && level === "es5") || (options.trailingComma === "all" && (level === "all" || level === "es5")) ); } /** * Tests if the leftmost node of the expression matches the predicate. E.g., * used to check whether an expression statement needs to be wrapped in extra * parentheses because it starts with: * * - `{` * - `function`, `class`, or `do {}` * - `let[` * * Will be overzealous if there already are necessary grouping parentheses. * * @param {Estree.Node} node * @param {(leftmostNode: Estree.Node) => boolean} predicate * @returns {boolean} */ function startsWithNoLookaheadToken(node, predicate) { switch (node.type) { case "BinaryExpression": case "LogicalExpression": case "AssignmentExpression": case "NGPipeExpression": return startsWithNoLookaheadToken(node.left, predicate); case "MemberExpression": case "OptionalMemberExpression": return startsWithNoLookaheadToken(node.object, predicate); case "TaggedTemplateExpression": if (node.tag.type === "FunctionExpression") { // IIFEs are always already parenthesized return false; } return startsWithNoLookaheadToken(node.tag, predicate); case "CallExpression": case "OptionalCallExpression": if (node.callee.type === "FunctionExpression") { // IIFEs are always already parenthesized return false; } return startsWithNoLookaheadToken(node.callee, predicate); case "ConditionalExpression": return startsWithNoLookaheadToken(node.test, predicate); case "UpdateExpression": return ( !node.prefix && startsWithNoLookaheadToken(node.argument, predicate) ); case "BindExpression": return node.object && startsWithNoLookaheadToken(node.object, predicate); case "SequenceExpression": return startsWithNoLookaheadToken(node.expressions[0], predicate); case "ChainExpression": case "TSSatisfiesExpression": case "TSAsExpression": case "TSNonNullExpression": case "AsExpression": case "AsConstExpression": case "SatisfiesExpression": return startsWithNoLookaheadToken(node.expression, predicate); default: return predicate(node); } } const equalityOperators = { "==": true, "!=": true, "===": true, "!==": true, }; const multiplicativeOperators = { "*": true, "/": true, "%": true, }; const bitshiftOperators = { ">>": true, ">>>": true, "<<": true, }; function shouldFlatten(parentOp, nodeOp) { if (getPrecedence(nodeOp) !== getPrecedence(parentOp)) { return false; } // ** is right-associative // x ** y ** z --> x ** (y ** z) if (parentOp === "**") { return false; } // x == y == z --> (x == y) == z if (equalityOperators[parentOp] && equalityOperators[nodeOp]) { return false; } // x * y % z --> (x * y) % z if ( (nodeOp === "%" && multiplicativeOperators[parentOp]) || (parentOp === "%" && multiplicativeOperators[nodeOp]) ) { return false; } // x * y / z --> (x * y) / z // x / y * z --> (x / y) * z if ( nodeOp !== parentOp && multiplicativeOperators[nodeOp] && multiplicativeOperators[parentOp] ) { return false; } // x << y << z --> (x << y) << z if (bitshiftOperators[parentOp] && bitshiftOperators[nodeOp]) { return false; } return true; } const PRECEDENCE = new Map( [ ["|>"], ["??"], ["||"], ["&&"], ["|"], ["^"], ["&"], ["==", "===", "!=", "!=="], ["<", ">", "<=", ">=", "in", "instanceof"], [">>", "<<", ">>>"], ["+", "-"], ["*", "/", "%"], ["**"], ].flatMap((operators, index) => operators.map((operator) => [operator, index]), ), ); function getPrecedence(operator) { return PRECEDENCE.get(operator); } function isBitwiseOperator(operator) { return ( Boolean(bitshiftOperators[operator]) || operator === "|" || operator === "^" || operator === "&" ); } function hasRestParameter(node) { if (node.rest) { return true; } const parameters = getFunctionParameters(node); return parameters.at(-1)?.type === "RestElement"; } const functionParametersCache = new WeakMap(); function getFunctionParameters(node) { if (functionParametersCache.has(node)) { return functionParametersCache.get(node); } const parameters = []; if (node.this) { parameters.push(node.this); } parameters.push(...node.params); if (node.rest) { parameters.push(node.rest); } functionParametersCache.set(node, parameters); return parameters; } function iterateFunctionParametersPath(path, iteratee) { const { node } = path; let index = 0; const callback = () => iteratee(path, index++); if (node.this) { path.call(callback, "this"); } path.each(callback, "params"); if (node.rest) { path.call(callback, "rest"); } } const callArgumentsCache = new WeakMap(); function getCallArguments(node) { if (callArgumentsCache.has(node)) { return callArgumentsCache.get(node); } if (node.type === "ChainExpression") { return getCallArguments(node.expression); } let args; if (node.type === "ImportExpression" || node.type === "TSImportType") { args = [node.source]; if (node.options) { args.push(node.options); } } else if (node.type === "TSExternalModuleReference") { args = [node.expression]; } else { args = node.arguments; } callArgumentsCache.set(node, args); return args; } function iterateCallArgumentsPath(path, iteratee) { const { node } = path; if (node.type === "ChainExpression") { return path.call( () => iterateCallArgumentsPath(path, iteratee), "expression", ); } if (node.type === "ImportExpression" || node.type === "TSImportType") { path.call(() => iteratee(path, 0), "source"); if (node.options) { path.call(() => iteratee(path, 1), "options"); } } else if (node.type === "TSExternalModuleReference") { path.call(() => iteratee(path, 0), "expression"); } else { path.each(iteratee, "arguments"); } } function getCallArgumentSelector(node, index) { const selectors = []; if (node.type === "ChainExpression") { node = node.expression; selectors.push("expression"); } if (node.type === "ImportExpression" || node.type === "TSImportType") { if (index === 0 || index === (node.options ? -2 : -1)) { return [...selectors, "source"]; } if (node.options && (index === 1 || index === -1)) { return [...selectors, "options"]; } throw new RangeError("Invalid argument index"); } else if (node.type === "TSExternalModuleReference") { if (index === 0 || index === -1) { return [...selectors, "expression"]; } } else { if (index < 0) { index = node.arguments.length + index; } if (index >= 0 && index < node.arguments.length) { return [...selectors, "arguments", index]; } } /* c8 ignore next */ throw new RangeError("Invalid argument index"); } function isPrettierIgnoreComment(comment) { return comment.value.trim() === "prettier-ignore" && !comment.unignore; } function hasNodeIgnoreComment(node) { return ( node?.prettierIgnore || hasComment(node, CommentCheckFlags.PrettierIgnore) ); } /** @enum {number} */ const CommentCheckFlags = { /** Check comment is a leading comment */ Leading: 1 << 1, /** Check comment is a trailing comment */ Trailing: 1 << 2, /** Check comment is a dangling comment */ Dangling: 1 << 3, /** Check comment is a block comment */ Block: 1 << 4, /** Check comment is a line comment */ Line: 1 << 5, /** Check comment is a `prettier-ignore` comment */ PrettierIgnore: 1 << 6, /** Check comment is the first attached comment */ First: 1 << 7, /** Check comment is the last attached comment */ Last: 1 << 8, }; const getCommentTestFunction = (flags, fn) => { if (typeof flags === "function") { fn = flags; flags = 0; } if (flags || fn) { return (comment, index, comments) => !( (flags & CommentCheckFlags.Leading && !comment.leading) || (flags & CommentCheckFlags.Trailing && !comment.trailing) || (flags & CommentCheckFlags.Dangling && (comment.leading || comment.trailing)) || (flags & CommentCheckFlags.Block && !isBlockComment(comment)) || (flags & CommentCheckFlags.Line && !isLineComment(comment)) || (flags & CommentCheckFlags.First && index !== 0) || (flags & CommentCheckFlags.Last && index !== comments.length - 1) || (flags & CommentCheckFlags.PrettierIgnore && !isPrettierIgnoreComment(comment)) || (fn && !fn(comment)) ); } }; /** * @param {Estree.Node} node * @param {number | function} [flags] * @param {function} [fn] * @returns {boolean} */ function hasComment(node, flags, fn) { if (!isNonEmptyArray(node?.comments)) { return false; } const test = getCommentTestFunction(flags, fn); return test ? node.comments.some(test) : true; } /** * @param {Estree.Node} node * @param {number | function} [flags] * @param {function} [fn] * @returns {Estree.Comment[]} */ function getComments(node, flags, fn) { if (!Array.isArray(node?.comments)) { return []; } const test = getCommentTestFunction(flags, fn); return test ? node.comments.filter(test) : node.comments; } /** * @param {Estree.Node} node * @returns {boolean} */ const isNextLineEmpty = (node, { originalText }) => isNextLineEmptyAfterIndex(originalText, locEnd(node)); function isCallLikeExpression(node) { return ( isCallExpression(node) || node.type === "NewExpression" || node.type === "ImportExpression" ); } function isObjectProperty(node) { return ( node && (node.type === "ObjectProperty" || (node.type === "Property" && !isMethod(node))) ); } const isBinaryCastExpression = createTypeCheckFunction([ // TS "TSAsExpression", "TSSatisfiesExpression", // Flow "AsExpression", "AsConstExpression", "SatisfiesExpression", ]); const isUnionType = createTypeCheckFunction([ "TSUnionType", "UnionTypeAnnotation", ]); const isIntersectionType = createTypeCheckFunction([ "TSIntersectionType", "IntersectionTypeAnnotation", ]); const isConditionalType = createTypeCheckFunction([ "TSConditionalType", "ConditionalTypeAnnotation", ]); const isTsAsConstExpression = (node) => node?.type === "TSAsExpression" && node.typeAnnotation.type === "TSTypeReference" && node.typeAnnotation.typeName.type === "Identifier" && node.typeAnnotation.typeName.name === "const"; const isTypeAlias = createTypeCheckFunction([ "TSTypeAliasDeclaration", "TypeAlias", ]); function shouldUnionTypePrintOwnComments({ key, parent }) { if ( // If it's `types` of union type, parent will print comment for it (key === "types" && isUnionType(parent)) || // Inside intersection type let the comment print outside of parentheses (key === "types" && isIntersectionType(parent)) ) { return false; } return true; } /** @returns {boolean} */ function isBooleanTypeCoercion(node) { return ( node.type === "CallExpression" && !node.optional && node.arguments.length === 1 && node.callee.type === "Identifier" && node.callee.name === "Boolean" ); } export { CommentCheckFlags, createTypeCheckFunction, getCallArguments, getCallArgumentSelector, getComments, getFunctionParameters, getLeftSide, getLeftSidePathName, getPrecedence, hasComment, hasLeadingOwnLineComment, hasNakedLeftSide, hasNode, hasNodeIgnoreComment, hasRestParameter, isArrayExpression, isBinaryCastExpression, isBinaryish, isBitwiseOperator, isBooleanLiteral, isBooleanTypeCoercion, isCallExpression, isCallLikeExpression, isConditionalType, isExportDeclaration, isFlowObjectTypePropertyAFunction, isFunctionCompositionArgs, isFunctionOrArrowExpression, isIntersectionType, isJsxElement, isLiteral, isLoneShortArgument, isLongCurriedCallExpression, isMemberExpression, isMemberish, isMethod, isNextLineEmpty, isNullishCoalescing, isNumericLiteral, isObjectExpression, isObjectProperty, isObjectType, isPrettierIgnoreComment, isRegExpLiteral, isSignedNumericLiteral, isSimpleCallArgument, isSimpleExpressionByNodeCount, isSimpleType, isStringLiteral, isTemplateOnItsOwnLine, isTestCall, isTsAsConstExpression, isTypeAlias, isTypeAnnotationAFunction, isUnionType, iterateCallArgumentsPath, iterateFunctionParametersPath, needsHardlineAfterDanglingComment, shouldFlatten, shouldPrintComma, shouldUnionTypePrintOwnComments, startsWithNoLookaheadToken, }; export { default as isMeaningfulEmptyStatement } from "./is-meaningful-empty-statement.js"; export { default as isNodeMatches } from "./is-node-matches.js";