eslint-config-typed 3.9.0 → 3.10.0
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/README.md +1 -1
- package/dist/plugins/react-coding-style/rules/display-name.d.mts +14 -0
- package/dist/plugins/react-coding-style/rules/display-name.d.mts.map +1 -0
- package/dist/plugins/react-coding-style/rules/display-name.mjs +86 -0
- package/dist/plugins/react-coding-style/rules/display-name.mjs.map +1 -0
- package/dist/plugins/react-coding-style/rules/rules.d.mts +3 -0
- package/dist/plugins/react-coding-style/rules/rules.d.mts.map +1 -1
- package/dist/plugins/react-coding-style/rules/rules.mjs +2 -0
- package/dist/plugins/react-coding-style/rules/rules.mjs.map +1 -1
- package/dist/plugins/ts-restrictions/rules/check-destructuring-completeness.d.mts.map +1 -1
- package/dist/plugins/ts-restrictions/rules/check-destructuring-completeness.mjs +46 -46
- package/dist/plugins/ts-restrictions/rules/check-destructuring-completeness.mjs.map +1 -1
- package/dist/rules/eslint-react-coding-style-rules.d.mts +3 -0
- package/dist/rules/eslint-react-coding-style-rules.d.mts.map +1 -1
- package/dist/rules/eslint-react-coding-style-rules.mjs +1 -0
- package/dist/rules/eslint-react-coding-style-rules.mjs.map +1 -1
- package/dist/types/rules/eslint-react-coding-style-rules.d.mts +37 -0
- package/dist/types/rules/eslint-react-coding-style-rules.d.mts.map +1 -1
- package/package.json +1 -1
- package/src/plugins/react-coding-style/README.md +22 -0
- package/src/plugins/react-coding-style/rules/display-name.mts +117 -0
- package/src/plugins/react-coding-style/rules/display-name.test.mts +87 -0
- package/src/plugins/react-coding-style/rules/rules.mts +2 -0
- package/src/plugins/ts-restrictions/rules/check-destructuring-completeness.mts +59 -59
- package/src/rules/eslint-react-coding-style-rules.mts +1 -0
- package/src/types/rules/eslint-react-coding-style-rules.mts +42 -0
package/README.md
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
type Options = readonly [
|
|
3
|
+
Readonly<{
|
|
4
|
+
ignoreTranspilerName?: boolean;
|
|
5
|
+
}>?
|
|
6
|
+
];
|
|
7
|
+
type MessageIds = 'missingDisplayName';
|
|
8
|
+
/**
|
|
9
|
+
* Rule to require displayName property for React components
|
|
10
|
+
* This helps with debugging and component identification in React DevTools
|
|
11
|
+
*/
|
|
12
|
+
export declare const displayNameRule: TSESLint.RuleModule<MessageIds, Options>;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=display-name.d.mts.map
|
|
@@ -0,0 +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,oBAAoB,CAAC,EAAE,OAAO,CAAC;KAChC,CAAC,CAAC;CACJ,CAAC;AAEF,KAAK,UAAU,GAAG,oBAAoB,CAAC;AAEvC;;;GAGG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,UAAU,CAAC,UAAU,EAAE,OAAO,CAgGpE,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
|
|
2
|
+
import 'ts-data-forge';
|
|
3
|
+
import { isReactApiCall } from './shared.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Rule to require displayName property for React components
|
|
7
|
+
* This helps with debugging and component identification in React DevTools
|
|
8
|
+
*/
|
|
9
|
+
const displayNameRule = {
|
|
10
|
+
meta: {
|
|
11
|
+
type: 'suggestion',
|
|
12
|
+
docs: {
|
|
13
|
+
description: 'Require displayName property for React components created with React.memo',
|
|
14
|
+
},
|
|
15
|
+
schema: [
|
|
16
|
+
{
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
ignoreTranspilerName: {
|
|
20
|
+
type: 'boolean',
|
|
21
|
+
description: 'When true, ignores components that get displayName from variable name',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
additionalProperties: false,
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
messages: {
|
|
28
|
+
missingDisplayName: 'Component should have a displayName property for better debugging',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
create: (context) => {
|
|
32
|
+
const options = context.options[0] ?? {};
|
|
33
|
+
const ignoreTranspilerName = options.ignoreTranspilerName ?? false;
|
|
34
|
+
const checkComponent = (node) => {
|
|
35
|
+
if (node.id.type !== AST_NODE_TYPES.Identifier) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (node.init?.type !== AST_NODE_TYPES.CallExpression) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!isReactApiCall(context, node.init, 'memo')) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const componentName = node.id.name;
|
|
45
|
+
if (ignoreTranspilerName) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const parent = node.parent;
|
|
49
|
+
const grandParent = parent.parent;
|
|
50
|
+
if (grandParent.type !== AST_NODE_TYPES.Program) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const program = grandParent;
|
|
54
|
+
const componentIndex = program.body.indexOf(parent);
|
|
55
|
+
if (componentIndex === -1) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const nextStatement = program.body[componentIndex + 1];
|
|
59
|
+
const hasDisplayName = nextStatement !== undefined &&
|
|
60
|
+
nextStatement.type === AST_NODE_TYPES.ExpressionStatement &&
|
|
61
|
+
nextStatement.expression.type === AST_NODE_TYPES.AssignmentExpression &&
|
|
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) {
|
|
71
|
+
context.report({
|
|
72
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
73
|
+
node: node.id,
|
|
74
|
+
messageId: 'missingDisplayName',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
VariableDeclarator: checkComponent,
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
defaultOptions: [{ ignoreTranspilerName: false }],
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export { displayNameRule };
|
|
86
|
+
//# sourceMappingURL=display-name.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
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,5 +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
|
+
ignoreTranspilerName?: boolean;
|
|
17
|
+
}> | undefined)?], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
15
18
|
};
|
|
16
19
|
//# sourceMappingURL=rules.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.d.mts","sourceRoot":"","sources":["../../../../src/plugins/react-coding-style/rules/rules.mts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rules.d.mts","sourceRoot":"","sources":["../../../../src/plugins/react-coding-style/rules/rules.mts"],"names":[],"mappings":"AAWA,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;CAUQ,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { banUseImperativeHandleHook } from './ban-use-imperative-handle-hook.mjs';
|
|
2
2
|
import { componentNameRule } from './component-name.mjs';
|
|
3
3
|
import { componentVarTypeAnnotationRule } from './component-var-type-annotation.mjs';
|
|
4
|
+
import { displayNameRule } from './display-name.mjs';
|
|
4
5
|
import { importStyleRule } from './import-style.mjs';
|
|
5
6
|
import { propsTypeAnnotationStyleRule } from './props-type-annotation-style.mjs';
|
|
6
7
|
import { reactMemoPropsArgumentNameRule } from './react-memo-props-argument-name.mjs';
|
|
@@ -16,6 +17,7 @@ const reactCodingStyleRules = {
|
|
|
16
17
|
'react-memo-type-parameter': reactMemoTypeParameterRule,
|
|
17
18
|
'use-memo-hook-style': useMemoHooksStyleRule,
|
|
18
19
|
'ban-use-imperative-handle-hook': banUseImperativeHandleHook,
|
|
20
|
+
'display-name': displayNameRule,
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
export { reactCodingStyleRules };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.mjs","sources":["../../../../src/plugins/react-coding-style/rules/rules.mts"],"sourcesContent":[null],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rules.mjs","sources":["../../../../src/plugins/react-coding-style/rules/rules.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;;;;;AAWO,MAAM,qBAAqB,GAAG;AACnC,IAAA,gBAAgB,EAAE,iBAAiB;AACnC,IAAA,+BAA+B,EAAE,8BAA8B;AAC/D,IAAA,cAAc,EAAE,eAAe;AAC/B,IAAA,6BAA6B,EAAE,4BAA4B;AAC3D,IAAA,gCAAgC,EAAE,8BAA8B;AAChE,IAAA,2BAA2B,EAAE,0BAA0B;AACvD,IAAA,qBAAqB,EAAE,qBAAqB;AAC5C,IAAA,gCAAgC,EAAE,0BAA0B;AAC5D,IAAA,cAAc,EAAE,eAAe;;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-destructuring-completeness.d.mts","sourceRoot":"","sources":["../../../../src/plugins/ts-restrictions/rules/check-destructuring-completeness.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAGlC,KAAK,OAAO,GAAG,SAAS;IACtB,QAAQ,CAAC;QACP,8BAA8B,CAAC,EAAE,OAAO,CAAC;QACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC,CAAC;CACJ,CAAC;AAEF,KAAK,UAAU,GAAG,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"check-destructuring-completeness.d.mts","sourceRoot":"","sources":["../../../../src/plugins/ts-restrictions/rules/check-destructuring-completeness.mts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,QAAQ,EAEd,MAAM,0BAA0B,CAAC;AAGlC,KAAK,OAAO,GAAG,SAAS;IACtB,QAAQ,CAAC;QACP,8BAA8B,CAAC,EAAE,OAAO,CAAC;QACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC,CAAC;CACJ,CAAC;AAEF,KAAK,UAAU,GAAG,yBAAyB,CAAC;AAI5C,eAAO,MAAM,8BAA8B,EAAE,QAAQ,CAAC,UAAU,CAC9D,UAAU,EACV,OAAO,CAmOR,CAAC"}
|
|
@@ -1,51 +1,6 @@
|
|
|
1
1
|
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
|
|
2
2
|
|
|
3
3
|
const DEFAULT_DIRECTIVE_KEYWORD = '@check-destructuring-completeness';
|
|
4
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
5
|
-
const getObjectTypeProperties = (type) => {
|
|
6
|
-
try {
|
|
7
|
-
const properties = type.getProperties();
|
|
8
|
-
// Limit to reasonable number of properties to avoid hangs
|
|
9
|
-
if (properties.length > 1000) {
|
|
10
|
-
return [];
|
|
11
|
-
}
|
|
12
|
-
return properties
|
|
13
|
-
.map((prop) => prop.name)
|
|
14
|
-
.filter((name) =>
|
|
15
|
-
// Filter out symbol properties and internal properties
|
|
16
|
-
!name.startsWith('__') &&
|
|
17
|
-
// Only include string property names
|
|
18
|
-
typeof name === 'string' &&
|
|
19
|
-
name.length > 0);
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
// If there's any error getting properties, return empty array
|
|
23
|
-
return [];
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
const isReactComponentFunction = (node) => {
|
|
27
|
-
if (node === undefined || node === null)
|
|
28
|
-
return false;
|
|
29
|
-
// Arrow function component
|
|
30
|
-
if (node.type === AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
31
|
-
const { body } = node;
|
|
32
|
-
if (body.type === AST_NODE_TYPES.BlockStatement) {
|
|
33
|
-
return body.body.some((statement) => {
|
|
34
|
-
if (statement.type !== AST_NODE_TYPES.ReturnStatement)
|
|
35
|
-
return false;
|
|
36
|
-
const { argument } = statement;
|
|
37
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
38
|
-
if (argument === null || argument === undefined)
|
|
39
|
-
return false;
|
|
40
|
-
const argType = argument.type;
|
|
41
|
-
return argType === 'JSXElement' || argType === 'JSXFragment';
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
const bodyType = body.type;
|
|
45
|
-
return bodyType === 'JSXElement' || bodyType === 'JSXFragment';
|
|
46
|
-
}
|
|
47
|
-
return false;
|
|
48
|
-
};
|
|
49
4
|
const checkDestructuringCompleteness = {
|
|
50
5
|
meta: {
|
|
51
6
|
type: 'suggestion',
|
|
@@ -210,7 +165,52 @@ const checkDestructuringCompleteness = {
|
|
|
210
165
|
},
|
|
211
166
|
};
|
|
212
167
|
},
|
|
213
|
-
defaultOptions: [],
|
|
168
|
+
defaultOptions: [{ alwaysCheckReactComponentProps: true }],
|
|
169
|
+
};
|
|
170
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
171
|
+
const getObjectTypeProperties = (type) => {
|
|
172
|
+
try {
|
|
173
|
+
const properties = type.getProperties();
|
|
174
|
+
// Limit to reasonable number of properties to avoid hangs
|
|
175
|
+
if (properties.length > 1000) {
|
|
176
|
+
return [];
|
|
177
|
+
}
|
|
178
|
+
return properties
|
|
179
|
+
.map((prop) => prop.name)
|
|
180
|
+
.filter((name) =>
|
|
181
|
+
// Filter out symbol properties and internal properties
|
|
182
|
+
!name.startsWith('__') &&
|
|
183
|
+
// Only include string property names
|
|
184
|
+
typeof name === 'string' &&
|
|
185
|
+
name.length > 0);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
// If there's any error getting properties, return empty array
|
|
189
|
+
return [];
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
const isReactComponentFunction = (node) => {
|
|
193
|
+
if (node === undefined || node === null)
|
|
194
|
+
return false;
|
|
195
|
+
// Arrow function component
|
|
196
|
+
if (node.type === AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
197
|
+
const { body } = node;
|
|
198
|
+
if (body.type === AST_NODE_TYPES.BlockStatement) {
|
|
199
|
+
return body.body.some((statement) => {
|
|
200
|
+
if (statement.type !== AST_NODE_TYPES.ReturnStatement)
|
|
201
|
+
return false;
|
|
202
|
+
const { argument } = statement;
|
|
203
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
204
|
+
if (argument === null || argument === undefined)
|
|
205
|
+
return false;
|
|
206
|
+
const argType = argument.type;
|
|
207
|
+
return argType === 'JSXElement' || argType === 'JSXFragment';
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
const bodyType = body.type;
|
|
211
|
+
return bodyType === 'JSXElement' || bodyType === 'JSXFragment';
|
|
212
|
+
}
|
|
213
|
+
return false;
|
|
214
214
|
};
|
|
215
215
|
|
|
216
216
|
export { checkDestructuringCompleteness };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-destructuring-completeness.mjs","sources":["../../../../src/plugins/ts-restrictions/rules/check-destructuring-completeness.mts"],"sourcesContent":[null],"names":[],"mappings":";;AAgBA,MAAM,yBAAyB,GAAG,mCAAmC;
|
|
1
|
+
{"version":3,"file":"check-destructuring-completeness.mjs","sources":["../../../../src/plugins/ts-restrictions/rules/check-destructuring-completeness.mts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -11,5 +11,8 @@ 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
17
|
};
|
|
15
18
|
//# 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,6 +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', { ignoreTranspilerName: false }],
|
|
11
12
|
};
|
|
12
13
|
|
|
13
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;IACjD,iCAAiC,EAAE,CAAC,OAAO,EAAE,EAAE,oBAAoB,EAAE,KAAK,EAAE,CAAC;;;;;"}
|
|
@@ -160,6 +160,41 @@ declare namespace UseMemoHookStyle {
|
|
|
160
160
|
declare namespace BanUseImperativeHandleHook {
|
|
161
161
|
type RuleEntry = Linter.StringSeverity;
|
|
162
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* Require displayName property for React components created with React.memo
|
|
165
|
+
*
|
|
166
|
+
* ```md
|
|
167
|
+
* | key | value |
|
|
168
|
+
* | :--------- | :--------- |
|
|
169
|
+
* | type | suggestion |
|
|
170
|
+
* | deprecated | false |
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
declare namespace DisplayName {
|
|
174
|
+
/**
|
|
175
|
+
* ### schema
|
|
176
|
+
*
|
|
177
|
+
* ```json
|
|
178
|
+
* [
|
|
179
|
+
* {
|
|
180
|
+
* "type": "object",
|
|
181
|
+
* "properties": {
|
|
182
|
+
* "ignoreTranspilerName": {
|
|
183
|
+
* "type": "boolean",
|
|
184
|
+
* "description": "When true, ignores components that get displayName from variable name"
|
|
185
|
+
* }
|
|
186
|
+
* },
|
|
187
|
+
* "additionalProperties": false
|
|
188
|
+
* }
|
|
189
|
+
* ]
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
type Options = {
|
|
193
|
+
/** When true, ignores components that get displayName from variable name */
|
|
194
|
+
readonly ignoreTranspilerName?: boolean;
|
|
195
|
+
};
|
|
196
|
+
type RuleEntry = Linter.Severity | SpreadOptionsIfIsArray<readonly [Linter.StringSeverity, Options]> | 'off';
|
|
197
|
+
}
|
|
163
198
|
export type EslintReactCodingStyleRules = {
|
|
164
199
|
readonly 'react-coding-style/component-name': ComponentName.RuleEntry;
|
|
165
200
|
readonly 'react-coding-style/component-var-type-annotation': ComponentVarTypeAnnotation.RuleEntry;
|
|
@@ -169,10 +204,12 @@ export type EslintReactCodingStyleRules = {
|
|
|
169
204
|
readonly 'react-coding-style/react-memo-type-parameter': ReactMemoTypeParameter.RuleEntry;
|
|
170
205
|
readonly 'react-coding-style/use-memo-hook-style': UseMemoHookStyle.RuleEntry;
|
|
171
206
|
readonly 'react-coding-style/ban-use-imperative-handle-hook': BanUseImperativeHandleHook.RuleEntry;
|
|
207
|
+
readonly 'react-coding-style/display-name': DisplayName.RuleEntry;
|
|
172
208
|
};
|
|
173
209
|
export type EslintReactCodingStyleRulesOption = {
|
|
174
210
|
readonly 'react-coding-style/component-name': ComponentName.Options;
|
|
175
211
|
readonly 'react-coding-style/import-style': ImportStyle.Options;
|
|
212
|
+
readonly 'react-coding-style/display-name': DisplayName.Options;
|
|
176
213
|
};
|
|
177
214
|
export {};
|
|
178
215
|
//# 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/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;QACpB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC;KAClC,CAAC;IAEF,KAAY,SAAS,GACjB,MAAM,CAAC,QAAQ,GACf,sBAAsB,CAAC,SAAS,CAAC,MAAM,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,GACjE,KAAK,CAAC;CACX;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;QACpB;;;WAGG;QACH,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC;KAC9C,CAAC;IAEF,KAAY,SAAS,GACjB,MAAM,CAAC,QAAQ,GACf,sBAAsB,CAAC,SAAS,CAAC,MAAM,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,GACjE,KAAK,CAAC;CACX;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,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,CAAC,mCAAmC,EAAE,aAAa,CAAC,SAAS,CAAC;IACtE,QAAQ,CAAC,kDAAkD,EAAE,0BAA0B,CAAC,SAAS,CAAC;IAClG,QAAQ,CAAC,iCAAiC,EAAE,WAAW,CAAC,SAAS,CAAC;IAClE,QAAQ,CAAC,gDAAgD,EAAE,wBAAwB,CAAC,SAAS,CAAC;IAC9F,QAAQ,CAAC,mDAAmD,EAAE,0BAA0B,CAAC,SAAS,CAAC;IACnG,QAAQ,CAAC,8CAA8C,EAAE,sBAAsB,CAAC,SAAS,CAAC;IAC1F,QAAQ,CAAC,wCAAwC,EAAE,gBAAgB,CAAC,SAAS,CAAC;IAC9E,QAAQ,CAAC,mDAAmD,EAAE,0BAA0B,CAAC,SAAS,CAAC;
|
|
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
|
@@ -34,3 +34,25 @@ export const MemoWithoutProps = React.memo(() => <div>{1}</div>);
|
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
[typescript-eslint PlayGround](https://typescript-eslint.io/play/#ts=5.9.3&showAST=es&fileType=.tsx&code=JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwCwAUKJLIiugDYQAmaAFnCIlyXXqj61GjGAE8wWOAAViYdAF5MObhAB2HGQB4A3ozhwdAVxAAZYKhgAuOJWTa9M81YBGWKAG0AXXoGAF8APmDGAHoouAB5AGlGLAAPFnhcXXt4gGs4DWw8GAA6ECwQCANlCFUwgAo6sBVUAEp8sLgTBjNMnWzUK3zNIuKLVCwAWXKIOtMzODq2tQ6mmtRiyxs7EspuC1wsBuQAGjgvJY7kOABqM9OABhbjubM-VdUNq1t7AOfuuBakX%2BlBgFigOjgBm4wAAbmEjAMQCEDFFoXDgiFAVIGDF4kkGKl0nBetkAHIQbCg8EAZRgyBg5SwOhgAHVgDA%2BNSmlohoV8KVplVmvU6kZPFtspj2pC0fDNt8ShwmQBzDnI1GwsJYhjRWKJZJpaAZLLwcmUsE6Wn0xnMtkciAWGBclzcXlUEplCpCtYi96taVQzVGP2fCWKlVqlGy7W6uCkgDiBqJJPgAEEoMrUzodBA6QzXQV3QKvdVag0-U5S-7lp05im4Ii3SMxpNprN-mZFtKQ-LtsVdvtDnUTmcLigbnc4I8-vM4G9mqGFb85jHgVgqRDA3CEVZ1bKMavcQmk0biSa4OnM9nc9buKnUABJPTAHQKQsjT2VKu%2B5pOMW9%2BwnBdXR9HFHx-ACOApRrLoenPRt335FspgqdtZy7Gsey%2BPsBwOI5TnOaUrluLwHieF45ywsNl3%2BVczBBC0ZSDRE901A8gSPRMCUNVh60vLMczzLA70fZ9XztPgABU5CwbACF8JkDibflP29MtGl-YY3H0YxxQVICtBAjxNnAwIoK1aVYLPPp4AQ4YkPGFCZgojCVgXACdmEwd8NHIiJ1IqdyI7Sj3Own4ZwBIF6PXRit3hFiozYxhMQ42Jj245NzyciSHRgaT5EvKwmUceySicgBRFJc2AXAAGESEgV9mQMDS1krZoxz5EoAClqQADWKcqlTKZkOkQj021a1Qxys%2Bs7K60ZHLbFyxyohV%2By8vDhwIsdiMnacKPnNZF22GizDo5wYvBJjtwSjV0WSw80q4wlT3rJyBJvfN7yzDxxuLSpkB0GQfzWGa63gwZ-uQ5bgtcuA1pwzahxHQiaz2gKDuCo6Pg8s7IuxaKNxu%2BLd0Sh7QieuMXp440bLgD7ryEkSnw4F831KgHdI8gzXCMsDfDM8Jyw6yyIfp%2Baixh1CVu7UKww2vYttR3b-LIiLXkR8KVyiy7ibinckXJiJHtS6mT14rLpk%2B5n71Z9mJPy2SsHkygdCU-7VMKbTDH-MKSuA9wBYg8zQemsX-jmqHOel5y4dW%2BX1twlGdr8kj1cOrWYHxi6GOug27v3U3sU4i26eyS9nS0YTlImkthQaP2wyg8HI8h0hoaWmX4%2BlDzFe87bfPRtXAo1uc8Yi3Ors3WVDdYimUpL57GHrSSIHXjfN637eN%2BsXRlXq8BdGK0lkDKWuue-EWwYjuCJejhbY7Q%2BZ4az-vldT4f09HzPE9OyfdZ5xnsxMm90TaUyBEAA&eslintrc=N4KABGBEBOCuA2BTAzpAXGUEKQHYHsBaaFAF2gEsBjUxAE0OQE9dSBDAD3TAG1xsciaNHzRIAGn4CsA7JGSIkNUd0gBJALYAHUaQAiiKvDbQ2pCvlw9k%2BWNCqIAdADc28WIgC8AchJsa3gC61lqGFABmFELIjgAMjqRMoQCEPpo60KQAcmwaKFr%2BiADKoVQRUdBBElKykHnIyGwA5oiqAEqI-qRgyAAWtvB0YABGiGAU2rr0YGzIYAAGExndAFQzcx1dYOEiGmC%2BnQHzjpA1EAC%2BkrKYZ3IKSqQqGOp0iKzlQjy4uYip3psBYIFEisBJJLzedK6HL1AoOEphSJCII8YFvUiONGgxKhNKTTIGIwmMwWXAorEYimOGx2BwuNweHx%2BQHVa44eqNFqqAAqvTG31hhTAArGSymQx2%2BD2By63jAGlgyG6o32ANI3hOt0uIB11xk13kikMjzEz25RQAirA3B86DDEDwkOEMSKfGqUZQmr0XT8fAAxADCVSubLqKE5rWeAFUFGA1Y48hp8AAeAAKIi0yAAfAAKHNaDPIACUYE8WcwYEcVbA5xLFFwSs6dE1121etuOHuxqeUHNVptSLtP0diGdjld-0O6uCnu94993j9sFwNFJAalOlw6ODHagHOakagMbG8cTKfT%2BEzufzhZLZYrVccNbrDdobGbp1bkl1sn1tS7yimlAACyiAaKM0AAKIcAW4akjw%2BDDAAVsa855G6U7khmQiJGhEJnuSJjomCoQAILQE0sB5KwyCeMuryRFudCBKyob7lyzynmB%2BA9P0CBDL0bDOGMOJjMCPy0NACzeBembeMcn6yG2v67oaDw9pAoHgUI0GwQ08GIShNB4RhspAthmRMCZ3gEUCRHYuC5GUdRpDIKk9GjvW9B2SCGKiU5VHojEol-Oa3LgqmxJ5JJaivmw7wkpYO5snu4YHu0U4JtxvEDAJQkieCYDidFQjSbJyDyS2SkhtIqkASaqhaRBukkPplgIchqETu65mXjhVkTrZqL2X5jkUYFNHubgDFecxw2%2BSRiABS5wXgj4YURVFiAxXFCXmElPnEf540rZiUUxPEIXrUU4WhB04RCG8DjJWxaUcVAXFJjl-FgIJwlgKJRVbZJZWFpVikCMptUpWp3ZAZpYHNTBrXIAZnXGd1mG9aElnWUNVLHc5QVTTNTGHQ5ZEnUFi3XbdiCRaYJXQLFSrxeYiVkuTo2U0TNFnYzF0094G13aOj0roghELYTE2ufzuSC6J9p4X85UvQa7GHpAn08X0uW-flAOFcV22lfMMlgwpWrfrcf4CLDgE8jdjm4AQ7D7VYBNrZCrzvEilRc-zR3e%2BRIgAO5LiuHstXBB3zcRVJe7i3gBm48Ax21nPx6CicjY4VBp4gTiGV1C49dnlJ51SBfwEgTgFn1uODdx6u1JrGVaMYDgLDreYN5mGDlXe5bAJW1a1vMYBhxQpC9D3mVnmmhbXv3xaliPY9PhPVWQzb7Yw-VGlqL75j%2B18Px-KvUvB8nof4BHy6rpYGeo3HVKIMjse4Kk5AeIH1eF2LujH06FJxmQrkHUENc66YgsrhZuSZW723bmaPkMwqasGFD8MA%2BBwjoPDtsR%2BHsgYNGmI8OMC9sp6x%2BiqbwV8d7YChtgO2dwjSO2eAAeWAZFUgklPZ51Ej4O%2BD8o6khfqSa%2BoIP56Vft-Twv9ED-zztAoujgS4YzLljCBADa6qP7v1PGLcWI1Tbm9LWvIxKFhwXgtgYAqAbksOiOxlh2D1nrE0GYYASCkDsLgHo7swJOKImAeAEwZ5kJ4rY1wlA2DDCQFgvIQxzZXytl%2BHUts6psIas8Y%2B6IPjQHPuhAA9AAPUcMAAALAAJnEOcAAJEUwOgjvAADUTAUFiUgQkxhTAmkkRiNx3MISp1ruIt%2BedBn50AWo4B1ly5UkmSo%2BucCBoLlsqxDWZieRoPsZMLcmCRQhLeE0We31BgjDGEgBoANBJ%2BOqU%2BdcDYKCvCkiQMOlBaDrAWPY18YB1x7KcZ4ChXQspJiXpebMfdbzrwrLWAA3GAaRugKz-M3EEuYRQpSIFRY4zB5w4UQ0YXvFSB8skaSajpT%2BmcOpGRARCeZKzrKKkQOkIQJJhIAAl4p0CQEguQKCQL4H%2BvGZlRQAkAx4jPOY8UHBKlEM4gFmD6yNnfAwi4JiIAsM7GS%2BGIslqu3wO7eCudpbexGenKlsj%2BmQIxEsmZtK5laJNcRO1%2Bim4LmZVpfAfL2RbNQWMaJHS4kFVCGAbkZyhgqnim7Mw0xZjzxBZ67iyZuRZknvK%2BYPylRgGcBgcNQKRUKC9TmR8RZUlKRqIEfg5wQDnCAA&tsconfig=N4KABGBEDGD2C2AHAlgGwKYCcDyiAuysAdgM6QBcYoEEkJemy0eFYDArugDTg2RGwAqkWgALdNADW6ACYBJIjPQAPWQEFo0dCTKUO6XgF8QhoA&tokens=false)
|
|
37
|
+
|
|
38
|
+
## `display-name`
|
|
39
|
+
|
|
40
|
+
Requires React components created with `React.memo` to have a `displayName` property for better debugging in React DevTools.
|
|
41
|
+
|
|
42
|
+
**Options:**
|
|
43
|
+
|
|
44
|
+
- `ignoreTranspilerName` (boolean, default: `false`): When true, ignores components that get displayName from variable name (many transpilers add this automatically).
|
|
45
|
+
|
|
46
|
+
**Examples:**
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
// ❌ Bad
|
|
50
|
+
const MyComponent = React.memo(() => <div>Hello</div>);
|
|
51
|
+
|
|
52
|
+
// ✅ Good
|
|
53
|
+
const MyComponent = React.memo(() => <div>Hello</div>);
|
|
54
|
+
MyComponent.displayName = 'MyComponent';
|
|
55
|
+
|
|
56
|
+
// ✅ Good (with ignoreTranspilerName: true)
|
|
57
|
+
const MyComponent = React.memo(() => <div>Hello</div>);
|
|
58
|
+
```
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AST_NODE_TYPES,
|
|
3
|
+
type TSESLint,
|
|
4
|
+
type TSESTree,
|
|
5
|
+
} from '@typescript-eslint/utils';
|
|
6
|
+
import { expectType } from 'ts-data-forge';
|
|
7
|
+
import { isReactApiCall } from './shared.mjs';
|
|
8
|
+
|
|
9
|
+
type Options = readonly [
|
|
10
|
+
Readonly<{
|
|
11
|
+
ignoreTranspilerName?: boolean;
|
|
12
|
+
}>?,
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
type MessageIds = 'missingDisplayName';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Rule to require displayName property for React components
|
|
19
|
+
* This helps with debugging and component identification in React DevTools
|
|
20
|
+
*/
|
|
21
|
+
export const displayNameRule: TSESLint.RuleModule<MessageIds, Options> = {
|
|
22
|
+
meta: {
|
|
23
|
+
type: 'suggestion',
|
|
24
|
+
docs: {
|
|
25
|
+
description:
|
|
26
|
+
'Require displayName property for React components created with React.memo',
|
|
27
|
+
},
|
|
28
|
+
schema: [
|
|
29
|
+
{
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
ignoreTranspilerName: {
|
|
33
|
+
type: 'boolean',
|
|
34
|
+
description:
|
|
35
|
+
'When true, ignores components that get displayName from variable name',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
additionalProperties: false,
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
messages: {
|
|
42
|
+
missingDisplayName:
|
|
43
|
+
'Component should have a displayName property for better debugging',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
create: (context) => {
|
|
47
|
+
const options = context.options[0] ?? {};
|
|
48
|
+
const ignoreTranspilerName = options.ignoreTranspilerName ?? false;
|
|
49
|
+
|
|
50
|
+
const checkComponent = (
|
|
51
|
+
node: DeepReadonly<TSESTree.VariableDeclarator>,
|
|
52
|
+
): void => {
|
|
53
|
+
if (node.id.type !== AST_NODE_TYPES.Identifier) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (node.init?.type !== AST_NODE_TYPES.CallExpression) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!isReactApiCall(context, node.init, 'memo')) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const componentName = node.id.name;
|
|
66
|
+
|
|
67
|
+
if (ignoreTranspilerName) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const parent = node.parent;
|
|
72
|
+
|
|
73
|
+
expectType<typeof parent.type, AST_NODE_TYPES.VariableDeclaration>('=');
|
|
74
|
+
|
|
75
|
+
const grandParent = parent.parent;
|
|
76
|
+
|
|
77
|
+
if (grandParent.type !== AST_NODE_TYPES.Program) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const program = grandParent;
|
|
82
|
+
const componentIndex = program.body.indexOf(parent);
|
|
83
|
+
|
|
84
|
+
if (componentIndex === -1) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const nextStatement = program.body[componentIndex + 1];
|
|
89
|
+
|
|
90
|
+
const hasDisplayName =
|
|
91
|
+
nextStatement !== undefined &&
|
|
92
|
+
nextStatement.type === AST_NODE_TYPES.ExpressionStatement &&
|
|
93
|
+
nextStatement.expression.type === AST_NODE_TYPES.AssignmentExpression &&
|
|
94
|
+
nextStatement.expression.left.type ===
|
|
95
|
+
AST_NODE_TYPES.MemberExpression &&
|
|
96
|
+
nextStatement.expression.left.object.type ===
|
|
97
|
+
AST_NODE_TYPES.Identifier &&
|
|
98
|
+
nextStatement.expression.left.object.name === componentName &&
|
|
99
|
+
nextStatement.expression.left.property.type ===
|
|
100
|
+
AST_NODE_TYPES.Identifier &&
|
|
101
|
+
nextStatement.expression.left.property.name === 'displayName';
|
|
102
|
+
|
|
103
|
+
if (!hasDisplayName) {
|
|
104
|
+
context.report({
|
|
105
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
106
|
+
node: node.id as never,
|
|
107
|
+
messageId: 'missingDisplayName',
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
VariableDeclarator: checkComponent,
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
defaultOptions: [{ ignoreTranspilerName: false }],
|
|
117
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import parser from '@typescript-eslint/parser';
|
|
2
|
+
import { RuleTester } from '@typescript-eslint/rule-tester';
|
|
3
|
+
import dedent from 'dedent';
|
|
4
|
+
import { displayNameRule } from './display-name.mjs';
|
|
5
|
+
|
|
6
|
+
const tester = new RuleTester({
|
|
7
|
+
languageOptions: {
|
|
8
|
+
parser,
|
|
9
|
+
parserOptions: {
|
|
10
|
+
ecmaVersion: 2020,
|
|
11
|
+
sourceType: 'module',
|
|
12
|
+
ecmaFeatures: {
|
|
13
|
+
jsx: true,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('display-name', () => {
|
|
20
|
+
describe('default behavior', () => {
|
|
21
|
+
tester.run('display-name', displayNameRule, {
|
|
22
|
+
valid: [
|
|
23
|
+
{
|
|
24
|
+
name: 'Component with displayName',
|
|
25
|
+
code: dedent`
|
|
26
|
+
const MyComponent = React.memo(() => <div>Hello</div>);
|
|
27
|
+
MyComponent.displayName = 'MyComponent';
|
|
28
|
+
`,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'Named import with displayName',
|
|
32
|
+
code: dedent`
|
|
33
|
+
import { memo } from 'react';
|
|
34
|
+
const MyComponent = memo(() => <div>Hello</div>);
|
|
35
|
+
MyComponent.displayName = 'MyComponent';
|
|
36
|
+
`,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'Non-component variable',
|
|
40
|
+
code: dedent`
|
|
41
|
+
const notAComponent = someFunction();
|
|
42
|
+
`,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
invalid: [
|
|
46
|
+
{
|
|
47
|
+
name: 'Component without displayName',
|
|
48
|
+
code: dedent`
|
|
49
|
+
const MyComponent = React.memo(() => <div>Hello</div>);
|
|
50
|
+
`,
|
|
51
|
+
errors: [{ messageId: 'missingDisplayName' }],
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'Named import without displayName',
|
|
55
|
+
code: dedent`
|
|
56
|
+
import { memo } from 'react';
|
|
57
|
+
const MyComponent = memo(() => <div>Hello</div>);
|
|
58
|
+
`,
|
|
59
|
+
errors: [{ messageId: 'missingDisplayName' }],
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('ignoreTranspilerName option', () => {
|
|
66
|
+
tester.run('display-name with ignoreTranspilerName', displayNameRule, {
|
|
67
|
+
valid: [
|
|
68
|
+
{
|
|
69
|
+
name: 'Component without displayName (ignored)',
|
|
70
|
+
code: dedent`
|
|
71
|
+
const MyComponent = React.memo(() => <div>Hello</div>);
|
|
72
|
+
`,
|
|
73
|
+
options: [{ ignoreTranspilerName: true }],
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'Component with displayName',
|
|
77
|
+
code: dedent`
|
|
78
|
+
const MyComponent = React.memo(() => <div>Hello</div>);
|
|
79
|
+
MyComponent.displayName = 'MyComponent';
|
|
80
|
+
`,
|
|
81
|
+
options: [{ ignoreTranspilerName: true }],
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
invalid: [],
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -2,6 +2,7 @@ import { type ESLintPlugin } from '../../../types/index.mjs';
|
|
|
2
2
|
import { banUseImperativeHandleHook } from './ban-use-imperative-handle-hook.mjs';
|
|
3
3
|
import { componentNameRule } from './component-name.mjs';
|
|
4
4
|
import { componentVarTypeAnnotationRule } from './component-var-type-annotation.mjs';
|
|
5
|
+
import { displayNameRule } from './display-name.mjs';
|
|
5
6
|
import { importStyleRule } from './import-style.mjs';
|
|
6
7
|
import { propsTypeAnnotationStyleRule } from './props-type-annotation-style.mjs';
|
|
7
8
|
import { reactMemoPropsArgumentNameRule } from './react-memo-props-argument-name.mjs';
|
|
@@ -17,4 +18,5 @@ export const reactCodingStyleRules = {
|
|
|
17
18
|
'react-memo-type-parameter': reactMemoTypeParameterRule,
|
|
18
19
|
'use-memo-hook-style': useMemoHooksStyleRule,
|
|
19
20
|
'ban-use-imperative-handle-hook': banUseImperativeHandleHook,
|
|
21
|
+
'display-name': displayNameRule,
|
|
20
22
|
} as const satisfies ESLintPlugin['rules'];
|
|
@@ -16,64 +16,6 @@ type MessageIds = 'incompleteDestructuring';
|
|
|
16
16
|
|
|
17
17
|
const DEFAULT_DIRECTIVE_KEYWORD = '@check-destructuring-completeness';
|
|
18
18
|
|
|
19
|
-
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
20
|
-
const getObjectTypeProperties = (type: ts.Type): readonly string[] => {
|
|
21
|
-
try {
|
|
22
|
-
const properties = type.getProperties();
|
|
23
|
-
|
|
24
|
-
// Limit to reasonable number of properties to avoid hangs
|
|
25
|
-
if (properties.length > 1000) {
|
|
26
|
-
return [];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return properties
|
|
30
|
-
.map((prop) => prop.name)
|
|
31
|
-
.filter(
|
|
32
|
-
(name) =>
|
|
33
|
-
// Filter out symbol properties and internal properties
|
|
34
|
-
!name.startsWith('__') &&
|
|
35
|
-
// Only include string property names
|
|
36
|
-
typeof name === 'string' &&
|
|
37
|
-
name.length > 0,
|
|
38
|
-
);
|
|
39
|
-
} catch {
|
|
40
|
-
// If there's any error getting properties, return empty array
|
|
41
|
-
return [];
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const isReactComponentFunction = (
|
|
46
|
-
node: DeepReadonly<TSESTree.Node> | undefined | null,
|
|
47
|
-
): boolean => {
|
|
48
|
-
if (node === undefined || node === null) return false;
|
|
49
|
-
|
|
50
|
-
// Arrow function component
|
|
51
|
-
if (node.type === AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
52
|
-
const { body } = node;
|
|
53
|
-
|
|
54
|
-
if (body.type === AST_NODE_TYPES.BlockStatement) {
|
|
55
|
-
return body.body.some((statement) => {
|
|
56
|
-
if (statement.type !== AST_NODE_TYPES.ReturnStatement) return false;
|
|
57
|
-
|
|
58
|
-
const { argument } = statement;
|
|
59
|
-
|
|
60
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
61
|
-
if (argument === null || argument === undefined) return false;
|
|
62
|
-
|
|
63
|
-
const argType = (argument as { type?: string }).type;
|
|
64
|
-
|
|
65
|
-
return argType === 'JSXElement' || argType === 'JSXFragment';
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const bodyType = (body as { type?: string }).type;
|
|
70
|
-
|
|
71
|
-
return bodyType === 'JSXElement' || bodyType === 'JSXFragment';
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return false;
|
|
75
|
-
};
|
|
76
|
-
|
|
77
19
|
export const checkDestructuringCompleteness: TSESLint.RuleModule<
|
|
78
20
|
MessageIds,
|
|
79
21
|
Options
|
|
@@ -302,5 +244,63 @@ export const checkDestructuringCompleteness: TSESLint.RuleModule<
|
|
|
302
244
|
},
|
|
303
245
|
};
|
|
304
246
|
},
|
|
305
|
-
defaultOptions: [],
|
|
247
|
+
defaultOptions: [{ alwaysCheckReactComponentProps: true }],
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
251
|
+
const getObjectTypeProperties = (type: ts.Type): readonly string[] => {
|
|
252
|
+
try {
|
|
253
|
+
const properties = type.getProperties();
|
|
254
|
+
|
|
255
|
+
// Limit to reasonable number of properties to avoid hangs
|
|
256
|
+
if (properties.length > 1000) {
|
|
257
|
+
return [];
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return properties
|
|
261
|
+
.map((prop) => prop.name)
|
|
262
|
+
.filter(
|
|
263
|
+
(name) =>
|
|
264
|
+
// Filter out symbol properties and internal properties
|
|
265
|
+
!name.startsWith('__') &&
|
|
266
|
+
// Only include string property names
|
|
267
|
+
typeof name === 'string' &&
|
|
268
|
+
name.length > 0,
|
|
269
|
+
);
|
|
270
|
+
} catch {
|
|
271
|
+
// If there's any error getting properties, return empty array
|
|
272
|
+
return [];
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const isReactComponentFunction = (
|
|
277
|
+
node: DeepReadonly<TSESTree.Node> | undefined | null,
|
|
278
|
+
): boolean => {
|
|
279
|
+
if (node === undefined || node === null) return false;
|
|
280
|
+
|
|
281
|
+
// Arrow function component
|
|
282
|
+
if (node.type === AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
283
|
+
const { body } = node;
|
|
284
|
+
|
|
285
|
+
if (body.type === AST_NODE_TYPES.BlockStatement) {
|
|
286
|
+
return body.body.some((statement) => {
|
|
287
|
+
if (statement.type !== AST_NODE_TYPES.ReturnStatement) return false;
|
|
288
|
+
|
|
289
|
+
const { argument } = statement;
|
|
290
|
+
|
|
291
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
292
|
+
if (argument === null || argument === undefined) return false;
|
|
293
|
+
|
|
294
|
+
const argType = (argument as { type?: string }).type;
|
|
295
|
+
|
|
296
|
+
return argType === 'JSXElement' || argType === 'JSXFragment';
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const bodyType = (body as { type?: string }).type;
|
|
301
|
+
|
|
302
|
+
return bodyType === 'JSXElement' || bodyType === 'JSXFragment';
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return false;
|
|
306
306
|
};
|
|
@@ -11,4 +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', { ignoreTranspilerName: false }],
|
|
14
15
|
} as const satisfies EslintReactCodingStyleRules;
|
|
@@ -183,6 +183,46 @@ namespace BanUseImperativeHandleHook {
|
|
|
183
183
|
export type RuleEntry = Linter.StringSeverity;
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Require displayName property for React components created with React.memo
|
|
188
|
+
*
|
|
189
|
+
* ```md
|
|
190
|
+
* | key | value |
|
|
191
|
+
* | :--------- | :--------- |
|
|
192
|
+
* | type | suggestion |
|
|
193
|
+
* | deprecated | false |
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
namespace DisplayName {
|
|
197
|
+
/**
|
|
198
|
+
* ### schema
|
|
199
|
+
*
|
|
200
|
+
* ```json
|
|
201
|
+
* [
|
|
202
|
+
* {
|
|
203
|
+
* "type": "object",
|
|
204
|
+
* "properties": {
|
|
205
|
+
* "ignoreTranspilerName": {
|
|
206
|
+
* "type": "boolean",
|
|
207
|
+
* "description": "When true, ignores components that get displayName from variable name"
|
|
208
|
+
* }
|
|
209
|
+
* },
|
|
210
|
+
* "additionalProperties": false
|
|
211
|
+
* }
|
|
212
|
+
* ]
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
export type Options = {
|
|
216
|
+
/** When true, ignores components that get displayName from variable name */
|
|
217
|
+
readonly ignoreTranspilerName?: boolean;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
export type RuleEntry =
|
|
221
|
+
| Linter.Severity
|
|
222
|
+
| SpreadOptionsIfIsArray<readonly [Linter.StringSeverity, Options]>
|
|
223
|
+
| 'off';
|
|
224
|
+
}
|
|
225
|
+
|
|
186
226
|
export type EslintReactCodingStyleRules = {
|
|
187
227
|
readonly 'react-coding-style/component-name': ComponentName.RuleEntry;
|
|
188
228
|
readonly 'react-coding-style/component-var-type-annotation': ComponentVarTypeAnnotation.RuleEntry;
|
|
@@ -192,9 +232,11 @@ export type EslintReactCodingStyleRules = {
|
|
|
192
232
|
readonly 'react-coding-style/react-memo-type-parameter': ReactMemoTypeParameter.RuleEntry;
|
|
193
233
|
readonly 'react-coding-style/use-memo-hook-style': UseMemoHookStyle.RuleEntry;
|
|
194
234
|
readonly 'react-coding-style/ban-use-imperative-handle-hook': BanUseImperativeHandleHook.RuleEntry;
|
|
235
|
+
readonly 'react-coding-style/display-name': DisplayName.RuleEntry;
|
|
195
236
|
};
|
|
196
237
|
|
|
197
238
|
export type EslintReactCodingStyleRulesOption = {
|
|
198
239
|
readonly 'react-coding-style/component-name': ComponentName.Options;
|
|
199
240
|
readonly 'react-coding-style/import-style': ImportStyle.Options;
|
|
241
|
+
readonly 'react-coding-style/display-name': DisplayName.Options;
|
|
200
242
|
};
|