224 lines
6.5 KiB
JavaScript
224 lines
6.5 KiB
JavaScript
import * as assert from "#universal/assert";
|
|
import {
|
|
group,
|
|
hardline,
|
|
indent,
|
|
line,
|
|
softline,
|
|
} from "../../document/index.js";
|
|
import { printDanglingComments } from "../../main/comments/print.js";
|
|
import hasNewline from "../../utilities/has-newline.js";
|
|
import hasNewlineInRange from "../../utilities/has-newline-in-range.js";
|
|
import isNonEmptyArray from "../../utilities/is-non-empty-array.js";
|
|
import { locEnd, locStart } from "../location/index.js";
|
|
import {
|
|
CommentCheckFlags,
|
|
getComments,
|
|
hasComment,
|
|
} from "../utilities/comments.js";
|
|
import { createTypeCheckFunction } from "../utilities/create-type-check-function.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,
|
|
printOptionalToken,
|
|
printTrailingComma,
|
|
} from "./miscellaneous.js";
|
|
import { printTypeAnnotationProperty } from "./type-annotation.js";
|
|
|
|
/** @import {Doc} from "../../document/index.js" */
|
|
|
|
const isPrintingImportAttributes = createTypeCheckFunction([
|
|
"ImportDeclaration",
|
|
"ExportDefaultDeclaration",
|
|
"ExportNamedDeclaration",
|
|
"ExportAllDeclaration",
|
|
"DeclareExportDeclaration",
|
|
"DeclareExportAllDeclaration",
|
|
]);
|
|
|
|
const isPrintingFlowEnumBody = createTypeCheckFunction([
|
|
"EnumBody",
|
|
"EnumBooleanBody",
|
|
"EnumNumberBody",
|
|
"EnumBigIntBody",
|
|
"EnumStringBody",
|
|
"EnumSymbolBody",
|
|
]);
|
|
|
|
/*
|
|
- `ObjectExpression`
|
|
- `ObjectPattern`
|
|
- `ImportDeclaration`
|
|
- `ExportDefaultDeclaration`
|
|
- `ExportNamedDeclaration`
|
|
- `ExportAllDeclaration`
|
|
- `EnumBody` (Flow)
|
|
- `EnumBooleanBody` (Flow, removed)
|
|
- `EnumNumberBody` (Flow, removed)
|
|
- `EnumBigIntBody` (Flow, removed)
|
|
- `EnumStringBody` (Flow, removed)
|
|
- `EnumSymbolBody` (Flow, removed)
|
|
- `DeclareExportDeclaration` (Flow)
|
|
- `DeclareExportAllDeclaration` (Flow)
|
|
- `TSEnumDeclaration`(TypeScript)
|
|
*/
|
|
function printObject(path, options, print) {
|
|
const { node, parent } = path;
|
|
|
|
const isFlowEnumBody = isPrintingFlowEnumBody(node);
|
|
const isEnumBody = node.type === "TSEnumBody" || isFlowEnumBody;
|
|
const isImportAttributes = isPrintingImportAttributes(node);
|
|
const hasUnknownMembers = isFlowEnumBody && node.hasUnknownMembers;
|
|
|
|
const property = isEnumBody
|
|
? "members"
|
|
: isImportAttributes
|
|
? "attributes"
|
|
: "properties";
|
|
const children = node[property];
|
|
|
|
const shouldBreak =
|
|
isEnumBody ||
|
|
(node.type === "ObjectPattern" &&
|
|
parent.type !== "FunctionDeclaration" &&
|
|
parent.type !== "FunctionExpression" &&
|
|
parent.type !== "ArrowFunctionExpression" &&
|
|
parent.type !== "ObjectMethod" &&
|
|
parent.type !== "ClassMethod" &&
|
|
parent.type !== "ClassPrivateMethod" &&
|
|
parent.type !== "AssignmentPattern" &&
|
|
parent.type !== "CatchClause" &&
|
|
node.properties.some(
|
|
(property) =>
|
|
property.value &&
|
|
(property.value.type === "ObjectPattern" ||
|
|
property.value.type === "ArrayPattern"),
|
|
)) ||
|
|
(node.type !== "ObjectPattern" &&
|
|
options.objectWrap === "preserve" &&
|
|
children.length > 0 &&
|
|
hasNewLineAfterOpeningBrace(node, children[0], options));
|
|
|
|
/** @type {Doc[]} */
|
|
let separatorParts = [];
|
|
const parts = path.map(({ node }) => {
|
|
const result = [...separatorParts, print()];
|
|
separatorParts = [",", line];
|
|
if (isNextLineEmpty(node, options)) {
|
|
separatorParts.push(hardline);
|
|
}
|
|
return result;
|
|
}, property);
|
|
|
|
if (hasUnknownMembers) {
|
|
let printed;
|
|
if (hasComment(node, CommentCheckFlags.Dangling)) {
|
|
const hasLineComments = hasComment(node, CommentCheckFlags.Line);
|
|
const printedDanglingComments = printDanglingComments(path, options);
|
|
printed = [
|
|
printedDanglingComments,
|
|
hasLineComments ||
|
|
hasNewline(options.originalText, locEnd(getComments(node).at(-1)))
|
|
? hardline
|
|
: line,
|
|
"...",
|
|
];
|
|
} else {
|
|
printed = ["..."];
|
|
}
|
|
parts.push([...separatorParts, ...printed]);
|
|
}
|
|
|
|
const canHaveTrailingSeparator = !(
|
|
hasUnknownMembers || children.at(-1)?.type === "RestElement"
|
|
);
|
|
|
|
let content;
|
|
if (parts.length === 0) {
|
|
content = group([
|
|
"{",
|
|
printDanglingCommentsInList(path, options),
|
|
"}",
|
|
printOptionalToken(path),
|
|
printTypeAnnotationProperty(path, print),
|
|
]);
|
|
} else {
|
|
const spacing = options.bracketSpacing ? line : softline;
|
|
content = [
|
|
"{",
|
|
indent([spacing, ...parts]),
|
|
canHaveTrailingSeparator ? printTrailingComma(options) : "",
|
|
spacing,
|
|
"}",
|
|
printOptionalToken(path),
|
|
printTypeAnnotationProperty(path, print),
|
|
];
|
|
}
|
|
|
|
// If we inline the object as first argument of the parent, we don't want
|
|
// to create another group so that the object breaks before the return
|
|
// type
|
|
if (
|
|
path.match(
|
|
(node) =>
|
|
node.type === "ObjectPattern" && !isNonEmptyArray(node.decorators),
|
|
shouldHugTheOnlyParameter,
|
|
) ||
|
|
(isObjectType(node) &&
|
|
(path.match(
|
|
undefined,
|
|
(node, name) => name === "typeAnnotation",
|
|
(node, name) => name === "typeAnnotation",
|
|
shouldHugTheOnlyParameter,
|
|
) ||
|
|
path.match(
|
|
undefined,
|
|
(node, name) =>
|
|
node.type === "FunctionTypeParam" && name === "typeAnnotation",
|
|
shouldHugTheOnlyParameter,
|
|
))) ||
|
|
// Assignment printing logic (printAssignment) is responsible
|
|
// for adding a group if needed
|
|
(!shouldBreak &&
|
|
path.match(
|
|
(node) => node.type === "ObjectPattern",
|
|
(node) =>
|
|
node.type === "AssignmentExpression" ||
|
|
node.type === "VariableDeclarator",
|
|
))
|
|
) {
|
|
return content;
|
|
}
|
|
|
|
return group(content, { shouldBreak });
|
|
}
|
|
|
|
function hasNewLineAfterOpeningBrace(node, firstProperty, options) {
|
|
const text = options.originalText;
|
|
const firstPropertyStart = locStart(firstProperty);
|
|
|
|
let openingBraceIndex = locStart(node);
|
|
if (isPrintingImportAttributes(node)) {
|
|
openingBraceIndex = stripComments(options).lastIndexOf(
|
|
"{",
|
|
firstPropertyStart,
|
|
);
|
|
/* c8 ignore next 3 */
|
|
if (process.env.NODE_ENV !== "production") {
|
|
assert.ok(openingBraceIndex !== -1);
|
|
}
|
|
}
|
|
|
|
/* c8 ignore next 3 */
|
|
if (process.env.NODE_ENV !== "production") {
|
|
assert.equal(text.charAt(openingBraceIndex), "{");
|
|
}
|
|
|
|
return hasNewlineInRange(text, openingBraceIndex, firstPropertyStart);
|
|
}
|
|
|
|
export { printObject };
|