From 61eb43473a108c3ce067982f1bb1ff5af3e254aa Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Tue, 14 Jan 2020 11:58:02 -0800 Subject: [PATCH] feat(eslint-plugin): [naming-convention] allow not check format (#1455) --- .../docs/rules/naming-convention.md | 24 ++++++++----- .../src/rules/naming-convention.ts | 34 +++++++++++-------- .../tests/rules/naming-convention.test.ts | 28 +++++++++++++++ 3 files changed, 62 insertions(+), 24 deletions(-) diff --git a/packages/eslint-plugin/docs/rules/naming-convention.md b/packages/eslint-plugin/docs/rules/naming-convention.md index d49d8f26a2e..5ede1186e99 100644 --- a/packages/eslint-plugin/docs/rules/naming-convention.md +++ b/packages/eslint-plugin/docs/rules/naming-convention.md @@ -19,14 +19,16 @@ Each property will be described in detail below. Also see the examples section b ```ts type Options = { // format options - format: ( - | 'camelCase' - | 'strictCamelCase' - | 'PascalCase' - | 'StrictPascalCase' - | 'snake_case' - | 'UPPER_CASE' - )[]; + format: + | ( + | 'camelCase' + | 'strictCamelCase' + | 'PascalCase' + | 'StrictPascalCase' + | 'snake_case' + | 'UPPER_CASE' + )[] + | null; custom?: { regex: string; match: boolean; @@ -79,7 +81,7 @@ When the format of an identifier is checked, it is checked in the following orde 1. validate custom 1. validate format -At each step, if the identifier matches the option, the matching part will be removed. +For steps 1-4, if the identifier matches the option, the matching part will be removed. For example, if you provide the following formatting option: `{ leadingUnderscore: 'allow', prefix: ['I'], format: ['StrictPascalCase'] }`, for the identifier `_IMyInterface`, then the following checks will occur: 1. `name = _IMyInterface` @@ -89,6 +91,7 @@ For example, if you provide the following formatting option: `{ leadingUnderscor 1. validate prefix - pass - Trim prefix - `name = MyInterface` 1. validate suffix - no check +1. validate custom - no check 1. validate format - pass One final note is that if the name were to become empty via this trimming process, it is considered to match all `format`s. An example of where this might be useful is for generic type parameters, where you want all names to be prefixed with `T`, but also want to allow for the single character `T` name. @@ -104,6 +107,9 @@ The `format` option defines the allowed formats for the identifier. This option - `snake_case` - standard snake_case format - all characters must be lower-case, and underscores are allowed. - `UPPER_CASE` - same as `snake_case`, except all characters must be upper-case. +Instead of an array, you may also pass `null`. This signifies "this selector shall not have its format checked". +This can be useful if you want to enforce no particular format for a specific selector, after applying a group selector. + ### `custom` The `custom` option defines a custom regex that the identifier must (or must not) match. This option allows you to have a bit more finer-grained control over identifiers, letting you ban (or force) certain patterns and substrings. diff --git a/packages/eslint-plugin/src/rules/naming-convention.ts b/packages/eslint-plugin/src/rules/naming-convention.ts index aa66caee0f9..8f3fe91ed5d 100644 --- a/packages/eslint-plugin/src/rules/naming-convention.ts +++ b/packages/eslint-plugin/src/rules/naming-convention.ts @@ -100,7 +100,7 @@ type TypeModifiersString = keyof typeof TypeModifiers; interface Selector { // format options - format: PredefinedFormatsString[]; + format: PredefinedFormatsString[] | null; custom?: { regex: string; match: boolean; @@ -117,7 +117,7 @@ interface Selector { } interface NormalizedSelector { // format options - format: PredefinedFormats[]; + format: PredefinedFormats[] | null; custom: { regex: RegExp; match: boolean; @@ -154,19 +154,24 @@ const PREFIX_SUFFIX_SCHEMA: JSONSchema.JSONSchema4 = { type: 'string', minLength: 1, }, - minItems: 1, additionalItems: false, }; type JSONSchemaProperties = Record; const FORMAT_OPTIONS_PROPERTIES: JSONSchemaProperties = { format: { - type: 'array', - items: { - type: 'string', - enum: util.getEnumNames(PredefinedFormats), - }, - minItems: 1, - additionalItems: false, + oneOf: [ + { + type: 'array', + items: { + type: 'string', + enum: util.getEnumNames(PredefinedFormats), + }, + additionalItems: false, + }, + { + type: 'null', + }, + ], }, custom: { type: 'object', @@ -235,7 +240,6 @@ function selectorSchema( } const SCHEMA: JSONSchema.JSONSchema4 = { type: 'array', - minItems: 1, items: { oneOf: [ ...selectorSchema('default', false, util.getEnumNames(Modifiers)), @@ -1024,7 +1028,7 @@ function createValidator( originalName: string, ): boolean { const formats = config.format; - if (formats.length === 0) { + if (formats === null || formats.length === 0) { return true; } @@ -1189,7 +1193,7 @@ function normalizeOption(option: Selector): NormalizedSelector { return { // format options - format: option.format.map(f => PredefinedFormats[f]), + format: option.format ? option.format.map(f => PredefinedFormats[f]) : null, custom: option.custom ? { regex: new RegExp(option.custom.regex), @@ -1204,8 +1208,8 @@ function normalizeOption(option: Selector): NormalizedSelector { option.trailingUnderscore !== undefined ? UnderscoreOptions[option.trailingUnderscore] : null, - prefix: option.prefix ?? null, - suffix: option.suffix ?? null, + prefix: option.prefix && option.prefix.length > 0 ? option.prefix : null, + suffix: option.suffix && option.suffix.length > 0 ? option.suffix : null, // selector options selector: isMetaSelector(option.selector) ? MetaSelectors[option.selector] diff --git a/packages/eslint-plugin/tests/rules/naming-convention.test.ts b/packages/eslint-plugin/tests/rules/naming-convention.test.ts index 04bd8503554..04d83717522 100644 --- a/packages/eslint-plugin/tests/rules/naming-convention.test.ts +++ b/packages/eslint-plugin/tests/rules/naming-convention.test.ts @@ -603,6 +603,7 @@ const cases: Cases = [ ruleTester.run('naming-convention', rule, { valid: [ + `const x = 1;`, // no options shouldn't crash ...createValidTestCases(cases), { code: ` @@ -696,6 +697,33 @@ ruleTester.run('naming-convention', rule, { }, ], }, + // no format selector + { + code: 'const snake_case = 1;', + options: [ + { + selector: 'default', + format: ['camelCase'], + }, + { + selector: 'variable', + format: null, + }, + ], + }, + { + code: 'const snake_case = 1;', + options: [ + { + selector: 'default', + format: ['camelCase'], + }, + { + selector: 'variable', + format: [], + }, + ], + }, ], invalid: [ ...createInvalidTestCases(cases),