Files
test-repo/src/language-js/print/object.js
T

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 };