Improve super class (or single heritage) breaking (#18325)

This commit is contained in:
fisker Cheung authored and GitHub committed 2025-11-26 19:43:08 +08:00
1 parent bbdec54a41
commit 6ba133e80c
10 files changed
+337 -12

No files matched your search

+15
View File
@@ -0,0 +1,15 @@
#### Improve super class format (#18325 by @fisker)
<!-- prettier-ignore -->
```jsx
// Input
class EnsureNoDisposablesAreLeakedInTestSuiteSuite extends eslint.Rule.RuleModule {};
// Prettier stable
class EnsureNoDisposablesAreLeakedInTestSuiteSuite extends eslint.Rule
.RuleModule {}
// Prettier main
class EnsureNoDisposablesAreLeakedInTestSuiteSuite
extends eslint.Rule.RuleModule {}
```
+65 -12
View File
@@ -18,6 +18,7 @@ import {
CommentCheckFlags, CommentCheckFlags,
createTypeCheckFunction, createTypeCheckFunction,
hasComment, hasComment,
isMemberExpression,
} from "../utils/index.js"; } from "../utils/index.js";
import { printAssignment } from "./assignment.js"; import { printAssignment } from "./assignment.js";
import { printClassMemberDecorators } from "./decorators.js"; import { printClassMemberDecorators } from "./decorators.js";
@@ -67,11 +68,7 @@ function printClass(path, options, print) {
// Keep old behaviour of extends in same line // Keep old behaviour of extends in same line
// If there is only on extends and there are not comments // If there is only on extends and there are not comments
const groupMode = const groupMode = shouldPrintClassInGroupMode(path);
hasComment(node.id, CommentCheckFlags.Trailing) ||
hasComment(node.typeParameters, CommentCheckFlags.Trailing) ||
hasComment(node.superClass) ||
hasMultipleHeritage(node);
const partsGroup = []; const partsGroup = [];
const extendsParts = []; const extendsParts = [];
@@ -110,14 +107,12 @@ function printClass(path, options, print) {
extendsParts.push(" ", printedWithComments); extendsParts.push(" ", printedWithComments);
} }
} else { } else {
extendsParts.push( extendsParts.push(printHeritageClauses(path, options, print, "extends"));
printHeritageClauses(path, options, print, "extends", groupMode),
);
} }
extendsParts.push( extendsParts.push(
printHeritageClauses(path, options, print, "mixins", groupMode), printHeritageClauses(path, options, print, "mixins"),
printHeritageClauses(path, options, print, "implements", groupMode), printHeritageClauses(path, options, print, "implements"),
); );
let heritageGroupId; let heritageGroupId;
@@ -170,7 +165,65 @@ function hasMultipleHeritage(node) {
return count > 1; return count > 1;
} }
function printHeritageClauses(path, options, print, listName, groupMode) { /**
@returns {boolean}
*/
function shouldPrintClassInGroupModeWithoutCache(path) {
const { node } = path;
if (
hasComment(node.id, CommentCheckFlags.Trailing) ||
hasComment(node.typeParameters, CommentCheckFlags.Trailing) ||
hasComment(node.superClass) ||
hasMultipleHeritage(node)
) {
return true;
}
if (node.superClass) {
if (path.parent.type === "AssignmentExpression") {
return false;
}
const superTypeArguments =
node.superTypeArguments ?? node.superTypeParameters;
return !superTypeArguments && isMemberExpression(node.superClass);
}
const heritage =
node.extends?.[0] ?? node.mixins?.[0] ?? node.implements?.[0];
if (!heritage) {
return false;
}
const groupMode =
// `ClassImplements` seem not allow `QualifiedTypeIdentifier`
(heritage.type === "InterfaceExtends" &&
heritage.id.type === "QualifiedTypeIdentifier" &&
!heritage.typeParameters) ||
((heritage.type === "TSClassImplements" ||
heritage.type === "TSInterfaceHeritage") &&
isMemberExpression(heritage.expression) &&
!heritage.typeArguments);
return groupMode;
}
const shouldPrintClassInGroupModeCache = new WeakMap();
function shouldPrintClassInGroupMode(path) {
const { node } = path;
if (!shouldPrintClassInGroupModeCache.has(node)) {
shouldPrintClassInGroupModeCache.set(
node,
shouldPrintClassInGroupModeWithoutCache(path),
);
}
return shouldPrintClassInGroupModeCache.get(node);
}
function printHeritageClauses(path, options, print, listName) {
const { node } = path; const { node } = path;
if (!isNonEmptyArray(node[listName])) { if (!isNonEmptyArray(node[listName])) {
return ""; return "";
@@ -189,7 +242,7 @@ function printHeritageClauses(path, options, print, listName, groupMode) {
printedLeadingComments, printedLeadingComments,
heritageClausesDoc, heritageClausesDoc,
]; ];
if (groupMode) { if (shouldPrintClassInGroupMode(path)) {
return [line, group(printed)]; return [line, group(printed)];
} }
return [" ", printed]; return [" ", printed];
@@ -0,0 +1,98 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`member-expression-like.js format 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
| printWidth
=====================================input======================================
class A_long_long_long_long_long_long_long_long_name1
extends eslint.Rule.RuleModule {}
class Short
extends eslint.Rule.RuleModule {}
class A_long_long_long_long_long_long_long_long_name12
extends eslint.Rule?.RuleModule {}
interface A_long_long_long_long_long_long_long_long_name2
extends eslint.Rule.RuleModule {}
declare class A_long_long_long_long_long_long_long_long_name2
mixins eslint.Rule.RuleModule {}
declare class A_long_long_long_long_long_long_long_long_name3
extends eslint.Rule.RuleModule
mixins eslint.Rule.RuleModule {}
=====================================output=====================================
class A_long_long_long_long_long_long_long_long_name1
extends eslint.Rule.RuleModule {}
class Short extends eslint.Rule.RuleModule {}
class A_long_long_long_long_long_long_long_long_name12
extends eslint.Rule?.RuleModule {}
interface A_long_long_long_long_long_long_long_long_name2
extends eslint.Rule.RuleModule {}
declare class A_long_long_long_long_long_long_long_long_name2
mixins eslint.Rule.RuleModule {}
declare class A_long_long_long_long_long_long_long_long_name3
extends eslint.Rule.RuleModule
mixins eslint.Rule.RuleModule {}
================================================================================
`;
exports[`type-arguments.js format 1`] = `
====================================options=====================================
parsers: ["flow"]
printWidth: 80
| printWidth
=====================================input======================================
class CommentBox extends React.Component<
{ url: string; pollInterval: number },
CommentData
> {}
interface CommentBox extends React.Component<
{ url: string; pollInterval: number },
CommentData
> {}
declare class CommentBox extends React.Component<
{ url: string; pollInterval: number },
CommentData
> {}
declare class CommentBox mixins React.Component<
{ url: string; pollInterval: number },
CommentData
> {}
=====================================output=====================================
class CommentBox extends React.Component<
{ url: string, pollInterval: number },
CommentData,
> {}
interface CommentBox extends React.Component<
{ url: string, pollInterval: number },
CommentData,
> {}
declare class CommentBox extends React.Component<
{ url: string, pollInterval: number },
CommentData,
> {}
declare class CommentBox mixins React.Component<
{ url: string, pollInterval: number },
CommentData,
> {}
================================================================================
`;
@@ -0,0 +1 @@
runFormatTest(import.meta, ["flow"]);
@@ -0,0 +1,18 @@
class A_long_long_long_long_long_long_long_long_name1
extends eslint.Rule.RuleModule {}
class Short
extends eslint.Rule.RuleModule {}
class A_long_long_long_long_long_long_long_long_name12
extends eslint.Rule?.RuleModule {}
interface A_long_long_long_long_long_long_long_long_name2
extends eslint.Rule.RuleModule {}
declare class A_long_long_long_long_long_long_long_long_name2
mixins eslint.Rule.RuleModule {}
declare class A_long_long_long_long_long_long_long_long_name3
extends eslint.Rule.RuleModule
mixins eslint.Rule.RuleModule {}
@@ -0,0 +1,19 @@
class CommentBox extends React.Component<
{ url: string; pollInterval: number },
CommentData
> {}
interface CommentBox extends React.Component<
{ url: string; pollInterval: number },
CommentData
> {}
declare class CommentBox extends React.Component<
{ url: string; pollInterval: number },
CommentData
> {}
declare class CommentBox mixins React.Component<
{ url: string; pollInterval: number },
CommentData
> {}
@@ -0,0 +1,88 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`member-expression-like.ts format 1`] = `
====================================options=====================================
parsers: ["typescript"]
printWidth: 80
| printWidth
=====================================input======================================
class A_long_long_long_long_long_long_long_long_name1
extends eslint.Rule.RuleModule {}
class Short
extends eslint.Rule.RuleModule {}
class A_long_long_long_long_long_long_long_long_name12
extends eslint.Rule?.RuleModule {}
interface A_long_long_long_long_long_long_long_long_name2
extends eslint.Rule.RuleModule {}
class A_long_long_long_long_long_long_long_long_name2
implements eslint.Rule.RuleModule {}
class A_long_long_long_long_long_long_long_long_name3
extends eslint.Rule.RuleModule
implements eslint.Rule.RuleModule {}
=====================================output=====================================
class A_long_long_long_long_long_long_long_long_name1
extends eslint.Rule.RuleModule {}
class Short extends eslint.Rule.RuleModule {}
class A_long_long_long_long_long_long_long_long_name12
extends eslint.Rule?.RuleModule {}
interface A_long_long_long_long_long_long_long_long_name2
extends eslint.Rule.RuleModule {}
class A_long_long_long_long_long_long_long_long_name2
implements eslint.Rule.RuleModule {}
class A_long_long_long_long_long_long_long_long_name3
extends eslint.Rule.RuleModule
implements eslint.Rule.RuleModule {}
================================================================================
`;
exports[`type-arguments.ts format 1`] = `
====================================options=====================================
parsers: ["typescript"]
printWidth: 80
| printWidth
=====================================input======================================
class CommentBox extends React.Component<
{ url: string; pollInterval: number },
CommentData
> {}
interface CommentBox extends React.Component<
{ url: string; pollInterval: number },
CommentData
> {}
declare class CommentBox implements React.Component<
{ url: string; pollInterval: number },
CommentData
> {}
=====================================output=====================================
class CommentBox extends React.Component<
{ url: string; pollInterval: number },
CommentData
> {}
interface CommentBox extends React.Component<
{ url: string; pollInterval: number },
CommentData
> {}
declare class CommentBox implements React.Component<
{ url: string; pollInterval: number },
CommentData
> {}
================================================================================
`;
@@ -0,0 +1 @@
runFormatTest(import.meta, ["typescript"]);
@@ -0,0 +1,18 @@
class A_long_long_long_long_long_long_long_long_name1
extends eslint.Rule.RuleModule {}
class Short
extends eslint.Rule.RuleModule {}
class A_long_long_long_long_long_long_long_long_name12
extends eslint.Rule?.RuleModule {}
interface A_long_long_long_long_long_long_long_long_name2
extends eslint.Rule.RuleModule {}
class A_long_long_long_long_long_long_long_long_name2
implements eslint.Rule.RuleModule {}
class A_long_long_long_long_long_long_long_long_name3
extends eslint.Rule.RuleModule
implements eslint.Rule.RuleModule {}
@@ -0,0 +1,14 @@
class CommentBox extends React.Component<
{ url: string; pollInterval: number },
CommentData
> {}
interface CommentBox extends React.Component<
{ url: string; pollInterval: number },
CommentData
> {}
declare class CommentBox implements React.Component<
{ url: string; pollInterval: number },
CommentData
> {}