diff --git a/docs/content/rules/sort-classes.mdx b/docs/content/rules/sort-classes.mdx index a5878de75..f448126dd 100644 --- a/docs/content/rules/sort-classes.mdx +++ b/docs/content/rules/sort-classes.mdx @@ -223,18 +223,27 @@ Predefined groups are characterized by a single selector and potentially multipl - Modifiers: `protected`, `private`, `public`. - Example: `protected-constructor`, `private-constructor`, `public-constructor` or `constructor`. -#### Methods and accessors -- Method selectors: `get-method`, `set-method`, `method`. -- Accessors selector: `accessor-property`. -- Modifiers: `static`, `abstract`, `decorated`, `override`, `protected`, `private`, `public`. +#### Methods +- Selectors: `get-method`, `set-method`, `method`. +- Modifiers: `static`, `abstract`, `decorated`, `override`, `protected`, `private`, `public`, `optional`. - Example: `private-static-accessor-property`, `protected-abstract-override-method` or `static-get-method`. +The `optional` modifier is incompatible with the `get-method` and `set-method` selectors. + The `abstract` modifier is incompatible with the `static`, `private` and `decorated` modifiers. + `constructor`, `get-method` and `set-method` elements will also be matched as `method`. +#### Accessors +- Selector: `accessor-property`. +- Modifiers: `static`, `abstract`, `decorated`, `override`, `protected`, `private`, `public`. +- Example: `private-static-accessor-property`, `protected-abstract-override-method` or `static-get-method`. + +The `abstract` modifier is incompatible with the `static`, `private` and `decorated` modifiers. + #### Properties - Selectors: `function-property`, `property`. -- Modifiers: `static`, `declare`, `abstract`, `decorated`, `override`, `readonly`, `protected`, `private`, `public`. +- Modifiers: `static`, `declare`, `abstract`, `decorated`, `override`, `readonly`, `protected`, `private`, `public`, `optional`. - Example: `readonly-decorated-property`. The `abstract` modifier is incompatible with the `static`, `private` and `decorated` modifiers. diff --git a/rules/sort-classes.ts b/rules/sort-classes.ts index da4f6f5aa..054251d60 100644 --- a/rules/sort-classes.ts +++ b/rules/sort-classes.ts @@ -34,10 +34,12 @@ type OverrideModifier = 'override' type ReadonlyModifier = 'readonly' type DecoratedModifier = 'decorated' type DeclareModifier = 'declare' +type OptionalModifier = 'optional' export type Modifier = | ProtectedModifier | DecoratedModifier | AbstractModifier + | OptionalModifier | OverrideModifier | ReadonlyModifier | PrivateModifier @@ -72,6 +74,7 @@ type PublicOrProtectedOrPrivateModifierPrefix = WithDashSuffixOrEmpty< > type OverrideModifierPrefix = WithDashSuffixOrEmpty +type OptionalModifierPrefix = WithDashSuffixOrEmpty type ReadonlyModifierPrefix = WithDashSuffixOrEmpty type DecoratedModifierPrefix = WithDashSuffixOrEmpty type DeclareModifierPrefix = WithDashSuffixOrEmpty @@ -92,10 +95,12 @@ type ConstructorGroup = type FunctionPropertyGroup = `${PublicOrProtectedOrPrivateModifierPrefix}${StaticModifierPrefix}${OverrideModifierPrefix}${ReadonlyModifierPrefix}${DecoratedModifierPrefix}${FunctionPropertySelector}` type DeclarePropertyGroup = - `${DeclareModifierPrefix}${PublicOrProtectedOrPrivateModifierPrefix}${StaticOrAbstractModifierPrefix}${ReadonlyModifierPrefix}${PropertySelector}` + `${DeclareModifierPrefix}${PublicOrProtectedOrPrivateModifierPrefix}${StaticOrAbstractModifierPrefix}${ReadonlyModifierPrefix}${OptionalModifierPrefix}${PropertySelector}` type NonDeclarePropertyGroup = - `${PublicOrProtectedOrPrivateModifierPrefix}${StaticOrAbstractModifierPrefix}${OverrideModifierPrefix}${ReadonlyModifierPrefix}${DecoratedModifierPrefix}${PropertySelector}` -type MethodOrGetMethodOrSetMethodGroup = + `${PublicOrProtectedOrPrivateModifierPrefix}${StaticOrAbstractModifierPrefix}${OverrideModifierPrefix}${ReadonlyModifierPrefix}${DecoratedModifierPrefix}${OptionalModifierPrefix}${PropertySelector}` +type MethodGroup = + `${PublicOrProtectedOrPrivateModifierPrefix}${StaticOrAbstractModifierPrefix}${OverrideModifierPrefix}${DecoratedModifierPrefix}${OptionalModifierPrefix}${MethodSelector}` +type GetMethodOrSetMethodGroup = `${PublicOrProtectedOrPrivateModifierPrefix}${StaticOrAbstractModifierPrefix}${OverrideModifierPrefix}${DecoratedModifierPrefix}${MethodOrGetMethodOrSetMethodSelector}` type AccessorPropertyGroup = `${PublicOrProtectedOrPrivateModifierPrefix}${StaticOrAbstractModifierPrefix}${OverrideModifierPrefix}${DecoratedModifierPrefix}${AccessorPropertySelector}` @@ -109,7 +114,7 @@ type StaticBlockGroup = `${StaticBlockSelector}` * - abstract decorated X */ type Group = - | MethodOrGetMethodOrSetMethodGroup + | GetMethodOrSetMethodGroup | NonDeclarePropertyGroup | AccessorPropertyGroup | FunctionPropertyGroup @@ -117,6 +122,7 @@ type Group = | IndexSignatureGroup | ConstructorGroup | StaticBlockGroup + | MethodGroup | 'unknown' | string @@ -398,6 +404,10 @@ export default createEslintRule({ modifiers.push('public') } + if (member.optional) { + modifiers.push('optional') + } + if (member.kind === 'constructor') { selectors.push('constructor') } @@ -487,6 +497,10 @@ export default createEslintRule({ modifiers.push('public') } + if (member.optional) { + modifiers.push('optional') + } + if ( member.value?.type === 'ArrowFunctionExpression' || member.value?.type === 'FunctionExpression' diff --git a/test/sort-classes.test.ts b/test/sort-classes.test.ts index 35edd472e..486fc9b8a 100644 --- a/test/sort-classes.test.ts +++ b/test/sort-classes.test.ts @@ -181,6 +181,10 @@ describe(ruleName, () => { code: dedent` abstract class Class { + p?(): void; + + o?; + static {} static readonly [key: string]: string; @@ -252,6 +256,10 @@ describe(ruleName, () => { static readonly [key: string]: string; static {} + + o?; + + p?(): void; } `, options: [ @@ -274,10 +282,30 @@ describe(ruleName, () => { 'function-property', 'static-readonly-index-signature', 'static-block', + 'public-optional-property', + 'public-optional-method', ], }, ], errors: [ + { + messageId: 'unexpectedClassesGroupOrder', + data: { + left: 'p', + leftGroup: 'public-optional-method', + right: 'o', + rightGroup: 'public-optional-property', + }, + }, + { + messageId: 'unexpectedClassesGroupOrder', + data: { + left: 'o', + leftGroup: 'public-optional-property', + right: 'static', + rightGroup: 'static-block', + }, + }, { messageId: 'unexpectedClassesGroupOrder', data: { @@ -840,6 +868,55 @@ describe(ruleName, () => { ], }, ) + + ruleTester.run( + `${ruleName}(${type}): prioritize ${accessibilityModifier} accessibility over optional`, + rule, + { + valid: [], + invalid: [ + { + code: dedent` + export class Class { + + a: string; + + ${accessibilityModifier} z?(): string; + } + `, + output: dedent` + export class Class { + + ${accessibilityModifier} z?(): string; + + a: string; + } + `, + options: [ + { + ...options, + groups: [ + `${accessibilityModifier}-method`, + 'property', + 'optional-method', + ], + }, + ], + errors: [ + { + messageId: 'unexpectedClassesGroupOrder', + data: { + left: 'a', + leftGroup: 'property', + right: 'z', + rightGroup: `${accessibilityModifier}-method`, + }, + }, + ], + }, + ], + }, + ) } }) @@ -1461,6 +1538,55 @@ describe(ruleName, () => { ], }, ) + + ruleTester.run( + `${ruleName}(${type}): prioritize ${accessibilityModifier} accessibility over optional`, + rule, + { + valid: [], + invalid: [ + { + code: dedent` + export class Class { + + a(): void {} + + ${accessibilityModifier} z?: string; + } + `, + output: dedent` + export class Class { + + ${accessibilityModifier} z?: string; + + a(): void {} + } + `, + options: [ + { + ...options, + groups: [ + `${accessibilityModifier}-property`, + 'method', + 'optional-property', + ], + }, + ], + errors: [ + { + messageId: 'unexpectedClassesGroupOrder', + data: { + left: 'a', + leftGroup: 'method', + right: 'z', + rightGroup: `${accessibilityModifier}-property`, + }, + }, + ], + }, + ], + }, + ) } })