2025-09-24 08:24:17 +08:00
|
|
|
import * as assert from "#universal/assert";
|
2025-12-06 16:51:31 +08:00
|
|
|
import { group } from "../../document/index.js";
|
2026-01-28 12:36:39 +08:00
|
|
|
import { getCallArguments } from "../utilities/call-arguments.js";
|
2026-01-28 10:30:30 +08:00
|
|
|
import { CommentCheckFlags, hasComment } from "../utilities/comments.js";
|
2026-01-28 12:36:39 +08:00
|
|
|
import { getFunctionParameters } from "../utilities/function-parameters.js";
|
|
|
|
|
import { isMethod } from "../utilities/is-method.js";
|
2026-01-28 10:30:30 +08:00
|
|
|
import { isCallExpression } from "../utilities/node-types.js";
|
2022-02-21 15:53:02 +08:00
|
|
|
import {
|
2021-02-23 16:38:56 +02:00
|
|
|
printFunctionParameters,
|
2023-03-12 00:06:39 +09:00
|
|
|
shouldBreakFunctionParameters,
|
2023-12-17 19:34:12 +00:00
|
|
|
shouldGroupFunctionParameters,
|
2022-02-21 15:53:02 +08:00
|
|
|
} from "./function-parameters.js";
|
2026-01-21 10:08:06 +08:00
|
|
|
import { printKey } from "./key.js";
|
2025-12-06 14:39:00 +08:00
|
|
|
import { printDeclareToken } from "./miscellaneous.js";
|
2023-01-28 09:27:06 +08:00
|
|
|
import { printTypeAnnotationProperty } from "./type-annotation.js";
|
2022-02-21 15:53:02 +08:00
|
|
|
|
2023-04-27 12:01:35 +09:00
|
|
|
/**
|
2024-10-01 19:45:37 +08:00
|
|
|
* @import AstPath from "../../common/ast-path.js"
|
2025-11-03 01:52:26 +08:00
|
|
|
* @import {Doc} from "../../document/index.js"
|
2023-04-27 12:01:35 +09:00
|
|
|
*/
|
|
|
|
|
|
2024-02-03 20:50:44 +08:00
|
|
|
const isMethodValue = ({ node, key, parent }) =>
|
|
|
|
|
key === "value" &&
|
|
|
|
|
node.type === "FunctionExpression" &&
|
|
|
|
|
(parent.type === "ObjectMethod" ||
|
|
|
|
|
parent.type === "ClassMethod" ||
|
|
|
|
|
parent.type === "ClassPrivateMethod" ||
|
|
|
|
|
parent.type === "MethodDefinition" ||
|
|
|
|
|
parent.type === "TSAbstractMethodDefinition" ||
|
|
|
|
|
parent.type === "TSDeclareMethod" ||
|
|
|
|
|
(parent.type === "Property" && isMethod(parent)));
|
2023-01-25 11:03:02 +08:00
|
|
|
|
2023-01-14 10:53:24 +08:00
|
|
|
/*
|
2026-01-14 01:42:32 +08:00
|
|
|
- `FunctionDeclaration`
|
|
|
|
|
- `FunctionExpression`
|
2026-01-15 04:04:03 +08:00
|
|
|
- `HookDeclaration` (Flow)
|
2023-01-14 10:53:24 +08:00
|
|
|
- `TSDeclareFunction`(TypeScript)
|
|
|
|
|
*/
|
2025-04-09 11:07:46 +08:00
|
|
|
function printFunction(path, options, print, args) {
|
2023-01-25 11:03:02 +08:00
|
|
|
if (isMethodValue(path)) {
|
|
|
|
|
return printMethodValue(path, options, print);
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-11 18:39:06 +09:00
|
|
|
const { node } = path;
|
2021-05-27 18:00:31 +08:00
|
|
|
|
2026-01-18 02:08:47 +08:00
|
|
|
let shouldExpandParameters = false;
|
|
|
|
|
if (node.type === "FunctionExpression" && args?.expandLastArg) {
|
2022-10-18 14:23:13 +09:00
|
|
|
const { parent } = path;
|
2022-09-03 17:49:57 +03:00
|
|
|
if (
|
|
|
|
|
isCallExpression(parent) &&
|
|
|
|
|
(getCallArguments(parent).length > 1 ||
|
|
|
|
|
getFunctionParameters(node).every(
|
|
|
|
|
(param) => param.type === "Identifier" && !param.typeAnnotation,
|
|
|
|
|
))
|
|
|
|
|
) {
|
2026-01-18 02:08:47 +08:00
|
|
|
shouldExpandParameters = true;
|
2021-05-27 18:00:31 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-23 16:38:56 +02:00
|
|
|
const parametersDoc = printFunctionParameters(
|
|
|
|
|
path,
|
|
|
|
|
options,
|
2025-04-09 11:07:46 +08:00
|
|
|
print,
|
2026-01-18 02:08:47 +08:00
|
|
|
shouldExpandParameters,
|
2021-02-23 16:38:56 +02:00
|
|
|
);
|
2022-10-23 01:00:01 +03:00
|
|
|
const returnTypeDoc = printReturnType(path, print);
|
2021-03-11 04:14:08 +08:00
|
|
|
const shouldGroupParameters = shouldGroupFunctionParameters(
|
|
|
|
|
node,
|
|
|
|
|
returnTypeDoc,
|
|
|
|
|
);
|
2021-02-23 16:38:56 +02:00
|
|
|
|
2026-01-15 04:04:03 +08:00
|
|
|
const isFlowHookDeclaration = node.type === "HookDeclaration";
|
|
|
|
|
const keyword = isFlowHookDeclaration ? "hook" : "function";
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
printDeclareToken(path),
|
|
|
|
|
node.async ? "async " : "",
|
|
|
|
|
keyword,
|
|
|
|
|
node.generator ? "*" : "",
|
|
|
|
|
" ",
|
|
|
|
|
node.id ? print("id") : "",
|
2025-10-10 21:20:07 +08:00
|
|
|
print("typeParameters"),
|
2021-01-11 13:46:05 +08:00
|
|
|
group([
|
2021-02-23 16:38:56 +02:00
|
|
|
shouldGroupParameters ? group(parametersDoc) : parametersDoc,
|
|
|
|
|
returnTypeDoc,
|
2021-01-11 13:46:05 +08:00
|
|
|
]),
|
2021-03-11 04:14:08 +08:00
|
|
|
node.body ? " " : "",
|
2021-03-22 22:57:03 +08:00
|
|
|
print("body"),
|
2026-01-15 04:04:03 +08:00
|
|
|
options.semi && (node.declare || !node.body) ? ";" : "",
|
|
|
|
|
];
|
2020-11-19 23:27:58 +08:00
|
|
|
}
|
|
|
|
|
|
2023-01-25 11:03:02 +08:00
|
|
|
/*
|
2026-01-14 01:42:32 +08:00
|
|
|
- `FunctionDeclaration`
|
|
|
|
|
- `FunctionExpression`
|
|
|
|
|
- `TSDeclareFunction`(TypeScript)
|
2023-01-25 11:03:02 +08:00
|
|
|
- `ObjectMethod`
|
|
|
|
|
- `Property`
|
|
|
|
|
- `ObjectProperty`
|
|
|
|
|
- `ClassMethod`
|
|
|
|
|
- `ClassPrivateMethod`
|
|
|
|
|
- `MethodDefinition
|
|
|
|
|
- `TSAbstractMethodDefinition` (TypeScript)
|
|
|
|
|
- `TSDeclareMethod` (TypeScript)
|
|
|
|
|
*/
|
2020-11-19 23:27:58 +08:00
|
|
|
function printMethod(path, options, print) {
|
2022-09-11 18:39:06 +09:00
|
|
|
const { node } = path;
|
2020-11-19 23:27:58 +08:00
|
|
|
const { kind } = node;
|
|
|
|
|
const value = node.value || node;
|
|
|
|
|
const parts = [];
|
|
|
|
|
|
|
|
|
|
if (!kind || kind === "init" || kind === "method" || kind === "constructor") {
|
|
|
|
|
if (value.async) {
|
|
|
|
|
parts.push("async ");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
assert.ok(kind === "get" || kind === "set");
|
|
|
|
|
|
|
|
|
|
parts.push(kind, " ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A `getter`/`setter` can't be a generator, but it's recoverable
|
|
|
|
|
if (value.generator) {
|
|
|
|
|
parts.push("*");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parts.push(
|
2026-01-21 10:08:06 +08:00
|
|
|
printKey(path, options, print),
|
2025-05-28 03:26:56 +08:00
|
|
|
node.optional ? "?" : "",
|
2023-01-25 11:03:02 +08:00
|
|
|
node === value ? printMethodValue(path, options, print) : print("value"),
|
2020-11-19 23:27:58 +08:00
|
|
|
);
|
|
|
|
|
|
2021-01-11 13:46:05 +08:00
|
|
|
return parts;
|
2020-11-19 23:27:58 +08:00
|
|
|
}
|
|
|
|
|
|
2026-01-14 01:42:32 +08:00
|
|
|
/*
|
|
|
|
|
- `ObjectMethod`
|
|
|
|
|
- `Property`
|
|
|
|
|
- `ObjectProperty`
|
|
|
|
|
- `ClassMethod`
|
|
|
|
|
- `ClassPrivateMethod`
|
|
|
|
|
- `MethodDefinition
|
|
|
|
|
- `TSAbstractMethodDefinition` (TypeScript)
|
|
|
|
|
- `TSDeclareMethod` (TypeScript)
|
|
|
|
|
- `TSEmptyBodyFunctionExpression` (TypeScript)
|
|
|
|
|
*/
|
2023-01-25 11:03:02 +08:00
|
|
|
function printMethodValue(path, options, print) {
|
2022-09-11 18:39:06 +09:00
|
|
|
const { node } = path;
|
2025-04-09 11:07:46 +08:00
|
|
|
const parametersDoc = printFunctionParameters(path, options, print);
|
2022-10-23 01:00:01 +03:00
|
|
|
const returnTypeDoc = printReturnType(path, print);
|
2023-03-12 00:06:39 +09:00
|
|
|
const shouldBreakParameters = shouldBreakFunctionParameters(node);
|
2021-02-23 16:38:56 +02:00
|
|
|
const shouldGroupParameters = shouldGroupFunctionParameters(
|
|
|
|
|
node,
|
|
|
|
|
returnTypeDoc,
|
|
|
|
|
);
|
2020-11-19 23:27:58 +08:00
|
|
|
const parts = [
|
2025-10-10 21:20:07 +08:00
|
|
|
print("typeParameters"),
|
2021-01-11 13:46:05 +08:00
|
|
|
group([
|
2023-03-12 00:06:39 +09:00
|
|
|
shouldBreakParameters
|
|
|
|
|
? group(parametersDoc, { shouldBreak: true })
|
|
|
|
|
: shouldGroupParameters
|
|
|
|
|
? group(parametersDoc)
|
|
|
|
|
: parametersDoc,
|
2021-02-23 16:38:56 +02:00
|
|
|
returnTypeDoc,
|
2021-01-11 13:46:05 +08:00
|
|
|
]),
|
2020-11-19 23:27:58 +08:00
|
|
|
];
|
|
|
|
|
|
2021-02-23 16:38:56 +02:00
|
|
|
if (node.body) {
|
2021-03-22 22:57:03 +08:00
|
|
|
parts.push(" ", print("body"));
|
2020-11-19 23:27:58 +08:00
|
|
|
} else {
|
|
|
|
|
parts.push(options.semi ? ";" : "");
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-11 13:46:05 +08:00
|
|
|
return parts;
|
2020-11-19 23:27:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function canPrintParamsWithoutParens(node) {
|
|
|
|
|
const parameters = getFunctionParameters(node);
|
|
|
|
|
return (
|
|
|
|
|
parameters.length === 1 &&
|
|
|
|
|
!node.typeParameters &&
|
2020-11-25 22:14:27 +08:00
|
|
|
!hasComment(node, CommentCheckFlags.Dangling) &&
|
2020-11-19 23:27:58 +08:00
|
|
|
parameters[0].type === "Identifier" &&
|
|
|
|
|
!parameters[0].typeAnnotation &&
|
2020-11-25 22:14:27 +08:00
|
|
|
!hasComment(parameters[0]) &&
|
2020-11-19 23:27:58 +08:00
|
|
|
!parameters[0].optional &&
|
|
|
|
|
!node.predicate &&
|
|
|
|
|
!node.returnType
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function shouldPrintParamsWithoutParens(path, options) {
|
|
|
|
|
if (options.arrowParens === "always") {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options.arrowParens === "avoid") {
|
2022-09-11 18:39:06 +09:00
|
|
|
const { node } = path;
|
2020-11-19 23:27:58 +08:00
|
|
|
return canPrintParamsWithoutParens(node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fallback default; should be unreachable
|
2022-10-22 23:33:08 +08:00
|
|
|
/* c8 ignore next */
|
2020-11-19 23:27:58 +08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-25 10:39:51 +03:00
|
|
|
/** @returns {Doc} */
|
2022-10-23 01:00:01 +03:00
|
|
|
function printReturnType(path, print) {
|
2022-09-11 18:39:06 +09:00
|
|
|
const { node } = path;
|
2023-01-28 09:27:06 +08:00
|
|
|
const returnType = printTypeAnnotationProperty(path, print, "returnType");
|
2020-11-19 23:27:58 +08:00
|
|
|
|
|
|
|
|
const parts = [returnType];
|
|
|
|
|
|
2021-03-11 04:14:08 +08:00
|
|
|
if (node.predicate) {
|
2023-04-22 22:18:48 +08:00
|
|
|
parts.push(print("predicate"));
|
2020-11-19 23:27:58 +08:00
|
|
|
}
|
|
|
|
|
|
2021-01-11 13:46:05 +08:00
|
|
|
return parts;
|
2020-11-19 23:27:58 +08:00
|
|
|
}
|
|
|
|
|
|
2022-02-21 15:53:02 +08:00
|
|
|
export {
|
2021-05-27 18:00:31 +08:00
|
|
|
printFunction,
|
2020-11-19 23:27:58 +08:00
|
|
|
printMethod,
|
2023-01-25 11:03:02 +08:00
|
|
|
printMethodValue,
|
2023-04-27 12:01:35 +09:00
|
|
|
printReturnType,
|
2020-11-19 23:27:58 +08:00
|
|
|
shouldPrintParamsWithoutParens,
|
|
|
|
|
};
|