eslint-plugin-absolute 0.2.0 → 0.2.2

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/index.js CHANGED
@@ -2,24 +2,9 @@
2
2
  // src/rules/no-nested-jsx-return.ts
3
3
  import { AST_NODE_TYPES } from "@typescript-eslint/utils";
4
4
  var noNestedJSXReturn = {
5
- meta: {
6
- type: "problem",
7
- docs: {
8
- description: "Disallow nested functions that return non-component, non-singular JSX to enforce one component per file"
9
- },
10
- schema: [],
11
- messages: {
12
- nestedFunctionJSX: "Nested function returning non-component, non-singular JSX detected. Extract it into its own component.",
13
- nestedArrowJSX: "Nested arrow function returning non-component, non-singular JSX detected. Extract it into its own component.",
14
- nestedArrowFragment: "Nested arrow function returning a non-singular JSX fragment detected. Extract it into its own component."
15
- }
16
- },
17
- defaultOptions: [],
18
5
  create(context) {
19
- function isJSX(node) {
20
- return !!node && (node.type === AST_NODE_TYPES.JSXElement || node.type === AST_NODE_TYPES.JSXFragment);
21
- }
22
- function getLeftmostJSXIdentifier(name) {
6
+ const isJSX = (node) => node !== null && node !== undefined && (node.type === AST_NODE_TYPES.JSXElement || node.type === AST_NODE_TYPES.JSXFragment);
7
+ const getLeftmostJSXIdentifier = (name) => {
23
8
  let current = name;
24
9
  while (current.type === AST_NODE_TYPES.JSXMemberExpression) {
25
10
  current = current.object;
@@ -28,8 +13,8 @@ var noNestedJSXReturn = {
28
13
  return current;
29
14
  }
30
15
  return null;
31
- }
32
- function isJSXComponentElement(node) {
16
+ };
17
+ const isJSXComponentElement = (node) => {
33
18
  if (!node || node.type !== AST_NODE_TYPES.JSXElement) {
34
19
  return false;
35
20
  }
@@ -43,286 +28,334 @@ var noNestedJSXReturn = {
43
28
  return false;
44
29
  }
45
30
  return /^[A-Z]/.test(leftmost.name);
46
- }
47
- function isSingularJSXReturn(node) {
48
- if (!isJSX(node))
49
- return false;
50
- const children = node.children.filter((child) => {
31
+ };
32
+ const hasNoMeaningfulChildren = (children) => {
33
+ const filtered = children.filter((child) => {
51
34
  if (child.type === AST_NODE_TYPES.JSXText) {
52
35
  return child.value.trim() !== "";
53
36
  }
54
37
  return true;
55
38
  });
39
+ return filtered.length === 0;
40
+ };
41
+ const isSingularJSXReturn = (node) => {
42
+ if (!isJSX(node))
43
+ return false;
44
+ const children = node.children.filter((child2) => {
45
+ if (child2.type === AST_NODE_TYPES.JSXText) {
46
+ return child2.value.trim() !== "";
47
+ }
48
+ return true;
49
+ });
56
50
  if (children.length === 0) {
57
51
  return true;
58
52
  }
59
- if (children.length === 1) {
60
- const child = children[0];
61
- if (!child) {
62
- return false;
63
- }
64
- if (child.type === AST_NODE_TYPES.JSXElement || child.type === AST_NODE_TYPES.JSXFragment) {
65
- const innerChildren = child.children.filter((innerChild) => {
66
- if (innerChild.type === AST_NODE_TYPES.JSXText) {
67
- return innerChild.value.trim() !== "";
68
- }
69
- return true;
70
- });
71
- return innerChildren.length === 0;
72
- }
73
- return true;
53
+ if (children.length !== 1) {
54
+ return false;
74
55
  }
75
- return false;
76
- }
56
+ const [child] = children;
57
+ if (!child) {
58
+ return false;
59
+ }
60
+ if (child.type === AST_NODE_TYPES.JSXElement || child.type === AST_NODE_TYPES.JSXFragment) {
61
+ return hasNoMeaningfulChildren(child.children);
62
+ }
63
+ return true;
64
+ };
77
65
  const functionStack = [];
78
- function pushFunction(node) {
66
+ const pushFunction = (node) => {
79
67
  functionStack.push(node);
80
- }
81
- function popFunction() {
68
+ };
69
+ const popFunction = () => {
82
70
  functionStack.pop();
83
- }
71
+ };
84
72
  return {
85
- "FunctionDeclaration, FunctionExpression, ArrowFunctionExpression"(node) {
86
- pushFunction(node);
87
- },
88
- "FunctionDeclaration:exit"(_node) {
89
- popFunction();
90
- },
91
- "FunctionExpression:exit"(_node) {
92
- popFunction();
93
- },
94
- "ArrowFunctionExpression:exit"(_node) {
95
- popFunction();
96
- },
97
- ReturnStatement(node) {
73
+ "ArrowFunctionExpression > JSXElement"(node) {
98
74
  if (functionStack.length <= 1) {
99
75
  return;
100
76
  }
101
- const argument = node.argument;
102
- if (!isJSX(argument)) {
103
- return;
104
- }
105
- if (!isJSXComponentElement(argument) && !isSingularJSXReturn(argument)) {
77
+ if (!isJSXComponentElement(node) && !isSingularJSXReturn(node)) {
106
78
  context.report({
107
- node,
108
- messageId: "nestedFunctionJSX"
79
+ messageId: "nestedArrowJSX",
80
+ node
109
81
  });
110
82
  }
111
83
  },
112
- "ArrowFunctionExpression > JSXElement"(node) {
84
+ "ArrowFunctionExpression > JSXFragment"(node) {
113
85
  if (functionStack.length <= 1) {
114
86
  return;
115
87
  }
116
- if (!isJSXComponentElement(node) && !isSingularJSXReturn(node)) {
88
+ if (!isSingularJSXReturn(node)) {
117
89
  context.report({
118
- node,
119
- messageId: "nestedArrowJSX"
90
+ messageId: "nestedArrowFragment",
91
+ node
120
92
  });
121
93
  }
122
94
  },
123
- "ArrowFunctionExpression > JSXFragment"(node) {
95
+ "ArrowFunctionExpression:exit"() {
96
+ popFunction();
97
+ },
98
+ "FunctionDeclaration, FunctionExpression, ArrowFunctionExpression"(node) {
99
+ pushFunction(node);
100
+ },
101
+ "FunctionDeclaration:exit"() {
102
+ popFunction();
103
+ },
104
+ "FunctionExpression:exit"() {
105
+ popFunction();
106
+ },
107
+ ReturnStatement(node) {
124
108
  if (functionStack.length <= 1) {
125
109
  return;
126
110
  }
127
- if (!isSingularJSXReturn(node)) {
111
+ const { argument } = node;
112
+ if (!isJSX(argument)) {
113
+ return;
114
+ }
115
+ if (!isJSXComponentElement(argument) && !isSingularJSXReturn(argument)) {
128
116
  context.report({
129
- node,
130
- messageId: "nestedArrowFragment"
117
+ messageId: "nestedFunctionJSX",
118
+ node
131
119
  });
132
120
  }
133
121
  }
134
122
  };
123
+ },
124
+ defaultOptions: [],
125
+ meta: {
126
+ docs: {
127
+ description: "Disallow nested functions that return non-component, non-singular JSX to enforce one component per file"
128
+ },
129
+ messages: {
130
+ nestedArrowFragment: "Nested arrow function returning a non-singular JSX fragment detected. Extract it into its own component.",
131
+ nestedArrowJSX: "Nested arrow function returning non-component, non-singular JSX detected. Extract it into its own component.",
132
+ nestedFunctionJSX: "Nested function returning non-component, non-singular JSX detected. Extract it into its own component."
133
+ },
134
+ schema: [],
135
+ type: "problem"
135
136
  }
136
137
  };
137
138
 
138
139
  // src/rules/explicit-object-types.ts
139
140
  var explicitObjectTypes = {
140
- meta: {
141
- type: "problem",
142
- docs: {
143
- description: "Require explicit type annotations for object literals and arrays of object literals"
144
- },
145
- schema: [],
146
- messages: {
147
- objectLiteralNeedsType: "Object literal must have an explicit type annotation.",
148
- arrayOfObjectLiteralsNeedsType: "Array of object literals must have an explicit type annotation."
149
- }
150
- },
151
- defaultOptions: [],
152
141
  create(context) {
153
- function isObjectLiteral(node) {
154
- return !!node && node.type === "ObjectExpression";
155
- }
142
+ const isObjectLiteral = (node) => node !== null && node !== undefined && node.type === "ObjectExpression";
156
143
  return {
157
144
  VariableDeclarator(node) {
158
145
  if (!node.init)
159
146
  return;
160
147
  if (node.id.type === "Identifier" && node.id.typeAnnotation)
161
148
  return;
162
- if (isObjectLiteral(node.init)) {
163
- if (node.id.type === "Identifier") {
164
- context.report({
165
- node: node.id,
166
- messageId: "objectLiteralNeedsType"
167
- });
168
- }
149
+ if (isObjectLiteral(node.init) && node.id.type === "Identifier") {
150
+ context.report({
151
+ messageId: "objectLiteralNeedsType",
152
+ node: node.id
153
+ });
154
+ return;
155
+ }
156
+ if (node.init.type !== "ArrayExpression") {
169
157
  return;
170
158
  }
171
- if (node.init.type === "ArrayExpression") {
172
- const hasObjectLiteral = node.init.elements.some((element) => {
173
- if (!element || element.type === "SpreadElement")
174
- return false;
175
- return isObjectLiteral(element);
159
+ const hasObjectLiteral = node.init.elements.some((element) => {
160
+ if (!element || element.type === "SpreadElement")
161
+ return false;
162
+ return isObjectLiteral(element);
163
+ });
164
+ if (hasObjectLiteral && node.id.type === "Identifier") {
165
+ context.report({
166
+ messageId: "arrayOfObjectLiteralsNeedsType",
167
+ node: node.id
176
168
  });
177
- if (hasObjectLiteral && node.id.type === "Identifier") {
178
- context.report({
179
- node: node.id,
180
- messageId: "arrayOfObjectLiteralsNeedsType"
181
- });
182
- }
183
169
  }
184
170
  }
185
171
  };
172
+ },
173
+ defaultOptions: [],
174
+ meta: {
175
+ docs: {
176
+ description: "Require explicit type annotations for object literals and arrays of object literals"
177
+ },
178
+ messages: {
179
+ arrayOfObjectLiteralsNeedsType: "Array of object literals must have an explicit type annotation.",
180
+ objectLiteralNeedsType: "Object literal must have an explicit type annotation."
181
+ },
182
+ schema: [],
183
+ type: "problem"
186
184
  }
187
185
  };
188
186
 
189
187
  // src/rules/sort-keys-fixable.ts
188
+ var SORT_BEFORE = -1;
189
+ var hasDuplicateNames = (names) => {
190
+ const seen = new Set;
191
+ const nonNullNames = names.flatMap((name) => name === null ? [] : [name]);
192
+ for (const name of nonNullNames) {
193
+ if (seen.has(name)) {
194
+ return true;
195
+ }
196
+ seen.add(name);
197
+ }
198
+ return false;
199
+ };
200
+ var isSafeStaticTemplate = (node) => node.expressions.length === 0;
201
+ var isSafeArrayElement = (node) => {
202
+ if (!node || node.type === "SpreadElement") {
203
+ return false;
204
+ }
205
+ return isSafeToReorderExpression(node);
206
+ };
207
+ var isSafeObjectProperty = (property) => {
208
+ if (property.type !== "Property" || property.computed || property.kind !== "init") {
209
+ return false;
210
+ }
211
+ if (property.key.type !== "Identifier" && property.key.type !== "Literal") {
212
+ return false;
213
+ }
214
+ if (property.method) {
215
+ return true;
216
+ }
217
+ return isSafeToReorderExpression(property.value);
218
+ };
219
+ var isSafeToReorderExpression = (node) => {
220
+ if (!node || node.type === "PrivateIdentifier") {
221
+ return false;
222
+ }
223
+ switch (node.type) {
224
+ case "Identifier":
225
+ case "Literal":
226
+ case "ThisExpression":
227
+ case "FunctionExpression":
228
+ case "ArrowFunctionExpression":
229
+ case "ClassExpression":
230
+ return true;
231
+ case "TemplateLiteral":
232
+ return isSafeStaticTemplate(node);
233
+ case "UnaryExpression":
234
+ return isSafeToReorderExpression(node.argument);
235
+ case "ArrayExpression":
236
+ return node.elements.every(isSafeArrayElement);
237
+ case "ObjectExpression":
238
+ return node.properties.every(isSafeObjectProperty);
239
+ default:
240
+ return false;
241
+ }
242
+ };
243
+ var isSafeJSXAttributeValue = (value) => {
244
+ if (value === null) {
245
+ return true;
246
+ }
247
+ if (value.type === "Literal") {
248
+ return true;
249
+ }
250
+ if (value.type !== "JSXExpressionContainer") {
251
+ return false;
252
+ }
253
+ if (value.expression.type === "JSXEmptyExpression") {
254
+ return false;
255
+ }
256
+ return isSafeToReorderExpression(value.expression);
257
+ };
190
258
  var sortKeysFixable = {
191
- meta: {
192
- type: "suggestion",
193
- docs: {
194
- description: "enforce sorted keys in object literals with auto-fix (limited to simple cases, preserving comments)"
195
- },
196
- fixable: "code",
197
- schema: [
198
- {
199
- type: "object",
200
- properties: {
201
- order: {
202
- type: "string",
203
- enum: ["asc", "desc"]
204
- },
205
- caseSensitive: {
206
- type: "boolean"
207
- },
208
- natural: {
209
- type: "boolean"
210
- },
211
- minKeys: {
212
- type: "integer",
213
- minimum: 2
214
- },
215
- variablesBeforeFunctions: {
216
- type: "boolean"
217
- }
218
- },
219
- additionalProperties: false
220
- }
221
- ],
222
- messages: {
223
- unsorted: "Object keys are not sorted."
224
- }
225
- },
226
- defaultOptions: [{}],
227
259
  create(context) {
228
- const sourceCode = context.sourceCode;
229
- const option = context.options[0];
260
+ const { sourceCode } = context;
261
+ const [option] = context.options;
230
262
  const order = option && option.order ? option.order : "asc";
231
263
  const caseSensitive = option && typeof option.caseSensitive === "boolean" ? option.caseSensitive : false;
232
264
  const natural = option && typeof option.natural === "boolean" ? option.natural : false;
233
265
  const minKeys = option && typeof option.minKeys === "number" ? option.minKeys : 2;
234
266
  const variablesBeforeFunctions = option && typeof option.variablesBeforeFunctions === "boolean" ? option.variablesBeforeFunctions : false;
235
- function compareKeys(a, b) {
236
- let keyA = a;
237
- let keyB = b;
267
+ const compareKeys = (keyLeft, keyRight) => {
268
+ let left = keyLeft;
269
+ let right = keyRight;
238
270
  if (!caseSensitive) {
239
- keyA = keyA.toLowerCase();
240
- keyB = keyB.toLowerCase();
271
+ left = left.toLowerCase();
272
+ right = right.toLowerCase();
241
273
  }
242
274
  if (natural) {
243
- return keyA.localeCompare(keyB, undefined, {
275
+ return left.localeCompare(right, undefined, {
244
276
  numeric: true
245
277
  });
246
278
  }
247
- return keyA.localeCompare(keyB);
248
- }
249
- function isFunctionProperty(prop) {
250
- const value = prop.value;
251
- return !!value && (value.type === "FunctionExpression" || value.type === "ArrowFunctionExpression" || prop.method === true);
252
- }
253
- function getPropertyKeyName(prop) {
254
- const key = prop.key;
279
+ return left.localeCompare(right);
280
+ };
281
+ const isFunctionProperty = (prop) => {
282
+ const { value } = prop;
283
+ return Boolean(value) && (value.type === "FunctionExpression" || value.type === "ArrowFunctionExpression" || prop.method === true);
284
+ };
285
+ const getPropertyKeyName = (prop) => {
286
+ const { key } = prop;
255
287
  if (key.type === "Identifier") {
256
288
  return key.name;
257
289
  }
258
290
  if (key.type === "Literal") {
259
- const value = key.value;
291
+ const { value } = key;
260
292
  if (typeof value === "string") {
261
293
  return value;
262
294
  }
263
295
  return String(value);
264
296
  }
265
297
  return "";
266
- }
267
- function getLeadingComments(prop, prevProp) {
298
+ };
299
+ const getLeadingComments = (prop, prevProp) => {
268
300
  const comments = sourceCode.getCommentsBefore(prop);
269
301
  if (!prevProp || comments.length === 0) {
270
302
  return comments;
271
303
  }
272
- return comments.filter((c) => c.loc.start.line !== prevProp.loc.end.line);
273
- }
274
- function getTrailingComments(prop, nextProp) {
275
- const after = sourceCode.getCommentsAfter(prop).filter((c) => c.loc.start.line === prop.loc.end.line);
276
- if (nextProp) {
277
- const beforeNext = sourceCode.getCommentsBefore(nextProp);
278
- const trailingOfPrev = beforeNext.filter((c) => c.loc.start.line === prop.loc.end.line);
279
- for (const c of trailingOfPrev) {
280
- if (!after.some((a) => a.range[0] === c.range[0])) {
281
- after.push(c);
282
- }
283
- }
284
- }
304
+ return comments.filter((comment) => comment.loc.start.line !== prevProp.loc.end.line);
305
+ };
306
+ const getTrailingComments = (prop, nextProp) => {
307
+ const after = sourceCode.getCommentsAfter(prop).filter((comment) => comment.loc.start.line === prop.loc.end.line);
308
+ if (!nextProp) {
309
+ return after;
310
+ }
311
+ const beforeNext = sourceCode.getCommentsBefore(nextProp);
312
+ const trailingOfPrev = beforeNext.filter((comment) => comment.loc.start.line === prop.loc.end.line);
313
+ const newComments = trailingOfPrev.filter((comment) => !after.some((existing) => existing.range[0] === comment.range[0]));
314
+ after.push(...newComments);
285
315
  return after;
286
- }
287
- function buildSortedText(fixableProps, rangeStart) {
316
+ };
317
+ const getChunkStart = (idx, fixableProps, rangeStart, fullStart) => {
318
+ if (idx === 0) {
319
+ return rangeStart;
320
+ }
321
+ const prevProp = fixableProps[idx - 1];
322
+ const currentProp = fixableProps[idx];
323
+ const prevTrailing = getTrailingComments(prevProp, currentProp);
324
+ const prevEnd = prevTrailing.length > 0 ? prevTrailing[prevTrailing.length - 1].range[1] : prevProp.range[1];
325
+ const allTokens = sourceCode.getTokensBetween(prevProp, currentProp, {
326
+ includeComments: false
327
+ });
328
+ const tokenAfterPrev = allTokens.find((tok) => tok.range[0] >= prevEnd) ?? null;
329
+ if (tokenAfterPrev && tokenAfterPrev.value === "," && tokenAfterPrev.range[1] <= fullStart) {
330
+ return tokenAfterPrev.range[1];
331
+ }
332
+ return prevEnd;
333
+ };
334
+ const buildSortedText = (fixableProps, rangeStart) => {
288
335
  const chunks = [];
289
- for (let i = 0;i < fixableProps.length; i++) {
290
- const prop = fixableProps[i];
291
- const prevProp = i > 0 ? fixableProps[i - 1] : null;
292
- const nextProp = i < fixableProps.length - 1 ? fixableProps[i + 1] : null;
336
+ for (let idx = 0;idx < fixableProps.length; idx++) {
337
+ const prop = fixableProps[idx];
338
+ const prevProp = idx > 0 ? fixableProps[idx - 1] : null;
339
+ const nextProp = idx < fixableProps.length - 1 ? fixableProps[idx + 1] : null;
293
340
  const leading = getLeadingComments(prop, prevProp);
294
341
  const trailing = getTrailingComments(prop, nextProp);
295
342
  const fullStart = leading.length > 0 ? leading[0].range[0] : prop.range[0];
296
343
  const fullEnd = trailing.length > 0 ? trailing[trailing.length - 1].range[1] : prop.range[1];
297
- let chunkStart;
298
- if (i === 0) {
299
- chunkStart = rangeStart;
300
- } else {
301
- const prevTrailing = getTrailingComments(prevProp, prop);
302
- const prevEnd = prevTrailing.length > 0 ? prevTrailing[prevTrailing.length - 1].range[1] : prevProp.range[1];
303
- const tokenAfterPrev = sourceCode.getTokenAfter({
304
- range: [prevEnd, prevEnd]
305
- }, { includeComments: false });
306
- if (tokenAfterPrev && tokenAfterPrev.value === "," && tokenAfterPrev.range[1] <= fullStart) {
307
- chunkStart = tokenAfterPrev.range[1];
308
- } else {
309
- chunkStart = prevEnd;
310
- }
311
- }
344
+ const chunkStart = getChunkStart(idx, fixableProps, rangeStart, fullStart);
312
345
  const text = sourceCode.text.slice(chunkStart, fullEnd);
313
346
  chunks.push({ prop, text });
314
347
  }
315
- const sorted = chunks.slice().sort((a, b) => {
348
+ const sorted = chunks.slice().sort((left, right) => {
316
349
  if (variablesBeforeFunctions) {
317
- const aIsFunc = isFunctionProperty(a.prop);
318
- const bIsFunc = isFunctionProperty(b.prop);
319
- if (aIsFunc !== bIsFunc) {
320
- return aIsFunc ? 1 : -1;
350
+ const leftIsFunc = isFunctionProperty(left.prop);
351
+ const rightIsFunc = isFunctionProperty(right.prop);
352
+ if (leftIsFunc !== rightIsFunc) {
353
+ return leftIsFunc ? 1 : SORT_BEFORE;
321
354
  }
322
355
  }
323
- const aKey = getPropertyKeyName(a.prop);
324
- const bKey = getPropertyKeyName(b.prop);
325
- let res = compareKeys(aKey, bKey);
356
+ const leftKey = getPropertyKeyName(left.prop);
357
+ const rightKey = getPropertyKeyName(right.prop);
358
+ let res = compareKeys(leftKey, rightKey);
326
359
  if (order === "desc") {
327
360
  res = -res;
328
361
  }
@@ -336,12 +369,12 @@ var sortKeysFixable = {
336
369
  const col = fixableProps[0].loc.start.column;
337
370
  const indent = sourceCode.text.slice(fixableProps[0].range[0] - col, fixableProps[0].range[0]);
338
371
  separator = `,
339
- ` + indent;
372
+ ${indent}`;
340
373
  } else {
341
374
  separator = ", ";
342
375
  }
343
- return sorted.map((chunk, i) => {
344
- if (i === 0) {
376
+ return sorted.map((chunk, idx) => {
377
+ if (idx === 0) {
345
378
  const originalFirstChunk = chunks[0];
346
379
  const originalLeadingWs = originalFirstChunk.text.match(/^(\s*)/)?.[1] ?? "";
347
380
  const stripped2 = chunk.text.replace(/^\s*/, "");
@@ -350,8 +383,9 @@ var sortKeysFixable = {
350
383
  const stripped = chunk.text.replace(/^\s*/, "");
351
384
  return separator + stripped;
352
385
  }).join("");
353
- }
354
- function checkObjectExpression(node) {
386
+ };
387
+ const getFixableProps = (node) => node.properties.filter((prop) => prop.type === "Property" && !prop.computed && (prop.key.type === "Identifier" || prop.key.type === "Literal"));
388
+ const checkObjectExpression = (node) => {
355
389
  if (node.properties.length < minKeys) {
356
390
  return;
357
391
  }
@@ -359,380 +393,392 @@ var sortKeysFixable = {
359
393
  const keys = node.properties.map((prop) => {
360
394
  let keyName = null;
361
395
  let isFunc = false;
362
- if (prop.type === "Property") {
363
- if (prop.computed) {
364
- autoFixable = false;
365
- }
366
- if (prop.key.type === "Identifier") {
367
- keyName = prop.key.name;
368
- } else if (prop.key.type === "Literal") {
369
- const value = prop.key.value;
370
- keyName = typeof value === "string" ? value : String(value);
371
- } else {
372
- autoFixable = false;
373
- }
374
- if (isFunctionProperty(prop)) {
375
- isFunc = true;
376
- }
396
+ if (prop.type !== "Property") {
397
+ autoFixable = false;
398
+ return {
399
+ isFunction: isFunc,
400
+ keyName,
401
+ node: prop
402
+ };
403
+ }
404
+ if (prop.computed) {
405
+ autoFixable = false;
406
+ }
407
+ if (prop.key.type === "Identifier") {
408
+ keyName = prop.key.name;
409
+ } else if (prop.key.type === "Literal") {
410
+ const { value } = prop.key;
411
+ keyName = typeof value === "string" ? value : String(value);
377
412
  } else {
378
413
  autoFixable = false;
379
414
  }
415
+ if (isFunctionProperty(prop)) {
416
+ isFunc = true;
417
+ }
380
418
  return {
419
+ isFunction: isFunc,
381
420
  keyName,
382
- node: prop,
383
- isFunction: isFunc
421
+ node: prop
384
422
  };
385
423
  });
386
- const getFixableProps = () => {
387
- const props = [];
388
- for (const prop of node.properties) {
389
- if (prop.type !== "Property") {
390
- continue;
391
- }
392
- if (prop.computed) {
393
- continue;
394
- }
395
- if (prop.key.type !== "Identifier" && prop.key.type !== "Literal") {
396
- continue;
397
- }
398
- props.push(prop);
399
- }
400
- return props;
401
- };
424
+ if (hasDuplicateNames(keys.map((key) => key.keyName))) {
425
+ autoFixable = false;
426
+ }
427
+ if (autoFixable && keys.some((key) => key.node.type === "Property" && !isSafeToReorderExpression(key.node.value))) {
428
+ autoFixable = false;
429
+ }
402
430
  let fixProvided = false;
403
- for (let i = 1;i < keys.length; i++) {
404
- const prev = keys[i - 1];
405
- const curr = keys[i];
406
- if (!prev || !curr) {
407
- continue;
431
+ const createReportWithFix = (curr, shouldFix) => {
432
+ context.report({
433
+ fix: shouldFix ? (fixer) => {
434
+ const fixableProps = getFixableProps(node);
435
+ if (fixableProps.length < minKeys) {
436
+ return null;
437
+ }
438
+ const [firstProp] = fixableProps;
439
+ const lastProp = fixableProps[fixableProps.length - 1];
440
+ if (!firstProp || !lastProp) {
441
+ return null;
442
+ }
443
+ const firstLeading = getLeadingComments(firstProp, null);
444
+ const [firstLeadingComment] = firstLeading;
445
+ const rangeStart = firstLeadingComment ? firstLeadingComment.range[0] : firstProp.range[0];
446
+ const lastTrailing = getTrailingComments(lastProp, null);
447
+ const rangeEnd = lastTrailing.length > 0 ? lastTrailing[lastTrailing.length - 1].range[1] : lastProp.range[1];
448
+ const sortedText = buildSortedText(fixableProps, rangeStart);
449
+ return fixer.replaceTextRange([rangeStart, rangeEnd], sortedText);
450
+ } : null,
451
+ messageId: "unsorted",
452
+ node: curr.node.type === "Property" ? curr.node.key : curr.node
453
+ });
454
+ fixProvided = true;
455
+ };
456
+ keys.forEach((curr, idx) => {
457
+ if (idx === 0) {
458
+ return;
408
459
  }
409
- if (prev.keyName === null || curr.keyName === null) {
410
- continue;
460
+ const prev = keys[idx - 1];
461
+ if (!prev || !curr || prev.keyName === null || curr.keyName === null) {
462
+ return;
411
463
  }
412
464
  const shouldFix = !fixProvided && autoFixable;
413
- const reportWithFix = () => {
414
- context.report({
415
- node: curr.node.type === "Property" ? curr.node.key : curr.node,
416
- messageId: "unsorted",
417
- fix: shouldFix ? (fixer) => {
418
- const fixableProps = getFixableProps();
419
- if (fixableProps.length < minKeys) {
420
- return null;
421
- }
422
- const firstProp = fixableProps[0];
423
- const lastProp = fixableProps[fixableProps.length - 1];
424
- if (!firstProp || !lastProp) {
425
- return null;
426
- }
427
- const firstLeading = getLeadingComments(firstProp, null);
428
- const rangeStart = firstLeading.length > 0 ? firstLeading[0].range[0] : firstProp.range[0];
429
- const lastTrailing = getTrailingComments(lastProp, null);
430
- const rangeEnd = lastTrailing.length > 0 ? lastTrailing[lastTrailing.length - 1].range[1] : lastProp.range[1];
431
- const sortedText = buildSortedText(fixableProps, rangeStart);
432
- return fixer.replaceTextRange([rangeStart, rangeEnd], sortedText);
433
- } : null
434
- });
435
- fixProvided = true;
436
- };
437
- if (variablesBeforeFunctions) {
438
- if (prev.isFunction && !curr.isFunction) {
439
- reportWithFix();
440
- continue;
441
- }
442
- if (prev.isFunction === curr.isFunction && compareKeys(prev.keyName, curr.keyName) > 0) {
443
- reportWithFix();
444
- }
445
- } else {
446
- if (compareKeys(prev.keyName, curr.keyName) > 0) {
447
- reportWithFix();
448
- }
465
+ if (variablesBeforeFunctions && prev.isFunction && !curr.isFunction) {
466
+ createReportWithFix(curr, shouldFix);
467
+ return;
449
468
  }
450
- }
451
- }
452
- function checkJSXAttributeObject(attr) {
453
- const value = attr.value;
454
- if (value && value.type === "JSXExpressionContainer") {
455
- const expr = value.expression;
456
- if (expr && expr.type === "ObjectExpression") {
457
- checkObjectExpression(expr);
469
+ if (variablesBeforeFunctions && prev.isFunction === curr.isFunction && compareKeys(prev.keyName, curr.keyName) > 0) {
470
+ createReportWithFix(curr, shouldFix);
471
+ return;
472
+ }
473
+ if (!variablesBeforeFunctions && compareKeys(prev.keyName, curr.keyName) > 0) {
474
+ createReportWithFix(curr, shouldFix);
458
475
  }
476
+ });
477
+ };
478
+ const checkJSXAttributeObject = (attr) => {
479
+ const { value } = attr;
480
+ if (value && value.type === "JSXExpressionContainer" && value.expression && value.expression.type === "ObjectExpression") {
481
+ checkObjectExpression(value.expression);
459
482
  }
460
- }
461
- function checkJSXOpeningElement(node) {
483
+ };
484
+ const getAttrName = (attr) => {
485
+ if (attr.type !== "JSXAttribute" || attr.name.type !== "JSXIdentifier") {
486
+ return "";
487
+ }
488
+ return attr.name.name;
489
+ };
490
+ const compareAttrNames = (nameLeft, nameRight) => {
491
+ let res = compareKeys(nameLeft, nameRight);
492
+ if (order === "desc") {
493
+ res = -res;
494
+ }
495
+ return res;
496
+ };
497
+ const isOutOfOrder = (names) => names.some((currName, idx) => {
498
+ if (idx === 0 || !currName) {
499
+ return false;
500
+ }
501
+ const prevName = names[idx - 1];
502
+ return prevName !== undefined && compareAttrNames(prevName, currName) > 0;
503
+ });
504
+ const checkJSXOpeningElement = (node) => {
462
505
  const attrs = node.attributes;
463
506
  if (attrs.length < minKeys) {
464
507
  return;
465
508
  }
466
- if (attrs.some((a) => a.type !== "JSXAttribute")) {
509
+ if (attrs.some((attr) => attr.type !== "JSXAttribute")) {
467
510
  return;
468
511
  }
469
- if (attrs.some((a) => a.type === "JSXAttribute" && a.name.type !== "JSXIdentifier")) {
512
+ if (attrs.some((attr) => attr.type === "JSXAttribute" && attr.name.type !== "JSXIdentifier")) {
470
513
  return;
471
514
  }
472
- const names = attrs.map((a) => {
473
- if (a.type !== "JSXAttribute") {
474
- return "";
475
- }
476
- if (a.name.type !== "JSXIdentifier") {
477
- return "";
478
- }
479
- return a.name.name;
480
- });
481
- const cmp = (a, b) => {
482
- let res = compareKeys(a, b);
483
- if (order === "desc") {
484
- res = -res;
485
- }
486
- return res;
487
- };
488
- let outOfOrder = false;
489
- for (let i = 1;i < names.length; i++) {
490
- const prevName = names[i - 1];
491
- const currName = names[i];
492
- if (!prevName || !currName) {
493
- continue;
494
- }
495
- if (cmp(prevName, currName) > 0) {
496
- outOfOrder = true;
497
- break;
498
- }
515
+ const names = attrs.map((attr) => getAttrName(attr));
516
+ if (!isOutOfOrder(names)) {
517
+ return;
499
518
  }
500
- if (!outOfOrder) {
519
+ if (hasDuplicateNames(names)) {
520
+ context.report({
521
+ messageId: "unsorted",
522
+ node: attrs[0].type === "JSXAttribute" ? attrs[0].name : attrs[0]
523
+ });
524
+ return;
525
+ }
526
+ if (attrs.some((attr) => attr.type === "JSXAttribute" && !isSafeJSXAttributeValue(attr.value))) {
527
+ context.report({
528
+ messageId: "unsorted",
529
+ node: attrs[0].type === "JSXAttribute" ? attrs[0].name : attrs[0]
530
+ });
501
531
  return;
502
532
  }
503
- for (let i = 1;i < attrs.length; i++) {
504
- const prevAttr = attrs[i - 1];
505
- const currAttr = attrs[i];
506
- if (!prevAttr || !currAttr) {
507
- continue;
533
+ const braceConflict = attrs.find((currAttr, idx) => {
534
+ if (idx === 0) {
535
+ return false;
508
536
  }
509
- const between = sourceCode.text.slice(prevAttr.range[1], currAttr.range[0]);
510
- if (between.includes("{")) {
511
- context.report({
512
- node: currAttr.type === "JSXAttribute" ? currAttr.name : currAttr,
513
- messageId: "unsorted"
514
- });
515
- return;
537
+ const prevAttr = attrs[idx - 1];
538
+ if (!prevAttr) {
539
+ return false;
516
540
  }
517
- }
518
- const sortedAttrs = attrs.slice().sort((a, b) => {
519
- const aName = a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" ? a.name.name : "";
520
- const bName = b.type === "JSXAttribute" && b.name.type === "JSXIdentifier" ? b.name.name : "";
521
- return cmp(aName, bName);
541
+ const between = sourceCode.text.slice(prevAttr.range[1], currAttr.range[0]);
542
+ return between.includes("{");
522
543
  });
523
- const firstAttr = attrs[0];
544
+ if (braceConflict) {
545
+ context.report({
546
+ messageId: "unsorted",
547
+ node: braceConflict.type === "JSXAttribute" ? braceConflict.name : braceConflict
548
+ });
549
+ return;
550
+ }
551
+ const sortedAttrs = attrs.slice().sort((left, right) => compareAttrNames(getAttrName(left), getAttrName(right)));
552
+ const [firstAttr] = attrs;
524
553
  const lastAttr = attrs[attrs.length - 1];
525
554
  if (!firstAttr || !lastAttr) {
526
555
  return;
527
556
  }
528
- const replacement = sortedAttrs.map((a) => sourceCode.getText(a)).join(" ");
557
+ const replacement = sortedAttrs.map((attr) => sourceCode.getText(attr)).join(" ");
529
558
  context.report({
530
- node: firstAttr.type === "JSXAttribute" ? firstAttr.name : firstAttr,
531
- messageId: "unsorted",
532
559
  fix(fixer) {
533
560
  return fixer.replaceTextRange([firstAttr.range[0], lastAttr.range[1]], replacement);
534
- }
561
+ },
562
+ messageId: "unsorted",
563
+ node: firstAttr.type === "JSXAttribute" ? firstAttr.name : firstAttr
535
564
  });
536
- }
565
+ };
537
566
  return {
538
- ObjectExpression: checkObjectExpression,
539
567
  JSXAttribute(node) {
540
568
  checkJSXAttributeObject(node);
541
569
  },
542
- JSXOpeningElement: checkJSXOpeningElement
570
+ JSXOpeningElement: checkJSXOpeningElement,
571
+ ObjectExpression: checkObjectExpression
543
572
  };
573
+ },
574
+ defaultOptions: [{}],
575
+ meta: {
576
+ docs: {
577
+ description: "enforce sorted keys in object literals with auto-fix (limited to simple cases, preserving comments)"
578
+ },
579
+ fixable: "code",
580
+ messages: {
581
+ unsorted: "Object keys are not sorted."
582
+ },
583
+ schema: [
584
+ {
585
+ additionalProperties: false,
586
+ properties: {
587
+ caseSensitive: {
588
+ type: "boolean"
589
+ },
590
+ minKeys: {
591
+ minimum: 2,
592
+ type: "integer"
593
+ },
594
+ natural: {
595
+ type: "boolean"
596
+ },
597
+ order: {
598
+ enum: ["asc", "desc"],
599
+ type: "string"
600
+ },
601
+ variablesBeforeFunctions: {
602
+ type: "boolean"
603
+ }
604
+ },
605
+ type: "object"
606
+ }
607
+ ],
608
+ type: "suggestion"
544
609
  }
545
610
  };
546
611
 
547
612
  // src/rules/no-transition-cssproperties.ts
613
+ var getKeyName = (prop) => {
614
+ if (prop.key.type === "Identifier") {
615
+ return prop.key.name;
616
+ }
617
+ if (prop.key.type !== "Literal") {
618
+ return null;
619
+ }
620
+ return typeof prop.key.value === "string" ? prop.key.value : String(prop.key.value);
621
+ };
622
+ var checkPropForTransition = (context, prop) => {
623
+ if (prop.computed) {
624
+ return;
625
+ }
626
+ const keyName = getKeyName(prop);
627
+ if (keyName === "transition") {
628
+ context.report({
629
+ messageId: "forbiddenTransition",
630
+ node: prop
631
+ });
632
+ }
633
+ };
548
634
  var noTransitionCSSProperties = {
549
- meta: {
550
- type: "problem",
551
- docs: {
552
- description: "Objects typed as CSSProperties must not include a 'transition' property as it conflicts with react-spring."
553
- },
554
- schema: [],
555
- messages: {
556
- forbiddenTransition: "Objects typed as CSSProperties must not include a 'transition' property as it conflicts with react-spring."
557
- }
558
- },
559
- defaultOptions: [],
560
635
  create(context) {
561
- const sourceCode = context.sourceCode;
636
+ const { sourceCode } = context;
637
+ const isCSSPropertiesType = (typeAnnotation) => {
638
+ if (typeAnnotation.type !== "TSTypeReference") {
639
+ return false;
640
+ }
641
+ const { typeName } = typeAnnotation;
642
+ if (typeName.type === "Identifier" && typeName.name === "CSSProperties") {
643
+ return true;
644
+ }
645
+ return typeName.type === "TSQualifiedName" && typeName.right && typeName.right.type === "Identifier" && typeName.right.name === "CSSProperties";
646
+ };
562
647
  return {
563
648
  VariableDeclarator(node) {
564
649
  if (!node.id || node.id.type !== "Identifier" || !node.id.typeAnnotation) {
565
650
  return;
566
651
  }
567
- let isStyleType = false;
568
- const typeAnnotation = node.id.typeAnnotation.typeAnnotation;
569
- if (typeAnnotation && typeAnnotation.type === "TSTypeReference") {
570
- const typeName = typeAnnotation.typeName;
571
- if (typeName.type === "Identifier" && typeName.name === "CSSProperties") {
572
- isStyleType = true;
573
- } else if (typeName.type === "TSQualifiedName" && typeName.right && typeName.right.type === "Identifier" && typeName.right.name === "CSSProperties") {
574
- isStyleType = true;
575
- }
576
- }
652
+ const { typeAnnotation } = node.id.typeAnnotation;
653
+ let isStyleType = isCSSPropertiesType(typeAnnotation);
577
654
  if (!isStyleType) {
578
655
  const annotationText = sourceCode.getText(node.id.typeAnnotation);
579
- if (annotationText.includes("CSSProperties")) {
580
- isStyleType = true;
581
- }
656
+ isStyleType = annotationText.includes("CSSProperties");
582
657
  }
583
658
  if (!isStyleType) {
584
659
  return;
585
660
  }
586
- const init = node.init;
661
+ const { init } = node;
587
662
  if (!init || init.type !== "ObjectExpression") {
588
663
  return;
589
664
  }
590
- for (const prop of init.properties) {
591
- if (prop.type !== "Property") {
592
- continue;
593
- }
594
- if (prop.computed) {
595
- continue;
596
- }
597
- let keyName = null;
598
- if (prop.key.type === "Identifier") {
599
- keyName = prop.key.name;
600
- } else if (prop.key.type === "Literal") {
601
- if (typeof prop.key.value === "string") {
602
- keyName = prop.key.value;
603
- } else {
604
- keyName = String(prop.key.value);
605
- }
606
- }
607
- if (keyName === "transition") {
608
- context.report({
609
- node: prop,
610
- messageId: "forbiddenTransition"
611
- });
612
- }
613
- }
665
+ const properties = init.properties.filter((prop) => prop.type === "Property");
666
+ properties.forEach((prop) => {
667
+ checkPropForTransition(context, prop);
668
+ });
614
669
  }
615
670
  };
671
+ },
672
+ defaultOptions: [],
673
+ meta: {
674
+ docs: {
675
+ description: "Objects typed as CSSProperties must not include a 'transition' property as it conflicts with react-spring."
676
+ },
677
+ messages: {
678
+ forbiddenTransition: "Objects typed as CSSProperties must not include a 'transition' property as it conflicts with react-spring."
679
+ },
680
+ schema: [],
681
+ type: "problem"
616
682
  }
617
683
  };
618
684
 
619
685
  // src/rules/no-explicit-return-types.ts
620
686
  var noExplicitReturnTypes = {
621
- meta: {
622
- type: "suggestion",
623
- docs: {
624
- description: "Disallow explicit return type annotations on functions, except when using type predicates for type guards or inline object literal returns (e.g., style objects)."
625
- },
626
- schema: [],
627
- messages: {
628
- noExplicitReturnType: "Explicit return types are disallowed; rely on TypeScript's inference instead."
629
- }
630
- },
631
- defaultOptions: [],
632
687
  create(context) {
633
- function hasSingleObjectReturn(body) {
634
- let returnCount = 0;
635
- let returnedObject = null;
636
- for (const stmt of body.body) {
637
- if (stmt.type === "ReturnStatement") {
638
- returnCount++;
639
- const arg = stmt.argument;
640
- if (arg && arg.type === "ObjectExpression") {
641
- returnedObject = arg;
642
- }
643
- }
688
+ const hasSingleObjectReturn = (body) => {
689
+ const returnStatements = body.body.filter((stmt) => stmt.type === "ReturnStatement");
690
+ if (returnStatements.length !== 1) {
691
+ return false;
644
692
  }
645
- return returnCount === 1 && returnedObject !== null;
646
- }
693
+ const [returnStmt] = returnStatements;
694
+ return returnStmt?.argument?.type === "ObjectExpression";
695
+ };
647
696
  return {
648
697
  "FunctionDeclaration, FunctionExpression, ArrowFunctionExpression"(node) {
649
- const returnType = node.returnType;
698
+ const { returnType } = node;
650
699
  if (!returnType) {
651
700
  return;
652
701
  }
653
- const typeAnnotation = returnType.typeAnnotation;
702
+ const { typeAnnotation } = returnType;
654
703
  if (typeAnnotation && typeAnnotation.type === "TSTypePredicate") {
655
704
  return;
656
705
  }
657
706
  if (node.type === "ArrowFunctionExpression" && node.expression === true && node.body.type === "ObjectExpression") {
658
707
  return;
659
708
  }
660
- if (node.body && node.body.type === "BlockStatement") {
661
- if (hasSingleObjectReturn(node.body)) {
662
- return;
663
- }
709
+ if (node.body && node.body.type === "BlockStatement" && hasSingleObjectReturn(node.body)) {
710
+ return;
664
711
  }
665
712
  context.report({
666
- node: returnType,
667
- messageId: "noExplicitReturnType"
713
+ messageId: "noExplicitReturnType",
714
+ node: returnType
668
715
  });
669
716
  }
670
717
  };
718
+ },
719
+ defaultOptions: [],
720
+ meta: {
721
+ docs: {
722
+ description: "Disallow explicit return type annotations on functions, except when using type predicates for type guards or inline object literal returns (e.g., style objects)."
723
+ },
724
+ messages: {
725
+ noExplicitReturnType: "Explicit return types are disallowed; rely on TypeScript's inference instead."
726
+ },
727
+ schema: [],
728
+ type: "suggestion"
671
729
  }
672
730
  };
673
731
 
674
732
  // src/rules/max-jsx-nesting.ts
733
+ var isJSXAncestor = (node) => node.type === "JSXElement" || node.type === "JSXFragment";
675
734
  var maxJSXNesting = {
676
- meta: {
677
- type: "suggestion",
678
- docs: {
679
- description: "Warn when JSX elements are nested too deeply, suggesting refactoring into a separate component."
680
- },
681
- schema: [
682
- {
683
- type: "number",
684
- minimum: 1
685
- }
686
- ],
687
- messages: {
688
- tooDeeplyNested: "JSX element is nested too deeply ({{level}} levels, allowed is {{maxAllowed}} levels). Consider refactoring into a separate component."
689
- }
690
- },
691
- defaultOptions: [1],
692
735
  create(context) {
693
- const option = context.options[0];
736
+ const [option] = context.options;
694
737
  const maxAllowed = typeof option === "number" ? option : 1;
695
- function getJSXNestingLevel(node) {
738
+ const getJSXNestingLevel = (node) => {
696
739
  let level = 1;
697
740
  let current = node.parent;
698
741
  while (current) {
699
- if (current.type === "JSXElement" || current.type === "JSXFragment") {
700
- level++;
701
- }
742
+ level += isJSXAncestor(current) ? 1 : 0;
702
743
  current = current.parent;
703
744
  }
704
745
  return level;
705
- }
746
+ };
706
747
  return {
707
748
  JSXElement(node) {
708
749
  const level = getJSXNestingLevel(node);
709
750
  if (level > maxAllowed) {
710
751
  context.report({
711
- node,
752
+ data: { level, maxAllowed },
712
753
  messageId: "tooDeeplyNested",
713
- data: { level, maxAllowed }
754
+ node
714
755
  });
715
756
  }
716
757
  }
717
758
  };
759
+ },
760
+ defaultOptions: [1],
761
+ meta: {
762
+ docs: {
763
+ description: "Warn when JSX elements are nested too deeply, suggesting refactoring into a separate component."
764
+ },
765
+ messages: {
766
+ tooDeeplyNested: "JSX element is nested too deeply ({{level}} levels, allowed is {{maxAllowed}} levels). Consider refactoring into a separate component."
767
+ },
768
+ schema: [
769
+ {
770
+ minimum: 1,
771
+ type: "number"
772
+ }
773
+ ],
774
+ type: "suggestion"
718
775
  }
719
776
  };
720
777
 
721
778
  // src/rules/seperate-style-files.ts
722
779
  var seperateStyleFiles = {
723
- meta: {
724
- type: "suggestion",
725
- docs: {
726
- description: "Warn when a component file (.jsx or .tsx) contains a style object typed as CSSProperties. " + "Style objects should be moved to their own file under the style folder."
727
- },
728
- schema: [],
729
- messages: {
730
- moveToFile: 'Style object "{{name}}" is typed as {{typeName}}. Move it to its own file under the style folder.'
731
- }
732
- },
733
- defaultOptions: [],
734
780
  create(context) {
735
- const filename = context.filename;
781
+ const { filename } = context;
736
782
  if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) {
737
783
  return {};
738
784
  }
@@ -755,39 +801,46 @@ var seperateStyleFiles = {
755
801
  if (typeNameNode.type === "Identifier") {
756
802
  typeName = typeNameNode.name;
757
803
  } else if (typeNameNode.type === "TSQualifiedName") {
758
- const right = typeNameNode.right;
804
+ const { right } = typeNameNode;
759
805
  typeName = right.name;
760
806
  }
761
807
  if (typeName === "CSSProperties") {
762
808
  context.report({
763
- node,
764
- messageId: "moveToFile",
765
809
  data: {
766
810
  name: identifier.name,
767
811
  typeName
768
- }
812
+ },
813
+ messageId: "moveToFile",
814
+ node
769
815
  });
770
816
  }
771
817
  }
772
818
  };
819
+ },
820
+ defaultOptions: [],
821
+ meta: {
822
+ docs: {
823
+ description: "Warn when a component file (.jsx or .tsx) contains a style object typed as CSSProperties. " + "Style objects should be moved to their own file under the style folder."
824
+ },
825
+ messages: {
826
+ moveToFile: 'Style object "{{name}}" is typed as {{typeName}}. Move it to its own file under the style folder.'
827
+ },
828
+ schema: [],
829
+ type: "suggestion"
773
830
  }
774
831
  };
775
832
 
776
833
  // src/rules/no-unnecessary-key.ts
834
+ var isMapCallExpression = (node) => {
835
+ if (node.type !== "CallExpression" || node.callee.type !== "MemberExpression") {
836
+ return false;
837
+ }
838
+ const { property } = node.callee;
839
+ return property.type === "Identifier" && property.name === "map" || property.type === "Literal" && property.value === "map";
840
+ };
777
841
  var noUnnecessaryKey = {
778
- meta: {
779
- type: "problem",
780
- docs: {
781
- description: "enforce that the key prop is only used on components rendered as part of a mapping"
782
- },
783
- schema: [],
784
- messages: {
785
- unnecessaryKey: "The key prop should only be used on elements that are directly rendered as part of an array mapping."
786
- }
787
- },
788
- defaultOptions: [],
789
842
  create(context) {
790
- function getAncestors(node) {
843
+ const getAncestors = (node) => {
791
844
  const ancestors = [];
792
845
  let current = node.parent;
793
846
  while (current) {
@@ -795,40 +848,15 @@ var noUnnecessaryKey = {
795
848
  current = current.parent;
796
849
  }
797
850
  return ancestors;
798
- }
799
- function isInsideMapCall(ancestors) {
800
- for (const node of ancestors) {
801
- if (node.type === "CallExpression" && node.callee.type === "MemberExpression") {
802
- const property = node.callee.property;
803
- if (property.type === "Identifier" && property.name === "map") {
804
- return true;
805
- }
806
- if (property.type === "Literal" && property.value === "map") {
807
- return true;
808
- }
809
- }
810
- }
811
- return false;
812
- }
813
- function isReturnedFromFunction(ancestors) {
814
- for (const node of ancestors) {
815
- if (node.type === "ReturnStatement") {
816
- return true;
817
- }
818
- }
819
- return false;
820
- }
821
- function checkJSXOpeningElement(node) {
851
+ };
852
+ const isInsideMapCall = (ancestors) => ancestors.some(isMapCallExpression);
853
+ const isReturnedFromFunction = (ancestors) => ancestors.some((ancestor) => ancestor.type === "ReturnStatement");
854
+ const checkJSXOpeningElement = (node) => {
822
855
  const keyAttribute = node.attributes.find((attr) => attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && attr.name.name === "key");
823
856
  if (!keyAttribute) {
824
857
  return;
825
858
  }
826
- let ancestors;
827
- if (typeof context.getAncestors === "function") {
828
- ancestors = context.getAncestors();
829
- } else {
830
- ancestors = getAncestors(node);
831
- }
859
+ const ancestors = getAncestors(node);
832
860
  if (isInsideMapCall(ancestors)) {
833
861
  return;
834
862
  }
@@ -836,304 +864,389 @@ var noUnnecessaryKey = {
836
864
  return;
837
865
  }
838
866
  context.report({
839
- node: keyAttribute,
840
- messageId: "unnecessaryKey"
867
+ messageId: "unnecessaryKey",
868
+ node: keyAttribute
841
869
  });
842
- }
870
+ };
843
871
  return {
844
872
  JSXOpeningElement: checkJSXOpeningElement
845
873
  };
846
- }
847
- };
848
-
849
- // src/rules/sort-exports.ts
850
- var sortExports = {
874
+ },
875
+ defaultOptions: [],
851
876
  meta: {
852
- type: "suggestion",
853
877
  docs: {
854
- description: "Enforce that top-level export declarations are sorted by exported name and, optionally, that variable exports come before function exports"
878
+ description: "enforce that the key prop is only used on components rendered as part of a mapping"
855
879
  },
856
- fixable: "code",
857
- schema: [
858
- {
859
- type: "object",
860
- properties: {
861
- order: {
862
- type: "string",
863
- enum: ["asc", "desc"]
864
- },
865
- caseSensitive: {
866
- type: "boolean"
867
- },
868
- natural: {
869
- type: "boolean"
870
- },
871
- minKeys: {
872
- type: "integer",
873
- minimum: 2
874
- },
875
- variablesBeforeFunctions: {
876
- type: "boolean"
877
- }
878
- },
879
- additionalProperties: false
880
- }
881
- ],
882
880
  messages: {
883
- alphabetical: "Export declarations are not sorted alphabetically. Expected order: {{expectedOrder}}.",
884
- variablesBeforeFunctions: "Non-function exports should come before function exports."
881
+ unnecessaryKey: "The key prop should only be used on elements that are directly rendered as part of an array mapping."
882
+ },
883
+ schema: [],
884
+ type: "problem"
885
+ }
886
+ };
887
+
888
+ // src/rules/sort-exports.ts
889
+ var SORT_BEFORE2 = Number.parseInt("-1", 10);
890
+ var hasStringTypeProperty = (value) => {
891
+ const maybeType = Reflect.get(value, "type");
892
+ return typeof maybeType === "string";
893
+ };
894
+ var isNodeLike = (value) => value !== null && value !== undefined && typeof value === "object" && ("type" in value) && hasStringTypeProperty(value);
895
+ var shouldSkipNodeEntry = (key, value) => key === "parent" || value === null || value === undefined;
896
+ var visitNodeArray = (values, visit) => values.filter(isNodeLike).forEach(visit);
897
+ var visitNodeEntryValue = (value, visit) => {
898
+ if (Array.isArray(value)) {
899
+ visitNodeArray(value, visit);
900
+ return;
901
+ }
902
+ if (isNodeLike(value)) {
903
+ visit(value);
904
+ }
905
+ };
906
+ var visitNodeEntries = (current, visit) => Object.entries(current).filter(([key, value]) => !shouldSkipNodeEntry(key, value)).forEach(([, value]) => {
907
+ visitNodeEntryValue(value, visit);
908
+ });
909
+ var getVariableDeclaratorName = (declaration) => {
910
+ if (declaration.declarations.length !== 1) {
911
+ return null;
912
+ }
913
+ const [firstDeclarator] = declaration.declarations;
914
+ if (firstDeclarator && firstDeclarator.id.type === "Identifier") {
915
+ return firstDeclarator.id.name;
916
+ }
917
+ return null;
918
+ };
919
+ var getDeclarationName = (declaration) => {
920
+ if (!declaration) {
921
+ return null;
922
+ }
923
+ if (declaration.type === "VariableDeclaration") {
924
+ return getVariableDeclaratorName(declaration);
925
+ }
926
+ if ((declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") && declaration.id && declaration.id.type === "Identifier") {
927
+ return declaration.id.name;
928
+ }
929
+ return null;
930
+ };
931
+ var getSpecifierName = (node) => {
932
+ if (node.specifiers.length !== 1) {
933
+ return null;
934
+ }
935
+ const [spec] = node.specifiers;
936
+ if (!spec) {
937
+ return null;
938
+ }
939
+ if (spec.exported.type === "Identifier") {
940
+ return spec.exported.name;
941
+ }
942
+ if (spec.exported.type === "Literal" && typeof spec.exported.value === "string") {
943
+ return spec.exported.value;
944
+ }
945
+ return null;
946
+ };
947
+ var getExportName = (node) => getDeclarationName(node.declaration) ?? getSpecifierName(node);
948
+ var isFixableExport = (exportNode) => {
949
+ const { declaration } = exportNode;
950
+ if (!declaration) {
951
+ return exportNode.specifiers.length === 1;
952
+ }
953
+ if (declaration.type === "VariableDeclaration" && declaration.declarations.length === 1) {
954
+ const [firstDecl] = declaration.declarations;
955
+ return firstDecl !== undefined && firstDecl.id.type === "Identifier";
956
+ }
957
+ return (declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") && declaration.id !== null && declaration.id.type === "Identifier";
958
+ };
959
+ var visitImmediateReferences = (node, onReference) => {
960
+ if (!node) {
961
+ return;
962
+ }
963
+ const visit = (current) => {
964
+ if (!current) {
965
+ return;
885
966
  }
886
- },
887
- defaultOptions: [{}],
967
+ switch (current.type) {
968
+ case "Identifier":
969
+ onReference(current.name);
970
+ return;
971
+ case "FunctionDeclaration":
972
+ case "FunctionExpression":
973
+ case "ArrowFunctionExpression":
974
+ return;
975
+ case "MemberExpression":
976
+ visit(current.object);
977
+ if (current.computed) {
978
+ visit(current.property);
979
+ }
980
+ return;
981
+ case "Property":
982
+ if (current.computed) {
983
+ visit(current.key);
984
+ }
985
+ visit(current.value);
986
+ return;
987
+ case "PropertyDefinition":
988
+ if (current.computed) {
989
+ visit(current.key);
990
+ }
991
+ if (current.static) {
992
+ visit(current.value);
993
+ }
994
+ return;
995
+ case "MethodDefinition":
996
+ if (current.computed) {
997
+ visit(current.key);
998
+ }
999
+ return;
1000
+ case "StaticBlock":
1001
+ for (const statement of current.body) {
1002
+ visit(statement);
1003
+ }
1004
+ return;
1005
+ }
1006
+ visitNodeEntries(current, visit);
1007
+ };
1008
+ visit(node);
1009
+ };
1010
+ var getImmediateDependencyNames = (node) => {
1011
+ const names = new Set;
1012
+ const { declaration } = node;
1013
+ const addName = names.add.bind(names);
1014
+ const addDeclaratorDependencies = (declarator) => visitImmediateReferences(declarator.init, addName);
1015
+ const addClassElementDependencies = (element) => visitImmediateReferences(element, addName);
1016
+ if (!declaration) {
1017
+ return names;
1018
+ }
1019
+ if (declaration.type === "VariableDeclaration") {
1020
+ declaration.declarations.forEach(addDeclaratorDependencies);
1021
+ return names;
1022
+ }
1023
+ if (declaration.type === "ClassDeclaration") {
1024
+ visitImmediateReferences(declaration.superClass, addName);
1025
+ declaration.body.body.forEach(addClassElementDependencies);
1026
+ }
1027
+ return names;
1028
+ };
1029
+ var sortExports = {
888
1030
  create(context) {
889
- const sourceCode = context.sourceCode;
890
- const option = context.options[0];
1031
+ const { sourceCode } = context;
1032
+ const [option] = context.options;
891
1033
  const order = option && option.order ? option.order : "asc";
892
1034
  const caseSensitive = option && typeof option.caseSensitive === "boolean" ? option.caseSensitive : false;
893
1035
  const natural = option && typeof option.natural === "boolean" ? option.natural : false;
894
1036
  const minKeys = option && typeof option.minKeys === "number" ? option.minKeys : 2;
895
1037
  const variablesBeforeFunctions = option && typeof option.variablesBeforeFunctions === "boolean" ? option.variablesBeforeFunctions : false;
896
- function generateExportText(node) {
897
- return sourceCode.getText(node).trim().replace(/\s*;?\s*$/, ";");
898
- }
899
- function compareStrings(a, b) {
900
- let strA = a;
901
- let strB = b;
1038
+ const generateExportText = (node) => sourceCode.getText(node).trim().replace(/\s*;?\s*$/, ";");
1039
+ const compareStrings = (strLeft, strRight) => {
1040
+ let left = strLeft;
1041
+ let right = strRight;
902
1042
  if (!caseSensitive) {
903
- strA = strA.toLowerCase();
904
- strB = strB.toLowerCase();
1043
+ left = left.toLowerCase();
1044
+ right = right.toLowerCase();
905
1045
  }
906
- const cmp = natural ? strA.localeCompare(strB, undefined, { numeric: true }) : strA.localeCompare(strB);
1046
+ const cmp = natural ? left.localeCompare(right, undefined, { numeric: true }) : left.localeCompare(right);
907
1047
  return order === "asc" ? cmp : -cmp;
908
- }
909
- function getExportName(node) {
910
- const declaration = node.declaration;
911
- if (declaration) {
912
- if (declaration.type === "VariableDeclaration") {
913
- if (declaration.declarations.length === 1) {
914
- const firstDeclarator = declaration.declarations[0];
915
- if (firstDeclarator && firstDeclarator.id.type === "Identifier") {
916
- return firstDeclarator.id.name;
917
- }
918
- }
919
- } else if (declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") {
920
- const id = declaration.id;
921
- if (id && id.type === "Identifier") {
922
- return id.name;
923
- }
924
- }
925
- } else if (node.specifiers.length === 1) {
926
- const spec = node.specifiers[0];
927
- if (!spec) {
928
- return null;
929
- }
930
- if (spec.exported.type === "Identifier") {
931
- return spec.exported.name;
932
- }
933
- if (spec.exported.type === "Literal" && typeof spec.exported.value === "string") {
934
- return spec.exported.value;
935
- }
936
- }
937
- return null;
938
- }
939
- function isFunctionExport(node) {
940
- const declaration = node.declaration;
1048
+ };
1049
+ const isFunctionExport = (node) => {
1050
+ const { declaration } = node;
941
1051
  if (!declaration) {
942
1052
  return false;
943
1053
  }
944
- if (declaration.type === "VariableDeclaration") {
945
- if (declaration.declarations.length === 1) {
946
- const firstDeclarator = declaration.declarations[0];
947
- if (!firstDeclarator) {
948
- return false;
949
- }
950
- const init = firstDeclarator.init;
951
- if (!init) {
952
- return false;
953
- }
954
- return init.type === "FunctionExpression" || init.type === "ArrowFunctionExpression";
955
- }
956
- return false;
957
- }
958
1054
  if (declaration.type === "FunctionDeclaration") {
959
1055
  return true;
960
1056
  }
961
- return false;
962
- }
963
- function sortComparator(a, b) {
964
- const kindA = a.node.exportKind ?? "value";
965
- const kindB = b.node.exportKind ?? "value";
966
- if (kindA !== kindB) {
967
- return kindA === "type" ? -1 : 1;
1057
+ if (declaration.type !== "VariableDeclaration") {
1058
+ return false;
968
1059
  }
969
- if (variablesBeforeFunctions) {
970
- if (a.isFunction !== b.isFunction) {
971
- return a.isFunction ? 1 : -1;
972
- }
1060
+ if (declaration.declarations.length !== 1) {
1061
+ return false;
973
1062
  }
974
- return compareStrings(a.name, b.name);
975
- }
976
- function hasForwardDependency(node, laterNames) {
977
- const text = sourceCode.getText(node);
978
- for (const name of laterNames) {
979
- if (text.includes(name)) {
980
- return true;
981
- }
1063
+ const [firstDeclarator] = declaration.declarations;
1064
+ if (!firstDeclarator) {
1065
+ return false;
982
1066
  }
983
- return false;
984
- }
985
- function processExportBlock(block) {
986
- if (block.length < minKeys) {
987
- return;
1067
+ const { init } = firstDeclarator;
1068
+ if (!init) {
1069
+ return false;
988
1070
  }
989
- const items = [];
990
- for (const node of block) {
991
- const name = getExportName(node);
992
- if (!name) {
993
- continue;
994
- }
995
- items.push({
996
- name,
997
- node,
998
- isFunction: isFunctionExport(node),
999
- text: sourceCode.getText(node)
1000
- });
1071
+ return init.type === "FunctionExpression" || init.type === "ArrowFunctionExpression";
1072
+ };
1073
+ const sortComparator = (left, right) => {
1074
+ const kindA = left.node.exportKind ?? "value";
1075
+ const kindB = right.node.exportKind ?? "value";
1076
+ if (kindA !== kindB) {
1077
+ return kindA === "type" ? SORT_BEFORE2 : 1;
1001
1078
  }
1002
- if (items.length < minKeys) {
1003
- return;
1079
+ if (variablesBeforeFunctions && left.isFunction !== right.isFunction) {
1080
+ return left.isFunction ? 1 : SORT_BEFORE2;
1004
1081
  }
1005
- const sortedItems = items.slice().sort(sortComparator);
1006
- let reportNeeded = false;
1082
+ return compareStrings(left.name, right.name);
1083
+ };
1084
+ const buildItems = (block) => block.map((node) => {
1085
+ const name = getExportName(node);
1086
+ if (!name) {
1087
+ return null;
1088
+ }
1089
+ const item = {
1090
+ isFunction: isFunctionExport(node),
1091
+ name,
1092
+ node,
1093
+ text: sourceCode.getText(node)
1094
+ };
1095
+ return item;
1096
+ }).filter((item) => item !== null);
1097
+ const findFirstUnsorted = (items) => {
1007
1098
  let messageId = "alphabetical";
1008
- for (let i = 1;i < items.length; i++) {
1009
- const prev = items[i - 1];
1010
- const current = items[i];
1011
- if (!prev || !current) {
1012
- continue;
1013
- }
1014
- if (sortComparator(prev, current) > 0) {
1015
- reportNeeded = true;
1016
- if (variablesBeforeFunctions && prev.isFunction && !current.isFunction) {
1017
- messageId = "variablesBeforeFunctions";
1099
+ const unsorted = items.some((current, idx) => {
1100
+ if (idx === 0) {
1101
+ return false;
1102
+ }
1103
+ const prev = items[idx - 1];
1104
+ if (!prev) {
1105
+ return false;
1106
+ }
1107
+ if (sortComparator(prev, current) <= 0) {
1108
+ return false;
1109
+ }
1110
+ if (variablesBeforeFunctions && prev.isFunction && !current.isFunction) {
1111
+ messageId = "variablesBeforeFunctions";
1112
+ }
1113
+ return true;
1114
+ });
1115
+ return unsorted ? messageId : null;
1116
+ };
1117
+ const checkForwardDependencies = (items) => {
1118
+ const exportNames = items.map((item) => item.name);
1119
+ return items.some((item, idx) => {
1120
+ const laterNames = new Set(exportNames.slice(idx + 1));
1121
+ if (laterNames.size === 0) {
1122
+ return false;
1123
+ }
1124
+ const dependencies = getImmediateDependencyNames(item.node);
1125
+ for (const dependency of dependencies) {
1126
+ if (laterNames.has(dependency)) {
1127
+ return true;
1018
1128
  }
1019
- break;
1020
1129
  }
1130
+ return false;
1131
+ });
1132
+ };
1133
+ const processExportBlock = (block) => {
1134
+ if (block.length < minKeys) {
1135
+ return;
1021
1136
  }
1022
- if (!reportNeeded) {
1137
+ const items = buildItems(block);
1138
+ if (items.length < minKeys) {
1023
1139
  return;
1024
1140
  }
1025
- const exportNames = items.map((item) => item.name);
1026
- for (let i = 0;i < items.length; i++) {
1027
- const item = items[i];
1028
- if (!item) {
1029
- continue;
1030
- }
1031
- const laterNames = new Set(exportNames.slice(i + 1));
1032
- const nodeToCheck = item.node.declaration ?? item.node;
1033
- if (hasForwardDependency(nodeToCheck, laterNames)) {
1034
- return;
1035
- }
1141
+ const messageId = findFirstUnsorted(items);
1142
+ if (!messageId) {
1143
+ return;
1144
+ }
1145
+ if (checkForwardDependencies(items)) {
1146
+ return;
1036
1147
  }
1148
+ const sortedItems = items.slice().sort(sortComparator);
1037
1149
  const expectedOrder = sortedItems.map((item) => item.name).join(", ");
1038
- const firstNode = block[0];
1150
+ const [firstNode] = block;
1039
1151
  const lastNode = block[block.length - 1];
1040
1152
  if (!firstNode || !lastNode) {
1041
1153
  return;
1042
1154
  }
1043
1155
  context.report({
1044
- node: firstNode,
1045
- messageId,
1046
1156
  data: {
1047
1157
  expectedOrder
1048
1158
  },
1049
1159
  fix(fixer) {
1050
- const fixableNodes = [];
1051
- for (const n of block) {
1052
- const declaration = n.declaration;
1053
- if (declaration) {
1054
- if (declaration.type === "VariableDeclaration" && declaration.declarations.length === 1) {
1055
- const firstDecl = declaration.declarations[0];
1056
- if (firstDecl && firstDecl.id.type === "Identifier") {
1057
- fixableNodes.push(n);
1058
- continue;
1059
- }
1060
- }
1061
- if ((declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") && declaration.id && declaration.id.type === "Identifier") {
1062
- fixableNodes.push(n);
1063
- continue;
1064
- }
1065
- continue;
1066
- }
1067
- if (n.specifiers.length === 1) {
1068
- fixableNodes.push(n);
1069
- }
1070
- }
1160
+ const fixableNodes = block.filter(isFixableExport);
1071
1161
  if (fixableNodes.length < minKeys) {
1072
1162
  return null;
1073
1163
  }
1074
1164
  const sortedText = sortedItems.map((item) => generateExportText(item.node)).join(`
1075
1165
  `);
1076
- const rangeStart = firstNode.range[0];
1077
- const rangeEnd = lastNode.range[1];
1166
+ const [rangeStart] = firstNode.range;
1167
+ const [, rangeEnd] = lastNode.range;
1078
1168
  const fullText = sourceCode.getText();
1079
1169
  const originalText = fullText.slice(rangeStart, rangeEnd);
1080
1170
  if (originalText === sortedText) {
1081
1171
  return null;
1082
1172
  }
1083
1173
  return fixer.replaceTextRange([rangeStart, rangeEnd], sortedText);
1084
- }
1174
+ },
1175
+ messageId,
1176
+ node: firstNode
1085
1177
  });
1086
- }
1178
+ };
1087
1179
  return {
1088
1180
  "Program:exit"(node) {
1089
- const body = node.body;
1181
+ const { body } = node;
1090
1182
  const block = [];
1091
- for (let i = 0;i < body.length; i++) {
1092
- const stmt = body[i];
1093
- if (!stmt) {
1094
- continue;
1095
- }
1183
+ body.forEach((stmt) => {
1096
1184
  if (stmt.type === "ExportNamedDeclaration" && !stmt.source && getExportName(stmt) !== null) {
1097
1185
  block.push(stmt);
1098
- } else {
1099
- if (block.length > 0) {
1100
- processExportBlock(block);
1101
- block.length = 0;
1102
- }
1186
+ return;
1103
1187
  }
1104
- }
1188
+ if (block.length > 0) {
1189
+ processExportBlock(block);
1190
+ block.length = 0;
1191
+ }
1192
+ });
1105
1193
  if (block.length > 0) {
1106
1194
  processExportBlock(block);
1107
1195
  }
1108
1196
  }
1109
1197
  };
1198
+ },
1199
+ defaultOptions: [{}],
1200
+ meta: {
1201
+ docs: {
1202
+ description: "Enforce that top-level export declarations are sorted by exported name and, optionally, that variable exports come before function exports"
1203
+ },
1204
+ fixable: "code",
1205
+ messages: {
1206
+ alphabetical: "Export declarations are not sorted alphabetically. Expected order: {{expectedOrder}}.",
1207
+ variablesBeforeFunctions: "Non-function exports should come before function exports."
1208
+ },
1209
+ schema: [
1210
+ {
1211
+ additionalProperties: false,
1212
+ properties: {
1213
+ caseSensitive: {
1214
+ type: "boolean"
1215
+ },
1216
+ minKeys: {
1217
+ minimum: 2,
1218
+ type: "integer"
1219
+ },
1220
+ natural: {
1221
+ type: "boolean"
1222
+ },
1223
+ order: {
1224
+ enum: ["asc", "desc"],
1225
+ type: "string"
1226
+ },
1227
+ variablesBeforeFunctions: {
1228
+ type: "boolean"
1229
+ }
1230
+ },
1231
+ type: "object"
1232
+ }
1233
+ ],
1234
+ type: "suggestion"
1110
1235
  }
1111
1236
  };
1112
1237
 
1113
1238
  // src/rules/localize-react-props.ts
1114
1239
  import { AST_NODE_TYPES as AST_NODE_TYPES2 } from "@typescript-eslint/utils";
1115
1240
  var localizeReactProps = {
1116
- meta: {
1117
- type: "suggestion",
1118
- docs: {
1119
- description: "Disallow variables that are only passed to a single custom child component. For useState, only report if both the state and its setter are exclusively passed to a single custom child. For general variables, only report if a given child receives exactly one such candidate \u2013 if two or more are passed to the same component type, they\u2019re assumed to be settings that belong on the parent."
1120
- },
1121
- schema: [],
1122
- messages: {
1123
- stateAndSetterToChild: "State variable '{{stateVarName}}' and its setter '{{setterVarName}}' are only passed to a single custom child component. Consider moving the state into that component.",
1124
- variableToChild: "Variable '{{varName}}' is only passed to a single custom child component. Consider moving it to that component."
1125
- }
1126
- },
1127
- defaultOptions: [],
1128
1241
  create(context) {
1129
1242
  const candidateVariables = [];
1130
- function getSingleSetElement(set) {
1243
+ const getSingleSetElement = (set) => {
1131
1244
  for (const value of set) {
1132
1245
  return value;
1133
1246
  }
1134
1247
  return null;
1135
- }
1136
- function getRightmostJSXIdentifier(name) {
1248
+ };
1249
+ const getRightmostJSXIdentifier = (name) => {
1137
1250
  let current = name;
1138
1251
  while (current.type === AST_NODE_TYPES2.JSXMemberExpression) {
1139
1252
  current = current.property;
@@ -1142,8 +1255,8 @@ var localizeReactProps = {
1142
1255
  return current;
1143
1256
  }
1144
1257
  return null;
1145
- }
1146
- function getLeftmostJSXIdentifier(name) {
1258
+ };
1259
+ const getLeftmostJSXIdentifier = (name) => {
1147
1260
  let current = name;
1148
1261
  while (current.type === AST_NODE_TYPES2.JSXMemberExpression) {
1149
1262
  current = current.object;
@@ -1152,8 +1265,8 @@ var localizeReactProps = {
1152
1265
  return current;
1153
1266
  }
1154
1267
  return null;
1155
- }
1156
- function getJSXElementName(jsxElement) {
1268
+ };
1269
+ const getJSXElementName = (jsxElement) => {
1157
1270
  if (!jsxElement || !jsxElement.openingElement || !jsxElement.openingElement.name) {
1158
1271
  return "";
1159
1272
  }
@@ -1166,14 +1279,10 @@ var localizeReactProps = {
1166
1279
  return rightmost.name;
1167
1280
  }
1168
1281
  return "";
1169
- }
1170
- function isUseStateCall(node) {
1171
- return node !== null && node.type === AST_NODE_TYPES2.CallExpression && node.callee !== null && (node.callee.type === AST_NODE_TYPES2.Identifier && node.callee.name === "useState" || node.callee.type === AST_NODE_TYPES2.MemberExpression && node.callee.property !== null && node.callee.property.type === AST_NODE_TYPES2.Identifier && node.callee.property.name === "useState");
1172
- }
1173
- function isHookCall(node) {
1174
- return node !== null && node.type === AST_NODE_TYPES2.CallExpression && node.callee !== null && node.callee.type === AST_NODE_TYPES2.Identifier && /^use[A-Z]/.test(node.callee.name) && node.callee.name !== "useState";
1175
- }
1176
- function getJSXAncestor(node) {
1282
+ };
1283
+ const isUseStateCall = (node) => node !== null && node.type === AST_NODE_TYPES2.CallExpression && node.callee !== null && (node.callee.type === AST_NODE_TYPES2.Identifier && node.callee.name === "useState" || node.callee.type === AST_NODE_TYPES2.MemberExpression && node.callee.property !== null && node.callee.property.type === AST_NODE_TYPES2.Identifier && node.callee.property.name === "useState");
1284
+ const isHookCall = (node) => node !== null && node.type === AST_NODE_TYPES2.CallExpression && node.callee !== null && node.callee.type === AST_NODE_TYPES2.Identifier && /^use[A-Z]/.test(node.callee.name) && node.callee.name !== "useState";
1285
+ const getJSXAncestor = (node) => {
1177
1286
  let current = node.parent;
1178
1287
  while (current) {
1179
1288
  if (current.type === AST_NODE_TYPES2.JSXElement) {
@@ -1182,33 +1291,31 @@ var localizeReactProps = {
1182
1291
  current = current.parent;
1183
1292
  }
1184
1293
  return null;
1185
- }
1186
- function isContextProviderValueProp(node) {
1294
+ };
1295
+ const getTagNameFromOpening = (openingElement) => {
1296
+ const nameNode = openingElement.name;
1297
+ if (nameNode.type === AST_NODE_TYPES2.JSXIdentifier) {
1298
+ return nameNode.name;
1299
+ }
1300
+ const rightmost = getRightmostJSXIdentifier(nameNode);
1301
+ return rightmost ? rightmost.name : null;
1302
+ };
1303
+ const isProviderOrContext = (tagName) => tagName.endsWith("Provider") || tagName.endsWith("Context");
1304
+ const isValueAttributeOnProvider = (node) => node.type === AST_NODE_TYPES2.JSXAttribute && node.name && node.name.type === AST_NODE_TYPES2.JSXIdentifier && node.name.name === "value" && node.parent && node.parent.type === AST_NODE_TYPES2.JSXOpeningElement && (() => {
1305
+ const tagName = getTagNameFromOpening(node.parent);
1306
+ return tagName !== null && isProviderOrContext(tagName);
1307
+ })();
1308
+ const isContextProviderValueProp = (node) => {
1187
1309
  let current = node.parent;
1188
1310
  while (current) {
1189
- if (current.type === AST_NODE_TYPES2.JSXAttribute && current.name && current.name.type === AST_NODE_TYPES2.JSXIdentifier && current.name.name === "value") {
1190
- if (current.parent && current.parent.type === AST_NODE_TYPES2.JSXOpeningElement) {
1191
- const nameNode = current.parent.name;
1192
- if (nameNode.type === AST_NODE_TYPES2.JSXIdentifier) {
1193
- const tagName = nameNode.name;
1194
- if (tagName.endsWith("Provider") || tagName.endsWith("Context")) {
1195
- return true;
1196
- }
1197
- } else {
1198
- const rightmost = getRightmostJSXIdentifier(nameNode);
1199
- if (rightmost) {
1200
- if (rightmost.name.endsWith("Provider") || rightmost.name.endsWith("Context")) {
1201
- return true;
1202
- }
1203
- }
1204
- }
1205
- }
1311
+ if (isValueAttributeOnProvider(current)) {
1312
+ return true;
1206
1313
  }
1207
1314
  current = current.parent;
1208
1315
  }
1209
1316
  return false;
1210
- }
1211
- function isCustomJSXElement(jsxElement) {
1317
+ };
1318
+ const isCustomJSXElement = (jsxElement) => {
1212
1319
  if (!jsxElement || !jsxElement.openingElement || !jsxElement.openingElement.name) {
1213
1320
  return false;
1214
1321
  }
@@ -1217,12 +1324,9 @@ var localizeReactProps = {
1217
1324
  return /^[A-Z]/.test(nameNode.name);
1218
1325
  }
1219
1326
  const leftmost = getLeftmostJSXIdentifier(nameNode);
1220
- if (leftmost && /^[A-Z]/.test(leftmost.name)) {
1221
- return true;
1222
- }
1223
- return false;
1224
- }
1225
- function getComponentFunction(node) {
1327
+ return leftmost !== null && /^[A-Z]/.test(leftmost.name);
1328
+ };
1329
+ const getComponentFunction = (node) => {
1226
1330
  let current = node;
1227
1331
  while (current) {
1228
1332
  if (current.type === AST_NODE_TYPES2.FunctionDeclaration || current.type === AST_NODE_TYPES2.FunctionExpression || current.type === AST_NODE_TYPES2.ArrowFunctionExpression) {
@@ -1231,145 +1335,125 @@ var localizeReactProps = {
1231
1335
  current = current.parent;
1232
1336
  }
1233
1337
  return null;
1234
- }
1235
- function findVariableForIdentifier(id) {
1236
- let scope = context.sourceCode.getScope(id);
1338
+ };
1339
+ const findVariableForIdentifier = (identifier) => {
1340
+ let scope = context.sourceCode.getScope(identifier);
1237
1341
  while (scope) {
1238
- for (const variable of scope.variables) {
1239
- for (const def of variable.defs) {
1240
- if (def.name === id) {
1241
- return variable;
1242
- }
1243
- }
1342
+ const found = scope.variables.find((variable) => variable.defs.some((def) => def.name === identifier));
1343
+ if (found) {
1344
+ return found;
1244
1345
  }
1245
1346
  scope = scope.upper ?? null;
1246
1347
  }
1247
1348
  return null;
1248
- }
1249
- function analyzeVariableUsage(declarationId) {
1349
+ };
1350
+ const classifyReference = (reference, declarationId, jsxUsageSet) => {
1351
+ const { identifier } = reference;
1352
+ if (identifier === declarationId || isContextProviderValueProp(identifier)) {
1353
+ return false;
1354
+ }
1355
+ const jsxAncestor = getJSXAncestor(identifier);
1356
+ if (jsxAncestor && isCustomJSXElement(jsxAncestor)) {
1357
+ jsxUsageSet.add(jsxAncestor);
1358
+ return false;
1359
+ }
1360
+ return true;
1361
+ };
1362
+ const analyzeVariableUsage = (declarationId) => {
1250
1363
  const variable = findVariableForIdentifier(declarationId);
1251
1364
  if (!variable) {
1252
1365
  return {
1253
- jsxUsageSet: new Set,
1254
- hasOutsideUsage: false
1366
+ hasOutsideUsage: false,
1367
+ jsxUsageSet: new Set
1255
1368
  };
1256
1369
  }
1257
1370
  const jsxUsageSet = new Set;
1258
- let hasOutsideUsage = false;
1259
- for (const reference of variable.references) {
1260
- const identifier = reference.identifier;
1261
- if (identifier === declarationId) {
1262
- continue;
1263
- }
1264
- if (isContextProviderValueProp(identifier)) {
1265
- continue;
1266
- }
1267
- const jsxAncestor = getJSXAncestor(identifier);
1268
- if (jsxAncestor && isCustomJSXElement(jsxAncestor)) {
1269
- jsxUsageSet.add(jsxAncestor);
1270
- } else {
1271
- hasOutsideUsage = true;
1272
- }
1273
- }
1371
+ const hasOutsideUsage = variable.references.some((ref) => classifyReference(ref, declarationId, jsxUsageSet));
1274
1372
  return {
1275
- jsxUsageSet,
1276
- hasOutsideUsage
1373
+ hasOutsideUsage,
1374
+ jsxUsageSet
1277
1375
  };
1278
- }
1376
+ };
1279
1377
  const componentHookVars = new WeakMap;
1280
- function getHookSet(componentFunction) {
1378
+ const getHookSet = (componentFunction) => {
1281
1379
  let hookSet = componentHookVars.get(componentFunction);
1282
1380
  if (!hookSet) {
1283
1381
  hookSet = new Set;
1284
1382
  componentHookVars.set(componentFunction, hookSet);
1285
1383
  }
1286
1384
  return hookSet;
1287
- }
1288
- function hasHookDependency(node, hookSet) {
1385
+ };
1386
+ const isRangeContained = (refRange, nodeRange) => refRange[0] >= nodeRange[0] && refRange[1] <= nodeRange[1];
1387
+ const variableHasReferenceInRange = (variable, nodeRange) => variable.references.some((reference) => reference.identifier.range !== undefined && isRangeContained(reference.identifier.range, nodeRange));
1388
+ const hasHookDependency = (node, hookSet) => {
1289
1389
  if (!node.range) {
1290
1390
  return false;
1291
1391
  }
1292
1392
  const nodeRange = node.range;
1293
- const nodeStart = nodeRange[0];
1294
- const nodeEnd = nodeRange[1];
1295
1393
  let scope = context.sourceCode.getScope(node);
1296
1394
  while (scope) {
1297
- for (const variable of scope.variables) {
1298
- if (!hookSet.has(variable.name)) {
1299
- continue;
1300
- }
1301
- for (const reference of variable.references) {
1302
- const identifier = reference.identifier;
1303
- if (!identifier.range) {
1304
- continue;
1305
- }
1306
- const refRange = identifier.range;
1307
- const refStart = refRange[0];
1308
- const refEnd = refRange[1];
1309
- if (refStart >= nodeStart && refEnd <= nodeEnd) {
1310
- return true;
1311
- }
1312
- }
1395
+ const hookVars = scope.variables.filter((variable) => hookSet.has(variable.name));
1396
+ if (hookVars.some((variable) => variableHasReferenceInRange(variable, nodeRange))) {
1397
+ return true;
1313
1398
  }
1314
1399
  scope = scope.upper ?? null;
1315
1400
  }
1316
1401
  return false;
1317
- }
1318
- return {
1319
- VariableDeclarator(node) {
1320
- const componentFunction = getComponentFunction(node);
1321
- if (!componentFunction || !componentFunction.body)
1402
+ };
1403
+ const processUseStateDeclarator = (node) => {
1404
+ if (!node.init || !isUseStateCall(node.init) || node.id.type !== AST_NODE_TYPES2.ArrayPattern || node.id.elements.length < 2) {
1405
+ return false;
1406
+ }
1407
+ const [stateElem, setterElem] = node.id.elements;
1408
+ if (!stateElem || stateElem.type !== AST_NODE_TYPES2.Identifier || !setterElem || setterElem.type !== AST_NODE_TYPES2.Identifier) {
1409
+ return false;
1410
+ }
1411
+ const stateVarName = stateElem.name;
1412
+ const setterVarName = setterElem.name;
1413
+ const stateUsage = analyzeVariableUsage(stateElem);
1414
+ const setterUsage = analyzeVariableUsage(setterElem);
1415
+ const stateExclusivelySingleJSX = !stateUsage.hasOutsideUsage && stateUsage.jsxUsageSet.size === 1;
1416
+ const setterExclusivelySingleJSX = !setterUsage.hasOutsideUsage && setterUsage.jsxUsageSet.size === 1;
1417
+ if (!stateExclusivelySingleJSX || !setterExclusivelySingleJSX) {
1418
+ return true;
1419
+ }
1420
+ const stateTarget = getSingleSetElement(stateUsage.jsxUsageSet);
1421
+ const setterTarget = getSingleSetElement(setterUsage.jsxUsageSet);
1422
+ if (stateTarget && stateTarget === setterTarget) {
1423
+ context.report({
1424
+ data: { setterVarName, stateVarName },
1425
+ messageId: "stateAndSetterToChild",
1426
+ node
1427
+ });
1428
+ }
1429
+ return true;
1430
+ };
1431
+ const processGeneralVariable = (node, componentFunction) => {
1432
+ if (!node.id || node.id.type !== AST_NODE_TYPES2.Identifier) {
1433
+ return;
1434
+ }
1435
+ const varName = node.id.name;
1436
+ if (node.init) {
1437
+ const hookSet = getHookSet(componentFunction);
1438
+ if (hasHookDependency(node.init, hookSet)) {
1322
1439
  return;
1323
- if (node.init && node.id && node.id.type === AST_NODE_TYPES2.Identifier && node.init.type === AST_NODE_TYPES2.CallExpression && isHookCall(node.init)) {
1324
- const hookSet = getHookSet(componentFunction);
1325
- hookSet.add(node.id.name);
1326
- }
1327
- if (node.init && isUseStateCall(node.init) && node.id.type === AST_NODE_TYPES2.ArrayPattern && node.id.elements.length >= 2) {
1328
- const stateElem = node.id.elements[0];
1329
- const setterElem = node.id.elements[1];
1330
- if (!stateElem || stateElem.type !== AST_NODE_TYPES2.Identifier || !setterElem || setterElem.type !== AST_NODE_TYPES2.Identifier) {
1331
- return;
1332
- }
1333
- const stateVarName = stateElem.name;
1334
- const setterVarName = setterElem.name;
1335
- const stateUsage = analyzeVariableUsage(stateElem);
1336
- const setterUsage = analyzeVariableUsage(setterElem);
1337
- const stateExclusivelySingleJSX = !stateUsage.hasOutsideUsage && stateUsage.jsxUsageSet.size === 1;
1338
- const setterExclusivelySingleJSX = !setterUsage.hasOutsideUsage && setterUsage.jsxUsageSet.size === 1;
1339
- if (stateExclusivelySingleJSX && setterExclusivelySingleJSX) {
1340
- const stateTarget = getSingleSetElement(stateUsage.jsxUsageSet);
1341
- const setterTarget = getSingleSetElement(setterUsage.jsxUsageSet);
1342
- if (stateTarget && stateTarget === setterTarget) {
1343
- context.report({
1344
- node,
1345
- messageId: "stateAndSetterToChild",
1346
- data: { stateVarName, setterVarName }
1347
- });
1348
- }
1349
- }
1350
- } else if (node.id && node.id.type === AST_NODE_TYPES2.Identifier) {
1351
- const varName = node.id.name;
1352
- if (node.init) {
1353
- const hookSet = getHookSet(componentFunction);
1354
- if (hasHookDependency(node.init, hookSet)) {
1355
- return;
1356
- }
1357
- }
1358
- const usage = analyzeVariableUsage(node.id);
1359
- if (!usage.hasOutsideUsage && usage.jsxUsageSet.size === 1) {
1360
- const target = getSingleSetElement(usage.jsxUsageSet);
1361
- const componentName = getJSXElementName(target);
1362
- candidateVariables.push({
1363
- node,
1364
- varName,
1365
- componentName
1366
- });
1367
- }
1368
1440
  }
1369
- },
1441
+ }
1442
+ const usage = analyzeVariableUsage(node.id);
1443
+ if (!usage.hasOutsideUsage && usage.jsxUsageSet.size === 1) {
1444
+ const target = getSingleSetElement(usage.jsxUsageSet);
1445
+ const componentName = getJSXElementName(target);
1446
+ candidateVariables.push({
1447
+ componentName,
1448
+ node,
1449
+ varName
1450
+ });
1451
+ }
1452
+ };
1453
+ return {
1370
1454
  "Program:exit"() {
1371
1455
  const groups = new Map;
1372
- for (const candidate of candidateVariables) {
1456
+ candidateVariables.forEach((candidate) => {
1373
1457
  const key = candidate.componentName;
1374
1458
  const existing = groups.get(key);
1375
1459
  if (existing) {
@@ -1377,87 +1461,102 @@ var localizeReactProps = {
1377
1461
  } else {
1378
1462
  groups.set(key, [candidate]);
1379
1463
  }
1380
- }
1381
- for (const candidates of groups.values()) {
1382
- if (candidates.length === 1) {
1383
- const candidate = candidates[0];
1384
- if (!candidate) {
1385
- continue;
1386
- }
1387
- context.report({
1388
- node: candidate.node,
1389
- messageId: "variableToChild",
1390
- data: { varName: candidate.varName }
1391
- });
1464
+ });
1465
+ groups.forEach((candidates) => {
1466
+ if (candidates.length !== 1) {
1467
+ return;
1392
1468
  }
1469
+ const [candidate] = candidates;
1470
+ if (!candidate) {
1471
+ return;
1472
+ }
1473
+ context.report({
1474
+ data: { varName: candidate.varName },
1475
+ messageId: "variableToChild",
1476
+ node: candidate.node
1477
+ });
1478
+ });
1479
+ },
1480
+ VariableDeclarator(node) {
1481
+ const componentFunction = getComponentFunction(node);
1482
+ if (!componentFunction || !componentFunction.body)
1483
+ return;
1484
+ if (node.init && node.id && node.id.type === AST_NODE_TYPES2.Identifier && node.init.type === AST_NODE_TYPES2.CallExpression && isHookCall(node.init)) {
1485
+ const hookSet = getHookSet(componentFunction);
1486
+ hookSet.add(node.id.name);
1487
+ }
1488
+ const wasUseState = processUseStateDeclarator(node);
1489
+ if (!wasUseState) {
1490
+ processGeneralVariable(node, componentFunction);
1393
1491
  }
1394
1492
  }
1395
1493
  };
1494
+ },
1495
+ defaultOptions: [],
1496
+ meta: {
1497
+ docs: {
1498
+ description: "Disallow variables that are only passed to a single custom child component. For useState, only report if both the state and its setter are exclusively passed to a single custom child. For general variables, only report if a given child receives exactly one such candidate \u2013 if two or more are passed to the same component type, they're assumed to be settings that belong on the parent."
1499
+ },
1500
+ messages: {
1501
+ stateAndSetterToChild: "State variable '{{stateVarName}}' and its setter '{{setterVarName}}' are only passed to a single custom child component. Consider moving the state into that component.",
1502
+ variableToChild: "Variable '{{varName}}' is only passed to a single custom child component. Consider moving it to that component."
1503
+ },
1504
+ schema: [],
1505
+ type: "suggestion"
1396
1506
  }
1397
1507
  };
1398
1508
 
1399
1509
  // src/rules/no-or-none-component.ts
1400
1510
  var noOrNoneComponent = {
1401
- meta: {
1402
- type: "suggestion",
1403
- docs: {
1404
- description: "Prefer using logical && operator over ternary with null/undefined for conditional JSX rendering."
1405
- },
1406
- schema: [],
1407
- messages: {
1408
- useLogicalAnd: "Prefer using the logical '&&' operator instead of a ternary with null/undefined for conditional rendering."
1409
- }
1410
- },
1411
- defaultOptions: [],
1412
1511
  create(context) {
1413
1512
  return {
1414
1513
  ConditionalExpression(node) {
1415
- const alternate = node.alternate;
1514
+ const { alternate } = node;
1416
1515
  const isNullAlternate = alternate && alternate.type === "Literal" && alternate.value === null;
1417
1516
  const isUndefinedAlternate = alternate && alternate.type === "Identifier" && alternate.name === "undefined";
1418
1517
  if (!isNullAlternate && !isUndefinedAlternate) {
1419
1518
  return;
1420
1519
  }
1421
- const parent = node.parent;
1520
+ const { parent } = node;
1422
1521
  if (!parent || parent.type !== "JSXExpressionContainer") {
1423
1522
  return;
1424
1523
  }
1425
1524
  const containerParent = parent.parent;
1426
1525
  if (containerParent && containerParent.type !== "JSXAttribute") {
1427
1526
  context.report({
1428
- node,
1429
- messageId: "useLogicalAnd"
1527
+ messageId: "useLogicalAnd",
1528
+ node
1430
1529
  });
1431
1530
  }
1432
1531
  }
1433
1532
  };
1533
+ },
1534
+ defaultOptions: [],
1535
+ meta: {
1536
+ docs: {
1537
+ description: "Prefer using logical && operator over ternary with null/undefined for conditional JSX rendering."
1538
+ },
1539
+ messages: {
1540
+ useLogicalAnd: "Prefer using the logical '&&' operator instead of a ternary with null/undefined for conditional rendering."
1541
+ },
1542
+ schema: [],
1543
+ type: "suggestion"
1434
1544
  }
1435
1545
  };
1436
1546
 
1437
1547
  // src/rules/no-button-navigation.ts
1438
1548
  var noButtonNavigation = {
1439
- meta: {
1440
- type: "suggestion",
1441
- docs: {
1442
- description: "Enforce using anchor tags for navigation instead of buttons whose onClick handlers change the path. Allow only query/hash updates via window.location.search or history.replaceState(window.location.pathname + \u2026)."
1443
- },
1444
- schema: [],
1445
- messages: {
1446
- noButtonNavigation: "Use an anchor tag for navigation instead of a button whose onClick handler changes the path. Detected: {{reason}}. Only query/hash updates (reading window.location.search, .pathname, or .hash) are allowed."
1447
- }
1448
- },
1449
- defaultOptions: [],
1450
1549
  create(context) {
1451
1550
  const handlerStack = [];
1452
- function getCurrentHandler() {
1551
+ const getCurrentHandler = () => {
1453
1552
  const state = handlerStack[handlerStack.length - 1];
1454
1553
  if (!state) {
1455
1554
  return null;
1456
1555
  }
1457
1556
  return state;
1458
- }
1459
- function isOnClickButtonHandler(node) {
1460
- const parent = node.parent;
1557
+ };
1558
+ const isOnClickButtonHandler = (node) => {
1559
+ const { parent } = node;
1461
1560
  if (!parent || parent.type !== "JSXExpressionContainer") {
1462
1561
  return null;
1463
1562
  }
@@ -1473,37 +1572,50 @@ var noButtonNavigation = {
1473
1572
  if (!openingElementCandidate || openingElementCandidate.type !== "JSXOpeningElement") {
1474
1573
  return null;
1475
1574
  }
1476
- const openingElement = openingElementCandidate;
1477
- const tagNameNode = openingElement.name;
1575
+ const tagNameNode = openingElementCandidate.name;
1478
1576
  if (tagNameNode.type !== "JSXIdentifier" || tagNameNode.name !== "button") {
1479
1577
  return null;
1480
1578
  }
1481
1579
  return attr;
1482
- }
1483
- function isWindowLocationMember(member) {
1484
- const object = member.object;
1580
+ };
1581
+ const isWindowLocationMember = (member) => {
1582
+ const { object } = member;
1485
1583
  if (object.type !== "MemberExpression") {
1486
1584
  return false;
1487
1585
  }
1488
1586
  const outerObject = object.object;
1489
1587
  const outerProperty = object.property;
1490
- if (outerObject.type === "Identifier" && outerObject.name === "window" && outerProperty.type === "Identifier" && outerProperty.name === "location") {
1491
- return true;
1492
- }
1493
- return false;
1494
- }
1495
- function isWindowHistoryMember(member) {
1496
- const object = member.object;
1588
+ return outerObject.type === "Identifier" && outerObject.name === "window" && outerProperty.type === "Identifier" && outerProperty.name === "location";
1589
+ };
1590
+ const isWindowHistoryMember = (member) => {
1591
+ const { object } = member;
1497
1592
  if (object.type !== "MemberExpression") {
1498
1593
  return false;
1499
1594
  }
1500
1595
  const outerObject = object.object;
1501
1596
  const outerProperty = object.property;
1502
- if (outerObject.type === "Identifier" && outerObject.name === "window" && outerProperty.type === "Identifier" && outerProperty.name === "history") {
1503
- return true;
1597
+ return outerObject.type === "Identifier" && outerObject.name === "window" && outerProperty.type === "Identifier" && outerProperty.name === "history";
1598
+ };
1599
+ const reportHandlerExit = (state) => {
1600
+ const { reason, sawReplaceCall, sawAllowedLocationRead } = state;
1601
+ if (reason) {
1602
+ context.report({
1603
+ data: { reason },
1604
+ messageId: "noButtonNavigation",
1605
+ node: state.attribute
1606
+ });
1607
+ return;
1504
1608
  }
1505
- return false;
1506
- }
1609
+ if (sawReplaceCall && !sawAllowedLocationRead) {
1610
+ context.report({
1611
+ data: {
1612
+ reason: "history.replaceState/pushState without reading window.location"
1613
+ },
1614
+ messageId: "noButtonNavigation",
1615
+ node: state.attribute
1616
+ });
1617
+ }
1618
+ };
1507
1619
  return {
1508
1620
  ArrowFunctionExpression(node) {
1509
1621
  const attr = isOnClickButtonHandler(node);
@@ -1513,8 +1625,8 @@ var noButtonNavigation = {
1513
1625
  handlerStack.push({
1514
1626
  attribute: attr,
1515
1627
  reason: null,
1516
- sawReplaceCall: false,
1517
- sawAllowedLocationRead: false
1628
+ sawAllowedLocationRead: false,
1629
+ sawReplaceCall: false
1518
1630
  });
1519
1631
  },
1520
1632
  "ArrowFunctionExpression:exit"(node) {
@@ -1526,25 +1638,40 @@ var noButtonNavigation = {
1526
1638
  if (!state) {
1527
1639
  return;
1528
1640
  }
1529
- const reason = state.reason;
1530
- const sawReplaceCall = state.sawReplaceCall;
1531
- const sawAllowedLocationRead = state.sawAllowedLocationRead;
1532
- if (reason) {
1533
- context.report({
1534
- node: state.attribute,
1535
- messageId: "noButtonNavigation",
1536
- data: { reason }
1537
- });
1641
+ reportHandlerExit(state);
1642
+ },
1643
+ AssignmentExpression(node) {
1644
+ const state = getCurrentHandler();
1645
+ if (!state) {
1538
1646
  return;
1539
1647
  }
1540
- if (sawReplaceCall && !sawAllowedLocationRead) {
1541
- context.report({
1542
- node: state.attribute,
1543
- messageId: "noButtonNavigation",
1544
- data: {
1545
- reason: "history.replaceState/pushState without reading window.location"
1546
- }
1547
- });
1648
+ if (node.left.type !== "MemberExpression") {
1649
+ return;
1650
+ }
1651
+ const { left } = node;
1652
+ if (left.object.type === "Identifier" && left.object.name === "window" && left.property.type === "Identifier" && left.property.name === "location" && !state.reason) {
1653
+ state.reason = "assignment to window.location";
1654
+ return;
1655
+ }
1656
+ if (isWindowLocationMember(left) && !state.reason) {
1657
+ state.reason = "assignment to window.location sub-property";
1658
+ }
1659
+ },
1660
+ CallExpression(node) {
1661
+ const state = getCurrentHandler();
1662
+ if (!state) {
1663
+ return;
1664
+ }
1665
+ const { callee } = node;
1666
+ if (callee.type !== "MemberExpression") {
1667
+ return;
1668
+ }
1669
+ if (isWindowLocationMember(callee) && callee.property.type === "Identifier" && callee.property.name === "replace" && !state.reason) {
1670
+ state.reason = "window.location.replace";
1671
+ return;
1672
+ }
1673
+ if (isWindowHistoryMember(callee) && callee.property.type === "Identifier" && (callee.property.name === "pushState" || callee.property.name === "replaceState")) {
1674
+ state.sawReplaceCall = true;
1548
1675
  }
1549
1676
  },
1550
1677
  FunctionExpression(node) {
@@ -1555,8 +1682,8 @@ var noButtonNavigation = {
1555
1682
  handlerStack.push({
1556
1683
  attribute: attr,
1557
1684
  reason: null,
1558
- sawReplaceCall: false,
1559
- sawAllowedLocationRead: false
1685
+ sawAllowedLocationRead: false,
1686
+ sawReplaceCall: false
1560
1687
  });
1561
1688
  },
1562
1689
  "FunctionExpression:exit"(node) {
@@ -1568,159 +1695,99 @@ var noButtonNavigation = {
1568
1695
  if (!state) {
1569
1696
  return;
1570
1697
  }
1571
- const reason = state.reason;
1572
- const sawReplaceCall = state.sawReplaceCall;
1573
- const sawAllowedLocationRead = state.sawAllowedLocationRead;
1574
- if (reason) {
1575
- context.report({
1576
- node: state.attribute,
1577
- messageId: "noButtonNavigation",
1578
- data: { reason }
1579
- });
1580
- return;
1581
- }
1582
- if (sawReplaceCall && !sawAllowedLocationRead) {
1583
- context.report({
1584
- node: state.attribute,
1585
- messageId: "noButtonNavigation",
1586
- data: {
1587
- reason: "history.replaceState/pushState without reading window.location"
1588
- }
1589
- });
1590
- }
1698
+ reportHandlerExit(state);
1591
1699
  },
1592
1700
  MemberExpression(node) {
1593
1701
  const state = getCurrentHandler();
1594
1702
  if (!state) {
1595
1703
  return;
1596
1704
  }
1597
- if (node.object.type === "Identifier" && node.object.name === "window" && node.property.type === "Identifier" && node.property.name === "open") {
1598
- if (!state.reason) {
1599
- state.reason = "window.open";
1600
- }
1705
+ if (node.object.type === "Identifier" && node.object.name === "window" && node.property.type === "Identifier" && node.property.name === "open" && !state.reason) {
1706
+ state.reason = "window.open";
1601
1707
  }
1602
1708
  if (isWindowLocationMember(node) && node.property.type === "Identifier" && (node.property.name === "search" || node.property.name === "pathname" || node.property.name === "hash")) {
1603
1709
  state.sawAllowedLocationRead = true;
1604
1710
  }
1605
- },
1606
- AssignmentExpression(node) {
1607
- const state = getCurrentHandler();
1608
- if (!state) {
1609
- return;
1610
- }
1611
- if (node.left.type !== "MemberExpression") {
1612
- return;
1613
- }
1614
- const left = node.left;
1615
- if (left.object.type === "Identifier" && left.object.name === "window" && left.property.type === "Identifier" && left.property.name === "location") {
1616
- if (!state.reason) {
1617
- state.reason = "assignment to window.location";
1618
- }
1619
- return;
1620
- }
1621
- if (isWindowLocationMember(left)) {
1622
- if (!state.reason) {
1623
- state.reason = "assignment to window.location sub-property";
1624
- }
1625
- }
1626
- },
1627
- CallExpression(node) {
1628
- const state = getCurrentHandler();
1629
- if (!state) {
1630
- return;
1631
- }
1632
- const callee = node.callee;
1633
- if (callee.type !== "MemberExpression") {
1634
- return;
1635
- }
1636
- if (isWindowLocationMember(callee) && callee.property.type === "Identifier" && callee.property.name === "replace") {
1637
- if (!state.reason) {
1638
- state.reason = "window.location.replace";
1639
- }
1640
- return;
1641
- }
1642
- if (isWindowHistoryMember(callee) && callee.property.type === "Identifier" && (callee.property.name === "pushState" || callee.property.name === "replaceState")) {
1643
- state.sawReplaceCall = true;
1644
- }
1645
1711
  }
1646
1712
  };
1713
+ },
1714
+ defaultOptions: [],
1715
+ meta: {
1716
+ docs: {
1717
+ description: "Enforce using anchor tags for navigation instead of buttons whose onClick handlers change the path. Allow only query/hash updates via window.location.search or history.replaceState(window.location.pathname + \u2026)."
1718
+ },
1719
+ messages: {
1720
+ noButtonNavigation: "Use an anchor tag for navigation instead of a button whose onClick handler changes the path. Detected: {{reason}}. Only query/hash updates (reading window.location.search, .pathname, or .hash) are allowed."
1721
+ },
1722
+ schema: [],
1723
+ type: "suggestion"
1647
1724
  }
1648
1725
  };
1649
1726
 
1650
1727
  // src/rules/no-multi-style-objects.ts
1728
+ var getPropertyName = (prop) => {
1729
+ const { key } = prop;
1730
+ if (key.type === "Identifier") {
1731
+ return key.name;
1732
+ }
1733
+ if (key.type === "Literal" && typeof key.value === "string") {
1734
+ return key.value;
1735
+ }
1736
+ return null;
1737
+ };
1651
1738
  var noMultiStyleObjects = {
1652
- meta: {
1653
- type: "problem",
1654
- docs: {
1655
- description: "Disallow grouping CSS style objects in a single export; export each style separately."
1656
- },
1657
- schema: [],
1658
- messages: {
1659
- noMultiStyleObjects: "Do not group CSS style objects in a single export; export each style separately."
1660
- }
1661
- },
1662
- defaultOptions: [],
1663
1739
  create(context) {
1664
- function checkObjectExpression(node) {
1740
+ const checkObjectExpression = (node) => {
1665
1741
  if (!node.properties.length) {
1666
1742
  return;
1667
1743
  }
1668
- const cssStyleProperties = [];
1669
- for (const prop of node.properties) {
1744
+ const cssStyleProperties = node.properties.filter((prop) => {
1670
1745
  if (prop.type !== "Property") {
1671
- continue;
1672
- }
1673
- const key = prop.key;
1674
- let name = null;
1675
- if (key.type === "Identifier") {
1676
- name = key.name;
1677
- } else if (key.type === "Literal" && typeof key.value === "string") {
1678
- name = key.value;
1679
- }
1680
- if (name && name.endsWith("Style")) {
1681
- cssStyleProperties.push(prop);
1746
+ return false;
1682
1747
  }
1683
- }
1748
+ const name = getPropertyName(prop);
1749
+ return name !== null && name.endsWith("Style");
1750
+ });
1684
1751
  if (cssStyleProperties.length > 1) {
1685
1752
  context.report({
1686
- node,
1687
- messageId: "noMultiStyleObjects"
1753
+ messageId: "noMultiStyleObjects",
1754
+ node
1688
1755
  });
1689
1756
  }
1690
- }
1757
+ };
1691
1758
  return {
1692
1759
  ExportDefaultDeclaration(node) {
1693
- const declaration = node.declaration;
1760
+ const { declaration } = node;
1694
1761
  if (declaration && declaration.type === "ObjectExpression") {
1695
1762
  checkObjectExpression(declaration);
1696
1763
  }
1697
1764
  },
1698
1765
  ReturnStatement(node) {
1699
- const argument = node.argument;
1766
+ const { argument } = node;
1700
1767
  if (argument && argument.type === "ObjectExpression") {
1701
1768
  checkObjectExpression(argument);
1702
1769
  }
1703
1770
  }
1704
1771
  };
1772
+ },
1773
+ defaultOptions: [],
1774
+ meta: {
1775
+ docs: {
1776
+ description: "Disallow grouping CSS style objects in a single export; export each style separately."
1777
+ },
1778
+ messages: {
1779
+ noMultiStyleObjects: "Do not group CSS style objects in a single export; export each style separately."
1780
+ },
1781
+ schema: [],
1782
+ type: "problem"
1705
1783
  }
1706
1784
  };
1707
1785
 
1708
1786
  // src/rules/no-useless-function.ts
1709
1787
  var noUselessFunction = {
1710
- meta: {
1711
- type: "suggestion",
1712
- docs: {
1713
- description: "Disallow functions that have no parameters and just return an object literal; consider exporting the object directly, unless the function is used as a callback (e.g., in react-spring)."
1714
- },
1715
- schema: [],
1716
- messages: {
1717
- uselessFunction: "This function has no parameters and simply returns an object. Consider exporting the object directly instead of wrapping it in a function."
1718
- }
1719
- },
1720
- defaultOptions: [],
1721
1788
  create(context) {
1722
- function isCallbackFunction(node) {
1723
- const parent = node.parent;
1789
+ const isCallbackFunction = (node) => {
1790
+ const { parent } = node;
1724
1791
  if (!parent || parent.type !== "CallExpression") {
1725
1792
  return false;
1726
1793
  }
@@ -1730,7 +1797,7 @@ var noUselessFunction = {
1730
1797
  }
1731
1798
  }
1732
1799
  return false;
1733
- }
1800
+ };
1734
1801
  return {
1735
1802
  ArrowFunctionExpression(node) {
1736
1803
  if (node.params.length === 0 && node.body && node.body.type === "ObjectExpression") {
@@ -1738,55 +1805,73 @@ var noUselessFunction = {
1738
1805
  return;
1739
1806
  }
1740
1807
  context.report({
1741
- node,
1742
- messageId: "uselessFunction"
1808
+ messageId: "uselessFunction",
1809
+ node
1743
1810
  });
1744
1811
  }
1745
1812
  }
1746
1813
  };
1814
+ },
1815
+ defaultOptions: [],
1816
+ meta: {
1817
+ docs: {
1818
+ description: "Disallow functions that have no parameters and just return an object literal; consider exporting the object directly, unless the function is used as a callback (e.g., in react-spring)."
1819
+ },
1820
+ messages: {
1821
+ uselessFunction: "This function has no parameters and simply returns an object. Consider exporting the object directly instead of wrapping it in a function."
1822
+ },
1823
+ schema: [],
1824
+ type: "suggestion"
1747
1825
  }
1748
1826
  };
1749
1827
 
1750
1828
  // src/rules/min-var-length.ts
1829
+ var extractIdentifiersFromPattern = (pattern, identifiers = []) => {
1830
+ if (!pattern)
1831
+ return identifiers;
1832
+ switch (pattern.type) {
1833
+ case "Identifier":
1834
+ identifiers.push(pattern.name);
1835
+ break;
1836
+ case "ObjectPattern":
1837
+ pattern.properties.forEach((prop) => {
1838
+ if (prop.type === "Property") {
1839
+ extractIdentifiersFromPattern(prop.value, identifiers);
1840
+ } else if (prop.type === "RestElement") {
1841
+ extractIdentifiersFromPattern(prop.argument, identifiers);
1842
+ }
1843
+ });
1844
+ break;
1845
+ case "ArrayPattern":
1846
+ pattern.elements.filter((element) => element !== null).forEach((element) => {
1847
+ extractIdentifiersFromPattern(element, identifiers);
1848
+ });
1849
+ break;
1850
+ case "AssignmentPattern":
1851
+ extractIdentifiersFromPattern(pattern.left, identifiers);
1852
+ break;
1853
+ default:
1854
+ break;
1855
+ }
1856
+ return identifiers;
1857
+ };
1858
+ var getDeclaratorNames = (declarations) => declarations.filter((decl) => decl.id.type === "Identifier").map((decl) => decl.id.name);
1859
+ var collectParamNames = (params) => {
1860
+ const names = [];
1861
+ params.forEach((param) => {
1862
+ extractIdentifiersFromPattern(param, names);
1863
+ });
1864
+ return names;
1865
+ };
1751
1866
  var minVarLength = {
1752
- meta: {
1753
- type: "problem",
1754
- docs: {
1755
- description: "Disallow variable names shorter than the configured minimum length unless an outer variable with a longer name starting with the same characters exists. You can exempt specific variable names using the allowedVars option."
1756
- },
1757
- schema: [
1758
- {
1759
- type: "object",
1760
- properties: {
1761
- minLength: {
1762
- type: "number",
1763
- default: 1
1764
- },
1765
- allowedVars: {
1766
- type: "array",
1767
- items: {
1768
- type: "string",
1769
- minLength: 1
1770
- },
1771
- default: []
1772
- }
1773
- },
1774
- additionalProperties: false
1775
- }
1776
- ],
1777
- messages: {
1778
- variableNameTooShort: "Variable '{{name}}' is too short. Minimum allowed length is {{minLength}} characters unless an outer variable with a longer name starting with '{{name}}' exists."
1779
- }
1780
- },
1781
- defaultOptions: [{}],
1782
1867
  create(context) {
1783
- const sourceCode = context.sourceCode;
1784
- const options = context.options[0];
1868
+ const { sourceCode } = context;
1869
+ const [options] = context.options;
1785
1870
  const configuredMinLength = options && typeof options.minLength === "number" ? options.minLength : 1;
1786
1871
  const configuredAllowedVars = options && Array.isArray(options.allowedVars) ? options.allowedVars : [];
1787
1872
  const minLength = configuredMinLength;
1788
1873
  const allowedVars = configuredAllowedVars;
1789
- function getAncestors(node) {
1874
+ const getAncestors = (node) => {
1790
1875
  const ancestors = [];
1791
1876
  let current = node.parent;
1792
1877
  while (current) {
@@ -1794,9 +1879,9 @@ var minVarLength = {
1794
1879
  current = current.parent;
1795
1880
  }
1796
1881
  return ancestors;
1797
- }
1798
- function getScope(node) {
1799
- const scopeManager = sourceCode.scopeManager;
1882
+ };
1883
+ const getScope = (node) => {
1884
+ const { scopeManager } = sourceCode;
1800
1885
  if (!scopeManager) {
1801
1886
  return null;
1802
1887
  }
@@ -1805,119 +1890,79 @@ var minVarLength = {
1805
1890
  return acquired;
1806
1891
  }
1807
1892
  return scopeManager.globalScope ?? null;
1808
- }
1809
- function getVariablesInNearestBlock(node) {
1893
+ };
1894
+ const getVariablesInNearestBlock = (node) => {
1810
1895
  let current = node.parent;
1811
1896
  while (current && current.type !== "BlockStatement") {
1812
1897
  current = current.parent;
1813
1898
  }
1814
- const names = [];
1815
- if (current && current.type === "BlockStatement" && Array.isArray(current.body)) {
1816
- for (const stmt of current.body) {
1817
- if (stmt.type === "VariableDeclaration") {
1818
- for (const decl of stmt.declarations) {
1819
- if (decl.id && decl.id.type === "Identifier") {
1820
- names.push(decl.id.name);
1821
- }
1822
- }
1823
- }
1824
- }
1825
- }
1826
- return names;
1827
- }
1828
- function extractIdentifiersFromPattern(pattern, identifiers = []) {
1829
- if (!pattern)
1830
- return identifiers;
1831
- switch (pattern.type) {
1832
- case "Identifier":
1833
- identifiers.push(pattern.name);
1834
- break;
1835
- case "ObjectPattern":
1836
- for (const prop of pattern.properties) {
1837
- if (prop.type === "Property") {
1838
- extractIdentifiersFromPattern(prop.value, identifiers);
1839
- } else if (prop.type === "RestElement") {
1840
- extractIdentifiersFromPattern(prop.argument, identifiers);
1841
- }
1842
- }
1843
- break;
1844
- case "ArrayPattern":
1845
- for (const element of pattern.elements) {
1846
- if (element) {
1847
- extractIdentifiersFromPattern(element, identifiers);
1848
- }
1849
- }
1850
- break;
1851
- case "AssignmentPattern":
1852
- extractIdentifiersFromPattern(pattern.left, identifiers);
1853
- break;
1854
- default:
1855
- break;
1899
+ if (!current || current.type !== "BlockStatement" || !Array.isArray(current.body)) {
1900
+ return [];
1856
1901
  }
1857
- return identifiers;
1858
- }
1859
- function hasOuterCorrespondingIdentifier(shortName, node) {
1902
+ const varDeclarations = current.body.filter((stmt) => stmt.type === "VariableDeclaration");
1903
+ return varDeclarations.flatMap((stmt) => getDeclaratorNames(stmt.declarations));
1904
+ };
1905
+ const isLongerMatchInScope = (shortName, varName) => varName.length >= minLength && varName.length > shortName.length && varName.startsWith(shortName);
1906
+ const checkScopeVariables = (shortName, node) => {
1860
1907
  const startingScope = getScope(node);
1861
1908
  let outer = startingScope && startingScope.upper ? startingScope.upper : null;
1862
1909
  while (outer) {
1863
- for (const variable of outer.variables) {
1864
- if (variable.name.length >= minLength && variable.name.length > shortName.length && variable.name.startsWith(shortName)) {
1865
- return true;
1866
- }
1910
+ if (outer.variables.some((variable) => isLongerMatchInScope(shortName, variable.name))) {
1911
+ return true;
1867
1912
  }
1868
1913
  outer = outer.upper;
1869
1914
  }
1915
+ return false;
1916
+ };
1917
+ const checkBlockVariables = (shortName, node) => {
1870
1918
  const blockVars = getVariablesInNearestBlock(node);
1871
- for (const name of blockVars) {
1872
- if (name.length >= minLength && name.length > shortName.length && name.startsWith(shortName)) {
1873
- return true;
1874
- }
1919
+ return blockVars.some((varName) => isLongerMatchInScope(shortName, varName));
1920
+ };
1921
+ const checkAncestorDeclarators = (shortName, node) => {
1922
+ const ancestors = getAncestors(node);
1923
+ return ancestors.some((anc) => anc.type === "VariableDeclarator" && anc.id && anc.id.type === "Identifier" && isLongerMatchInScope(shortName, anc.id.name));
1924
+ };
1925
+ const checkFunctionAncestor = (shortName, anc) => {
1926
+ const names = collectParamNames(anc.params);
1927
+ return names.some((paramName) => isLongerMatchInScope(shortName, paramName));
1928
+ };
1929
+ const checkCatchAncestor = (shortName, anc) => {
1930
+ if (!anc.param) {
1931
+ return false;
1875
1932
  }
1933
+ const names = extractIdentifiersFromPattern(anc.param, []);
1934
+ return names.some((paramName) => isLongerMatchInScope(shortName, paramName));
1935
+ };
1936
+ const checkAncestorParams = (shortName, node) => {
1876
1937
  const ancestors = getAncestors(node);
1877
- for (const anc of ancestors) {
1878
- if (anc.type === "VariableDeclarator" && anc.id && anc.id.type === "Identifier") {
1879
- const outerName = anc.id.name;
1880
- if (outerName.length >= minLength && outerName.length > shortName.length && outerName.startsWith(shortName)) {
1881
- return true;
1882
- }
1883
- }
1884
- if ((anc.type === "FunctionDeclaration" || anc.type === "FunctionExpression" || anc.type === "ArrowFunctionExpression") && Array.isArray(anc.params)) {
1885
- for (const param of anc.params) {
1886
- const names = extractIdentifiersFromPattern(param, []);
1887
- for (const n of names) {
1888
- if (n.length >= minLength && n.length > shortName.length && n.startsWith(shortName)) {
1889
- return true;
1890
- }
1891
- }
1892
- }
1938
+ return ancestors.some((anc) => {
1939
+ if (anc.type === "FunctionDeclaration" || anc.type === "FunctionExpression" || anc.type === "ArrowFunctionExpression") {
1940
+ return checkFunctionAncestor(shortName, anc);
1893
1941
  }
1894
- if (anc.type === "CatchClause" && anc.param) {
1895
- const names = extractIdentifiersFromPattern(anc.param, []);
1896
- for (const n of names) {
1897
- if (n.length >= minLength && n.length > shortName.length && n.startsWith(shortName)) {
1898
- return true;
1899
- }
1900
- }
1942
+ if (anc.type === "CatchClause") {
1943
+ return checkCatchAncestor(shortName, anc);
1901
1944
  }
1945
+ return false;
1946
+ });
1947
+ };
1948
+ const hasOuterCorrespondingIdentifier = (shortName, node) => checkScopeVariables(shortName, node) || checkBlockVariables(shortName, node) || checkAncestorDeclarators(shortName, node) || checkAncestorParams(shortName, node);
1949
+ const checkIdentifier = (node) => {
1950
+ const { name } = node;
1951
+ if (name.length >= minLength) {
1952
+ return;
1902
1953
  }
1903
- return false;
1904
- }
1905
- function checkIdentifier(node) {
1906
- const name = node.name;
1907
- if (name.length < minLength) {
1908
- if (allowedVars.includes(name)) {
1909
- return;
1910
- }
1911
- if (!hasOuterCorrespondingIdentifier(name, node)) {
1912
- context.report({
1913
- node,
1914
- messageId: "variableNameTooShort",
1915
- data: { name, minLength }
1916
- });
1917
- }
1954
+ if (allowedVars.includes(name)) {
1955
+ return;
1956
+ }
1957
+ if (!hasOuterCorrespondingIdentifier(name, node)) {
1958
+ context.report({
1959
+ data: { minLength, name },
1960
+ messageId: "variableNameTooShort",
1961
+ node
1962
+ });
1918
1963
  }
1919
- }
1920
- function checkPattern(pattern) {
1964
+ };
1965
+ const checkPattern = (pattern) => {
1921
1966
  if (!pattern)
1922
1967
  return;
1923
1968
  switch (pattern.type) {
@@ -1925,20 +1970,18 @@ var minVarLength = {
1925
1970
  checkIdentifier(pattern);
1926
1971
  break;
1927
1972
  case "ObjectPattern":
1928
- for (const prop of pattern.properties) {
1973
+ pattern.properties.forEach((prop) => {
1929
1974
  if (prop.type === "Property") {
1930
1975
  checkPattern(prop.value);
1931
1976
  } else if (prop.type === "RestElement") {
1932
1977
  checkPattern(prop.argument);
1933
1978
  }
1934
- }
1979
+ });
1935
1980
  break;
1936
1981
  case "ArrayPattern":
1937
- for (const element of pattern.elements) {
1938
- if (element) {
1939
- checkPattern(element);
1940
- }
1941
- }
1982
+ pattern.elements.filter((element) => element !== null).forEach((element) => {
1983
+ checkPattern(element);
1984
+ });
1942
1985
  break;
1943
1986
  case "AssignmentPattern":
1944
1987
  checkPattern(pattern.left);
@@ -1946,49 +1989,64 @@ var minVarLength = {
1946
1989
  default:
1947
1990
  break;
1948
1991
  }
1949
- }
1992
+ };
1950
1993
  return {
1951
- VariableDeclarator(node) {
1952
- if (node.id) {
1953
- checkPattern(node.id);
1994
+ CatchClause(node) {
1995
+ if (node.param) {
1996
+ checkPattern(node.param);
1954
1997
  }
1955
1998
  },
1956
1999
  "FunctionDeclaration, FunctionExpression, ArrowFunctionExpression"(node) {
1957
- for (const param of node.params) {
2000
+ node.params.forEach((param) => {
1958
2001
  checkPattern(param);
1959
- }
2002
+ });
1960
2003
  },
1961
- CatchClause(node) {
1962
- if (node.param) {
1963
- checkPattern(node.param);
2004
+ VariableDeclarator(node) {
2005
+ if (node.id) {
2006
+ checkPattern(node.id);
1964
2007
  }
1965
2008
  }
1966
2009
  };
1967
- }
1968
- };
1969
-
1970
- // src/rules/max-depth-extended.ts
1971
- var maxDepthExtended = {
2010
+ },
2011
+ defaultOptions: [{}],
1972
2012
  meta: {
1973
- type: "suggestion",
1974
2013
  docs: {
1975
- description: "disallow too many nested blocks except when the block only contains an early exit (return or throw)"
2014
+ description: "Disallow variable names shorter than the configured minimum length unless an outer variable with a longer name starting with the same characters exists. You can exempt specific variable names using the allowedVars option."
2015
+ },
2016
+ messages: {
2017
+ variableNameTooShort: "Variable '{{name}}' is too short. Minimum allowed length is {{minLength}} characters unless an outer variable with a longer name starting with '{{name}}' exists."
1976
2018
  },
1977
2019
  schema: [
1978
2020
  {
1979
- type: "number"
2021
+ additionalProperties: false,
2022
+ properties: {
2023
+ allowedVars: {
2024
+ default: [],
2025
+ items: {
2026
+ minLength: 1,
2027
+ type: "string"
2028
+ },
2029
+ type: "array"
2030
+ },
2031
+ minLength: {
2032
+ default: 1,
2033
+ type: "number"
2034
+ }
2035
+ },
2036
+ type: "object"
1980
2037
  }
1981
2038
  ],
1982
- messages: {
1983
- tooDeep: "Blocks are nested too deeply ({{depth}}). Maximum allowed is {{maxDepth}} or an early exit."
1984
- }
1985
- },
1986
- defaultOptions: [1],
2039
+ type: "problem"
2040
+ }
2041
+ };
2042
+
2043
+ // src/rules/max-depth-extended.ts
2044
+ var maxDepthExtended = {
1987
2045
  create(context) {
1988
- const option = context.options[0];
2046
+ const [option] = context.options;
1989
2047
  const maxDepth = typeof option === "number" ? option : 1;
1990
2048
  const functionStack = [];
1991
- function getAncestors(node) {
2049
+ const getAncestors = (node) => {
1992
2050
  const ancestors = [];
1993
2051
  let current = node.parent;
1994
2052
  while (current) {
@@ -1996,18 +2054,23 @@ var maxDepthExtended = {
1996
2054
  current = current.parent;
1997
2055
  }
1998
2056
  return ancestors;
1999
- }
2000
- function isEarlyExitBlock(node) {
2057
+ };
2058
+ const isEarlyExitBlock = (node) => {
2001
2059
  if (node.body.length !== 1) {
2002
2060
  return false;
2003
2061
  }
2004
- const first = node.body[0];
2062
+ const [first] = node.body;
2005
2063
  if (!first) {
2006
2064
  return false;
2007
2065
  }
2008
2066
  return first.type === "ReturnStatement" || first.type === "ThrowStatement";
2009
- }
2010
- function incrementCurrentDepth() {
2067
+ };
2068
+ const isFunctionBody = (node) => {
2069
+ const ancestors = getAncestors(node);
2070
+ const [parent] = ancestors;
2071
+ return parent && (parent.type === "FunctionDeclaration" || parent.type === "FunctionExpression" || parent.type === "ArrowFunctionExpression") && node === parent.body;
2072
+ };
2073
+ const incrementCurrentDepth = () => {
2011
2074
  if (functionStack.length === 0) {
2012
2075
  return null;
2013
2076
  }
@@ -2019,8 +2082,8 @@ var maxDepthExtended = {
2019
2082
  const nextDepth = currentDepth + 1;
2020
2083
  functionStack[index] = nextDepth;
2021
2084
  return nextDepth;
2022
- }
2023
- function decrementCurrentDepth() {
2085
+ };
2086
+ const decrementCurrentDepth = () => {
2024
2087
  if (functionStack.length === 0) {
2025
2088
  return;
2026
2089
  }
@@ -2030,30 +2093,25 @@ var maxDepthExtended = {
2030
2093
  return;
2031
2094
  }
2032
2095
  functionStack[index] = currentDepth - 1;
2033
- }
2034
- function checkDepth(node, depth) {
2096
+ };
2097
+ const checkDepth = (node, depth) => {
2035
2098
  if (depth > maxDepth) {
2036
2099
  context.report({
2037
- node,
2100
+ data: { depth, maxDepth },
2038
2101
  messageId: "tooDeep",
2039
- data: { depth, maxDepth }
2102
+ node
2040
2103
  });
2041
2104
  }
2042
- }
2105
+ };
2043
2106
  return {
2044
- FunctionDeclaration() {
2045
- functionStack.push(0);
2046
- },
2047
- FunctionExpression() {
2048
- functionStack.push(0);
2049
- },
2050
2107
  ArrowFunctionExpression() {
2051
2108
  functionStack.push(0);
2052
2109
  },
2110
+ "ArrowFunctionExpression:exit"() {
2111
+ functionStack.pop();
2112
+ },
2053
2113
  BlockStatement(node) {
2054
- const ancestors = getAncestors(node);
2055
- const parent = ancestors.length > 0 ? ancestors[0] : undefined;
2056
- if (parent && (parent.type === "FunctionDeclaration" || parent.type === "FunctionExpression" || parent.type === "ArrowFunctionExpression") && node === parent.body) {
2114
+ if (isFunctionBody(node)) {
2057
2115
  return;
2058
2116
  }
2059
2117
  if (isEarlyExitBlock(node)) {
@@ -2065,9 +2123,7 @@ var maxDepthExtended = {
2065
2123
  }
2066
2124
  },
2067
2125
  "BlockStatement:exit"(node) {
2068
- const ancestors = getAncestors(node);
2069
- const parent = ancestors.length > 0 ? ancestors[0] : undefined;
2070
- if (parent && (parent.type === "FunctionDeclaration" || parent.type === "FunctionExpression" || parent.type === "ArrowFunctionExpression") && node === parent.body) {
2126
+ if (isFunctionBody(node)) {
2071
2127
  return;
2072
2128
  }
2073
2129
  if (isEarlyExitBlock(node)) {
@@ -2075,39 +2131,105 @@ var maxDepthExtended = {
2075
2131
  }
2076
2132
  decrementCurrentDepth();
2077
2133
  },
2134
+ FunctionDeclaration() {
2135
+ functionStack.push(0);
2136
+ },
2078
2137
  "FunctionDeclaration:exit"() {
2079
2138
  functionStack.pop();
2080
2139
  },
2081
- "FunctionExpression:exit"() {
2082
- functionStack.pop();
2140
+ FunctionExpression() {
2141
+ functionStack.push(0);
2083
2142
  },
2084
- "ArrowFunctionExpression:exit"() {
2143
+ "FunctionExpression:exit"() {
2085
2144
  functionStack.pop();
2086
2145
  }
2087
2146
  };
2147
+ },
2148
+ defaultOptions: [1],
2149
+ meta: {
2150
+ docs: {
2151
+ description: "disallow too many nested blocks except when the block only contains an early exit (return or throw)"
2152
+ },
2153
+ messages: {
2154
+ tooDeep: "Blocks are nested too deeply ({{depth}}). Maximum allowed is {{maxDepth}} or an early exit."
2155
+ },
2156
+ schema: [
2157
+ {
2158
+ type: "number"
2159
+ }
2160
+ ],
2161
+ type: "suggestion"
2088
2162
  }
2089
2163
  };
2090
2164
 
2091
2165
  // src/rules/spring-naming-convention.ts
2166
+ var SPRINGS_SUFFIX = "Springs";
2167
+ var checkUseSpring = (context, firstElem, secondElem) => {
2168
+ const firstName = firstElem.name;
2169
+ const secondName = secondElem.name;
2170
+ if (!firstName.endsWith(SPRINGS_SUFFIX)) {
2171
+ context.report({
2172
+ messageId: "firstMustEndWithSprings",
2173
+ node: firstElem
2174
+ });
2175
+ return;
2176
+ }
2177
+ const base = firstName.slice(0, -SPRINGS_SUFFIX.length);
2178
+ if (!base) {
2179
+ context.report({
2180
+ messageId: "firstMustHaveBase",
2181
+ node: firstElem
2182
+ });
2183
+ return;
2184
+ }
2185
+ const expectedSecond = `${base}Api`;
2186
+ if (secondName !== expectedSecond) {
2187
+ context.report({
2188
+ data: { expected: expectedSecond },
2189
+ messageId: "secondMustMatch",
2190
+ node: secondElem
2191
+ });
2192
+ }
2193
+ };
2194
+ var checkUseSprings = (context, firstElem, secondElem) => {
2195
+ const firstName = firstElem.name;
2196
+ const secondName = secondElem.name;
2197
+ if (!firstName.endsWith(SPRINGS_SUFFIX)) {
2198
+ context.report({
2199
+ messageId: "firstMustEndWithSprings",
2200
+ node: firstElem
2201
+ });
2202
+ return;
2203
+ }
2204
+ const basePlural = firstName.slice(0, -SPRINGS_SUFFIX.length);
2205
+ if (!basePlural) {
2206
+ context.report({
2207
+ messageId: "firstMustHaveBase",
2208
+ node: firstElem
2209
+ });
2210
+ return;
2211
+ }
2212
+ if (!basePlural.endsWith("s")) {
2213
+ context.report({
2214
+ messageId: "pluralRequired",
2215
+ node: firstElem
2216
+ });
2217
+ return;
2218
+ }
2219
+ const expectedSecond = `${basePlural}Api`;
2220
+ if (secondName !== expectedSecond) {
2221
+ context.report({
2222
+ data: { expected: expectedSecond },
2223
+ messageId: "secondMustMatch",
2224
+ node: secondElem
2225
+ });
2226
+ }
2227
+ };
2092
2228
  var springNamingConvention = {
2093
- meta: {
2094
- type: "problem",
2095
- docs: {
2096
- description: "Enforce correct naming for useSpring and useSprings hook destructuring"
2097
- },
2098
- schema: [],
2099
- messages: {
2100
- firstMustEndWithSprings: "The first variable must end with 'Springs'.",
2101
- firstMustHaveBase: "The first variable must have a non-empty name before 'Springs'.",
2102
- secondMustMatch: "The second variable must be named '{{expected}}'.",
2103
- pluralRequired: "The first variable for useSprings should be plural (ending with 's') before 'Springs'."
2104
- }
2105
- },
2106
- defaultOptions: [],
2107
2229
  create(context) {
2108
2230
  return {
2109
2231
  VariableDeclarator(node) {
2110
- const init = node.init;
2232
+ const { init } = node;
2111
2233
  if (!init || init.type !== "CallExpression" || init.callee.type !== "Identifier") {
2112
2234
  return;
2113
2235
  }
@@ -2118,87 +2240,74 @@ var springNamingConvention = {
2118
2240
  if (node.id.type !== "ArrayPattern") {
2119
2241
  return;
2120
2242
  }
2121
- const elements = node.id.elements;
2243
+ const { elements } = node.id;
2122
2244
  if (elements.length < 2) {
2123
2245
  return;
2124
2246
  }
2125
- const firstElem = elements[0];
2126
- const secondElem = elements[1];
2247
+ const [firstElem, secondElem] = elements;
2127
2248
  if (!firstElem || firstElem.type !== "Identifier" || !secondElem || secondElem.type !== "Identifier") {
2128
2249
  return;
2129
2250
  }
2130
- const firstName = firstElem.name;
2131
- const secondName = secondElem.name;
2132
2251
  if (hookName === "useSpring") {
2133
- if (!firstName.endsWith("Springs")) {
2134
- context.report({
2135
- node: firstElem,
2136
- messageId: "firstMustEndWithSprings"
2137
- });
2138
- return;
2139
- }
2140
- const base = firstName.slice(0, -"Springs".length);
2141
- if (!base) {
2142
- context.report({
2143
- node: firstElem,
2144
- messageId: "firstMustHaveBase"
2145
- });
2146
- return;
2147
- }
2148
- const expectedSecond = `${base}Api`;
2149
- if (secondName !== expectedSecond) {
2150
- context.report({
2151
- node: secondElem,
2152
- messageId: "secondMustMatch",
2153
- data: { expected: expectedSecond }
2154
- });
2155
- }
2252
+ checkUseSpring(context, firstElem, secondElem);
2156
2253
  return;
2157
2254
  }
2158
2255
  if (hookName === "useSprings") {
2159
- if (!firstName.endsWith("Springs")) {
2160
- context.report({
2161
- node: firstElem,
2162
- messageId: "firstMustEndWithSprings"
2163
- });
2164
- return;
2165
- }
2166
- const basePlural = firstName.slice(0, -"Springs".length);
2167
- if (!basePlural) {
2168
- context.report({
2169
- node: firstElem,
2170
- messageId: "firstMustHaveBase"
2171
- });
2172
- return;
2173
- }
2174
- if (!basePlural.endsWith("s")) {
2175
- context.report({
2176
- node: firstElem,
2177
- messageId: "pluralRequired"
2178
- });
2179
- return;
2180
- }
2181
- const expectedSecond = `${basePlural}Api`;
2182
- if (secondName !== expectedSecond) {
2183
- context.report({
2184
- node: secondElem,
2185
- messageId: "secondMustMatch",
2186
- data: { expected: expectedSecond }
2187
- });
2188
- }
2256
+ checkUseSprings(context, firstElem, secondElem);
2189
2257
  }
2190
2258
  }
2191
2259
  };
2260
+ },
2261
+ defaultOptions: [],
2262
+ meta: {
2263
+ docs: {
2264
+ description: "Enforce correct naming for useSpring and useSprings hook destructuring"
2265
+ },
2266
+ messages: {
2267
+ firstMustEndWithSprings: "The first variable must end with 'Springs'.",
2268
+ firstMustHaveBase: "The first variable must have a non-empty name before 'Springs'.",
2269
+ pluralRequired: "The first variable for useSprings should be plural (ending with 's') before 'Springs'.",
2270
+ secondMustMatch: "The second variable must be named '{{expected}}'."
2271
+ },
2272
+ schema: [],
2273
+ type: "problem"
2192
2274
  }
2193
2275
  };
2194
2276
 
2195
2277
  // src/rules/inline-style-limit.ts
2278
+ var DEFAULT_MAX_KEYS = 3;
2196
2279
  var inlineStyleLimit = {
2280
+ create(context) {
2281
+ const [option] = context.options;
2282
+ const maxKeys = typeof option === "number" ? option : option && option.maxKeys || DEFAULT_MAX_KEYS;
2283
+ return {
2284
+ JSXAttribute(node) {
2285
+ if (node.name.type !== "JSXIdentifier" || node.name.name !== "style") {
2286
+ return;
2287
+ }
2288
+ if (!node.value || node.value.type !== "JSXExpressionContainer" || !node.value.expression || node.value.expression.type !== "ObjectExpression") {
2289
+ return;
2290
+ }
2291
+ const styleObject = node.value.expression;
2292
+ const keyCount = styleObject.properties.filter((prop) => prop.type === "Property").length;
2293
+ if (keyCount > maxKeys) {
2294
+ context.report({
2295
+ data: { max: maxKeys },
2296
+ messageId: "extractStyle",
2297
+ node
2298
+ });
2299
+ }
2300
+ }
2301
+ };
2302
+ },
2303
+ defaultOptions: [DEFAULT_MAX_KEYS],
2197
2304
  meta: {
2198
- type: "suggestion",
2199
2305
  docs: {
2200
2306
  description: "Disallow inline style objects with too many keys and encourage extracting them"
2201
2307
  },
2308
+ messages: {
2309
+ extractStyle: "Inline style objects should be extracted into a separate object or file when containing more than {{max}} keys."
2310
+ },
2202
2311
  schema: [
2203
2312
  {
2204
2313
  anyOf: [
@@ -2206,119 +2315,78 @@ var inlineStyleLimit = {
2206
2315
  type: "number"
2207
2316
  },
2208
2317
  {
2209
- type: "object",
2318
+ additionalProperties: false,
2210
2319
  properties: {
2211
2320
  maxKeys: {
2212
- type: "number",
2213
- description: "Maximum number of keys allowed in an inline style object before it must be extracted."
2321
+ description: "Maximum number of keys allowed in an inline style object before it must be extracted.",
2322
+ type: "number"
2214
2323
  }
2215
2324
  },
2216
- additionalProperties: false
2325
+ type: "object"
2217
2326
  }
2218
2327
  ]
2219
2328
  }
2220
2329
  ],
2221
- messages: {
2222
- extractStyle: "Inline style objects should be extracted into a separate object or file when containing more than {{max}} keys."
2223
- }
2224
- },
2225
- defaultOptions: [3],
2226
- create(context) {
2227
- const option = context.options[0];
2228
- const maxKeys = typeof option === "number" ? option : option && option.maxKeys || 3;
2229
- return {
2230
- JSXAttribute(node) {
2231
- if (node.name.type !== "JSXIdentifier" || node.name.name !== "style") {
2232
- return;
2233
- }
2234
- if (node.value && node.value.type === "JSXExpressionContainer" && node.value.expression && node.value.expression.type === "ObjectExpression") {
2235
- const styleObject = node.value.expression;
2236
- const keyCount = styleObject.properties.filter((prop) => prop.type === "Property").length;
2237
- if (keyCount > maxKeys) {
2238
- context.report({
2239
- node,
2240
- messageId: "extractStyle",
2241
- data: { max: maxKeys }
2242
- });
2243
- }
2244
- }
2245
- }
2246
- };
2330
+ type: "suggestion"
2247
2331
  }
2248
2332
  };
2249
2333
 
2250
2334
  // src/rules/no-inline-prop-types.ts
2251
2335
  var noInlinePropTypes = {
2252
- meta: {
2253
- type: "suggestion",
2254
- docs: {
2255
- description: "Enforce that component prop types are not defined inline (using an object literal) but rather use a named type or interface."
2256
- },
2257
- schema: [],
2258
- messages: {
2259
- noInlinePropTypes: "Inline prop type definitions are not allowed. Use a named type alias or interface instead of an inline object type."
2260
- }
2261
- },
2262
- defaultOptions: [],
2263
2336
  create(context) {
2264
- function checkParameter(param) {
2265
- if (param.type === "ObjectPattern" && param.typeAnnotation && param.typeAnnotation.type === "TSTypeAnnotation") {
2266
- const annotation = param.typeAnnotation.typeAnnotation;
2267
- if (annotation.type === "TSTypeLiteral") {
2268
- context.report({
2269
- node: param,
2270
- messageId: "noInlinePropTypes"
2271
- });
2272
- }
2337
+ const checkParameter = (param) => {
2338
+ if (param.type !== "ObjectPattern" || !param.typeAnnotation || param.typeAnnotation.type !== "TSTypeAnnotation") {
2339
+ return;
2273
2340
  }
2274
- }
2341
+ const annotation = param.typeAnnotation.typeAnnotation;
2342
+ if (annotation.type === "TSTypeLiteral") {
2343
+ context.report({
2344
+ messageId: "noInlinePropTypes",
2345
+ node: param
2346
+ });
2347
+ }
2348
+ };
2275
2349
  return {
2276
2350
  "FunctionDeclaration, ArrowFunctionExpression, FunctionExpression"(node) {
2277
2351
  if (node.params.length === 0) {
2278
2352
  return;
2279
2353
  }
2280
- const firstParam = node.params[0];
2354
+ const [firstParam] = node.params;
2281
2355
  if (!firstParam) {
2282
2356
  return;
2283
2357
  }
2284
2358
  checkParameter(firstParam);
2285
2359
  }
2286
2360
  };
2361
+ },
2362
+ defaultOptions: [],
2363
+ meta: {
2364
+ docs: {
2365
+ description: "Enforce that component prop types are not defined inline (using an object literal) but rather use a named type or interface."
2366
+ },
2367
+ messages: {
2368
+ noInlinePropTypes: "Inline prop type definitions are not allowed. Use a named type alias or interface instead of an inline object type."
2369
+ },
2370
+ schema: [],
2371
+ type: "suggestion"
2287
2372
  }
2288
2373
  };
2289
2374
 
2290
2375
  // src/rules/no-unnecessary-div.ts
2291
2376
  import { AST_NODE_TYPES as AST_NODE_TYPES3 } from "@typescript-eslint/utils";
2292
2377
  var noUnnecessaryDiv = {
2293
- meta: {
2294
- type: "suggestion",
2295
- docs: {
2296
- description: "Flag unnecessary <div> wrappers that enclose a single JSX element. Remove the wrapper if it doesn't add semantic or functional value, or replace it with a semantic element if wrapping is needed."
2297
- },
2298
- schema: [],
2299
- messages: {
2300
- unnecessaryDivWrapper: "Unnecessary <div> wrapper detected. Remove it if not needed, or replace with a semantic element that reflects its purpose."
2301
- }
2302
- },
2303
- defaultOptions: [],
2304
2378
  create(context) {
2305
- function isDivElement(node) {
2379
+ const isDivElement = (node) => {
2306
2380
  const nameNode = node.openingElement.name;
2307
2381
  return nameNode.type === AST_NODE_TYPES3.JSXIdentifier && nameNode.name === "div";
2308
- }
2309
- function getMeaningfulChildren(node) {
2310
- const result = [];
2311
- for (const child of node.children) {
2312
- if (child.type === AST_NODE_TYPES3.JSXText) {
2313
- if (child.value.trim() !== "") {
2314
- result.push(child);
2315
- }
2316
- } else {
2317
- result.push(child);
2318
- }
2382
+ };
2383
+ const isMeaningfulChild = (child) => {
2384
+ if (child.type === AST_NODE_TYPES3.JSXText) {
2385
+ return child.value.trim() !== "";
2319
2386
  }
2320
- return result;
2321
- }
2387
+ return true;
2388
+ };
2389
+ const getMeaningfulChildren = (node) => node.children.filter(isMeaningfulChild);
2322
2390
  return {
2323
2391
  JSXElement(node) {
2324
2392
  if (!isDivElement(node)) {
@@ -2328,44 +2396,55 @@ var noUnnecessaryDiv = {
2328
2396
  if (meaningfulChildren.length !== 1) {
2329
2397
  return;
2330
2398
  }
2331
- const onlyChild = meaningfulChildren[0];
2399
+ const [onlyChild] = meaningfulChildren;
2332
2400
  if (!onlyChild) {
2333
2401
  return;
2334
2402
  }
2335
2403
  if (onlyChild.type === AST_NODE_TYPES3.JSXElement) {
2336
2404
  context.report({
2337
- node,
2338
- messageId: "unnecessaryDivWrapper"
2405
+ messageId: "unnecessaryDivWrapper",
2406
+ node
2339
2407
  });
2340
2408
  }
2341
2409
  }
2342
2410
  };
2411
+ },
2412
+ defaultOptions: [],
2413
+ meta: {
2414
+ docs: {
2415
+ description: "Flag unnecessary <div> wrappers that enclose a single JSX element. Remove the wrapper if it doesn't add semantic or functional value, or replace it with a semantic element if wrapping is needed."
2416
+ },
2417
+ messages: {
2418
+ unnecessaryDivWrapper: "Unnecessary <div> wrapper detected. Remove it if not needed, or replace with a semantic element that reflects its purpose."
2419
+ },
2420
+ schema: [],
2421
+ type: "suggestion"
2343
2422
  }
2344
2423
  };
2345
2424
 
2346
2425
  // src/index.ts
2347
2426
  var src_default = {
2348
2427
  rules: {
2349
- "no-nested-jsx-return": noNestedJSXReturn,
2350
2428
  "explicit-object-types": explicitObjectTypes,
2351
- "sort-keys-fixable": sortKeysFixable,
2352
- "no-transition-cssproperties": noTransitionCSSProperties,
2353
- "no-explicit-return-type": noExplicitReturnTypes,
2354
- "max-jsxnesting": maxJSXNesting,
2355
- "seperate-style-files": seperateStyleFiles,
2356
- "no-unnecessary-key": noUnnecessaryKey,
2357
- "sort-exports": sortExports,
2429
+ "inline-style-limit": inlineStyleLimit,
2358
2430
  "localize-react-props": localizeReactProps,
2359
- "no-or-none-component": noOrNoneComponent,
2431
+ "max-depth-extended": maxDepthExtended,
2432
+ "max-jsxnesting": maxJSXNesting,
2433
+ "min-var-length": minVarLength,
2360
2434
  "no-button-navigation": noButtonNavigation,
2435
+ "no-explicit-return-type": noExplicitReturnTypes,
2436
+ "no-inline-prop-types": noInlinePropTypes,
2361
2437
  "no-multi-style-objects": noMultiStyleObjects,
2438
+ "no-nested-jsx-return": noNestedJSXReturn,
2439
+ "no-or-none-component": noOrNoneComponent,
2440
+ "no-transition-cssproperties": noTransitionCSSProperties,
2441
+ "no-unnecessary-div": noUnnecessaryDiv,
2442
+ "no-unnecessary-key": noUnnecessaryKey,
2362
2443
  "no-useless-function": noUselessFunction,
2363
- "min-var-length": minVarLength,
2364
- "max-depth-extended": maxDepthExtended,
2365
- "spring-naming-convention": springNamingConvention,
2366
- "inline-style-limit": inlineStyleLimit,
2367
- "no-inline-prop-types": noInlinePropTypes,
2368
- "no-unnecessary-div": noUnnecessaryDiv
2444
+ "seperate-style-files": seperateStyleFiles,
2445
+ "sort-exports": sortExports,
2446
+ "sort-keys-fixable": sortKeysFixable,
2447
+ "spring-naming-convention": springNamingConvention
2369
2448
  }
2370
2449
  };
2371
2450
  export {