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.
@@ -1,10 +1,10 @@
1
1
  import { type TSESLint } from '@typescript-eslint/utils';
2
2
  type Options = readonly [
3
3
  Readonly<{
4
- ignoreTranspilerName?: boolean;
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,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,CAkGpE,CAAC"}
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 displayName property for React components created with React.memo',
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
- ignoreTranspilerName: {
20
- type: 'boolean',
21
- description: 'When true, ignores components that get displayName from variable name',
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 ignoreTranspilerName = options.ignoreTranspilerName ?? false;
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
- if (ignoreTranspilerName) {
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
- const parent = node.parent;
49
- const grandParent = parent.parent;
50
- if (grandParent.type !== AST_NODE_TYPES.Program) {
62
+ if (!isComponentDisplayNameAssignment(assignment, componentName)) {
63
+ context.report({
64
+ node: castDeepMutable(node),
65
+ messageId: 'missingDisplayName',
66
+ });
51
67
  return;
52
68
  }
53
- const program = grandParent;
54
- const componentIndex = program.body.indexOf(parent);
55
- if (componentIndex === -1) {
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
- 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) {
78
+ if (shouldIgnoreMismatch(componentName)) {
79
+ return;
80
+ }
81
+ if (displayName !== componentName) {
71
82
  context.report({
72
- // eslint-disable-next-line total-functions/no-unsafe-type-assertion
73
- node: node.id,
74
- messageId: 'missingDisplayName',
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: [{ ignoreTranspilerName: false }],
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,2EAA2E;AAC9E,SAAA;AACD,QAAA,MAAM,EAAE;AACN,YAAA;AACE,gBAAA,IAAI,EAAE,QAAQ;AACd,gBAAA,UAAU,EAAE;AACV,oBAAA,oBAAoB,EAAE;AACpB,wBAAA,IAAI,EAAE,SAAS;AACf,wBAAA,WAAW,EACT,uEAAuE;AAC1E,qBAAA;AACF,iBAAA;AACD,gBAAA,oBAAoB,EAAE,KAAK;AAC5B,aAAA;AACF,SAAA;AACD,QAAA,QAAQ,EAAE;AACR,YAAA,kBAAkB,EAChB,mEAAmE;AACtE,SAAA;AACF,KAAA;AACD,IAAA,MAAM,EAAE,CAAC,OAAO,KAAI;QAClB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE;AAExC,QAAA,MAAM,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,IAAI,KAAK;AAElE,QAAA,MAAM,cAAc,GAAG,CACrB,IAA+C,KACvC;YACR,IAAI,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,cAAc,CAAC,UAAU,EAAE;gBAC9C;;YAGF,IAAI,IAAI,CAAC,IAAI,EAAE,IAAI,KAAK,cAAc,CAAC,cAAc,EAAE;gBACrD;;AAGF,YAAA,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE;gBAC/C;;AAGF,YAAA,MAAM,aAAa,GAAG,IAAI,CAAC,EAAE,CAAC,IAAI;YAElC,IAAI,oBAAoB,EAAE;gBACxB;;AAGF,YAAA,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM;AAI1B,YAAA,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM;YAEjC,IAAI,WAAW,CAAC,IAAI,KAAK,cAAc,CAAC,OAAO,EAAE;gBAC/C;;YAGF,MAAM,OAAO,GAAG,WAAW;YAE3B,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;AAEnD,YAAA,IAAI,cAAc,KAAK,EAAE,EAAE;gBACzB;;YAGF,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC;AAEtD,YAAA,MAAM,cAAc,GAClB,aAAa,KAAK,SAAS;AAC3B,gBAAA,aAAa,CAAC,IAAI,KAAK,cAAc,CAAC,mBAAmB;AACzD,gBAAA,aAAa,CAAC,UAAU,CAAC,IAAI,KAAK,cAAc,CAAC,oBAAoB;AACrE,gBAAA,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI;AAChC,oBAAA,cAAc,CAAC,gBAAgB;AACjC,gBAAA,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI;AACvC,oBAAA,cAAc,CAAC,UAAU;gBAC3B,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,aAAa;AAC3D,gBAAA,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI;AACzC,oBAAA,cAAc,CAAC,UAAU;gBAC3B,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,aAAa;YAE/D,IAAI,CAAC,cAAc,EAAE;gBACnB,OAAO,CAAC,MAAM,CAAC;;oBAEb,IAAI,EAAE,IAAI,CAAC,EAAW;AACtB,oBAAA,SAAS,EAAE,oBAAoB;AAChC,iBAAA,CAAC;;AAEN,QAAA,CAAC;QAED,OAAO;AACL,YAAA,kBAAkB,EAAE,cAAc;SACnC;KACF;AACD,IAAA,cAAc,EAAE,CAAC,EAAE,oBAAoB,EAAE,KAAK,EAAE,CAAC;;;;;"}
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
- ignoreTranspilerName?: boolean;
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;;;;;;;;;;;;;;;;CAYQ,CAAC"}
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', { ignoreTranspilerName: false }],
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;IACjD,iCAAiC,EAAE,CAAC,OAAO,EAAE,EAAE,oBAAoB,EAAE,KAAK,EAAE,CAAC;;;;;"}
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': 2 | 1;
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': withDefaultOption('error'),
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":"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"}
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 displayName property for React components created with React.memo
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
- * "ignoreTranspilerName": {
183
- * "type": "boolean",
184
- * "description": "When true, ignores components that get displayName from variable name"
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
- /** When true, ignores components that get displayName from variable name */
194
- ignoreTranspilerName?: boolean;
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":"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"}
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.6",
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
- - `ignoreTranspilerName` (boolean, default: `false`): When true, ignores components that get displayName from variable name (many transpilers add this automatically).
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 ignoreTranspilerName: true)
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 { expectType } from 'ts-data-forge';
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
- ignoreTranspilerName?: boolean;
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 displayName property for React components created with React.memo',
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
- ignoreTranspilerName: {
33
- type: 'boolean',
32
+ ignoreName: {
34
33
  description:
35
- 'When true, ignores components that get displayName from variable name',
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 ignoreTranspilerName = options.ignoreTranspilerName ?? false;
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
- if (ignoreTranspilerName) {
69
- return;
70
- }
80
+ const assignment = getDisplayNameAssignment(node);
71
81
 
72
- const parent = node.parent;
82
+ if (assignment === undefined) {
83
+ context.report({
84
+ node: castDeepMutable(node),
85
+ messageId: 'missingDisplayName',
86
+ });
73
87
 
74
- expectType<typeof parent.type, AST_NODE_TYPES.VariableDeclaration>('=');
88
+ return;
89
+ }
75
90
 
76
- const grandParent = parent.parent;
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 program = grandParent;
100
+ const displayName = extractDisplayName(assignment.right);
83
101
 
84
- const componentIndex = program.body.indexOf(parent);
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 (componentIndex === -1) {
112
+ if (shouldIgnoreMismatch(componentName)) {
87
113
  return;
88
114
  }
89
115
 
90
- const nextStatement = program.body[componentIndex + 1];
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
- // eslint-disable-next-line total-functions/no-unsafe-type-assertion
108
- node: node.id as never,
109
- messageId: 'missingDisplayName',
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: [{ ignoreTranspilerName: false }],
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('ignoreTranspilerName option', () => {
66
- tester.run('display-name with ignoreTranspilerName', displayNameRule, {
119
+ describe('ignoreName option', () => {
120
+ tester.run('display-name with ignoreName', displayNameRule, {
67
121
  valid: [
68
122
  {
69
- name: 'Component without displayName (ignored)',
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: [{ ignoreTranspilerName: true }],
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: [{ ignoreTranspilerName: true }],
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', { ignoreTranspilerName: false }],
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': withDefaultOption('error'),
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 displayName property for React components created with React.memo
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
- * "ignoreTranspilerName": {
206
- * "type": "boolean",
207
- * "description": "When true, ignores components that get displayName from variable name"
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
- /** When true, ignores components that get displayName from variable name */
217
- ignoreTranspilerName?: boolean;
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 =