diff --git a/packages/eslint-plugin/docs/rules/naming-convention.md b/packages/eslint-plugin/docs/rules/naming-convention.md index d57bc1c69daf..3c0f83256315 100644 --- a/packages/eslint-plugin/docs/rules/naming-convention.md +++ b/packages/eslint-plugin/docs/rules/naming-convention.md @@ -154,21 +154,27 @@ If these are provided, the identifier must start with one of the provided values ### Selector Options -- `selector` (see "Allowed Selectors, Modifiers and Types" below). +- `selector` allows you to specify what types of identifiers to target. - Accepts one or array of selectors to define an option block that applies to one or multiple selectors. - For example, if you provide `{ selector: ['variable', 'function'] }`, then it will apply the same option to variable and function nodes. + - See [Allowed Selectors, Modifiers and Types](#allowed-selectors-modifiers-and-types) below for the complete list of allowed selectors. - `modifiers` allows you to specify which modifiers to granularly apply to, such as the accessibility (`private`/`public`/`protected`), or if the thing is `static`, etc. - The name must match _all_ of the modifiers. - For example, if you provide `{ modifiers: ['private', 'static', 'readonly'] }`, then it will only match something that is `private static readonly`, and something that is just `private` will not match. + - The following `modifiers` are allowed: + - `const` - matches a variable declared as being `const` (`const x = 1`). + - `public` - matches any member that is either explicitly declared as `public`, or has no visibility modifier (i.e. implicitly public). + - `readonly`, `static`, `abstract`, `protected`, `private` - matches any member explicitly declared with the given modifier. - `types` allows you to specify which types to match. This option supports simple, primitive types only (`boolean`, `string`, `number`, `array`, `function`). - The name must match _one_ of the types. - **_NOTE - Using this option will require that you lint with type information._** - For example, this lets you do things like enforce that `boolean` variables are prefixed with a verb. - - `boolean` matches any type assignable to `boolean | null | undefined` - - `string` matches any type assignable to `string | null | undefined` - - `number` matches any type assignable to `number | null | undefined` - - `array` matches any type assignable to `Array | null | undefined` - - `function` matches any type assignable to `Function | null | undefined` + - The following `types` are allowed: + - `boolean` matches any type assignable to `boolean | null | undefined` + - `string` matches any type assignable to `string | null | undefined` + - `number` matches any type assignable to `number | null | undefined` + - `array` matches any type assignable to `Array | null | undefined` + - `function` matches any type assignable to `Function | null | undefined` The ordering of selectors does not matter. The implementation will automatically sort the selectors to ensure they match from most-specific to least specific. It will keep checking selectors in that order until it finds one that matches the name. @@ -202,13 +208,25 @@ Individual Selectors match specific, well-defined sets. There is no overlap betw - `parameter` - matches any function parameter. Does not match parameter properties. - Allowed `modifiers`: none. - Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. -- `property` - matches any object, class, or object type property. Does not match properties that have direct function expression or arrow function expression values. +- `classProperty` - matches any class property. Does not match properties that have direct function expression or arrow function expression values. + - Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`. + - Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. +- `objectLiteralProperty` - matches any object literal property. Does not match properties that have direct function expression or arrow function expression values. + - Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`. + - Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. +- `typeProperty` - matches any object type property. Does not match properties that have direct function expression or arrow function expression values. - Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`. - Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. - `parameterProperty` - matches any parameter property. - Allowed `modifiers`: `private`, `protected`, `public`, `readonly`. - Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. -- `method` - matches any object, class, or object type method. Also matches properties that have direct function expression or arrow function expression values. Does not match accessors. +- `classMethod` - matches any class method. Also matches properties that have direct function expression or arrow function expression values. Does not match accessors. + - Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`. + - Allowed `types`: none. +- `objectLiteralMethod` - matches any object literal method. Also matches properties that have direct function expression or arrow function expression values. Does not match accessors. + - Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`. + - Allowed `types`: none. +- `typeMethod` - matches any object type method. Also matches properties that have direct function expression or arrow function expression values. Does not match accessors. - Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`. - Allowed `types`: none. - `accessor` - matches any accessor. @@ -249,6 +267,12 @@ Group Selectors are provided for convenience, and essentially bundle up sets of - `typeLike` - matches the same as `class`, `interface`, `typeAlias`, `enum`, `typeParameter`. - Allowed `modifiers`: `abstract`. - Allowed `types`: none. +- `property` - matches the same as `classProperty`, `objectLiteralProperty`, `typeProperty`. + - Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`. + - Allowed `types`: `boolean`, `string`, `number`, `function`, `array`. +- `method` - matches the same as `classMethod`, `objectLiteralMethod`, `typeMethod`. + - Allowed `modifiers`: `private`, `protected`, `public`, `static`, `readonly`, `abstract`. + - Allowed `types`: none. ## Examples diff --git a/packages/eslint-plugin/src/rules/naming-convention.ts b/packages/eslint-plugin/src/rules/naming-convention.ts index 295e8209e89b..9cc12967c9d2 100644 --- a/packages/eslint-plugin/src/rules/naming-convention.ts +++ b/packages/eslint-plugin/src/rules/naming-convention.ts @@ -41,21 +41,24 @@ enum Selectors { parameter = 1 << 2, // memberLike - property = 1 << 3, - parameterProperty = 1 << 4, - method = 1 << 5, - accessor = 1 << 6, - enumMember = 1 << 7, + parameterProperty = 1 << 3, + accessor = 1 << 4, + enumMember = 1 << 5, + classMethod = 1 << 6, + objectLiteralMethod = 1 << 7, + typeMethod = 1 << 8, + classProperty = 1 << 9, + objectLiteralProperty = 1 << 10, + typeProperty = 1 << 11, // typeLike - class = 1 << 8, - interface = 1 << 9, - typeAlias = 1 << 10, - enum = 1 << 11, - typeParameter = 1 << 12, + class = 1 << 12, + interface = 1 << 13, + typeAlias = 1 << 14, + enum = 1 << 15, + typeParameter = 1 << 17, } type SelectorsString = keyof typeof Selectors; -const SELECTOR_COUNT = util.getEnumNames(Selectors).length; enum MetaSelectors { default = -1, @@ -64,10 +67,14 @@ enum MetaSelectors { Selectors.function | Selectors.parameter, memberLike = 0 | - Selectors.property | + Selectors.classProperty | + Selectors.objectLiteralProperty | + Selectors.typeProperty | Selectors.parameterProperty | Selectors.enumMember | - Selectors.method | + Selectors.classMethod | + Selectors.objectLiteralMethod | + Selectors.typeMethod | Selectors.accessor, typeLike = 0 | Selectors.class | @@ -75,6 +82,14 @@ enum MetaSelectors { Selectors.typeAlias | Selectors.enum | Selectors.typeParameter, + method = 0 | + Selectors.classMethod | + Selectors.objectLiteralMethod | + Selectors.typeProperty, + property = 0 | + Selectors.classProperty | + Selectors.objectLiteralProperty | + Selectors.typeMethod, } type MetaSelectorsString = keyof typeof MetaSelectors; type IndividualAndMetaSelectorsString = SelectorsString | MetaSelectorsString; @@ -321,7 +336,23 @@ const SCHEMA: JSONSchema.JSONSchema4 = { 'readonly', 'abstract', ]), - ...selectorSchema('property', true, [ + ...selectorSchema('classProperty', true, [ + 'private', + 'protected', + 'public', + 'static', + 'readonly', + 'abstract', + ]), + ...selectorSchema('objectLiteralProperty', true, [ + 'private', + 'protected', + 'public', + 'static', + 'readonly', + 'abstract', + ]), + ...selectorSchema('typeProperty', true, [ 'private', 'protected', 'public', @@ -335,6 +366,36 @@ const SCHEMA: JSONSchema.JSONSchema4 = { 'public', 'readonly', ]), + ...selectorSchema('property', true, [ + 'private', + 'protected', + 'public', + 'static', + 'readonly', + 'abstract', + ]), + + ...selectorSchema('classMethod', false, [ + 'private', + 'protected', + 'public', + 'static', + 'abstract', + ]), + ...selectorSchema('objectLiteralMethod', false, [ + 'private', + 'protected', + 'public', + 'static', + 'abstract', + ]), + ...selectorSchema('typeMethod', false, [ + 'private', + 'protected', + 'public', + 'static', + 'abstract', + ]), ...selectorSchema('method', false, [ 'private', 'protected', @@ -584,7 +645,7 @@ export default util.createRule({ node: TSESTree.PropertyNonComputedName, ): void { const modifiers = new Set([Modifiers.public]); - handleMember(validators.property, node, modifiers); + handleMember(validators.objectLiteralProperty, node, modifiers); }, ':matches(ClassProperty, TSAbstractClassProperty)[computed = false][value.type != "ArrowFunctionExpression"][value.type != "FunctionExpression"][value.type != "TSEmptyBodyFunctionExpression"]'( @@ -593,7 +654,7 @@ export default util.createRule({ | TSESTree.TSAbstractClassPropertyNonComputedName, ): void { const modifiers = getMemberModifiers(node); - handleMember(validators.property, node, modifiers); + handleMember(validators.classProperty, node, modifiers); }, 'TSPropertySignature[computed = false]'( @@ -604,7 +665,7 @@ export default util.createRule({ modifiers.add(Modifiers.readonly); } - handleMember(validators.property, node, modifiers); + handleMember(validators.typeProperty, node, modifiers); }, // #endregion property @@ -615,14 +676,13 @@ export default util.createRule({ 'Property[computed = false][kind = "init"][value.type = "ArrowFunctionExpression"]', 'Property[computed = false][kind = "init"][value.type = "FunctionExpression"]', 'Property[computed = false][kind = "init"][value.type = "TSEmptyBodyFunctionExpression"]', - 'TSMethodSignature[computed = false]', ].join(', ')]( node: | TSESTree.PropertyNonComputedName | TSESTree.TSMethodSignatureNonComputedName, ): void { const modifiers = new Set([Modifiers.public]); - handleMember(validators.method, node, modifiers); + handleMember(validators.objectLiteralMethod, node, modifiers); }, [[ @@ -638,7 +698,14 @@ export default util.createRule({ | TSESTree.TSAbstractMethodDefinitionNonComputedName, ): void { const modifiers = getMemberModifiers(node); - handleMember(validators.method, node, modifiers); + handleMember(validators.classMethod, node, modifiers); + }, + + 'TSMethodSignature[computed = false]'( + node: TSESTree.TSMethodSignatureNonComputedName, + ): void { + const modifiers = new Set([Modifiers.public]); + handleMember(validators.typeMethod, node, modifiers); }, // #endregion method @@ -851,21 +918,20 @@ function createValidator( return b.modifierWeight - a.modifierWeight; } - /* - meta selectors will always be larger numbers than the normal selectors they contain, as they are the sum of all - of the selectors that they contain. - to give normal selectors a higher priority, shift them all SELECTOR_COUNT bits to the left before comparison, so - they are instead always guaranteed to be larger than the meta selectors. - */ - const aSelector = isMetaSelector(a.selector) - ? a.selector - : a.selector << SELECTOR_COUNT; - const bSelector = isMetaSelector(b.selector) - ? b.selector - : b.selector << SELECTOR_COUNT; + const aIsMeta = isMetaSelector(a.selector); + const bIsMeta = isMetaSelector(b.selector); + // non-meta selectors should go ahead of meta selectors + if (aIsMeta && !bIsMeta) { + return 1; + } + if (!aIsMeta && bIsMeta) { + return -1; + } + + // both aren't meta selectors // sort descending - the meta selectors are "least important" - return bSelector - aSelector; + return b.selector - a.selector; }); return ( @@ -1314,13 +1380,14 @@ function normalizeOption(option: Selector): NormalizedSelector[] { ? option.selector : [option.selector]; - const selectorsAllowedToHaveTypes: (Selectors | MetaSelectors)[] = [ - Selectors.variable, - Selectors.parameter, - Selectors.property, - Selectors.parameterProperty, - Selectors.accessor, - ]; + const selectorsAllowedToHaveTypes = + Selectors.variable | + Selectors.parameter | + Selectors.classProperty | + Selectors.objectLiteralProperty | + Selectors.typeProperty | + Selectors.parameterProperty | + Selectors.accessor; const config: NormalizedSelector[] = []; selectors @@ -1328,7 +1395,7 @@ function normalizeOption(option: Selector): NormalizedSelector[] { isMetaSelector(selector) ? MetaSelectors[selector] : Selectors[selector], ) .forEach(selector => - selectorsAllowedToHaveTypes.includes(selector) + (selectorsAllowedToHaveTypes & selector) !== 0 ? config.push({ selector: selector, ...normalizedOption }) : config.push({ selector: selector, diff --git a/packages/eslint-plugin/tests/rules/naming-convention.test.ts b/packages/eslint-plugin/tests/rules/naming-convention.test.ts index c57dfc984d47..adf93fa14905 100644 --- a/packages/eslint-plugin/tests/rules/naming-convention.test.ts +++ b/packages/eslint-plugin/tests/rules/naming-convention.test.ts @@ -214,7 +214,9 @@ function createInvalidTestCases( ...(selector !== 'default' && selector !== 'variableLike' && selector !== 'memberLike' && - selector !== 'typeLike' + selector !== 'typeLike' && + selector !== 'property' && + selector !== 'method' ? { data: { type: selectorTypeToMessageString(selector), @@ -444,12 +446,6 @@ const cases: Cases = [ // #region property { code: [ - 'const ignored = { % };', - 'const ignored = { "%": 1 };', - 'interface Ignored { % }', - 'interface Ignored { "%": string }', - 'type Ignored = { % }', - 'type Ignored = { "%": string }', 'class Ignored { private % }', 'class Ignored { private "%" = 1 }', 'class Ignored { private readonly % = 1 }', @@ -459,16 +455,24 @@ const cases: Cases = [ 'class Ignored { declare % }', ], options: { - selector: 'property', + selector: 'classProperty', + }, + }, + { + code: ['const ignored = { % };', 'const ignored = { "%": 1 };'], + options: { + selector: 'objectLiteralProperty', }, }, { code: [ - 'class Ignored { abstract private static readonly % = 1; ignoredDueToModifiers = 1; }', + 'interface Ignored { % }', + 'interface Ignored { "%": string }', + 'type Ignored = { % }', + 'type Ignored = { "%": string }', ], options: { - selector: 'property', - modifiers: ['static', 'readonly'], + selector: 'typeProperty', }, }, // #endregion property @@ -496,13 +500,6 @@ const cases: Cases = [ // #region method { code: [ - 'const ignored = { %() {} };', - 'const ignored = { "%"() {} };', - 'const ignored = { %: () => {} };', - 'interface Ignored { %(): string }', - 'interface Ignored { "%"(): string }', - 'type Ignored = { %(): string }', - 'type Ignored = { "%"(): string }', 'class Ignored { private %() {} }', 'class Ignored { private "%"() {} }', 'class Ignored { private readonly %() {} }', @@ -513,16 +510,28 @@ const cases: Cases = [ 'class Ignored { declare %() }', ], options: { - selector: 'method', + selector: 'classMethod', + }, + }, + { + code: [ + 'const ignored = { %() {} };', + 'const ignored = { "%"() {} };', + 'const ignored = { %: () => {} };', + ], + options: { + selector: 'objectLiteralMethod', }, }, { code: [ - 'class Ignored { abstract private static %() {}; ignoredDueToModifiers() {}; }', + 'interface Ignored { %(): string }', + 'interface Ignored { "%"(): string }', + 'type Ignored = { %(): string }', + 'type Ignored = { "%"(): string }', ], options: { - selector: 'method', - modifiers: ['abstract', 'static'], + selector: 'typeMethod', }, }, // #endregion method @@ -540,15 +549,6 @@ const cases: Cases = [ selector: 'accessor', }, }, - { - code: [ - 'class Ignored { private static get %() {}; get ignoredDueToModifiers() {}; }', - ], - options: { - selector: 'accessor', - modifiers: ['private', 'static'], - }, - }, // #endregion accessor // #region enumMember @@ -567,13 +567,6 @@ const cases: Cases = [ selector: 'class', }, }, - { - code: ['abstract class % {}; class ignoredDueToModifier {}'], - options: { - selector: 'class', - modifiers: ['abstract'], - }, - }, // #endregion class // #region interface @@ -914,12 +907,103 @@ ruleTester.run('naming-convention', rule, { export const { OtherConstant: otherConstant } = SomeClass; `, - parserOptions, options: [ { selector: 'property', format: ['PascalCase'] }, { selector: 'variable', format: ['camelCase'] }, ], }, + { + code: ` + class Ignored { + private static abstract readonly some_name = 1; + IgnoredDueToModifiers = 1; + } + `, + options: [ + { + selector: 'default', + format: ['PascalCase'], + }, + { + selector: 'classProperty', + format: ['snake_case'], + modifiers: ['static', 'readonly'], + }, + ], + }, + { + code: ` + class Ignored { + constructor(private readonly some_name, IgnoredDueToModifiers) {} + } + `, + options: [ + { + selector: 'default', + format: ['PascalCase'], + }, + { + selector: 'parameterProperty', + format: ['snake_case'], + modifiers: ['readonly'], + }, + ], + }, + { + code: ` + class Ignored { + private static abstract some_name() {} + IgnoredDueToModifiers() {} + } + `, + options: [ + { + selector: 'default', + format: ['PascalCase'], + }, + { + selector: 'classMethod', + format: ['snake_case'], + modifiers: ['abstract', 'static'], + }, + ], + }, + { + code: ` + class Ignored { + private static get some_name() {} + get IgnoredDueToModifiers() {} + } + `, + options: [ + { + selector: 'default', + format: ['PascalCase'], + }, + { + selector: 'accessor', + format: ['snake_case'], + modifiers: ['private', 'static'], + }, + ], + }, + { + code: ` + abstract class some_name {} + class IgnoredDueToModifier {} + `, + options: [ + { + selector: 'default', + format: ['PascalCase'], + }, + { + selector: 'class', + format: ['snake_case'], + modifiers: ['abstract'], + }, + ], + }, ], invalid: [ { @@ -1239,8 +1323,7 @@ ruleTester.run('naming-convention', rule, { line: 3, messageId: 'doesNotMatchFormat', data: { - // eslint-disable-next-line @typescript-eslint/internal/prefer-ast-types-enum - type: 'Property', + type: 'Object Literal Property', name: 'Property Name', formats: 'strictCamelCase', }, @@ -1331,5 +1414,102 @@ ruleTester.run('naming-convention', rule, { }, ], }, + { + code: ` + class Ignored { + private static abstract readonly some_name = 1; + IgnoredDueToModifiers = 1; + } + `, + options: [ + { + selector: 'default', + format: ['PascalCase'], + }, + { + selector: 'classProperty', + format: ['UPPER_CASE'], + modifiers: ['static', 'readonly'], + }, + ], + errors: [{ messageId: 'doesNotMatchFormat' }], + }, + { + code: ` + class Ignored { + constructor(private readonly some_name, IgnoredDueToModifiers) {} + } + `, + options: [ + { + selector: 'default', + format: ['PascalCase'], + }, + { + selector: 'parameterProperty', + format: ['UPPER_CASE'], + modifiers: ['readonly'], + }, + ], + errors: [{ messageId: 'doesNotMatchFormat' }], + }, + { + code: ` + class Ignored { + private static abstract some_name() {} + IgnoredDueToModifiers() {} + } + `, + options: [ + { + selector: 'default', + format: ['PascalCase'], + }, + { + selector: 'classMethod', + format: ['UPPER_CASE'], + modifiers: ['abstract', 'static'], + }, + ], + errors: [{ messageId: 'doesNotMatchFormat' }], + }, + { + code: ` + class Ignored { + private static get some_name() {} + get IgnoredDueToModifiers() {} + } + `, + options: [ + { + selector: 'default', + format: ['PascalCase'], + }, + { + selector: 'accessor', + format: ['UPPER_CASE'], + modifiers: ['private', 'static'], + }, + ], + errors: [{ messageId: 'doesNotMatchFormat' }], + }, + { + code: ` + abstract class some_name {} + class IgnoredDueToModifier {} + `, + options: [ + { + selector: 'default', + format: ['PascalCase'], + }, + { + selector: 'class', + format: ['UPPER_CASE'], + modifiers: ['abstract'], + }, + ], + errors: [{ messageId: 'doesNotMatchFormat' }], + }, ], }); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy