Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat(eslint-plugin): support negative matches for filter (#1517)
Co-authored-by: Brad Zacher <brad.zacher@gmail.com>
  • Loading branch information
G-Rath and bradzacher committed Jan 30, 2020
1 parent 6613fad commit b24fbe8
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 23 deletions.
21 changes: 19 additions & 2 deletions packages/eslint-plugin/docs/rules/naming-convention.md
Expand Up @@ -40,7 +40,12 @@ type Options = {

// selector options
selector: Selector;
filter?: string;
filter?:
| string
| {
regex: string;
match: boolean;
};
// the allowed values for these are dependent on the selector - see below
modifiers?: Modifiers<Selector>[];
types?: Types<Selector>[];
Expand Down Expand Up @@ -118,6 +123,19 @@ Accepts an object with the following properties:
- `regex` - accepts a regular expression (anything accepted into `new RegExp(regex)`).
- `match` - true if the identifier _must_ match the `regex`, false if the identifier _must not_ match the `regex`.

### `filter`

The `filter` option operates similar to `custom`, accepting the same shaped object, except that it controls if the rest of the configuration should or should not be applied to an identifier.

You can use this to include or exclude specific identifiers from specific configurations.

Accepts an object with the following properties:

- `regex` - accepts a regular expression (anything accepted into `new RegExp(regex)`).
- `match` - true if the identifier _must_ match the `regex`, false if the identifier _must not_ match the `regex`.

Alternatively, `filter` accepts a regular expression (anything accepted into `new RegExp(filter)`). In this case, it's treated as if you had passed an object with the regex and `match: true`.

#### `leadingUnderscore` / `trailingUnderscore`

The `leadingUnderscore` / `trailingUnderscore` options control whether leading/trailing underscores are considered valid. Accepts one of the following values:
Expand All @@ -135,7 +153,6 @@ If these are provided, the identifier must start with one of the provided values
### Selector Options

- `selector` (see "Allowed Selectors, Modifiers and Types" below).
- `filter` accepts a regular expression (anything accepted into `new RegExp(filter)`). It allows you to limit the scope of this configuration to names that match this regex.
- `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.
Expand Down
54 changes: 36 additions & 18 deletions packages/eslint-plugin/src/rules/naming-convention.ts
Expand Up @@ -113,7 +113,12 @@ interface Selector {
selector: IndividualAndMetaSelectorsString;
modifiers?: ModifiersString[];
types?: TypeModifiersString[];
filter?: string;
filter?:
| string
| {
regex: string;
match: boolean;
};
}
interface NormalizedSelector {
// format options
Expand All @@ -130,7 +135,10 @@ interface NormalizedSelector {
selector: Selectors | MetaSelectors;
modifiers: Modifiers[] | null;
types: TypeModifiers[] | null;
filter: RegExp | null;
filter: {
regex: RegExp;
match: boolean;
} | null;
// calculated ordering weight based on modifiers
modifierWeight: number;
}
Expand All @@ -156,6 +164,14 @@ const PREFIX_SUFFIX_SCHEMA: JSONSchema.JSONSchema4 = {
},
additionalItems: false,
};
const MATCH_REGEX_SCHEMA: JSONSchema.JSONSchema4 = {
type: 'object',
properties: {
match: { type: 'boolean' },
regex: { type: 'string' },
},
required: ['match', 'regex'],
};
type JSONSchemaProperties = Record<string, JSONSchema.JSONSchema4>;
const FORMAT_OPTIONS_PROPERTIES: JSONSchemaProperties = {
format: {
Expand All @@ -173,18 +189,7 @@ const FORMAT_OPTIONS_PROPERTIES: JSONSchemaProperties = {
},
],
},
custom: {
type: 'object',
properties: {
regex: {
type: 'string',
},
match: {
type: 'boolean',
},
},
required: ['regex', 'match'],
},
custom: MATCH_REGEX_SCHEMA,
leadingUnderscore: UNDERSCORE_SCHEMA,
trailingUnderscore: UNDERSCORE_SCHEMA,
prefix: PREFIX_SUFFIX_SCHEMA,
Expand All @@ -197,8 +202,13 @@ function selectorSchema(
): JSONSchema.JSONSchema4[] {
const selector: JSONSchemaProperties = {
filter: {
type: 'string',
minLength: 1,
oneOf: [
{
type: 'string',
minLength: 1,
},
MATCH_REGEX_SCHEMA,
],
},
selector: {
type: 'string',
Expand Down Expand Up @@ -797,7 +807,7 @@ function createValidator(
// return will break the loop and stop checking configs
// it is only used when the name is known to have failed or succeeded a config.
for (const config of configs) {
if (config.filter?.test(originalName) === false) {
if (config.filter?.regex.test(originalName) !== config.filter?.match) {
// name does not match the filter
continue;
}
Expand Down Expand Up @@ -1216,7 +1226,15 @@ function normalizeOption(option: Selector): NormalizedSelector {
: Selectors[option.selector],
modifiers: option.modifiers?.map(m => Modifiers[m]) ?? null,
types: option.types?.map(m => TypeModifiers[m]) ?? null,
filter: option.filter !== undefined ? new RegExp(option.filter) : null,
filter:
option.filter !== undefined
? typeof option.filter === 'string'
? { regex: new RegExp(option.filter), match: true }
: {
regex: new RegExp(option.filter.regex),
match: option.filter.match,
}
: null,

This comment has been minimized.

Copy link
@francisco380

francisco380 Apr 15, 2021

powetr 0

// calculated ordering weight based on modifiers
modifierWeight: weight,
};
Expand Down
43 changes: 40 additions & 3 deletions packages/eslint-plugin/tests/rules/naming-convention.test.ts
Expand Up @@ -80,7 +80,11 @@ const formatTestNames: Readonly<Record<
};

const REPLACE_REGEX = /%/g;
const IGNORED_REGEX = /^.(?!gnored)/; // negative lookahead to not match `[iI]gnored`
// filter to not match `[iI]gnored`
const IGNORED_FILTER = {

This comment has been minimized.

Copy link
@francisco380
match: false,
regex: /.gnored/.source,
};

type Cases = {

This comment has been minimized.

Copy link
@francisco380

francisco380 Apr 15, 2021

funtion 11

code: string[];
Expand All @@ -100,7 +104,7 @@ function createValidTestCases(cases: Cases): TSESLint.ValidTestCase<Options>[] {
options: [
{
...options,
filter: IGNORED_REGEX.source,

This comment has been minimized.

Copy link
@francisco380

francisco380 Apr 15, 2021

0.5

filter: IGNORED_FILTER,
},
],
code: `// ${JSON.stringify(options)}\n${test.code
Expand Down Expand Up @@ -206,7 +210,7 @@ function createInvalidTestCases(
options: [
{
...options,
filter: IGNORED_REGEX.source,
filter: IGNORED_FILTER,
},
],
code: `// ${JSON.stringify(options)}\n${test.code
Expand Down Expand Up @@ -606,6 +610,22 @@ ruleTester.run('naming-convention', rule, {
valid: [
`const x = 1;`, // no options shouldn't crash
...createValidTestCases(cases),
{
code: `
const child_process = require('child_process');
`,
parserOptions,
options: [
{
selector: 'default',
format: ['camelCase'],
filter: {
regex: 'child_process',
match: false,
},
},
],
},
{
code: `
declare const string_camelCase: string;
Expand Down Expand Up @@ -742,6 +762,23 @@ ruleTester.run('naming-convention', rule, {
],
invalid: [
...createInvalidTestCases(cases),
{

This comment has been minimized.

Copy link
@francisco380

francisco380 Apr 15, 2021

sernt point zeri 4

code: `
const child_process = require('child_process');
`,
parserOptions,
options: [
{
selector: 'default',
format: ['camelCase'],
filter: {
regex: 'child_process',
match: true,
},
},
],
errors: [{ messageId: 'doesNotMatchFormat' }],

This comment has been minimized.

Copy link
@francisco380

francisco380 Apr 15, 2021

IP1

},
{
code: `

This comment has been minimized.

Copy link
@francisco380

francisco380 Apr 15, 2021

cancel 8inputs

declare const string_camelCase01: string;
Expand Down

0 comments on commit b24fbe8

Please sign in to comment.