eslint-config-typed 4.0.6 → 4.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/plugins/react-coding-style/rules/display-name.d.mts +2 -2
- package/dist/plugins/react-coding-style/rules/display-name.d.mts.map +1 -1
- package/dist/plugins/react-coding-style/rules/display-name.mjs +110 -30
- package/dist/plugins/react-coding-style/rules/display-name.mjs.map +1 -1
- package/dist/plugins/react-coding-style/rules/rules.d.mts +2 -2
- package/dist/rules/eslint-react-coding-style-rules.d.mts +1 -3
- package/dist/rules/eslint-react-coding-style-rules.d.mts.map +1 -1
- package/dist/rules/eslint-react-coding-style-rules.mjs +1 -1
- package/dist/rules/eslint-react-coding-style-rules.mjs.map +1 -1
- package/dist/rules/eslint-react-rules.d.mts +1 -1
- package/dist/rules/eslint-react-rules.mjs +1 -1
- package/dist/rules/eslint-react-rules.mjs.map +1 -1
- package/dist/types/rules/eslint-react-coding-style-rules.d.mts +21 -6
- package/dist/types/rules/eslint-react-coding-style-rules.d.mts.map +1 -1
- package/package.json +2 -1
- package/src/plugins/react-coding-style/README.md +4 -3
- package/src/plugins/react-coding-style/rules/display-name.mts +160 -38
- package/src/plugins/react-coding-style/rules/display-name.test.mts +70 -6
- package/src/plugins/react-coding-style/rules/shared.test.mts +148 -0
- package/src/rules/eslint-react-coding-style-rules.mts +1 -1
- package/src/rules/eslint-react-rules.mts +1 -1
- package/src/types/rules/eslint-react-coding-style-rules.mts +21 -6
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { type TSESLint } from '@typescript-eslint/utils';
|
|
2
2
|
type Options = readonly [
|
|
3
3
|
Readonly<{
|
|
4
|
-
|
|
4
|
+
ignoreName?: string | readonly string[];
|
|
5
5
|
}>?
|
|
6
6
|
];
|
|
7
|
-
type MessageIds = 'missingDisplayName';
|
|
7
|
+
type MessageIds = 'missingDisplayName' | 'mismatchedDisplayName';
|
|
8
8
|
/**
|
|
9
9
|
* Rule to require displayName property for React components
|
|
10
10
|
* This helps with debugging and component identification in React DevTools
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"display-name.d.mts","sourceRoot":"","sources":["../../../../src/plugins/react-coding-style/rules/display-name.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAIlC,KAAK,OAAO,GAAG,SAAS;IACtB,QAAQ,CAAC;QACP,
|
|
1
|
+
{"version":3,"file":"display-name.d.mts","sourceRoot":"","sources":["../../../../src/plugins/react-coding-style/rules/display-name.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAIlC,KAAK,OAAO,GAAG,SAAS;IACtB,QAAQ,CAAC;QACP,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;KACzC,CAAC,CAAC;CACJ,CAAC;AAEF,KAAK,UAAU,GAAG,oBAAoB,GAAG,uBAAuB,CAAC;AAEjE;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,UAAU,CAAC,UAAU,EAAE,OAAO,CA6GpE,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
|
|
2
|
-
import 'ts-data-forge';
|
|
2
|
+
import { castDeepMutable } from 'ts-data-forge';
|
|
3
3
|
import { isReactApiCall } from './shared.mjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -10,15 +10,22 @@ const displayNameRule = {
|
|
|
10
10
|
meta: {
|
|
11
11
|
type: 'suggestion',
|
|
12
12
|
docs: {
|
|
13
|
-
description: 'Require
|
|
13
|
+
description: 'Require React.memo components to define displayName matching the component name',
|
|
14
14
|
},
|
|
15
15
|
schema: [
|
|
16
16
|
{
|
|
17
17
|
type: 'object',
|
|
18
18
|
properties: {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
ignoreName: {
|
|
20
|
+
description: 'Component names allowed to have displayName different from the variable name.',
|
|
21
|
+
oneOf: [
|
|
22
|
+
{ type: 'string' },
|
|
23
|
+
{
|
|
24
|
+
type: 'array',
|
|
25
|
+
items: { type: 'string' },
|
|
26
|
+
minItems: 0,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
22
29
|
},
|
|
23
30
|
},
|
|
24
31
|
additionalProperties: false,
|
|
@@ -26,11 +33,13 @@ const displayNameRule = {
|
|
|
26
33
|
],
|
|
27
34
|
messages: {
|
|
28
35
|
missingDisplayName: 'Component should have a displayName property for better debugging',
|
|
36
|
+
mismatchedDisplayName: 'displayName should match the component name "{{componentName}}"',
|
|
29
37
|
},
|
|
30
38
|
},
|
|
31
39
|
create: (context) => {
|
|
32
40
|
const options = context.options[0] ?? {};
|
|
33
|
-
const
|
|
41
|
+
const ignoreNameSet = normalizeNames(options.ignoreName);
|
|
42
|
+
const shouldIgnoreMismatch = (componentName) => ignoreNameSet.has(componentName);
|
|
34
43
|
const checkComponent = (node) => {
|
|
35
44
|
if (node.id.type !== AST_NODE_TYPES.Identifier) {
|
|
36
45
|
return;
|
|
@@ -42,36 +51,38 @@ const displayNameRule = {
|
|
|
42
51
|
return;
|
|
43
52
|
}
|
|
44
53
|
const componentName = node.id.name;
|
|
45
|
-
|
|
54
|
+
const assignment = getDisplayNameAssignment(node);
|
|
55
|
+
if (assignment === undefined) {
|
|
56
|
+
context.report({
|
|
57
|
+
node: castDeepMutable(node),
|
|
58
|
+
messageId: 'missingDisplayName',
|
|
59
|
+
});
|
|
46
60
|
return;
|
|
47
61
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
62
|
+
if (!isComponentDisplayNameAssignment(assignment, componentName)) {
|
|
63
|
+
context.report({
|
|
64
|
+
node: castDeepMutable(node),
|
|
65
|
+
messageId: 'missingDisplayName',
|
|
66
|
+
});
|
|
51
67
|
return;
|
|
52
68
|
}
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
69
|
+
const displayName = extractDisplayName(assignment.right);
|
|
70
|
+
if (displayName === undefined) {
|
|
71
|
+
context.report({
|
|
72
|
+
node: assignment.right,
|
|
73
|
+
messageId: 'mismatchedDisplayName',
|
|
74
|
+
data: { componentName },
|
|
75
|
+
});
|
|
56
76
|
return;
|
|
57
77
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
nextStatement.expression.left.type ===
|
|
63
|
-
AST_NODE_TYPES.MemberExpression &&
|
|
64
|
-
nextStatement.expression.left.object.type ===
|
|
65
|
-
AST_NODE_TYPES.Identifier &&
|
|
66
|
-
nextStatement.expression.left.object.name === componentName &&
|
|
67
|
-
nextStatement.expression.left.property.type ===
|
|
68
|
-
AST_NODE_TYPES.Identifier &&
|
|
69
|
-
nextStatement.expression.left.property.name === 'displayName';
|
|
70
|
-
if (!hasDisplayName) {
|
|
78
|
+
if (shouldIgnoreMismatch(componentName)) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (displayName !== componentName) {
|
|
71
82
|
context.report({
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
83
|
+
node: assignment.right,
|
|
84
|
+
messageId: 'mismatchedDisplayName',
|
|
85
|
+
data: { componentName },
|
|
75
86
|
});
|
|
76
87
|
}
|
|
77
88
|
};
|
|
@@ -79,7 +90,76 @@ const displayNameRule = {
|
|
|
79
90
|
VariableDeclarator: checkComponent,
|
|
80
91
|
};
|
|
81
92
|
},
|
|
82
|
-
defaultOptions: [{
|
|
93
|
+
defaultOptions: [{ ignoreName: [] }],
|
|
94
|
+
};
|
|
95
|
+
const normalizeNames = (names) => {
|
|
96
|
+
if (names === undefined) {
|
|
97
|
+
return new Set();
|
|
98
|
+
}
|
|
99
|
+
if (typeof names === 'string') {
|
|
100
|
+
return new Set([names]);
|
|
101
|
+
}
|
|
102
|
+
return new Set(names);
|
|
103
|
+
};
|
|
104
|
+
const getDisplayNameAssignment = (node) => {
|
|
105
|
+
let mut_current = node.parent;
|
|
106
|
+
let mut_statement = undefined;
|
|
107
|
+
while (mut_current !== undefined) {
|
|
108
|
+
if (mut_current.type === AST_NODE_TYPES.VariableDeclaration ||
|
|
109
|
+
mut_current.type === AST_NODE_TYPES.ExportNamedDeclaration) {
|
|
110
|
+
mut_statement = mut_current;
|
|
111
|
+
}
|
|
112
|
+
if (mut_current.type === AST_NODE_TYPES.Program) {
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
mut_current = mut_current.parent;
|
|
116
|
+
}
|
|
117
|
+
if (mut_current === undefined || mut_statement === undefined) {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
const program = mut_current;
|
|
121
|
+
const componentIndex = program.body.indexOf(
|
|
122
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
123
|
+
mut_statement);
|
|
124
|
+
if (componentIndex === -1) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
const nextStatement = program.body[componentIndex + 1];
|
|
128
|
+
if (nextStatement === undefined) {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
if (nextStatement.type !== AST_NODE_TYPES.ExpressionStatement) {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
if (nextStatement.expression.type !== AST_NODE_TYPES.AssignmentExpression) {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
return nextStatement.expression;
|
|
138
|
+
};
|
|
139
|
+
const isComponentDisplayNameAssignment = (assignment, componentName) => {
|
|
140
|
+
if (assignment.left.type !== AST_NODE_TYPES.MemberExpression) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
if (assignment.left.object.type !== AST_NODE_TYPES.Identifier) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
if (assignment.left.object.name !== componentName) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
return (assignment.left.property.type === AST_NODE_TYPES.Identifier &&
|
|
150
|
+
assignment.left.property.name === 'displayName');
|
|
151
|
+
};
|
|
152
|
+
const extractDisplayName = (expression) => {
|
|
153
|
+
if (expression.type === AST_NODE_TYPES.Literal &&
|
|
154
|
+
typeof expression.value === 'string') {
|
|
155
|
+
return expression.value;
|
|
156
|
+
}
|
|
157
|
+
if (expression.type === AST_NODE_TYPES.TemplateLiteral &&
|
|
158
|
+
expression.expressions.length === 0 &&
|
|
159
|
+
expression.quasis.length === 1) {
|
|
160
|
+
return expression.quasis[0]?.value.cooked ?? undefined;
|
|
161
|
+
}
|
|
162
|
+
return undefined;
|
|
83
163
|
};
|
|
84
164
|
|
|
85
165
|
export { displayNameRule };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"display-name.mjs","sources":["../../../../src/plugins/react-coding-style/rules/display-name.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;AAgBA;;;AAGG;AACI,MAAM,eAAe,GAA6C;AACvE,IAAA,IAAI,EAAE;AACJ,QAAA,IAAI,EAAE,YAAY;AAClB,QAAA,IAAI,EAAE;AACJ,YAAA,WAAW,EACT,
|
|
1
|
+
{"version":3,"file":"display-name.mjs","sources":["../../../../src/plugins/react-coding-style/rules/display-name.mts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -12,8 +12,8 @@ export declare const reactCodingStyleRules: {
|
|
|
12
12
|
readonly 'react-memo-type-parameter': import("@typescript-eslint/utils/ts-eslint").RuleModule<"requirePropsTypeParameter" | "omitTypeParameterWhenPropsEmpty", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
13
13
|
readonly 'use-memo-hook-style': import("@typescript-eslint/utils/ts-eslint").RuleModule<"disallowUseMemoTypeAnnotation", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
14
14
|
readonly 'ban-use-imperative-handle-hook': import("@typescript-eslint/utils/ts-eslint").RuleModule<"disallowUseImperativeHandle", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
15
|
-
readonly 'display-name': import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingDisplayName", readonly [(Readonly<{
|
|
16
|
-
|
|
15
|
+
readonly 'display-name': import("@typescript-eslint/utils/ts-eslint").RuleModule<"missingDisplayName" | "mismatchedDisplayName", readonly [(Readonly<{
|
|
16
|
+
ignoreName?: string | readonly string[];
|
|
17
17
|
}> | undefined)?], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
18
18
|
};
|
|
19
19
|
//# sourceMappingURL=rules.d.mts.map
|
|
@@ -11,8 +11,6 @@ export declare const eslintReactCodingStyleRules: {
|
|
|
11
11
|
readonly 'react-coding-style/react-memo-type-parameter': "error";
|
|
12
12
|
readonly 'react-coding-style/ban-use-imperative-handle-hook': "error";
|
|
13
13
|
readonly 'react-coding-style/use-memo-hook-style': "error";
|
|
14
|
-
readonly 'react-coding-style/display-name': readonly ["error", {
|
|
15
|
-
readonly ignoreTranspilerName: false;
|
|
16
|
-
}];
|
|
14
|
+
readonly 'react-coding-style/display-name': readonly ["error", {}];
|
|
17
15
|
};
|
|
18
16
|
//# sourceMappingURL=eslint-react-coding-style-rules.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eslint-react-coding-style-rules.d.mts","sourceRoot":"","sources":["../../src/rules/eslint-react-coding-style-rules.mts"],"names":[],"mappings":"AAEA,eAAO,MAAM,2BAA2B
|
|
1
|
+
{"version":3,"file":"eslint-react-coding-style-rules.d.mts","sourceRoot":"","sources":["../../src/rules/eslint-react-coding-style-rules.mts"],"names":[],"mappings":"AAEA,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;CAYQ,CAAC"}
|
|
@@ -8,7 +8,7 @@ const eslintReactCodingStyleRules = {
|
|
|
8
8
|
'react-coding-style/react-memo-type-parameter': 'error',
|
|
9
9
|
'react-coding-style/ban-use-imperative-handle-hook': 'error',
|
|
10
10
|
'react-coding-style/use-memo-hook-style': 'error',
|
|
11
|
-
'react-coding-style/display-name': ['error', {
|
|
11
|
+
'react-coding-style/display-name': ['error', {}],
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
export { eslintReactCodingStyleRules };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eslint-react-coding-style-rules.mjs","sources":["../../src/rules/eslint-react-coding-style-rules.mts"],"sourcesContent":[null],"names":[],"mappings":"AAEO,MAAM,2BAA2B,GAAG;;IAEzC,iCAAiC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;IAE1E,mCAAmC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;AACjE,IAAA,kDAAkD,EAAE,OAAO;AAC3D,IAAA,gDAAgD,EAAE,OAAO;AACzD,IAAA,mDAAmD,EAAE,OAAO;AAC5D,IAAA,8CAA8C,EAAE,OAAO;AACvD,IAAA,mDAAmD,EAAE,OAAO;AAC5D,IAAA,wCAAwC,EAAE,OAAO;
|
|
1
|
+
{"version":3,"file":"eslint-react-coding-style-rules.mjs","sources":["../../src/rules/eslint-react-coding-style-rules.mts"],"sourcesContent":[null],"names":[],"mappings":"AAEO,MAAM,2BAA2B,GAAG;;IAEzC,iCAAiC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;IAE1E,mCAAmC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;AACjE,IAAA,kDAAkD,EAAE,OAAO;AAC3D,IAAA,gDAAgD,EAAE,OAAO;AACzD,IAAA,mDAAmD,EAAE,OAAO;AAC5D,IAAA,8CAA8C,EAAE,OAAO;AACvD,IAAA,mDAAmD,EAAE,OAAO;AAC5D,IAAA,wCAAwC,EAAE,OAAO;AACjD,IAAA,iCAAiC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC;;;;;"}
|
|
@@ -24,7 +24,7 @@ export declare const eslintReactRules: {
|
|
|
24
24
|
readonly 'react/default-props-match-prop-types': 2 | 1;
|
|
25
25
|
/** Enforce consistent usage of props destructuring. */
|
|
26
26
|
readonly 'react/destructuring-assignment': 2 | 1;
|
|
27
|
-
readonly 'react/display-name':
|
|
27
|
+
readonly 'react/display-name': "off";
|
|
28
28
|
readonly 'react/forbid-component-props': readonly ["error", {
|
|
29
29
|
readonly forbid: readonly ["className"];
|
|
30
30
|
}];
|
|
@@ -26,7 +26,7 @@ const eslintReactRules = {
|
|
|
26
26
|
'react/default-props-match-prop-types': withDefaultOption('error'),
|
|
27
27
|
/** Enforce consistent usage of props destructuring. */
|
|
28
28
|
'react/destructuring-assignment': withDefaultOption('error'),
|
|
29
|
-
'react/display-name':
|
|
29
|
+
'react/display-name': 'off', // Covered by react-coding-style/display-name
|
|
30
30
|
'react/forbid-component-props': ['error', { forbid: ['className'] }],
|
|
31
31
|
'react/forbid-dom-props': withDefaultOption('error'),
|
|
32
32
|
'react/forbid-elements': withDefaultOption('error'),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eslint-react-rules.mjs","sources":["../../src/rules/eslint-react-rules.mts"],"sourcesContent":[null],"names":[],"mappings":";;AAEO,MAAM,gBAAgB,GAAG;AAC9B;;;;AAIG;AACH,IAAA,iCAAiC,EAAE,KAAK;AACxC,IAAA,oCAAoC,EAAE,KAAK;AAC3C,IAAA,gCAAgC,EAAE,KAAK;AACvC,IAAA,yBAAyB,EAAE,KAAK;AAChC,IAAA,yBAAyB,EAAE,KAAK;AAChC,IAAA,0BAA0B,EAAE,KAAK;AACjC,IAAA,+BAA+B,EAAE,KAAK;AACtC,IAAA,kBAAkB,EAAE,KAAK;AACzB,IAAA,wBAAwB,EAAE,KAAK;AAC/B,IAAA,8BAA8B,EAAE,KAAK;AACrC,IAAA,mBAAmB,EAAE,KAAK;AAC1B,IAAA,mCAAmC,EAAE,KAAK;AAC1C,IAAA,iCAAiC,EAAE,KAAK;AACxC,IAAA,uBAAuB,EAAE,KAAK;AAC9B,IAAA,2BAA2B,EAAE,KAAK;AAElC,IAAA,2BAA2B,EAAE,iBAAiB,CAAC,OAAO,CAAC;AACvD,IAAA,uBAAuB,EAAE,iBAAiB,CAAC,OAAO,CAAC;AACnD,IAAA,sCAAsC,EAAE,iBAAiB,CAAC,OAAO,CAAC;;AAGlE,IAAA,gCAAgC,EAAE,iBAAiB,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"eslint-react-rules.mjs","sources":["../../src/rules/eslint-react-rules.mts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -161,7 +161,8 @@ declare namespace BanUseImperativeHandleHook {
|
|
|
161
161
|
type RuleEntry = Linter.StringSeverity;
|
|
162
162
|
}
|
|
163
163
|
/**
|
|
164
|
-
* Require
|
|
164
|
+
* Require React.memo components to define displayName matching the component
|
|
165
|
+
* name
|
|
165
166
|
*
|
|
166
167
|
* ```md
|
|
167
168
|
* | key | value |
|
|
@@ -179,9 +180,20 @@ declare namespace DisplayName {
|
|
|
179
180
|
* {
|
|
180
181
|
* "type": "object",
|
|
181
182
|
* "properties": {
|
|
182
|
-
* "
|
|
183
|
-
* "
|
|
184
|
-
* "
|
|
183
|
+
* "ignoreName": {
|
|
184
|
+
* "description": "Component names allowed to have displayName different from the variable name.",
|
|
185
|
+
* "oneOf": [
|
|
186
|
+
* {
|
|
187
|
+
* "type": "string"
|
|
188
|
+
* },
|
|
189
|
+
* {
|
|
190
|
+
* "type": "array",
|
|
191
|
+
* "items": {
|
|
192
|
+
* "type": "string"
|
|
193
|
+
* },
|
|
194
|
+
* "minItems": 0
|
|
195
|
+
* }
|
|
196
|
+
* ]
|
|
185
197
|
* }
|
|
186
198
|
* },
|
|
187
199
|
* "additionalProperties": false
|
|
@@ -190,8 +202,11 @@ declare namespace DisplayName {
|
|
|
190
202
|
* ```
|
|
191
203
|
*/
|
|
192
204
|
type Options = Readonly<{
|
|
193
|
-
/**
|
|
194
|
-
|
|
205
|
+
/**
|
|
206
|
+
* Component names allowed to have displayName different from the variable
|
|
207
|
+
* name.
|
|
208
|
+
*/
|
|
209
|
+
ignoreName?: string | readonly string[];
|
|
195
210
|
}>;
|
|
196
211
|
type RuleEntry = 'off' | Linter.Severity | SpreadOptionsIfIsArray<readonly [Linter.StringSeverity, Options]>;
|
|
197
212
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eslint-react-coding-style-rules.d.mts","sourceRoot":"","sources":["../../../src/types/rules/eslint-react-coding-style-rules.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,KAAK,sBAAsB,CACzB,CAAC,SAAS,SAAS,CAAC,MAAM,CAAC,cAAc,EAAE,OAAO,CAAC,IACjD,CAAC,CAAC,CAAC,CAAC,SAAS,SAAS,OAAO,EAAE,GAC/B,SAAS,CAAC,MAAM,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GACzC,CAAC,CAAC;AAEN;;;;;;;;;;GAUG;AACH,kBAAU,aAAa,CAAC;IACtB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,KAAY,OAAO,GAAG,QAAQ,CAAC;QAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,aAAa,CAAC;KACzB,CAAC,CAAC;IAEH,KAAY,SAAS,GACjB,KAAK,GACL,MAAM,CAAC,QAAQ,GACf,sBAAsB,CAAC,SAAS,CAAC,MAAM,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC;CACvE;AAED;;;;;;;;;GASG;AACH,kBAAU,0BAA0B,CAAC;IACnC,KAAY,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC;CAC/C;AAED;;;;;;;;;GASG;AACH,kBAAU,WAAW,CAAC;IACpB;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,KAAY,OAAO,GAAG,QAAQ,CAAC;QAC7B;;;WAGG;QACH,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC;KACrC,CAAC,CAAC;IAEH,KAAY,SAAS,GACjB,KAAK,GACL,MAAM,CAAC,QAAQ,GACf,sBAAsB,CAAC,SAAS,CAAC,MAAM,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC;CACvE;AAED;;;;;;;;;GASG;AACH,kBAAU,wBAAwB,CAAC;IACjC,KAAY,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC;CAC/C;AAED;;;;;;;;;;GAUG;AACH,kBAAU,0BAA0B,CAAC;IACnC,KAAY,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC;CAC/C;AAED;;;;;;;;;GASG;AACH,kBAAU,sBAAsB,CAAC;IAC/B,KAAY,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC;CAC/C;AAED;;;;;;;;;GASG;AACH,kBAAU,gBAAgB,CAAC;IACzB,KAAY,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC;CAC/C;AAED;;;;;;;;;GASG;AACH,kBAAU,0BAA0B,CAAC;IACnC,KAAY,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC;CAC/C;AAED
|
|
1
|
+
{"version":3,"file":"eslint-react-coding-style-rules.d.mts","sourceRoot":"","sources":["../../../src/types/rules/eslint-react-coding-style-rules.mts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-config-typed",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript"
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"check-all": "tsx ./scripts/cmd/check-all.mts",
|
|
41
41
|
"check:ext": "tsx ./scripts/cmd/check-ext.mts",
|
|
42
42
|
"cspell": "cspell \"**\" --gitignore --gitignore-root ./ --no-progress",
|
|
43
|
+
"doc": "tsx ./scripts/cmd/gen-docs.mts",
|
|
43
44
|
"doc:embed": "tsx ./scripts/cmd/embed-samples.mts",
|
|
44
45
|
"fmt": "format-uncommitted",
|
|
45
46
|
"fmt:diff": "format-diff-from origin/main",
|
|
@@ -37,11 +37,11 @@ export const MemoWithoutProps = React.memo(() => <div>{1}</div>);
|
|
|
37
37
|
|
|
38
38
|
## `display-name`
|
|
39
39
|
|
|
40
|
-
Requires React components created with `React.memo` to have a `displayName` property for better debugging in React DevTools.
|
|
40
|
+
Requires React components created with `React.memo` to have a `displayName` property that matches the component name for better debugging in React DevTools.
|
|
41
41
|
|
|
42
42
|
**Options:**
|
|
43
43
|
|
|
44
|
-
- `
|
|
44
|
+
- `ignoreName` (string | string[], default: `[]`): Component names allowed to have a `displayName` different from their variable name.
|
|
45
45
|
|
|
46
46
|
**Examples:**
|
|
47
47
|
|
|
@@ -53,6 +53,7 @@ const MyComponent = React.memo(() => <div>Hello</div>);
|
|
|
53
53
|
const MyComponent = React.memo(() => <div>Hello</div>);
|
|
54
54
|
MyComponent.displayName = 'MyComponent';
|
|
55
55
|
|
|
56
|
-
// ✅ Good (with
|
|
56
|
+
// ✅ Good (mismatch allowed with ignoreName)
|
|
57
57
|
const MyComponent = React.memo(() => <div>Hello</div>);
|
|
58
|
+
MyComponent.displayName = 'SomeOtherName';
|
|
58
59
|
```
|
|
@@ -3,16 +3,16 @@ import {
|
|
|
3
3
|
type TSESLint,
|
|
4
4
|
type TSESTree,
|
|
5
5
|
} from '@typescript-eslint/utils';
|
|
6
|
-
import {
|
|
6
|
+
import { castDeepMutable } from 'ts-data-forge';
|
|
7
7
|
import { isReactApiCall } from './shared.mjs';
|
|
8
8
|
|
|
9
9
|
type Options = readonly [
|
|
10
10
|
Readonly<{
|
|
11
|
-
|
|
11
|
+
ignoreName?: string | readonly string[];
|
|
12
12
|
}>?,
|
|
13
13
|
];
|
|
14
14
|
|
|
15
|
-
type MessageIds = 'missingDisplayName';
|
|
15
|
+
type MessageIds = 'missingDisplayName' | 'mismatchedDisplayName';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Rule to require displayName property for React components
|
|
@@ -23,16 +23,23 @@ export const displayNameRule: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
23
23
|
type: 'suggestion',
|
|
24
24
|
docs: {
|
|
25
25
|
description:
|
|
26
|
-
'Require
|
|
26
|
+
'Require React.memo components to define displayName matching the component name',
|
|
27
27
|
},
|
|
28
28
|
schema: [
|
|
29
29
|
{
|
|
30
30
|
type: 'object',
|
|
31
31
|
properties: {
|
|
32
|
-
|
|
33
|
-
type: 'boolean',
|
|
32
|
+
ignoreName: {
|
|
34
33
|
description:
|
|
35
|
-
'
|
|
34
|
+
'Component names allowed to have displayName different from the variable name.',
|
|
35
|
+
oneOf: [
|
|
36
|
+
{ type: 'string' },
|
|
37
|
+
{
|
|
38
|
+
type: 'array',
|
|
39
|
+
items: { type: 'string' },
|
|
40
|
+
minItems: 0,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
36
43
|
},
|
|
37
44
|
},
|
|
38
45
|
additionalProperties: false,
|
|
@@ -41,12 +48,17 @@ export const displayNameRule: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
41
48
|
messages: {
|
|
42
49
|
missingDisplayName:
|
|
43
50
|
'Component should have a displayName property for better debugging',
|
|
51
|
+
mismatchedDisplayName:
|
|
52
|
+
'displayName should match the component name "{{componentName}}"',
|
|
44
53
|
},
|
|
45
54
|
},
|
|
46
55
|
create: (context) => {
|
|
47
56
|
const options = context.options[0] ?? {};
|
|
48
57
|
|
|
49
|
-
const
|
|
58
|
+
const ignoreNameSet = normalizeNames(options.ignoreName);
|
|
59
|
+
|
|
60
|
+
const shouldIgnoreMismatch = (componentName: string): boolean =>
|
|
61
|
+
ignoreNameSet.has(componentName);
|
|
50
62
|
|
|
51
63
|
const checkComponent = (
|
|
52
64
|
node: DeepReadonly<TSESTree.VariableDeclarator>,
|
|
@@ -65,48 +77,47 @@ export const displayNameRule: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
65
77
|
|
|
66
78
|
const componentName = node.id.name;
|
|
67
79
|
|
|
68
|
-
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
80
|
+
const assignment = getDisplayNameAssignment(node);
|
|
71
81
|
|
|
72
|
-
|
|
82
|
+
if (assignment === undefined) {
|
|
83
|
+
context.report({
|
|
84
|
+
node: castDeepMutable(node),
|
|
85
|
+
messageId: 'missingDisplayName',
|
|
86
|
+
});
|
|
73
87
|
|
|
74
|
-
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
75
90
|
|
|
76
|
-
|
|
91
|
+
if (!isComponentDisplayNameAssignment(assignment, componentName)) {
|
|
92
|
+
context.report({
|
|
93
|
+
node: castDeepMutable(node),
|
|
94
|
+
messageId: 'missingDisplayName',
|
|
95
|
+
});
|
|
77
96
|
|
|
78
|
-
if (grandParent.type !== AST_NODE_TYPES.Program) {
|
|
79
97
|
return;
|
|
80
98
|
}
|
|
81
99
|
|
|
82
|
-
const
|
|
100
|
+
const displayName = extractDisplayName(assignment.right);
|
|
83
101
|
|
|
84
|
-
|
|
102
|
+
if (displayName === undefined) {
|
|
103
|
+
context.report({
|
|
104
|
+
node: assignment.right,
|
|
105
|
+
messageId: 'mismatchedDisplayName',
|
|
106
|
+
data: { componentName },
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
85
111
|
|
|
86
|
-
if (
|
|
112
|
+
if (shouldIgnoreMismatch(componentName)) {
|
|
87
113
|
return;
|
|
88
114
|
}
|
|
89
115
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const hasDisplayName =
|
|
93
|
-
nextStatement !== undefined &&
|
|
94
|
-
nextStatement.type === AST_NODE_TYPES.ExpressionStatement &&
|
|
95
|
-
nextStatement.expression.type === AST_NODE_TYPES.AssignmentExpression &&
|
|
96
|
-
nextStatement.expression.left.type ===
|
|
97
|
-
AST_NODE_TYPES.MemberExpression &&
|
|
98
|
-
nextStatement.expression.left.object.type ===
|
|
99
|
-
AST_NODE_TYPES.Identifier &&
|
|
100
|
-
nextStatement.expression.left.object.name === componentName &&
|
|
101
|
-
nextStatement.expression.left.property.type ===
|
|
102
|
-
AST_NODE_TYPES.Identifier &&
|
|
103
|
-
nextStatement.expression.left.property.name === 'displayName';
|
|
104
|
-
|
|
105
|
-
if (!hasDisplayName) {
|
|
116
|
+
if (displayName !== componentName) {
|
|
106
117
|
context.report({
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
118
|
+
node: assignment.right,
|
|
119
|
+
messageId: 'mismatchedDisplayName',
|
|
120
|
+
data: { componentName },
|
|
110
121
|
});
|
|
111
122
|
}
|
|
112
123
|
};
|
|
@@ -115,5 +126,116 @@ export const displayNameRule: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
115
126
|
VariableDeclarator: checkComponent,
|
|
116
127
|
};
|
|
117
128
|
},
|
|
118
|
-
defaultOptions: [{
|
|
129
|
+
defaultOptions: [{ ignoreName: [] }],
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const normalizeNames = (
|
|
133
|
+
names: string | readonly string[] | undefined,
|
|
134
|
+
): ReadonlySet<string> => {
|
|
135
|
+
if (names === undefined) {
|
|
136
|
+
return new Set();
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (typeof names === 'string') {
|
|
140
|
+
return new Set([names]);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return new Set(names);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const getDisplayNameAssignment = (
|
|
147
|
+
node: DeepReadonly<TSESTree.VariableDeclarator>,
|
|
148
|
+
): DeepReadonly<TSESTree.AssignmentExpression> | undefined => {
|
|
149
|
+
let mut_current = node.parent as DeepReadonly<TSESTree.Node> | undefined;
|
|
150
|
+
|
|
151
|
+
let mut_statement: DeepReadonly<TSESTree.Statement> | undefined = undefined;
|
|
152
|
+
|
|
153
|
+
while (mut_current !== undefined) {
|
|
154
|
+
if (
|
|
155
|
+
mut_current.type === AST_NODE_TYPES.VariableDeclaration ||
|
|
156
|
+
mut_current.type === AST_NODE_TYPES.ExportNamedDeclaration
|
|
157
|
+
) {
|
|
158
|
+
mut_statement = mut_current as DeepReadonly<TSESTree.Statement>;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (mut_current.type === AST_NODE_TYPES.Program) {
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
mut_current = mut_current.parent;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (mut_current === undefined || mut_statement === undefined) {
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const program = mut_current;
|
|
173
|
+
|
|
174
|
+
const componentIndex = program.body.indexOf(
|
|
175
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
176
|
+
mut_statement as TSESTree.Statement,
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
if (componentIndex === -1) {
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const nextStatement = program.body[componentIndex + 1];
|
|
184
|
+
|
|
185
|
+
if (nextStatement === undefined) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (nextStatement.type !== AST_NODE_TYPES.ExpressionStatement) {
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (nextStatement.expression.type !== AST_NODE_TYPES.AssignmentExpression) {
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return nextStatement.expression;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const isComponentDisplayNameAssignment = (
|
|
201
|
+
assignment: DeepReadonly<TSESTree.AssignmentExpression>,
|
|
202
|
+
componentName: string,
|
|
203
|
+
): assignment is TSESTree.AssignmentExpression => {
|
|
204
|
+
if (assignment.left.type !== AST_NODE_TYPES.MemberExpression) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (assignment.left.object.type !== AST_NODE_TYPES.Identifier) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (assignment.left.object.name !== componentName) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return (
|
|
217
|
+
assignment.left.property.type === AST_NODE_TYPES.Identifier &&
|
|
218
|
+
assignment.left.property.name === 'displayName'
|
|
219
|
+
);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const extractDisplayName = (
|
|
223
|
+
expression: DeepReadonly<TSESTree.Expression>,
|
|
224
|
+
): string | undefined => {
|
|
225
|
+
if (
|
|
226
|
+
expression.type === AST_NODE_TYPES.Literal &&
|
|
227
|
+
typeof expression.value === 'string'
|
|
228
|
+
) {
|
|
229
|
+
return expression.value;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (
|
|
233
|
+
expression.type === AST_NODE_TYPES.TemplateLiteral &&
|
|
234
|
+
expression.expressions.length === 0 &&
|
|
235
|
+
expression.quasis.length === 1
|
|
236
|
+
) {
|
|
237
|
+
return expression.quasis[0]?.value.cooked ?? undefined;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return undefined;
|
|
119
241
|
};
|
|
@@ -41,6 +41,13 @@ describe('display-name', () => {
|
|
|
41
41
|
const notAComponent = someFunction();
|
|
42
42
|
`,
|
|
43
43
|
},
|
|
44
|
+
{
|
|
45
|
+
name: 'Exported component with displayName',
|
|
46
|
+
code: dedent`
|
|
47
|
+
export const MyComponent = React.memo(() => <div>Hello</div>);
|
|
48
|
+
MyComponent.displayName = 'MyComponent';
|
|
49
|
+
`,
|
|
50
|
+
},
|
|
44
51
|
],
|
|
45
52
|
invalid: [
|
|
46
53
|
{
|
|
@@ -50,6 +57,26 @@ describe('display-name', () => {
|
|
|
50
57
|
`,
|
|
51
58
|
errors: [{ messageId: 'missingDisplayName' }],
|
|
52
59
|
},
|
|
60
|
+
{
|
|
61
|
+
name: 'Exported component without displayName',
|
|
62
|
+
code: dedent`
|
|
63
|
+
export const MyComponent = React.memo(() => <div>Hello</div>);
|
|
64
|
+
`,
|
|
65
|
+
errors: [{ messageId: 'missingDisplayName' }],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'Component with mismatched displayName',
|
|
69
|
+
code: dedent`
|
|
70
|
+
const MyComponent = React.memo(() => <div>Hello</div>);
|
|
71
|
+
MyComponent.displayName = 'Other';
|
|
72
|
+
`,
|
|
73
|
+
errors: [
|
|
74
|
+
{
|
|
75
|
+
messageId: 'mismatchedDisplayName',
|
|
76
|
+
data: { componentName: 'MyComponent' },
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
53
80
|
{
|
|
54
81
|
name: 'Named import without displayName',
|
|
55
82
|
code: dedent`
|
|
@@ -58,19 +85,47 @@ describe('display-name', () => {
|
|
|
58
85
|
`,
|
|
59
86
|
errors: [{ messageId: 'missingDisplayName' }],
|
|
60
87
|
},
|
|
88
|
+
{
|
|
89
|
+
name: 'Named import with mismatched displayName',
|
|
90
|
+
code: dedent`
|
|
91
|
+
import { memo } from 'react';
|
|
92
|
+
const MyComponent = memo(() => <div>Hello</div>);
|
|
93
|
+
MyComponent.displayName = 'Component';
|
|
94
|
+
`,
|
|
95
|
+
errors: [
|
|
96
|
+
{
|
|
97
|
+
messageId: 'mismatchedDisplayName',
|
|
98
|
+
data: { componentName: 'MyComponent' },
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'Exported component with mismatched displayName',
|
|
104
|
+
code: dedent`
|
|
105
|
+
export const MyComponent = React.memo(() => <div>Hello</div>);
|
|
106
|
+
MyComponent.displayName = 'Component';
|
|
107
|
+
`,
|
|
108
|
+
errors: [
|
|
109
|
+
{
|
|
110
|
+
messageId: 'mismatchedDisplayName',
|
|
111
|
+
data: { componentName: 'MyComponent' },
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
},
|
|
61
115
|
],
|
|
62
116
|
});
|
|
63
117
|
});
|
|
64
118
|
|
|
65
|
-
describe('
|
|
66
|
-
tester.run('display-name with
|
|
119
|
+
describe('ignoreName option', () => {
|
|
120
|
+
tester.run('display-name with ignoreName', displayNameRule, {
|
|
67
121
|
valid: [
|
|
68
122
|
{
|
|
69
|
-
name: 'Component
|
|
123
|
+
name: 'Component with mismatched displayName (ignored)',
|
|
70
124
|
code: dedent`
|
|
71
125
|
const MyComponent = React.memo(() => <div>Hello</div>);
|
|
126
|
+
MyComponent.displayName = 'Other';
|
|
72
127
|
`,
|
|
73
|
-
options: [{
|
|
128
|
+
options: [{ ignoreName: 'MyComponent' }],
|
|
74
129
|
},
|
|
75
130
|
{
|
|
76
131
|
name: 'Component with displayName',
|
|
@@ -78,10 +133,19 @@ describe('display-name', () => {
|
|
|
78
133
|
const MyComponent = React.memo(() => <div>Hello</div>);
|
|
79
134
|
MyComponent.displayName = 'MyComponent';
|
|
80
135
|
`,
|
|
81
|
-
options: [{
|
|
136
|
+
options: [{ ignoreName: ['MyComponent'] }],
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
invalid: [
|
|
140
|
+
{
|
|
141
|
+
name: 'Component without displayName is still reported',
|
|
142
|
+
code: dedent`
|
|
143
|
+
const MyComponent = React.memo(() => <div>Hello</div>);
|
|
144
|
+
`,
|
|
145
|
+
options: [{ ignoreName: ['MyComponent'] }],
|
|
146
|
+
errors: [{ messageId: 'missingDisplayName' }],
|
|
82
147
|
},
|
|
83
148
|
],
|
|
84
|
-
invalid: [],
|
|
85
149
|
});
|
|
86
150
|
});
|
|
87
151
|
});
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import parser from '@typescript-eslint/parser';
|
|
2
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
3
|
+
import { type TSESLint } from '@typescript-eslint/utils';
|
|
4
|
+
import { getReactMemoArrowFunction, isReactApiCall } from './shared.mjs';
|
|
5
|
+
|
|
6
|
+
const tester = new RuleTester({
|
|
7
|
+
languageOptions: {
|
|
8
|
+
parser,
|
|
9
|
+
parserOptions: {
|
|
10
|
+
ecmaVersion: 2020,
|
|
11
|
+
sourceType: 'module',
|
|
12
|
+
ecmaFeatures: { jsx: true },
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const reactApiRule: TSESLint.RuleModule<'reactApiDetected', readonly []> = {
|
|
18
|
+
meta: {
|
|
19
|
+
type: 'problem',
|
|
20
|
+
docs: { description: 'test helper isReactApiCall' },
|
|
21
|
+
schema: [],
|
|
22
|
+
messages: {
|
|
23
|
+
reactApiDetected: 'React API call detected',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
defaultOptions: [],
|
|
27
|
+
create: (context) => ({
|
|
28
|
+
CallExpression: (node) => {
|
|
29
|
+
if (isReactApiCall(context, node, 'memo')) {
|
|
30
|
+
context.report({ node, messageId: 'reactApiDetected' });
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
}),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const reactMemoArrowRule: TSESLint.RuleModule<'arrowDetected', readonly []> = {
|
|
37
|
+
meta: {
|
|
38
|
+
type: 'problem',
|
|
39
|
+
docs: { description: 'test helper getReactMemoArrowFunction' },
|
|
40
|
+
schema: [],
|
|
41
|
+
messages: {
|
|
42
|
+
arrowDetected: 'React.memo received arrow function',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
defaultOptions: [],
|
|
46
|
+
create: (context) => ({
|
|
47
|
+
CallExpression: (node) => {
|
|
48
|
+
if (!isReactApiCall(context, node, 'memo')) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const arrow = getReactMemoArrowFunction(node);
|
|
53
|
+
|
|
54
|
+
if (arrow !== undefined) {
|
|
55
|
+
assert.strictEqual(arrow.type, 'ArrowFunctionExpression');
|
|
56
|
+
|
|
57
|
+
context.report({ node, messageId: 'arrowDetected' });
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
describe('shared helpers', () => {
|
|
64
|
+
tester.run('isReactApiCall', reactApiRule, {
|
|
65
|
+
valid: [
|
|
66
|
+
{
|
|
67
|
+
name: 'non React call',
|
|
68
|
+
code: 'const x = fn();',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'memo imported from non-react',
|
|
72
|
+
code: `
|
|
73
|
+
import { memo } from 'not-react';
|
|
74
|
+
const Component = memo(() => null);
|
|
75
|
+
`,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'React member but different method',
|
|
79
|
+
code: `
|
|
80
|
+
import * as React from 'react';
|
|
81
|
+
const Component = React.useMemo(() => null, []);
|
|
82
|
+
`,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
invalid: [
|
|
86
|
+
{
|
|
87
|
+
name: 'named memo import from react',
|
|
88
|
+
code: `
|
|
89
|
+
import { memo } from 'react';
|
|
90
|
+
const Component = memo(() => null);
|
|
91
|
+
`,
|
|
92
|
+
errors: [{ messageId: 'reactApiDetected' }],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'namespace React memo call',
|
|
96
|
+
code: `
|
|
97
|
+
import * as React from 'react';
|
|
98
|
+
const Component = React.memo(() => null);
|
|
99
|
+
`,
|
|
100
|
+
errors: [{ messageId: 'reactApiDetected' }],
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'global React memo call without import',
|
|
104
|
+
code: `
|
|
105
|
+
const Component = React.memo(() => null);
|
|
106
|
+
`,
|
|
107
|
+
errors: [{ messageId: 'reactApiDetected' }],
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
tester.run('getReactMemoArrowFunction', reactMemoArrowRule, {
|
|
113
|
+
valid: [
|
|
114
|
+
{
|
|
115
|
+
name: 'memo with non-arrow first argument',
|
|
116
|
+
code: `
|
|
117
|
+
import { memo } from 'react';
|
|
118
|
+
function Component() { return null; }
|
|
119
|
+
const Wrapped = memo(Component);
|
|
120
|
+
`,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
name: 'non React call with arrow argument',
|
|
124
|
+
code: `
|
|
125
|
+
const Wrapped = wrap(() => null);
|
|
126
|
+
`,
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
invalid: [
|
|
130
|
+
{
|
|
131
|
+
name: 'memo with arrow function argument',
|
|
132
|
+
code: `
|
|
133
|
+
import { memo } from 'react';
|
|
134
|
+
const Wrapped = memo(() => null);
|
|
135
|
+
`,
|
|
136
|
+
errors: [{ messageId: 'arrowDetected' }],
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: 'React namespace memo with arrow function argument',
|
|
140
|
+
code: `
|
|
141
|
+
import * as React from 'react';
|
|
142
|
+
const Wrapped = React.memo(() => null);
|
|
143
|
+
`,
|
|
144
|
+
errors: [{ messageId: 'arrowDetected' }],
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
});
|
|
148
|
+
});
|
|
@@ -11,5 +11,5 @@ export const eslintReactCodingStyleRules = {
|
|
|
11
11
|
'react-coding-style/react-memo-type-parameter': 'error',
|
|
12
12
|
'react-coding-style/ban-use-imperative-handle-hook': 'error',
|
|
13
13
|
'react-coding-style/use-memo-hook-style': 'error',
|
|
14
|
-
'react-coding-style/display-name': ['error', {
|
|
14
|
+
'react-coding-style/display-name': ['error', {}],
|
|
15
15
|
} as const satisfies EslintReactCodingStyleRules;
|
|
@@ -29,7 +29,7 @@ export const eslintReactRules = {
|
|
|
29
29
|
/** Enforce consistent usage of props destructuring. */
|
|
30
30
|
'react/destructuring-assignment': withDefaultOption('error'),
|
|
31
31
|
|
|
32
|
-
'react/display-name':
|
|
32
|
+
'react/display-name': 'off', // Covered by react-coding-style/display-name
|
|
33
33
|
'react/forbid-component-props': ['error', { forbid: ['className'] }],
|
|
34
34
|
'react/forbid-dom-props': withDefaultOption('error'),
|
|
35
35
|
'react/forbid-elements': withDefaultOption('error'),
|
|
@@ -184,7 +184,8 @@ namespace BanUseImperativeHandleHook {
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
/**
|
|
187
|
-
* Require
|
|
187
|
+
* Require React.memo components to define displayName matching the component
|
|
188
|
+
* name
|
|
188
189
|
*
|
|
189
190
|
* ```md
|
|
190
191
|
* | key | value |
|
|
@@ -202,9 +203,20 @@ namespace DisplayName {
|
|
|
202
203
|
* {
|
|
203
204
|
* "type": "object",
|
|
204
205
|
* "properties": {
|
|
205
|
-
* "
|
|
206
|
-
* "
|
|
207
|
-
* "
|
|
206
|
+
* "ignoreName": {
|
|
207
|
+
* "description": "Component names allowed to have displayName different from the variable name.",
|
|
208
|
+
* "oneOf": [
|
|
209
|
+
* {
|
|
210
|
+
* "type": "string"
|
|
211
|
+
* },
|
|
212
|
+
* {
|
|
213
|
+
* "type": "array",
|
|
214
|
+
* "items": {
|
|
215
|
+
* "type": "string"
|
|
216
|
+
* },
|
|
217
|
+
* "minItems": 0
|
|
218
|
+
* }
|
|
219
|
+
* ]
|
|
208
220
|
* }
|
|
209
221
|
* },
|
|
210
222
|
* "additionalProperties": false
|
|
@@ -213,8 +225,11 @@ namespace DisplayName {
|
|
|
213
225
|
* ```
|
|
214
226
|
*/
|
|
215
227
|
export type Options = Readonly<{
|
|
216
|
-
/**
|
|
217
|
-
|
|
228
|
+
/**
|
|
229
|
+
* Component names allowed to have displayName different from the variable
|
|
230
|
+
* name.
|
|
231
|
+
*/
|
|
232
|
+
ignoreName?: string | readonly string[];
|
|
218
233
|
}>;
|
|
219
234
|
|
|
220
235
|
export type RuleEntry =
|