@agilebot/eslint-plugin 0.8.10 → 0.9.1

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.
Files changed (3) hide show
  1. package/dist/index.js +146 -214
  2. package/package.json +6 -7
  3. package/dist/index.mjs +0 -2763
package/dist/index.mjs DELETED
@@ -1,2763 +0,0 @@
1
- /**
2
- * @license @agilebot/eslint-plugin v0.8.10
3
- *
4
- * Copyright (c) Agilebot, Inc. and its affiliates.
5
- *
6
- * This source code is licensed under the MIT license found in the
7
- * LICENSE file in the root directory of this source tree.
8
- */
9
-
10
- import { createRequire } from "node:module";
11
- import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
12
- import * as fs$1 from "node:fs";
13
- import * as fs from "node:fs";
14
- import * as path$2 from "node:path";
15
- import * as path$1 from "node:path";
16
- import path from "node:path";
17
- import { isCamelCase, isPascalCase, tsImport } from "@agilebot/eslint-utils";
18
- import * as Components from "eslint-plugin-react/lib/util/Components.js";
19
-
20
- //#region rolldown:runtime
21
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
22
-
23
- //#endregion
24
- //#region src/util/rule.js
25
- const docBaseUrl = "https://github.com/sh-agilebot/frontend-toolkit/blob/master/packages/eslint-plugin/src/rules/";
26
- const hasDocs = [
27
- "enforce-mui-icon-alias",
28
- "intl-id-missing",
29
- "intl-id-prefix",
30
- "intl-no-default",
31
- "react-better-exhaustive-deps"
32
- ];
33
- const createRule = ESLintUtils.RuleCreator((name) => hasDocs.includes(name) ? `${docBaseUrl}${name}.md` : `${docBaseUrl}${name}.test.js`);
34
-
35
- //#endregion
36
- //#region src/rules/enforce-mui-icon-alias.js
37
- const RULE_NAME$18 = "enforce-mui-icon-alias";
38
- var enforce_mui_icon_alias_default = createRule({
39
- name: RULE_NAME$18,
40
- meta: {
41
- type: "problem",
42
- docs: {
43
- description: "Enforce alias for @mui/icons-material imports",
44
- recommended: "recommended"
45
- },
46
- fixable: "code",
47
- schema: [],
48
- messages: { iconAlias: "Import for {{ name }} should be aliased." }
49
- },
50
- defaultOptions: [],
51
- create(context) {
52
- return { ImportDeclaration(node) {
53
- if (node.source.value !== "@mui/icons-material" && node.source.value !== "mdi-material-ui") return;
54
- for (const specifier of node.specifiers) {
55
- if (specifier.type !== AST_NODE_TYPES.ImportSpecifier) return;
56
- if (specifier.imported.name === specifier.local.name || !specifier.local.name.endsWith("Icon")) context.report({
57
- node,
58
- messageId: "iconAlias",
59
- data: { name: node.source.value },
60
- fix: (fixer) => fixer.replaceText(specifier, `${specifier.imported.name} as ${specifier.imported.name}Icon`)
61
- });
62
- }
63
- } };
64
- }
65
- });
66
-
67
- //#endregion
68
- //#region src/util/context.js
69
- /**
70
-
71
- * Get a setting from eslint config
72
-
73
- * @param {import('eslint').Rule.RuleContext} context - Context
74
-
75
- * @param {string} name - Name
76
-
77
- * @returns {*} - result
78
-
79
- */
80
- function getSetting(context, name) {
81
- return context.settings[`agilebot/${name}`];
82
- }
83
-
84
- //#endregion
85
- //#region src/rules/import-monorepo.js
86
- const RULE_NAME$17 = "import-monorepo";
87
- var import_monorepo_default = createRule({
88
- name: RULE_NAME$17,
89
- meta: {
90
- type: "problem",
91
- docs: {
92
- description: "Enforce import styles for monorepo",
93
- recommended: "recommended"
94
- },
95
- fixable: "code",
96
- schema: [],
97
- messages: { monorepoImport: "Import for {{ module }} should not contains src folder." }
98
- },
99
- defaultOptions: [],
100
- create(context) {
101
- return { ImportDeclaration(node) {
102
- let prefix = getSetting(context, "monorepo-scope");
103
- if (!prefix) return;
104
- prefix = `${prefix}/`;
105
- if (typeof node.source.value !== "string") return;
106
- if (!node.source.value.startsWith(prefix)) return;
107
- const values = node.source.value.split("/");
108
- if (values[2] === "src") context.report({
109
- node,
110
- messageId: "monorepoImport",
111
- data: { module: node.source.value },
112
- fix: (fixer) => {
113
- const correctedPath = values.filter((_, index) => index !== 2).join("/");
114
- return fixer.replaceText(node.source, `'${correctedPath}'`);
115
- }
116
- });
117
- } };
118
- }
119
- });
120
-
121
- //#endregion
122
- //#region src/util/intl.js
123
- /**
124
- * Finds an attribute in formatMessage using attribute name.
125
- * @param {object} node - parent formatMessage node
126
- * @param {string} attrName - attribute name.
127
- * @returns {*} node - returns node if it finds the attribute.
128
- */
129
- function findFormatMessageAttrNode(node, attrName) {
130
- if (node.type === AST_NODE_TYPES.CallExpression && (node.callee.name === "formatMessage" || node.callee.name === "$t") && node.arguments.length > 0 && node.arguments[0].properties) return node.arguments[0].properties.find((a) => a.key && a.key.name === attrName);
131
- if (node.type === AST_NODE_TYPES.CallExpression && node.callee.type === AST_NODE_TYPES.MemberExpression && (node.callee.object.name === "intl" || node.callee.object.name && node.callee.object.name.endsWith("Intl")) && (node.callee.property.name === "formatMessage" || node.callee.property.name === "$t")) return node.arguments[0].properties.find((a) => a.key && a.key.name === attrName);
132
- }
133
- /**
134
- * Finds an attribute in FormattedMessage using attribute name.
135
- * @param {object} node - parent FormattedMessage node
136
- * @param {string} attrName - attribute name.
137
- * @returns {*} node - returns node if it finds the attribute.
138
- */
139
- function findFormattedMessageAttrNode(node, attrName) {
140
- if (node.type === AST_NODE_TYPES.JSXIdentifier && node.name === "FormattedMessage" && node.parent && node.parent.type === AST_NODE_TYPES.JSXOpeningElement) return node.parent.attributes.find((a) => a.name && a.name.name === attrName);
141
- }
142
- /**
143
- * Finds an attribute in defineMessages using attribute name.
144
- * @param {object} node - parent defineMessages node
145
- * @param {string} attrName - attribute name.
146
- * @returns {*} node - returns node if it finds the attribute.
147
- */
148
- function findAttrNodeInDefineMessages(node, attrName) {
149
- if (node.type === "Property" && node.key.name === attrName && node.parent && node.parent.parent && node.parent.parent.parent && node.parent.parent.parent.parent && node.parent.parent.parent.parent.type === AST_NODE_TYPES.CallExpression && node.parent.parent.parent.parent.callee.name === "defineMessages") return node;
150
- }
151
- /**
152
- * Finds an attribute in defineMessages using attribute name.
153
- * @param {object} node - parent defineMessages node
154
- * @param {string} attrName - attribute name.
155
- * @returns {*} node - returns node if it finds the attribute.
156
- */
157
- function findAttrNodeInDefineMessage(node, attrName) {
158
- if (node.type === "Property" && node.key.name === attrName && node.parent && node.parent.parent && node.parent.parent.type === AST_NODE_TYPES.CallExpression && node.parent.parent.callee.name === "defineMessage") return node;
159
- }
160
- /**
161
- * Returns a sorted array of nodes, based on their starting posting in the locale id.
162
- * @param {object} node - parent node containing the locale id.
163
- * @returns {Array} child nodes - sorted list.
164
- */
165
- function sortedTemplateElements(node) {
166
- return [...node.quasis, ...node.expressions].sort((a, b) => a.range[0] - b.range[0]);
167
- }
168
- /**
169
- * Replaces place holders with asterisk and returns the resulting id.
170
- * @param {object} node - parent node containing the locale id.
171
- * @returns {string} id - fixed id.
172
- */
173
- function templateLiteralDisplayStr(node) {
174
- return sortedTemplateElements(node).map((e) => !e.value ? "*" : e.value.raw).join("");
175
- }
176
-
177
- //#endregion
178
- //#region src/util/translations.js
179
- /**
180
-
181
- * Map of locale file paths to keys and modified time
182
-
183
- * @type {Record<string, {keys: Array<string>, mtime: number}>}
184
-
185
- */
186
- const localeFilesKeys = {};
187
- /**
188
-
189
- * Get a list of ids keys from reading locale files
190
-
191
- * Keeps track of modified times and reloads if changed,; useful for realtime eslint in-editor
192
-
193
- * @param {import('eslint').Rule.RuleContext} context - Context
194
-
195
- * @returns {string[]} results - Array of ids
196
-
197
- */
198
- function getIntlIds(context) {
199
- const projectRoot = getSetting(context, "project-root");
200
- const localeFiles = getSetting(context, "locale-files");
201
- if (!localeFiles) throw new Error("localeFiles not in settings");
202
- const results = [];
203
- localeFiles.forEach((f) => {
204
- const fullPath = projectRoot ? path$2.join(projectRoot, f) : f;
205
- const mtime = fs$1.lstatSync(fullPath).mtime.getTime();
206
- if (!localeFilesKeys[fullPath] || mtime !== localeFilesKeys[fullPath].mtime) {
207
- let json;
208
- if (fullPath.endsWith(".json")) json = JSON.parse(fs$1.readFileSync(fullPath, "utf8"));
209
- else if (fullPath.endsWith(".ts")) {
210
- json = tsImport(fullPath);
211
- if (typeof json === "object" && json.default) json = json.default;
212
- } else if (fullPath.endsWith(".js")) {
213
- json = __require(fullPath);
214
- if (typeof json === "object" && json.default) json = json.default;
215
- } else throw new Error("unsupported file extension");
216
- localeFilesKeys[fullPath] = {
217
- keys: Object.keys(json),
218
- mtime
219
- };
220
- }
221
- results.push(...localeFilesKeys[fullPath].keys);
222
- });
223
- return results;
224
- }
225
-
226
- //#endregion
227
- //#region src/rules/intl-id-missing.js
228
- const RULE_NAME$16 = "intl-id-missing";
229
- var intl_id_missing_default = createRule({
230
- name: RULE_NAME$16,
231
- meta: {
232
- type: "problem",
233
- docs: { description: "Validates intl message ids are in locale file" },
234
- schema: [],
235
- messages: {
236
- missingId: "Missing id: {{value}}",
237
- missingIdPattern: "Missing id pattern: {{value}}",
238
- disallowInvoke: "Do not invoke intl by {{value}}"
239
- }
240
- },
241
- defaultOptions: [],
242
- create: function(context) {
243
- const translatedIds = getIntlIds(context);
244
- const translatedIdSet = new Set(translatedIds);
245
- function isLiteralTranslated(id) {
246
- return translatedIdSet.has(id);
247
- }
248
- function isTemplateTranslated(re) {
249
- return translatedIds.some((k) => re.test(k));
250
- }
251
- function processLiteral(node) {
252
- if (!isLiteralTranslated(node.value)) context.report({
253
- node,
254
- messageId: "missingId",
255
- data: { value: node.value }
256
- });
257
- }
258
- function processTemplateLiteral(node) {
259
- const exStr = sortedTemplateElements(node).map((e) => !e.value ? ".*" : e.value.raw).join("");
260
- const re = new RegExp(exStr);
261
- if (!isTemplateTranslated(re)) context.report({
262
- node,
263
- messageId: "missingIdPattern",
264
- data: { value: templateLiteralDisplayStr(node) }
265
- });
266
- }
267
- function processAttrNode(node) {
268
- if (node.value.type === AST_NODE_TYPES.Literal) return processLiteral(node.value);
269
- if (node.value.type === AST_NODE_TYPES.JSXExpressionContainer && node.value.expression.type === AST_NODE_TYPES.TemplateLiteral) return processTemplateLiteral(node.value.expression);
270
- if (node.value.type === AST_NODE_TYPES.TemplateLiteral) return processTemplateLiteral(node.value);
271
- context.report({
272
- node,
273
- messageId: "disallowInvoke",
274
- data: { value: node.value.type }
275
- });
276
- }
277
- return {
278
- JSXIdentifier: function(node) {
279
- const attrNode = findFormattedMessageAttrNode(node, "id");
280
- if (attrNode) return processAttrNode(attrNode);
281
- },
282
- CallExpression: function(node) {
283
- const attrNode = findFormatMessageAttrNode(node, "id");
284
- if (attrNode) return processAttrNode(attrNode);
285
- },
286
- Property: function(node) {
287
- const attrNode = findAttrNodeInDefineMessages(node, "id") || findAttrNodeInDefineMessage(node, "id");
288
- if (attrNode) return processAttrNode(attrNode);
289
- }
290
- };
291
- }
292
- });
293
-
294
- //#endregion
295
- //#region src/rules/intl-id-naming.js
296
- const RULE_NAME$15 = "intl-id-naming";
297
- var intl_id_naming_default = createRule({
298
- name: RULE_NAME$15,
299
- meta: {
300
- type: "problem",
301
- docs: { description: "Validates intl message ids naming convention" },
302
- schema: [{
303
- type: "object",
304
- properties: { format: {
305
- type: "string",
306
- enum: ["camelCase", "PascalCase"]
307
- } }
308
- }],
309
- messages: { invalidIdNaming: `Invalid id naming, expected {{format}}` }
310
- },
311
- defaultOptions: [],
312
- create: function(context) {
313
- if (!context.options[0]) throw new Error("Missing options");
314
- const format = context.options[0].format;
315
- function report(node, value) {
316
- const values = value.split(".");
317
- let isPass;
318
- for (const v of values) switch (format) {
319
- case "camelCase":
320
- if (!isCamelCase(v)) isPass = false;
321
- break;
322
- case "PascalCase":
323
- if (!isPascalCase(v)) isPass = false;
324
- break;
325
- }
326
- if (isPass === false) context.report({
327
- node,
328
- messageId: "invalidIdNaming",
329
- data: { format }
330
- });
331
- }
332
- function processLiteral(node) {
333
- report(node, node.value);
334
- }
335
- function processTemplateLiteral(node) {
336
- const displayStr = templateLiteralDisplayStr(node);
337
- report(node, displayStr);
338
- }
339
- function processAttrNode(node) {
340
- if (node.value.type === AST_NODE_TYPES.Literal) return processLiteral(node.value);
341
- if (node.value.type === AST_NODE_TYPES.JSXExpressionContainer && node.value.expression.type === AST_NODE_TYPES.TemplateLiteral) return processTemplateLiteral(node.value.expression);
342
- if (node.value.type === AST_NODE_TYPES.TemplateLiteral) return processTemplateLiteral(node.value);
343
- }
344
- return {
345
- JSXIdentifier: function(node) {
346
- const attrNode = findFormattedMessageAttrNode(node, "id");
347
- if (attrNode) return processAttrNode(attrNode);
348
- },
349
- CallExpression: function(node) {
350
- const attrNode = findFormatMessageAttrNode(node, "id");
351
- if (attrNode) return processAttrNode(attrNode);
352
- },
353
- Property: function(node) {
354
- const attrNode = findAttrNodeInDefineMessages(node, "id") || findAttrNodeInDefineMessage(node, "id");
355
- if (attrNode) return processAttrNode(attrNode);
356
- }
357
- };
358
- }
359
- });
360
-
361
- //#endregion
362
- //#region src/rules/intl-id-prefix.js
363
- const RULE_NAME$14 = "intl-id-prefix";
364
- var intl_id_prefix_default = createRule({
365
- name: RULE_NAME$14,
366
- meta: {
367
- type: "problem",
368
- docs: { description: "Validates intl message ids has correct prefixes" },
369
- schema: [{
370
- type: "array",
371
- items: { type: "string" }
372
- }],
373
- messages: { invalidIdPrefix: "Invalid id prefix: {{value}}" }
374
- },
375
- defaultOptions: [],
376
- create: function(context) {
377
- if (context.options[0].length === 0) throw new Error("Prefixes are required in settings");
378
- const hasPrefix = (value) => context.options[0].some((p) => value.startsWith(p));
379
- function report(node, value) {
380
- if (!hasPrefix(value)) context.report({
381
- node,
382
- messageId: "invalidIdPrefix",
383
- data: { value }
384
- });
385
- }
386
- function processLiteral(node) {
387
- report(node, node.value);
388
- }
389
- function processTemplateLiteral(node) {
390
- const displayStr = templateLiteralDisplayStr(node);
391
- report(node, displayStr);
392
- }
393
- function processAttrNode(node) {
394
- if (node.value.type === AST_NODE_TYPES.Literal) return processLiteral(node.value);
395
- if (node.value.type === AST_NODE_TYPES.JSXExpressionContainer && node.value.expression.type === AST_NODE_TYPES.TemplateLiteral) return processTemplateLiteral(node.value.expression);
396
- if (node.value.type === AST_NODE_TYPES.TemplateLiteral) return processTemplateLiteral(node.value);
397
- }
398
- return {
399
- JSXIdentifier: function(node) {
400
- const attrNode = findFormattedMessageAttrNode(node, "id");
401
- if (attrNode) return processAttrNode(attrNode);
402
- },
403
- CallExpression: function(node) {
404
- const attrNode = findFormatMessageAttrNode(node, "id");
405
- if (attrNode) return processAttrNode(attrNode);
406
- },
407
- Property: function(node) {
408
- const attrNode = findAttrNodeInDefineMessages(node, "id") || findAttrNodeInDefineMessage(node, "id");
409
- if (attrNode) return processAttrNode(attrNode);
410
- }
411
- };
412
- }
413
- });
414
-
415
- //#endregion
416
- //#region src/rules/intl-id-unused.js
417
- const RULE_NAME$13 = "intl-id-unused";
418
- const usedIds = /* @__PURE__ */ new Map();
419
- var intl_id_unused_default = createRule({
420
- name: RULE_NAME$13,
421
- meta: {
422
- type: "problem",
423
- docs: { description: "Finds unused intl message ids in locale file" },
424
- schema: [],
425
- messages: {}
426
- },
427
- defaultOptions: [],
428
- create: function(context) {
429
- const projectRoot = getSetting(context, "project-root");
430
- if (!projectRoot) throw new Error("projectRoot must be set in this rule");
431
- if (!usedIds.has(projectRoot)) usedIds.set(projectRoot, /* @__PURE__ */ new Set());
432
- const usedIdSet = usedIds.get(projectRoot);
433
- const translatedIds = getIntlIds(context);
434
- const translatedIdSet = new Set(translatedIds);
435
- function isLiteralTranslated(id) {
436
- return translatedIdSet.has(id);
437
- }
438
- function isTemplateTranslated(re) {
439
- return translatedIds.some((k) => re.test(k));
440
- }
441
- function processLiteral(node) {
442
- if (isLiteralTranslated(node.value)) usedIdSet.add(node.value);
443
- }
444
- function processTemplateLiteral(node) {
445
- const exStr = sortedTemplateElements(node).map((e) => !e.value ? ".*" : e.value.raw).join("");
446
- const re = new RegExp(exStr);
447
- if (isTemplateTranslated(re)) {}
448
- }
449
- function processAttrNode(node) {
450
- if (node.value.type === AST_NODE_TYPES.Literal) return processLiteral(node.value);
451
- if (node.value.type === AST_NODE_TYPES.JSXExpressionContainer && node.value.expression.type === AST_NODE_TYPES.TemplateLiteral) return processTemplateLiteral(node.value.expression);
452
- if (node.value.type === AST_NODE_TYPES.TemplateLiteral) return processTemplateLiteral(node.value);
453
- }
454
- return {
455
- JSXIdentifier: function(node) {
456
- const attrNode = findFormattedMessageAttrNode(node, "id");
457
- if (attrNode) return processAttrNode(attrNode);
458
- },
459
- CallExpression: function(node) {
460
- const attrNode = findFormatMessageAttrNode(node, "id");
461
- if (attrNode) return processAttrNode(attrNode);
462
- },
463
- Property: function(node) {
464
- const attrNode = findAttrNodeInDefineMessages(node, "id") || findAttrNodeInDefineMessage(node, "id");
465
- if (attrNode) return processAttrNode(attrNode);
466
- },
467
- "Program:exit": function() {
468
- const unusedIds = [...translatedIdSet].filter((id) => !usedIdSet.has(id));
469
- const jsonPath = path$1.join(projectRoot, "intl-unused.json");
470
- fs.writeFileSync(jsonPath, JSON.stringify(unusedIds, null, 2));
471
- }
472
- };
473
- }
474
- });
475
-
476
- //#endregion
477
- //#region src/rules/intl-no-default.js
478
- const RULE_NAME$12 = "intl-no-default";
479
- var intl_no_default_default = createRule({
480
- name: RULE_NAME$12,
481
- meta: {
482
- type: "problem",
483
- docs: { description: "Validates defaultMessage is not used with react-intl" },
484
- schema: [],
485
- messages: { noDefaultMessage: "Do not use defaultMessage" }
486
- },
487
- defaultOptions: [],
488
- create: function(context) {
489
- function processAttrNode(node) {
490
- context.report({
491
- node,
492
- messageId: "noDefaultMessage"
493
- });
494
- }
495
- return {
496
- JSXIdentifier: function(node) {
497
- const attrNode = findFormattedMessageAttrNode(node, "defaultMessage");
498
- if (attrNode) return processAttrNode(attrNode);
499
- },
500
- CallExpression: function(node) {
501
- const attrNode = findFormatMessageAttrNode(node, "defaultMessage");
502
- if (attrNode) return processAttrNode(attrNode);
503
- },
504
- Property: function(node) {
505
- const attrNode = findAttrNodeInDefineMessages(node, "defaultMessage") || findAttrNodeInDefineMessage(node, "defaultMessage");
506
- if (attrNode) return processAttrNode(attrNode);
507
- }
508
- };
509
- }
510
- });
511
-
512
- //#endregion
513
- //#region src/rules/no-async-array-methods.js
514
- const RULE_NAME$11 = "no-async-array-methods";
515
- var no_async_array_methods_default = createRule({
516
- name: RULE_NAME$11,
517
- meta: {
518
- type: "problem",
519
- docs: {
520
- description: "No async callback for Array methods forEach, map, filter, reduce, some, every, etc.",
521
- recommended: "recommended"
522
- },
523
- schema: [],
524
- messages: { noAsyncArrayMethods: `No async function in method '{{ methodName }}'` }
525
- },
526
- defaultOptions: [],
527
- create: function(context) {
528
- return { ExpressionStatement: function(node) {
529
- const notAllowedArrayMethods = [
530
- "forEach",
531
- "filter",
532
- "some",
533
- "every",
534
- "map",
535
- "reduce",
536
- "reduceRight",
537
- "flatMap",
538
- "find",
539
- "findIndex",
540
- "findLast",
541
- "findLastIndex"
542
- ];
543
- const { callee } = node.expression;
544
- if (!callee || !callee.property || !callee.property.name) return;
545
- if (notAllowedArrayMethods.includes(callee.property.name)) {
546
- const functionArguments = node.expression.arguments.find((n) => {
547
- return [AST_NODE_TYPES.ArrowFunctionExpression, AST_NODE_TYPES.FunctionExpression].includes(n.type);
548
- });
549
- if (functionArguments && functionArguments.async) context.report({
550
- node,
551
- messageId: "noAsyncArrayMethods",
552
- data: { methodName: callee.property.name }
553
- });
554
- }
555
- } };
556
- }
557
- });
558
-
559
- //#endregion
560
- //#region src/rules/no-extends-error.js
561
- const RULE_NAME$10 = "no-extends-error";
562
- var no_extends_error_default = createRule({
563
- name: RULE_NAME$10,
564
- meta: {
565
- type: "problem",
566
- docs: {
567
- description: "Disallow extending the Error class",
568
- recommended: "recommended"
569
- },
570
- schema: [],
571
- messages: { noExtendsError: "Extending the 'Error' class is prohibited." }
572
- },
573
- defaultOptions: [],
574
- create(context) {
575
- return { ClassDeclaration(node) {
576
- if (node.superClass && node.superClass.type === AST_NODE_TYPES.Identifier && node.superClass.name === "Error") context.report({
577
- node: node.superClass,
578
- messageId: "noExtendsError"
579
- });
580
- } };
581
- }
582
- });
583
-
584
- //#endregion
585
- //#region src/rules/no-import-css.js
586
- const RULE_NAME$9 = "no-import-css";
587
- var no_import_css_default = createRule({
588
- name: RULE_NAME$9,
589
- meta: {
590
- type: "problem",
591
- docs: {
592
- description: "Prevent importing CSS",
593
- recommended: "recommended"
594
- },
595
- schema: [],
596
- messages: { noImportCSS: "Do not import CSS files. Use CSS-in-JS instead." }
597
- },
598
- defaultOptions: [],
599
- create(context) {
600
- return { ImportDeclaration(node) {
601
- const ext = path.extname(node.source.value);
602
- if (ext.startsWith(".css") || ext.startsWith(".scss") || ext.startsWith(".sass") || ext.startsWith(".less") || ext.startsWith(".styl")) context.report({
603
- node,
604
- messageId: "noImportCSS",
605
- data: { path: node.source.value }
606
- });
607
- } };
608
- }
609
- });
610
-
611
- //#endregion
612
- //#region src/rules/no-then-catch-finally.js
613
- const RULE_NAME$8 = "no-then-catch-finally";
614
- var no_then_catch_finally_default = createRule({
615
- name: RULE_NAME$8,
616
- meta: {
617
- type: "suggestion",
618
- docs: { description: "Disallow the use of then()/catch()/finally() when invoking specific functions." },
619
- schema: [{
620
- type: "object",
621
- properties: { restrictedFunctions: {
622
- type: "array",
623
- uniqueItems: true,
624
- items: { type: "string" }
625
- } }
626
- }],
627
- messages: { forbiddenThenCatchFinally: `then()/catch()/finally() is forbidden when invoke {{ name }}().` }
628
- },
629
- defaultOptions: [],
630
- create(context) {
631
- const configuration = context.options[0] || {};
632
- const restrictedFunctions = configuration.restrictedFunctions || [];
633
- const getScope = typeof context.getScope === "function" ? () => {
634
- return context.getScope();
635
- } : (node) => {
636
- return context.sourceCode.getScope(node);
637
- };
638
- /**
639
- * Returns true if node is created at the top-level scope.
640
- * Await statements are not allowed at the top level,
641
- * only within function declarations.
642
- */
643
- function isTopLevelScoped(node) {
644
- return getScope(node).block.type === "Program";
645
- }
646
- /**
647
- * Returns true if node is a member expression that is a then/catch/finally
648
- * @param {*} node - The node to check
649
- */
650
- function isThenCatchFinally(node) {
651
- return node.property && (node.property.name === "then" || node.property.name === "catch" || node.property.name === "finally");
652
- }
653
- return { [`${AST_NODE_TYPES.CallExpression} > ${AST_NODE_TYPES.MemberExpression}.callee`]: (node) => {
654
- if (isTopLevelScoped(node)) return;
655
- if (!isThenCatchFinally(node)) return;
656
- const callExpression = node.object;
657
- if (callExpression.type === AST_NODE_TYPES.CallExpression && callExpression.callee.type === AST_NODE_TYPES.Identifier && restrictedFunctions.includes(callExpression.callee.name)) context.report({
658
- node: node.property,
659
- messageId: "forbiddenThenCatchFinally",
660
- data: { name: callExpression.callee.name }
661
- });
662
- } };
663
- }
664
- });
665
-
666
- //#endregion
667
- //#region src/rules/no-unnecessary-template-literals.js
668
- const RULE_NAME$7 = "no-unnecessary-template-literals";
669
- var no_unnecessary_template_literals_default = createRule({
670
- name: RULE_NAME$7,
671
- meta: {
672
- type: "problem",
673
- docs: {
674
- description: "Check if a template string contains only one ${}",
675
- recommended: "recommended"
676
- },
677
- fixable: "code",
678
- schema: [],
679
- messages: { unnecessaryTemplateString: "Unnecessary template string with only one ${}." }
680
- },
681
- defaultOptions: [],
682
- create(context) {
683
- return { TemplateLiteral(node) {
684
- const code = context.sourceCode.getText(node);
685
- if (code.startsWith("`${") && code.endsWith("}`") && code.split("${").length === 2) context.report({
686
- node,
687
- messageId: "unnecessaryTemplateString",
688
- fix(fixer) {
689
- return fixer.replaceText(node, code.substring(3, code.length - 2));
690
- }
691
- });
692
- } };
693
- }
694
- });
695
-
696
- //#endregion
697
- //#region src/rules/react-better-exhaustive-deps.js
698
- /**
699
- * Copyright (c) Meta Platforms, Inc. and affiliates.
700
- *
701
- * This source code is licensed under the MIT license found in the
702
- * LICENSE file in the root directory of this source tree.
703
- */
704
- const RULE_NAME$6 = "react-better-exhaustive-deps";
705
- /** @type {import('eslint').Rule.RuleModule} */
706
- var react_better_exhaustive_deps_default = {
707
- meta: {
708
- type: "suggestion",
709
- docs: {
710
- description: "verifies the list of dependencies for Hooks like useEffect and similar",
711
- url: "https://github.com/facebook/react/issues/14920"
712
- },
713
- fixable: "code",
714
- hasSuggestions: true,
715
- schema: [{
716
- type: "object",
717
- additionalProperties: false,
718
- enableDangerousAutofixThisMayCauseInfiniteLoops: false,
719
- properties: {
720
- additionalHooks: { type: "string" },
721
- enableDangerousAutofixThisMayCauseInfiniteLoops: { type: "boolean" },
722
- customHooks: {
723
- type: "object",
724
- additionalProperties: { callbackIndex: { type: "number" } }
725
- },
726
- staticHooks: {
727
- type: "object",
728
- additionalProperties: { oneOf: [
729
- { type: "boolean" },
730
- {
731
- type: "array",
732
- items: { type: "boolean" }
733
- },
734
- {
735
- type: "object",
736
- additionalProperties: { type: "boolean" }
737
- }
738
- ] }
739
- },
740
- checkMemoizedVariableIsStatic: { type: "boolean" }
741
- }
742
- }]
743
- },
744
- create(context) {
745
- const additionalHooks = context.options && context.options[0] && context.options[0].additionalHooks ? new RegExp(context.options[0].additionalHooks) : void 0;
746
- const enableDangerousAutofixThisMayCauseInfiniteLoops = context.options && context.options[0] && context.options[0].enableDangerousAutofixThisMayCauseInfiniteLoops || false;
747
- const customHooks = context.options && context.options[0] && context.options[0].customHooks || {};
748
- const staticHooks = context.options && context.options[0] && context.options[0].staticHooks || {};
749
- const checkMemoizedVariableIsStatic = context.options && context.options[0] && context.options[0].checkMemoizedVariableIsStatic || false;
750
- const options = {
751
- additionalHooks,
752
- enableDangerousAutofixThisMayCauseInfiniteLoops,
753
- customHooks,
754
- staticHooks,
755
- checkMemoizedVariableIsStatic
756
- };
757
- function reportProblem(problem) {
758
- if (enableDangerousAutofixThisMayCauseInfiniteLoops && Array.isArray(problem.suggest) && problem.suggest.length > 0) problem.fix = problem.suggest[0].fix;
759
- context.report(problem);
760
- }
761
- /**
762
- * SourceCode#getText that also works down to ESLint 3.0.0
763
- */
764
- const getSource = typeof context.getSource === "function" ? (node) => {
765
- return context.getSource(node);
766
- } : (node) => {
767
- return context.sourceCode.getText(node);
768
- };
769
- /**
770
- * SourceCode#getScope that also works down to ESLint 3.0.0
771
- */
772
- const getScope = typeof context.getScope === "function" ? () => {
773
- return context.getScope();
774
- } : (node) => {
775
- return context.sourceCode.getScope(node);
776
- };
777
- const scopeManager = context.getSourceCode().scopeManager;
778
- const setStateCallSites = /* @__PURE__ */ new WeakMap();
779
- const stateVariables = /* @__PURE__ */ new WeakSet();
780
- const stableKnownValueCache = /* @__PURE__ */ new WeakMap();
781
- const functionWithoutCapturedValueCache = /* @__PURE__ */ new WeakMap();
782
- const useEffectEventVariables = /* @__PURE__ */ new WeakSet();
783
- function memoizeWithWeakMap(fn, map) {
784
- return function(arg) {
785
- if (map.has(arg)) return map.get(arg);
786
- const result = fn(arg);
787
- map.set(arg, result);
788
- return result;
789
- };
790
- }
791
- function visitFunctionWithDependencies(node, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect) {
792
- if (isEffect && node.async) {
793
- let isCustomHook = false;
794
- if (options.customHooks) Object.entries(options.customHooks).forEach(([key, customParts]) => {
795
- if (typeof customParts === "object" && new RegExp(key).test(reactiveHookName)) isCustomHook = true;
796
- });
797
- if (!isCustomHook) reportProblem({
798
- node,
799
- message: "Effect callbacks are synchronous to prevent race conditions. Put the async function inside:\n\nuseEffect(() => {\n async function fetchData() {\n // You can await here\n const response = await MyAPI.getData(someId);\n // ...\n }\n fetchData();\n}, [someId]); // Or [] if effect doesn't need props or state\n\nLearn more about data fetching with Hooks: https://react.dev/link/hooks-data-fetching"
800
- });
801
- }
802
- const scope = scopeManager.acquire(node);
803
- const pureScopes = /* @__PURE__ */ new Set();
804
- let componentScope = null;
805
- {
806
- let currentScope = scope.upper;
807
- while (currentScope) {
808
- pureScopes.add(currentScope);
809
- if (currentScope.type === "function") break;
810
- currentScope = currentScope.upper;
811
- }
812
- if (!currentScope) return;
813
- componentScope = currentScope;
814
- }
815
- const isArray = Array.isArray;
816
- const memoizedIsStableKnownHookValue = memoizeWithWeakMap(isStableKnownHookValue, stableKnownValueCache);
817
- const memoizedIsFunctionWithoutCapturedValues = memoizeWithWeakMap(isFunctionWithoutCapturedValues, functionWithoutCapturedValueCache);
818
- function isStableKnownHookValue(resolved) {
819
- if (!isArray(resolved.defs)) return false;
820
- const def = resolved.defs[0];
821
- if (def == null) return false;
822
- if (def.node.type !== "VariableDeclarator") return false;
823
- let init = def.node.init;
824
- if (init == null) return false;
825
- while (init.type === "TSAsExpression" || init.type === "AsExpression") init = init.expression;
826
- let declaration = def.node.parent;
827
- if (declaration == null) {
828
- fastFindReferenceWithParent(componentScope.block, def.node.id);
829
- declaration = def.node.parent;
830
- if (declaration == null) return false;
831
- }
832
- if (declaration.kind === "const" && init.type === "Literal" && (typeof init.value === "string" || typeof init.value === "number" || init.value == null)) return true;
833
- if (init.type !== "CallExpression") return false;
834
- let callee = init.callee;
835
- if (callee.type === "MemberExpression" && callee.object.name === "React" && callee.property != null && !callee.computed) callee = callee.property;
836
- if (callee.type !== "Identifier") return false;
837
- const id = def.node.id;
838
- const { name } = callee;
839
- if (name === "useRef" && id.type === "Identifier") return true;
840
- else if (isUseEffectEventIdentifier(callee) && id.type === "Identifier") {
841
- for (const ref of resolved.references) if (ref !== id) useEffectEventVariables.add(ref.identifier);
842
- return true;
843
- } else if (name === "useState" || name === "useReducer" || name === "useActionState") {
844
- if (id.type === "ArrayPattern" && id.elements.length === 2 && isArray(resolved.identifiers)) {
845
- if (id.elements[1] === resolved.identifiers[0]) {
846
- if (name === "useState") {
847
- const references = resolved.references;
848
- let writeCount = 0;
849
- for (const reference of references) {
850
- if (reference.isWrite()) writeCount++;
851
- if (writeCount > 1) return false;
852
- setStateCallSites.set(reference.identifier, id.elements[0]);
853
- }
854
- }
855
- return true;
856
- } else if (id.elements[0] === resolved.identifiers[0]) {
857
- if (name === "useState") {
858
- const references = resolved.references;
859
- for (const reference of references) stateVariables.add(reference.identifier);
860
- }
861
- return false;
862
- }
863
- }
864
- } else if (name === "useTransition") {
865
- if (id.type === "ArrayPattern" && id.elements.length === 2 && Array.isArray(resolved.identifiers) && id.elements[1] === resolved.identifiers[0]) return true;
866
- } else if (options.checkMemoizedVariableIsStatic && (name === "useMemo" || name === "useCallback")) {
867
- const hookArgs = callee.parent.arguments;
868
- if (hookArgs.length < 2) return false;
869
- const dependencies$1 = hookArgs[1].elements;
870
- if (dependencies$1.length === 0) return true;
871
- for (const dependencyNode of dependencies$1) {
872
- const dependencyRefernece = resolved.scope.references.find((reference) => reference.identifier === dependencyNode);
873
- if (dependencyRefernece !== void 0 && memoizedIsStableKnownHookValue(dependencyRefernece.resolved)) continue;
874
- else return false;
875
- }
876
- return true;
877
- } else {
878
- Object.entries(options.staticHooks).forEach(([key, staticParts]) => {
879
- if (typeof staticParts === "object" && staticParts.regexp && new RegExp(key).test(name)) options.staticHooks[name] = staticParts.value;
880
- });
881
- if (options.staticHooks[name]) {
882
- const staticParts = options.staticHooks[name];
883
- if (staticParts === true) return true;
884
- else if (Array.isArray(staticParts)) {
885
- if (id.type === "ArrayPattern" && id.elements.length <= staticParts.length && Array.isArray(resolved.identifiers)) {
886
- const idx = id.elements.indexOf(resolved.identifiers[0]);
887
- if (idx !== -1) return staticParts[idx];
888
- }
889
- } else if (typeof staticParts === "object" && id.type === "ObjectPattern") {
890
- const property = id.properties.find((p) => p.key === resolved.identifiers[0]);
891
- if (property) return staticParts[property.key.name];
892
- }
893
- }
894
- }
895
- return false;
896
- }
897
- function isFunctionWithoutCapturedValues(resolved) {
898
- if (!isArray(resolved.defs)) return false;
899
- const def = resolved.defs[0];
900
- if (def == null) return false;
901
- if (def.node == null || def.node.id == null) return false;
902
- const fnNode = def.node;
903
- const childScopes = componentScope.childScopes;
904
- let fnScope = null;
905
- let i;
906
- for (i = 0; i < childScopes.length; i++) {
907
- const childScope = childScopes[i];
908
- const childScopeBlock = childScope.block;
909
- if (fnNode.type === "FunctionDeclaration" && childScopeBlock === fnNode || fnNode.type === "VariableDeclarator" && childScopeBlock.parent === fnNode) {
910
- fnScope = childScope;
911
- break;
912
- }
913
- }
914
- if (fnScope == null) return false;
915
- for (i = 0; i < fnScope.through.length; i++) {
916
- const ref = fnScope.through[i];
917
- if (ref.resolved == null) continue;
918
- if (pureScopes.has(ref.resolved.scope) && !memoizedIsStableKnownHookValue(ref.resolved)) return false;
919
- }
920
- return true;
921
- }
922
- const currentRefsInEffectCleanup = /* @__PURE__ */ new Map();
923
- function isInsideEffectCleanup(reference) {
924
- let curScope = reference.from;
925
- let isInReturnedFunction = false;
926
- while (curScope.block !== node) {
927
- if (curScope.type === "function") isInReturnedFunction = curScope.block.parent != null && curScope.block.parent.type === "ReturnStatement";
928
- curScope = curScope.upper;
929
- }
930
- return isInReturnedFunction;
931
- }
932
- const dependencies = /* @__PURE__ */ new Map();
933
- const optionalChains = /* @__PURE__ */ new Map();
934
- gatherDependenciesRecursively(scope);
935
- function gatherDependenciesRecursively(currentScope) {
936
- for (const reference of currentScope.references) {
937
- if (!reference.resolved) continue;
938
- if (!pureScopes.has(reference.resolved.scope)) continue;
939
- const referenceNode = fastFindReferenceWithParent(node, reference.identifier);
940
- const dependencyNode = getDependency(referenceNode);
941
- const dependency = analyzePropertyChain(dependencyNode, optionalChains);
942
- if (isEffect && dependencyNode.type === "Identifier" && (dependencyNode.parent.type === "MemberExpression" || dependencyNode.parent.type === "OptionalMemberExpression") && !dependencyNode.parent.computed && dependencyNode.parent.property.type === "Identifier" && dependencyNode.parent.property.name === "current" && isInsideEffectCleanup(reference)) currentRefsInEffectCleanup.set(dependency, {
943
- reference,
944
- dependencyNode
945
- });
946
- if (dependencyNode.parent.type === "TSTypeQuery" || dependencyNode.parent.type === "TSTypeReference") continue;
947
- const def = reference.resolved.defs[0];
948
- if (def == null) continue;
949
- if (def.node != null && def.node.init === node.parent) continue;
950
- if (def.type === "TypeParameter") continue;
951
- if (!dependencies.has(dependency)) {
952
- const resolved = reference.resolved;
953
- const isStable = memoizedIsStableKnownHookValue(resolved) || memoizedIsFunctionWithoutCapturedValues(resolved);
954
- dependencies.set(dependency, {
955
- isStable,
956
- references: [reference]
957
- });
958
- } else dependencies.get(dependency).references.push(reference);
959
- }
960
- for (const childScope of currentScope.childScopes) gatherDependenciesRecursively(childScope);
961
- }
962
- currentRefsInEffectCleanup.forEach(({ reference, dependencyNode }, dependency) => {
963
- const references = reference.resolved.references;
964
- let foundCurrentAssignment = false;
965
- for (const { identifier } of references) {
966
- const { parent } = identifier;
967
- if (parent != null && parent.type === "MemberExpression" && !parent.computed && parent.property.type === "Identifier" && parent.property.name === "current" && parent.parent.type === "AssignmentExpression" && parent.parent.left === parent) {
968
- foundCurrentAssignment = true;
969
- break;
970
- }
971
- }
972
- if (foundCurrentAssignment) return;
973
- reportProblem({
974
- node: dependencyNode.parent.property,
975
- message: `The ref value '${dependency}.current' will likely have changed by the time this effect cleanup function runs. If this ref points to a node rendered by React, copy '${dependency}.current' to a variable inside the effect, and use that variable in the cleanup function.`
976
- });
977
- });
978
- const staleAssignments = /* @__PURE__ */ new Set();
979
- function reportStaleAssignment(writeExpr, key) {
980
- if (staleAssignments.has(key)) return;
981
- staleAssignments.add(key);
982
- reportProblem({
983
- node: writeExpr,
984
- message: `Assignments to the '${key}' variable from inside React Hook ${getSource(reactiveHook)} will be lost after each render. To preserve the value over time, store it in a useRef Hook and keep the mutable value in the '.current' property. Otherwise, you can move this variable directly inside ${getSource(reactiveHook)}.`
985
- });
986
- }
987
- const stableDependencies = /* @__PURE__ */ new Set();
988
- dependencies.forEach(({ isStable, references }, key) => {
989
- if (isStable) stableDependencies.add(key);
990
- references.forEach((reference) => {
991
- if (reference.writeExpr) reportStaleAssignment(reference.writeExpr, key);
992
- });
993
- });
994
- if (staleAssignments.size > 0) return;
995
- if (!declaredDependenciesNode) {
996
- let setStateInsideEffectWithoutDeps = null;
997
- dependencies.forEach(({ references }, key) => {
998
- if (setStateInsideEffectWithoutDeps) return;
999
- references.forEach((reference) => {
1000
- if (setStateInsideEffectWithoutDeps) return;
1001
- const id = reference.identifier;
1002
- const isSetState = setStateCallSites.has(id);
1003
- if (!isSetState) return;
1004
- let fnScope = reference.from;
1005
- while (fnScope.type !== "function") fnScope = fnScope.upper;
1006
- const isDirectlyInsideEffect = fnScope.block === node;
1007
- if (isDirectlyInsideEffect) setStateInsideEffectWithoutDeps = key;
1008
- });
1009
- });
1010
- if (setStateInsideEffectWithoutDeps) {
1011
- const { suggestedDependencies: suggestedDependencies$1 } = collectRecommendations({
1012
- dependencies,
1013
- declaredDependencies: [],
1014
- stableDependencies,
1015
- externalDependencies: /* @__PURE__ */ new Set(),
1016
- isEffect: true
1017
- });
1018
- reportProblem({
1019
- node: reactiveHook,
1020
- message: `React Hook ${reactiveHookName} contains a call to '${setStateInsideEffectWithoutDeps}'. Without a list of dependencies, this can lead to an infinite chain of updates. To fix this, pass [` + suggestedDependencies$1.join(", ") + `] as a second argument to the ${reactiveHookName} Hook.`,
1021
- suggest: [{
1022
- desc: `Add dependencies array: [${suggestedDependencies$1.join(", ")}]`,
1023
- fix(fixer) {
1024
- return fixer.insertTextAfter(node, `, [${suggestedDependencies$1.join(", ")}]`);
1025
- }
1026
- }]
1027
- });
1028
- }
1029
- return;
1030
- }
1031
- const declaredDependencies = [];
1032
- const externalDependencies = /* @__PURE__ */ new Set();
1033
- const isArrayExpression = declaredDependenciesNode.type === "ArrayExpression";
1034
- const isTSAsArrayExpression = declaredDependenciesNode.type === "TSAsExpression" && declaredDependenciesNode.expression.type === "ArrayExpression";
1035
- if (!isArrayExpression && !isTSAsArrayExpression) reportProblem({
1036
- node: declaredDependenciesNode,
1037
- message: `React Hook ${getSource(reactiveHook)} was passed a dependency list that is not an array literal. This means we can't statically verify whether you've passed the correct dependencies.`
1038
- });
1039
- else {
1040
- const arrayExpression = isTSAsArrayExpression ? declaredDependenciesNode.expression : declaredDependenciesNode;
1041
- arrayExpression.elements.forEach((declaredDependencyNode) => {
1042
- if (declaredDependencyNode == null) return;
1043
- if (declaredDependencyNode.type === "SpreadElement") {
1044
- reportProblem({
1045
- node: declaredDependencyNode,
1046
- message: `React Hook ${getSource(reactiveHook)} has a spread element in its dependency array. This means we can't statically verify whether you've passed the correct dependencies.`
1047
- });
1048
- return;
1049
- }
1050
- if (useEffectEventVariables.has(declaredDependencyNode)) reportProblem({
1051
- node: declaredDependencyNode,
1052
- message: `Functions returned from \`useEffectEvent\` must not be included in the dependency array. Remove \`${getSource(declaredDependencyNode)}\` from the list.`,
1053
- suggest: [{
1054
- desc: `Remove the dependency \`${getSource(declaredDependencyNode)}\``,
1055
- fix(fixer) {
1056
- return fixer.removeRange(declaredDependencyNode.range);
1057
- }
1058
- }]
1059
- });
1060
- let declaredDependency;
1061
- try {
1062
- declaredDependency = analyzePropertyChain(declaredDependencyNode, null);
1063
- } catch (err) {
1064
- if (/Unsupported node type/.test(err.message)) {
1065
- if (declaredDependencyNode.type === "Literal") if (dependencies.has(declaredDependencyNode.value)) reportProblem({
1066
- node: declaredDependencyNode,
1067
- message: `The ${declaredDependencyNode.raw} literal is not a valid dependency because it never changes. Did you mean to include ${declaredDependencyNode.value} in the array instead?`
1068
- });
1069
- else reportProblem({
1070
- node: declaredDependencyNode,
1071
- message: `The ${declaredDependencyNode.raw} literal is not a valid dependency because it never changes. You can safely remove it.`
1072
- });
1073
- else reportProblem({
1074
- node: declaredDependencyNode,
1075
- message: `React Hook ${getSource(reactiveHook)} has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked.`
1076
- });
1077
- return;
1078
- }
1079
- throw err;
1080
- }
1081
- let maybeID = declaredDependencyNode;
1082
- while (maybeID.type === "MemberExpression" || maybeID.type === "OptionalMemberExpression" || maybeID.type === "ChainExpression") maybeID = maybeID.object || maybeID.expression.object;
1083
- const isDeclaredInComponent = !componentScope.through.some((ref) => ref.identifier === maybeID);
1084
- declaredDependencies.push({
1085
- key: declaredDependency,
1086
- node: declaredDependencyNode
1087
- });
1088
- if (!isDeclaredInComponent) externalDependencies.add(declaredDependency);
1089
- });
1090
- }
1091
- const { suggestedDependencies, unnecessaryDependencies, missingDependencies, duplicateDependencies } = collectRecommendations({
1092
- dependencies,
1093
- declaredDependencies,
1094
- stableDependencies,
1095
- externalDependencies,
1096
- isEffect
1097
- });
1098
- let suggestedDeps = suggestedDependencies;
1099
- const problemCount = duplicateDependencies.size + missingDependencies.size + unnecessaryDependencies.size;
1100
- if (problemCount === 0) {
1101
- const constructions = scanForConstructions({
1102
- declaredDependencies,
1103
- declaredDependenciesNode,
1104
- componentScope,
1105
- scope
1106
- });
1107
- constructions.forEach(({ construction, isUsedOutsideOfHook, depType }) => {
1108
- const wrapperHook = depType === "function" ? "useCallback" : "useMemo";
1109
- const constructionType = depType === "function" ? "definition" : "initialization";
1110
- const defaultAdvice = `wrap the ${constructionType} of '${construction.name.name}' in its own ${wrapperHook}() Hook.`;
1111
- const advice = isUsedOutsideOfHook ? `To fix this, ${defaultAdvice}` : `Move it inside the ${reactiveHookName} callback. Alternatively, ${defaultAdvice}`;
1112
- const causation = depType === "conditional" || depType === "logical expression" ? "could make" : "makes";
1113
- const message = `The '${construction.name.name}' ${depType} ${causation} the dependencies of ${reactiveHookName} Hook (at line ${declaredDependenciesNode.loc.start.line}) change on every render. ${advice}`;
1114
- let suggest;
1115
- if (isUsedOutsideOfHook && construction.type === "Variable" && depType === "function") suggest = [{
1116
- desc: `Wrap the ${constructionType} of '${construction.name.name}' in its own ${wrapperHook}() Hook.`,
1117
- fix(fixer) {
1118
- const [before, after] = wrapperHook === "useMemo" ? [`useMemo(() => { return `, "; })"] : ["useCallback(", ")"];
1119
- return [fixer.insertTextBefore(construction.node.init, before), fixer.insertTextAfter(construction.node.init, after)];
1120
- }
1121
- }];
1122
- reportProblem({
1123
- node: construction.node,
1124
- message,
1125
- suggest
1126
- });
1127
- });
1128
- return;
1129
- }
1130
- if (!isEffect && missingDependencies.size > 0) suggestedDeps = collectRecommendations({
1131
- dependencies,
1132
- declaredDependencies: [],
1133
- stableDependencies,
1134
- externalDependencies,
1135
- isEffect
1136
- }).suggestedDependencies;
1137
- function areDeclaredDepsAlphabetized() {
1138
- if (declaredDependencies.length === 0) return true;
1139
- const declaredDepKeys = declaredDependencies.map((dep) => dep.key);
1140
- const sortedDeclaredDepKeys = [...declaredDepKeys].sort();
1141
- return declaredDepKeys.join(",") === sortedDeclaredDepKeys.join(",");
1142
- }
1143
- if (areDeclaredDepsAlphabetized()) suggestedDeps.sort();
1144
- function formatDependency(path$3) {
1145
- const members = path$3.split(".");
1146
- let finalPath = "";
1147
- for (let i = 0; i < members.length; i++) {
1148
- if (i !== 0) {
1149
- const pathSoFar = members.slice(0, i + 1).join(".");
1150
- const isOptional = optionalChains.get(pathSoFar) === true;
1151
- finalPath += isOptional ? "?." : ".";
1152
- }
1153
- finalPath += members[i];
1154
- }
1155
- return finalPath;
1156
- }
1157
- function getWarningMessage(deps, singlePrefix, label, fixVerb) {
1158
- if (deps.size === 0) return null;
1159
- return (deps.size > 1 ? "" : singlePrefix + " ") + label + " " + (deps.size > 1 ? "dependencies" : "dependency") + ": " + joinEnglish([...deps].sort().map((name) => "'" + formatDependency(name) + "'")) + `. Either ${fixVerb} ${deps.size > 1 ? "them" : "it"} or remove the dependency array.`;
1160
- }
1161
- let extraWarning = "";
1162
- if (unnecessaryDependencies.size > 0) {
1163
- let badRef = null;
1164
- [...unnecessaryDependencies.keys()].forEach((key) => {
1165
- if (badRef != null) return;
1166
- if (key.endsWith(".current")) badRef = key;
1167
- });
1168
- if (badRef != null) extraWarning = ` Mutable values like '${badRef}' aren't valid dependencies because mutating them doesn't re-render the component.`;
1169
- else if (externalDependencies.size > 0) {
1170
- const dep = [...externalDependencies][0];
1171
- if (!scope.set.has(dep)) extraWarning = ` Outer scope values like '${dep}' aren't valid dependencies because mutating them doesn't re-render the component.`;
1172
- }
1173
- }
1174
- if (!extraWarning && missingDependencies.has("props")) {
1175
- const propDep = dependencies.get("props");
1176
- if (propDep == null) return;
1177
- const refs = propDep.references;
1178
- if (!Array.isArray(refs)) return;
1179
- let isPropsOnlyUsedInMembers = true;
1180
- for (const ref of refs) {
1181
- const id = fastFindReferenceWithParent(componentScope.block, ref.identifier);
1182
- if (!id) {
1183
- isPropsOnlyUsedInMembers = false;
1184
- break;
1185
- }
1186
- const parent = id.parent;
1187
- if (parent == null) {
1188
- isPropsOnlyUsedInMembers = false;
1189
- break;
1190
- }
1191
- if (parent.type !== "MemberExpression" && parent.type !== "OptionalMemberExpression") {
1192
- isPropsOnlyUsedInMembers = false;
1193
- break;
1194
- }
1195
- }
1196
- if (isPropsOnlyUsedInMembers) extraWarning = ` However, 'props' will change when *any* prop changes, so the preferred fix is to destructure the 'props' object outside of the ${reactiveHookName} call and refer to those specific props inside ${getSource(reactiveHook)}.`;
1197
- }
1198
- if (!extraWarning && missingDependencies.size > 0) {
1199
- let missingCallbackDep = null;
1200
- missingDependencies.forEach((missingDep) => {
1201
- if (missingCallbackDep) return;
1202
- const topScopeRef = componentScope.set.get(missingDep);
1203
- const usedDep = dependencies.get(missingDep);
1204
- if (usedDep.references[0].resolved !== topScopeRef) return;
1205
- const def = topScopeRef.defs[0];
1206
- if (def == null || def.name == null || def.type !== "Parameter") return;
1207
- let isFunctionCall = false;
1208
- let id;
1209
- for (let i = 0; i < usedDep.references.length; i++) {
1210
- id = usedDep.references[i].identifier;
1211
- if (id != null && id.parent != null && (id.parent.type === "CallExpression" || id.parent.type === "OptionalCallExpression") && id.parent.callee === id) {
1212
- isFunctionCall = true;
1213
- break;
1214
- }
1215
- }
1216
- if (!isFunctionCall) return;
1217
- missingCallbackDep = missingDep;
1218
- });
1219
- if (missingCallbackDep != null) extraWarning = ` If '${missingCallbackDep}' changes too often, find the parent component that defines it and wrap that definition in useCallback.`;
1220
- }
1221
- if (!extraWarning && missingDependencies.size > 0) {
1222
- let setStateRecommendation = null;
1223
- missingDependencies.forEach((missingDep) => {
1224
- if (setStateRecommendation != null) return;
1225
- const usedDep = dependencies.get(missingDep);
1226
- const references = usedDep.references;
1227
- let id;
1228
- let maybeCall;
1229
- for (const reference of references) {
1230
- id = reference.identifier;
1231
- maybeCall = id.parent;
1232
- while (maybeCall != null && maybeCall !== componentScope.block) {
1233
- if (maybeCall.type === "CallExpression") {
1234
- const correspondingStateVariable = setStateCallSites.get(maybeCall.callee);
1235
- if (correspondingStateVariable != null) {
1236
- if (correspondingStateVariable.name === missingDep) setStateRecommendation = {
1237
- missingDep,
1238
- setter: maybeCall.callee.name,
1239
- form: "updater"
1240
- };
1241
- else if (stateVariables.has(id)) setStateRecommendation = {
1242
- missingDep,
1243
- setter: maybeCall.callee.name,
1244
- form: "reducer"
1245
- };
1246
- else {
1247
- const resolved = reference.resolved;
1248
- if (resolved != null) {
1249
- const def = resolved.defs[0];
1250
- if (def != null && def.type === "Parameter") setStateRecommendation = {
1251
- missingDep,
1252
- setter: maybeCall.callee.name,
1253
- form: "inlineReducer"
1254
- };
1255
- }
1256
- }
1257
- break;
1258
- }
1259
- }
1260
- maybeCall = maybeCall.parent;
1261
- }
1262
- if (setStateRecommendation != null) break;
1263
- }
1264
- });
1265
- if (setStateRecommendation != null) switch (setStateRecommendation.form) {
1266
- case "reducer":
1267
- extraWarning = ` You can also replace multiple useState variables with useReducer if '${setStateRecommendation.setter}' needs the current value of '${setStateRecommendation.missingDep}'.`;
1268
- break;
1269
- case "inlineReducer":
1270
- extraWarning = ` If '${setStateRecommendation.setter}' needs the current value of '${setStateRecommendation.missingDep}', you can also switch to useReducer instead of useState and read '${setStateRecommendation.missingDep}' in the reducer.`;
1271
- break;
1272
- case "updater":
1273
- extraWarning = ` You can also do a functional update '${setStateRecommendation.setter}(${setStateRecommendation.missingDep.slice(0, 1)} => ...)' if you only need '${setStateRecommendation.missingDep}' in the '${setStateRecommendation.setter}' call.`;
1274
- break;
1275
- default: throw new Error("Unknown case.");
1276
- }
1277
- }
1278
- reportProblem({
1279
- node: declaredDependenciesNode,
1280
- message: `React Hook ${getSource(reactiveHook)} has ` + (getWarningMessage(missingDependencies, "a", "missing", "include") || getWarningMessage(unnecessaryDependencies, "an", "unnecessary", "exclude") || getWarningMessage(duplicateDependencies, "a", "duplicate", "omit")) + extraWarning,
1281
- suggest: [{
1282
- desc: `Update the dependencies array to be: [${suggestedDeps.map((element) => formatDependency(element)).join(", ")}]`,
1283
- fix(fixer) {
1284
- return fixer.replaceText(declaredDependenciesNode, `[${suggestedDeps.map((element) => formatDependency(element)).join(", ")}]`);
1285
- }
1286
- }]
1287
- });
1288
- }
1289
- function visitCallExpression(node) {
1290
- const callbackIndex = getReactiveHookCallbackIndex(node.callee, options);
1291
- if (callbackIndex === -1) return;
1292
- const callback = node.arguments[callbackIndex];
1293
- const reactiveHook = node.callee;
1294
- const reactiveHookName = getNodeWithoutReactNamespace(reactiveHook).name;
1295
- const maybeNode = node.arguments[callbackIndex + 1];
1296
- const declaredDependenciesNode = maybeNode && !(maybeNode.type === "Identifier" && maybeNode.name === "undefined") ? maybeNode : void 0;
1297
- const isEffect = /Effect($|[^a-z])/g.test(reactiveHookName);
1298
- if (!callback) {
1299
- reportProblem({
1300
- node: reactiveHook,
1301
- message: `React Hook ${reactiveHookName} requires an effect callback. Did you forget to pass a callback to the hook?`
1302
- });
1303
- return;
1304
- }
1305
- if (!declaredDependenciesNode && !isEffect) {
1306
- if (reactiveHookName === "useMemo" || reactiveHookName === "useCallback") reportProblem({
1307
- node: reactiveHook,
1308
- message: `React Hook ${reactiveHookName} does nothing when called with only one argument. Did you forget to pass an array of dependencies?`
1309
- });
1310
- return;
1311
- }
1312
- switch (callback.type) {
1313
- case "FunctionExpression":
1314
- case "ArrowFunctionExpression":
1315
- visitFunctionWithDependencies(callback, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
1316
- return;
1317
- case "TSAsExpression":
1318
- visitFunctionWithDependencies(callback.expression, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
1319
- return;
1320
- case "Identifier":
1321
- if (!declaredDependenciesNode) return;
1322
- if (declaredDependenciesNode.elements && declaredDependenciesNode.elements.some((el) => el && el.type === "Identifier" && el.name === callback.name)) return;
1323
- const variable = getScope(callback).set.get(callback.name);
1324
- if (variable == null || variable.defs == null) return;
1325
- const def = variable.defs[0];
1326
- if (!def || !def.node) break;
1327
- if (def.type !== "Variable" && def.type !== "FunctionName") break;
1328
- switch (def.node.type) {
1329
- case "FunctionDeclaration":
1330
- visitFunctionWithDependencies(def.node, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
1331
- return;
1332
- case "VariableDeclarator":
1333
- const init = def.node.init;
1334
- if (!init) break;
1335
- switch (init.type) {
1336
- case "ArrowFunctionExpression":
1337
- case "FunctionExpression":
1338
- visitFunctionWithDependencies(init, declaredDependenciesNode, reactiveHook, reactiveHookName, isEffect);
1339
- return;
1340
- }
1341
- break;
1342
- }
1343
- break;
1344
- default:
1345
- reportProblem({
1346
- node: reactiveHook,
1347
- message: `React Hook ${reactiveHookName} received a function whose dependencies are unknown. Pass an inline function instead.`
1348
- });
1349
- return;
1350
- }
1351
- reportProblem({
1352
- node: reactiveHook,
1353
- message: `React Hook ${reactiveHookName} has a missing dependency: '${callback.name}'. Either include it or remove the dependency array.`,
1354
- suggest: [{
1355
- desc: `Update the dependencies array to be: [${callback.name}]`,
1356
- fix(fixer) {
1357
- return fixer.replaceText(declaredDependenciesNode, `[${callback.name}]`);
1358
- }
1359
- }]
1360
- });
1361
- }
1362
- return { CallExpression: visitCallExpression };
1363
- }
1364
- };
1365
- function collectRecommendations({ dependencies, declaredDependencies, stableDependencies, externalDependencies, isEffect }) {
1366
- const depTree = createDepTree();
1367
- function createDepTree() {
1368
- return {
1369
- isUsed: false,
1370
- isSatisfiedRecursively: false,
1371
- isSubtreeUsed: false,
1372
- children: /* @__PURE__ */ new Map()
1373
- };
1374
- }
1375
- dependencies.forEach((_, key) => {
1376
- const node = getOrCreateNodeByPath(depTree, key);
1377
- node.isUsed = true;
1378
- markAllParentsByPath(depTree, key, (parent) => {
1379
- parent.isSubtreeUsed = true;
1380
- });
1381
- });
1382
- declaredDependencies.forEach(({ key }) => {
1383
- const node = getOrCreateNodeByPath(depTree, key);
1384
- node.isSatisfiedRecursively = true;
1385
- });
1386
- stableDependencies.forEach((key) => {
1387
- const node = getOrCreateNodeByPath(depTree, key);
1388
- node.isSatisfiedRecursively = true;
1389
- });
1390
- function getOrCreateNodeByPath(rootNode, path$3) {
1391
- const keys = path$3.split(".");
1392
- let node = rootNode;
1393
- for (const key of keys) {
1394
- let child = node.children.get(key);
1395
- if (!child) {
1396
- child = createDepTree();
1397
- node.children.set(key, child);
1398
- }
1399
- node = child;
1400
- }
1401
- return node;
1402
- }
1403
- function markAllParentsByPath(rootNode, path$3, fn) {
1404
- const keys = path$3.split(".");
1405
- let node = rootNode;
1406
- for (const key of keys) {
1407
- const child = node.children.get(key);
1408
- if (!child) return;
1409
- fn(child);
1410
- node = child;
1411
- }
1412
- }
1413
- const missingDependencies = /* @__PURE__ */ new Set();
1414
- const satisfyingDependencies = /* @__PURE__ */ new Set();
1415
- scanTreeRecursively(depTree, missingDependencies, satisfyingDependencies, (key) => key);
1416
- function scanTreeRecursively(node, missingPaths, satisfyingPaths, keyToPath) {
1417
- node.children.forEach((child, key) => {
1418
- const path$3 = keyToPath(key);
1419
- if (child.isSatisfiedRecursively) {
1420
- if (child.isSubtreeUsed) satisfyingPaths.add(path$3);
1421
- return;
1422
- }
1423
- if (child.isUsed) {
1424
- missingPaths.add(path$3);
1425
- return;
1426
- }
1427
- scanTreeRecursively(child, missingPaths, satisfyingPaths, (childKey) => path$3 + "." + childKey);
1428
- });
1429
- }
1430
- const suggestedDependencies = [];
1431
- const unnecessaryDependencies = /* @__PURE__ */ new Set();
1432
- const duplicateDependencies = /* @__PURE__ */ new Set();
1433
- declaredDependencies.forEach(({ key }) => {
1434
- if (satisfyingDependencies.has(key)) if (!suggestedDependencies.includes(key)) suggestedDependencies.push(key);
1435
- else duplicateDependencies.add(key);
1436
- else if (isEffect && !key.endsWith(".current") && !externalDependencies.has(key)) {
1437
- if (!suggestedDependencies.includes(key)) suggestedDependencies.push(key);
1438
- } else unnecessaryDependencies.add(key);
1439
- });
1440
- missingDependencies.forEach((key) => {
1441
- suggestedDependencies.push(key);
1442
- });
1443
- return {
1444
- suggestedDependencies,
1445
- unnecessaryDependencies,
1446
- duplicateDependencies,
1447
- missingDependencies
1448
- };
1449
- }
1450
- function getConstructionExpressionType(node) {
1451
- switch (node.type) {
1452
- case "ObjectExpression": return "object";
1453
- case "ArrayExpression": return "array";
1454
- case "ArrowFunctionExpression":
1455
- case "FunctionExpression": return "function";
1456
- case "ClassExpression": return "class";
1457
- case "ConditionalExpression":
1458
- if (getConstructionExpressionType(node.consequent) != null || getConstructionExpressionType(node.alternate) != null) return "conditional";
1459
- return null;
1460
- case "LogicalExpression":
1461
- if (getConstructionExpressionType(node.left) != null || getConstructionExpressionType(node.right) != null) return "logical expression";
1462
- return null;
1463
- case "JSXFragment": return "JSX fragment";
1464
- case "JSXElement": return "JSX element";
1465
- case "AssignmentExpression":
1466
- if (getConstructionExpressionType(node.right) != null) return "assignment expression";
1467
- return null;
1468
- case "NewExpression": return "object construction";
1469
- case "Literal":
1470
- if (node.value instanceof RegExp) return "regular expression";
1471
- return null;
1472
- case "TypeCastExpression":
1473
- case "AsExpression":
1474
- case "TSAsExpression": return getConstructionExpressionType(node.expression);
1475
- }
1476
- return null;
1477
- }
1478
- function scanForConstructions({ declaredDependencies, declaredDependenciesNode, componentScope, scope }) {
1479
- const constructions = declaredDependencies.map(({ key }) => {
1480
- const ref = componentScope.variables.find((v) => v.name === key);
1481
- if (ref == null) return null;
1482
- const node = ref.defs[0];
1483
- if (node == null) return null;
1484
- if (node.type === "Variable" && node.node.type === "VariableDeclarator" && node.node.id.type === "Identifier" && node.node.init != null) {
1485
- const constantExpressionType = getConstructionExpressionType(node.node.init);
1486
- if (constantExpressionType != null) return [ref, constantExpressionType];
1487
- }
1488
- if (node.type === "FunctionName" && node.node.type === "FunctionDeclaration") return [ref, "function"];
1489
- if (node.type === "ClassName" && node.node.type === "ClassDeclaration") return [ref, "class"];
1490
- return null;
1491
- }).filter(Boolean);
1492
- function isUsedOutsideOfHook(ref) {
1493
- let foundWriteExpr = false;
1494
- for (let i = 0; i < ref.references.length; i++) {
1495
- const reference = ref.references[i];
1496
- if (reference.writeExpr) {
1497
- if (foundWriteExpr) return true;
1498
- foundWriteExpr = true;
1499
- continue;
1500
- }
1501
- let currentScope = reference.from;
1502
- while (currentScope !== scope && currentScope != null) currentScope = currentScope.upper;
1503
- if (currentScope !== scope && !isAncestorNodeOf(declaredDependenciesNode, reference.identifier)) return true;
1504
- }
1505
- return false;
1506
- }
1507
- return constructions.map(([ref, depType]) => ({
1508
- construction: ref.defs[0],
1509
- depType,
1510
- isUsedOutsideOfHook: isUsedOutsideOfHook(ref)
1511
- }));
1512
- }
1513
- function getDependency(node) {
1514
- if ((node.parent.type === "MemberExpression" || node.parent.type === "OptionalMemberExpression") && node.parent.object === node && node.parent.property.name !== "current" && !node.parent.computed && !(node.parent.parent != null && (node.parent.parent.type === "CallExpression" || node.parent.parent.type === "OptionalCallExpression") && node.parent.parent.callee === node.parent)) return getDependency(node.parent);
1515
- else if (node.type === "MemberExpression" && node.parent && node.parent.type === "AssignmentExpression" && node.parent.left === node) return node.object;
1516
- return node;
1517
- }
1518
- /**
1519
- * Mark a node as either optional or required.
1520
- * Note: If the node argument is an OptionalMemberExpression, it doesn't necessarily mean it is optional.
1521
- * It just means there is an optional member somewhere inside.
1522
- * This particular node might still represent a required member, so check .optional field.
1523
- * @param {*} node - Node
1524
- * @param {*} optionalChains - Map of optional chains
1525
- * @param {*} result - Resulting property chain
1526
- */
1527
- function markNode(node, optionalChains, result) {
1528
- if (optionalChains) if (node.optional) {
1529
- if (!optionalChains.has(result)) optionalChains.set(result, true);
1530
- } else optionalChains.set(result, false);
1531
- }
1532
- /**
1533
- * Assuming () means the passed node.
1534
- * (foo) -> 'foo'
1535
- * foo(.)bar -> 'foo.bar'
1536
- * foo.bar(.)baz -> 'foo.bar.baz'
1537
- * Otherwise throw.
1538
- * @param {*} node - Node
1539
- * @param {*} optionalChains - Map of optional chains
1540
- */
1541
- function analyzePropertyChain(node, optionalChains) {
1542
- if (node.type === "Identifier" || node.type === "JSXIdentifier") {
1543
- const result = node.name;
1544
- if (optionalChains) optionalChains.set(result, false);
1545
- return result;
1546
- } else if (node.type === "MemberExpression" && !node.computed) {
1547
- const object = analyzePropertyChain(node.object, optionalChains);
1548
- const property = analyzePropertyChain(node.property, null);
1549
- const result = `${object}.${property}`;
1550
- markNode(node, optionalChains, result);
1551
- return result;
1552
- } else if (node.type === "OptionalMemberExpression" && !node.computed) {
1553
- const object = analyzePropertyChain(node.object, optionalChains);
1554
- const property = analyzePropertyChain(node.property, null);
1555
- const result = `${object}.${property}`;
1556
- markNode(node, optionalChains, result);
1557
- return result;
1558
- } else if (node.type === "ChainExpression" && !node.computed) {
1559
- const expression = node.expression;
1560
- if (expression.type === "CallExpression") throw new Error(`Unsupported node type: ${expression.type}`);
1561
- const object = analyzePropertyChain(expression.object, optionalChains);
1562
- const property = analyzePropertyChain(expression.property, null);
1563
- const result = `${object}.${property}`;
1564
- markNode(expression, optionalChains, result);
1565
- return result;
1566
- }
1567
- throw new Error(`Unsupported node type: ${node.type}`);
1568
- }
1569
- function getNodeWithoutReactNamespace(node) {
1570
- if (node.type === "MemberExpression" && node.object.type === "Identifier" && node.object.name === "React" && node.property.type === "Identifier" && !node.computed) return node.property;
1571
- return node;
1572
- }
1573
- function getReactiveHookCallbackIndex(calleeNode, options) {
1574
- const node = getNodeWithoutReactNamespace(calleeNode);
1575
- if (node.type !== "Identifier") return -1;
1576
- switch (node.name) {
1577
- case "useEffect":
1578
- case "useLayoutEffect":
1579
- case "useCallback":
1580
- case "useMemo": return 0;
1581
- case "useImperativeHandle": return 1;
1582
- default:
1583
- if (node === calleeNode && options && options.customHooks) {
1584
- let name;
1585
- try {
1586
- name = analyzePropertyChain(node, null);
1587
- } catch (err) {
1588
- if (/Unsupported node type/.test(err.message)) return 0;
1589
- throw err;
1590
- }
1591
- for (const [key, customParts] of Object.entries(options.customHooks)) if (typeof customParts === "object" && typeof customParts.callbackIndex === "number" && new RegExp(key).test(name)) return customParts.callbackIndex;
1592
- }
1593
- if (node === calleeNode && options && options.additionalHooks) {
1594
- let name;
1595
- try {
1596
- name = analyzePropertyChain(node, null);
1597
- } catch (err) {
1598
- if (/Unsupported node type/.test(err.message)) return 0;
1599
- throw err;
1600
- }
1601
- return options.additionalHooks.test(name) ? 0 : -1;
1602
- }
1603
- return -1;
1604
- }
1605
- }
1606
- /**
1607
- * ESLint won't assign node.parent to references from context.getScope()
1608
- *
1609
- * So instead we search for the node from an ancestor assigning node.parent
1610
- * as we go. This mutates the AST.
1611
- *
1612
- * This traversal is:
1613
- * - optimized by only searching nodes with a range surrounding our target node
1614
- * - agnostic to AST node types, it looks for `{ type: string, ... }`
1615
- * @param {*} start - Start Node
1616
- * @param {*} target - Target Node
1617
- */
1618
- function fastFindReferenceWithParent(start, target) {
1619
- const queue = [start];
1620
- let item = null;
1621
- while (queue.length > 0) {
1622
- item = queue.shift();
1623
- if (isSameIdentifier(item, target)) return item;
1624
- if (!isAncestorNodeOf(item, target)) continue;
1625
- for (const [key, value] of Object.entries(item)) {
1626
- if (key === "parent") continue;
1627
- if (isNodeLike(value)) {
1628
- value.parent = item;
1629
- queue.push(value);
1630
- } else if (Array.isArray(value)) value.forEach((val) => {
1631
- if (isNodeLike(val)) {
1632
- val.parent = item;
1633
- queue.push(val);
1634
- }
1635
- });
1636
- }
1637
- }
1638
- return null;
1639
- }
1640
- function joinEnglish(arr) {
1641
- let s = "";
1642
- for (let i = 0; i < arr.length; i++) {
1643
- s += arr[i];
1644
- if (i === 0 && arr.length === 2) s += " and ";
1645
- else if (i === arr.length - 2 && arr.length > 2) s += ", and ";
1646
- else if (i < arr.length - 1) s += ", ";
1647
- }
1648
- return s;
1649
- }
1650
- function isNodeLike(val) {
1651
- return typeof val === "object" && val != null && !Array.isArray(val) && typeof val.type === "string";
1652
- }
1653
- function isSameIdentifier(a, b) {
1654
- return (a.type === "Identifier" || a.type === "JSXIdentifier") && a.type === b.type && a.name === b.name && a.range[0] === b.range[0] && a.range[1] === b.range[1];
1655
- }
1656
- function isAncestorNodeOf(a, b) {
1657
- return a.range[0] <= b.range[0] && a.range[1] >= b.range[1];
1658
- }
1659
- function isUseEffectEventIdentifier(node) {
1660
- return false;
1661
- }
1662
-
1663
- //#endregion
1664
- //#region src/rules/react-hook-use-ref.js
1665
- const RULE_NAME$5 = "react-hook-use-ref";
1666
- var react_hook_use_ref_default = createRule({
1667
- name: RULE_NAME$5,
1668
- meta: {
1669
- type: "suggestion",
1670
- docs: {
1671
- description: "Ensure naming of useRef hook value.",
1672
- recommended: "recommended"
1673
- },
1674
- schema: [],
1675
- hasSuggestions: true,
1676
- messages: { useRefName: "useRef call is not end with \"Ref\"" }
1677
- },
1678
- defaultOptions: [],
1679
- create: Components.default.detect((context, component, util) => ({ CallExpression(node) {
1680
- const isImmediateReturn = node.parent && node.parent.type === AST_NODE_TYPES.ReturnStatement;
1681
- if (isImmediateReturn || !util.isReactHookCall(node, ["useRef"])) return;
1682
- if (node.parent.id.type !== AST_NODE_TYPES.Identifier) return;
1683
- const variable = node.parent.id.name;
1684
- if (!variable.endsWith("Ref")) context.report({
1685
- node,
1686
- messageId: "useRefName"
1687
- });
1688
- } }))
1689
- });
1690
-
1691
- //#endregion
1692
- //#region src/rules/react-prefer-sx-prop.js
1693
- const RULE_NAME$4 = "react-prefer-sx-prop";
1694
- var react_prefer_sx_prop_default = createRule({
1695
- name: RULE_NAME$4,
1696
- meta: {
1697
- type: "problem",
1698
- docs: {
1699
- description: "Prefer using sx prop instead of inline styles",
1700
- recommended: "recommended"
1701
- },
1702
- messages: { preferSxProp: "Avoid using inline styles, use sx prop or tss-react or styled-component instead" },
1703
- schema: [{
1704
- type: "object",
1705
- properties: { allowedFor: {
1706
- type: "array",
1707
- uniqueItems: true,
1708
- items: { type: "string" }
1709
- } }
1710
- }]
1711
- },
1712
- defaultOptions: [],
1713
- create(context) {
1714
- const configuration = context.options[0] || {};
1715
- const allowedFor = configuration.allowedFor || [];
1716
- function checkComponent(node) {
1717
- const parentName = node.parent.name;
1718
- const tag = parentName.name || `${parentName.object.name}.${parentName.property.name}`;
1719
- const componentName = parentName.name || parentName.property.name;
1720
- if (componentName && typeof componentName[0] === "string" && componentName[0] !== componentName[0].toUpperCase()) return;
1721
- if (allowedFor.includes(tag)) return;
1722
- const prop = node.name.name;
1723
- if (prop === "style") context.report({
1724
- node,
1725
- messageId: "preferSxProp"
1726
- });
1727
- }
1728
- function checkDOMNodes(node) {
1729
- const tag = node.parent.name.name;
1730
- if (!(tag && typeof tag === "string" && tag[0] !== tag[0].toUpperCase())) return;
1731
- if (allowedFor.includes(tag)) return;
1732
- const prop = node.name.name;
1733
- if (prop === "style") context.report({
1734
- node,
1735
- messageId: "preferSxProp"
1736
- });
1737
- }
1738
- return { JSXAttribute(node) {
1739
- checkComponent(node);
1740
- checkDOMNodes(node);
1741
- } };
1742
- }
1743
- });
1744
-
1745
- //#endregion
1746
- //#region src/util/tss.js
1747
- /**
1748
- * Helper function to get the basic identifier from various node types
1749
- * @param {import('eslint').Rule.Node} node - The node to get the identifier from
1750
- * @returns {string | null} The identifier name or null if it can't be determined
1751
- */
1752
- function getBasicIdentifier(node) {
1753
- if (node.type === AST_NODE_TYPES.Identifier) return node.name;
1754
- if (node.type === AST_NODE_TYPES.Literal) return node.value;
1755
- if (node.type === AST_NODE_TYPES.TemplateLiteral) {
1756
- if (node.expressions.length > 0) return null;
1757
- return node.quasis[0].value.raw;
1758
- }
1759
- return null;
1760
- }
1761
- /**
1762
- * Helper function to recursively get the base identifier from a MemberExpression node
1763
- * @param {*} node - The node to get the identifier from
1764
- * @returns {import('estree').Identifier | null} The identifier or null if it can't be determined
1765
- */
1766
- function getBaseIdentifier(node) {
1767
- switch (node.type) {
1768
- case AST_NODE_TYPES.Identifier: return node;
1769
- case AST_NODE_TYPES.CallExpression: return getBaseIdentifier(node.callee);
1770
- case AST_NODE_TYPES.MemberExpression: return getBaseIdentifier(node.object);
1771
- }
1772
- return null;
1773
- }
1774
- /**
1775
- * get the styles object from a makeStyles or tss.create call
1776
- * @param {import('estree').CallExpression & import('eslint').Rule.NodeParentExtension} node - The node to get the styles object from
1777
- * @returns - The styles object or null if it can't be determined
1778
- */
1779
- function getStyesObj(node) {
1780
- const isMakeStyles = node.callee.name === "makeStyles";
1781
- const isModernApi = node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.property.name === "create" && getBaseIdentifier(node.callee.object) && getBaseIdentifier(node.callee.object).name === "tss";
1782
- if (!isMakeStyles && !isModernApi) return;
1783
- const styles = (() => {
1784
- if (isMakeStyles) return node.parent.arguments[0];
1785
- if (isModernApi) return node.callee.parent.arguments[0];
1786
- })();
1787
- if (!styles) return;
1788
- switch (styles.type) {
1789
- case AST_NODE_TYPES.ObjectExpression: return styles;
1790
- case AST_NODE_TYPES.ArrowFunctionExpression:
1791
- {
1792
- const { body } = styles;
1793
- switch (body.type) {
1794
- case AST_NODE_TYPES.ObjectExpression: return body;
1795
- case AST_NODE_TYPES.BlockStatement: {
1796
- let stylesObj;
1797
- body.body.forEach((bodyNode) => {
1798
- if (bodyNode.type === AST_NODE_TYPES.ReturnStatement && bodyNode.argument.type === AST_NODE_TYPES.ObjectExpression) stylesObj = bodyNode.argument;
1799
- });
1800
- return stylesObj;
1801
- }
1802
- }
1803
- }
1804
- break;
1805
- }
1806
- }
1807
- /**
1808
- * Loop through the styles object and call the callback function for each property
1809
- * @param {import('estree').CallExpression & import('eslint').Rule.NodeParentExtension} node - The node to get the styles object from
1810
- * @param {Function} callback - The callback function to call for each property
1811
- * @returns - The styles object or null if it can't be determined
1812
- */
1813
- function loopStylesObj(node, callback) {
1814
- if (node && node.type === AST_NODE_TYPES.ObjectExpression) node.properties.forEach((property) => {
1815
- if (property.type === AST_NODE_TYPES.Property && property.value) if (property.value.type === AST_NODE_TYPES.ObjectExpression) loopStylesObj(property.value, callback);
1816
- else callback(property.value);
1817
- });
1818
- }
1819
-
1820
- //#endregion
1821
- //#region src/rules/tss-class-naming.js
1822
- const RULE_NAME$3 = "tss-class-naming";
1823
- var tss_class_naming_default = createRule({
1824
- name: RULE_NAME$3,
1825
- meta: {
1826
- type: "problem",
1827
- docs: {
1828
- description: "Enforce camelCase class names in TSS",
1829
- recommended: "recommended"
1830
- },
1831
- schema: [],
1832
- messages: { camelCase: "Class `{{ className }}` must be camelCase in TSS." }
1833
- },
1834
- defaultOptions: [],
1835
- create: function rule(context) {
1836
- return { CallExpression(node) {
1837
- const stylesObj = getStyesObj(node);
1838
- if (stylesObj === void 0) return;
1839
- stylesObj.properties.forEach((property) => {
1840
- if (property.computed) return;
1841
- if (property.type === "ExperimentalSpreadProperty" || property.type === AST_NODE_TYPES.SpreadElement) return;
1842
- const className = property.key.value || property.key.name;
1843
- if (!isCamelCase(className)) context.report({
1844
- node: property,
1845
- messageId: "camelCase",
1846
- data: { className }
1847
- });
1848
- });
1849
- } };
1850
- }
1851
- });
1852
-
1853
- //#endregion
1854
- //#region ../../node_modules/.pnpm/color-name@2.0.0/node_modules/color-name/index.js
1855
- var color_name_default = {
1856
- aliceblue: [
1857
- 240,
1858
- 248,
1859
- 255
1860
- ],
1861
- antiquewhite: [
1862
- 250,
1863
- 235,
1864
- 215
1865
- ],
1866
- aqua: [
1867
- 0,
1868
- 255,
1869
- 255
1870
- ],
1871
- aquamarine: [
1872
- 127,
1873
- 255,
1874
- 212
1875
- ],
1876
- azure: [
1877
- 240,
1878
- 255,
1879
- 255
1880
- ],
1881
- beige: [
1882
- 245,
1883
- 245,
1884
- 220
1885
- ],
1886
- bisque: [
1887
- 255,
1888
- 228,
1889
- 196
1890
- ],
1891
- black: [
1892
- 0,
1893
- 0,
1894
- 0
1895
- ],
1896
- blanchedalmond: [
1897
- 255,
1898
- 235,
1899
- 205
1900
- ],
1901
- blue: [
1902
- 0,
1903
- 0,
1904
- 255
1905
- ],
1906
- blueviolet: [
1907
- 138,
1908
- 43,
1909
- 226
1910
- ],
1911
- brown: [
1912
- 165,
1913
- 42,
1914
- 42
1915
- ],
1916
- burlywood: [
1917
- 222,
1918
- 184,
1919
- 135
1920
- ],
1921
- cadetblue: [
1922
- 95,
1923
- 158,
1924
- 160
1925
- ],
1926
- chartreuse: [
1927
- 127,
1928
- 255,
1929
- 0
1930
- ],
1931
- chocolate: [
1932
- 210,
1933
- 105,
1934
- 30
1935
- ],
1936
- coral: [
1937
- 255,
1938
- 127,
1939
- 80
1940
- ],
1941
- cornflowerblue: [
1942
- 100,
1943
- 149,
1944
- 237
1945
- ],
1946
- cornsilk: [
1947
- 255,
1948
- 248,
1949
- 220
1950
- ],
1951
- crimson: [
1952
- 220,
1953
- 20,
1954
- 60
1955
- ],
1956
- cyan: [
1957
- 0,
1958
- 255,
1959
- 255
1960
- ],
1961
- darkblue: [
1962
- 0,
1963
- 0,
1964
- 139
1965
- ],
1966
- darkcyan: [
1967
- 0,
1968
- 139,
1969
- 139
1970
- ],
1971
- darkgoldenrod: [
1972
- 184,
1973
- 134,
1974
- 11
1975
- ],
1976
- darkgray: [
1977
- 169,
1978
- 169,
1979
- 169
1980
- ],
1981
- darkgreen: [
1982
- 0,
1983
- 100,
1984
- 0
1985
- ],
1986
- darkgrey: [
1987
- 169,
1988
- 169,
1989
- 169
1990
- ],
1991
- darkkhaki: [
1992
- 189,
1993
- 183,
1994
- 107
1995
- ],
1996
- darkmagenta: [
1997
- 139,
1998
- 0,
1999
- 139
2000
- ],
2001
- darkolivegreen: [
2002
- 85,
2003
- 107,
2004
- 47
2005
- ],
2006
- darkorange: [
2007
- 255,
2008
- 140,
2009
- 0
2010
- ],
2011
- darkorchid: [
2012
- 153,
2013
- 50,
2014
- 204
2015
- ],
2016
- darkred: [
2017
- 139,
2018
- 0,
2019
- 0
2020
- ],
2021
- darksalmon: [
2022
- 233,
2023
- 150,
2024
- 122
2025
- ],
2026
- darkseagreen: [
2027
- 143,
2028
- 188,
2029
- 143
2030
- ],
2031
- darkslateblue: [
2032
- 72,
2033
- 61,
2034
- 139
2035
- ],
2036
- darkslategray: [
2037
- 47,
2038
- 79,
2039
- 79
2040
- ],
2041
- darkslategrey: [
2042
- 47,
2043
- 79,
2044
- 79
2045
- ],
2046
- darkturquoise: [
2047
- 0,
2048
- 206,
2049
- 209
2050
- ],
2051
- darkviolet: [
2052
- 148,
2053
- 0,
2054
- 211
2055
- ],
2056
- deeppink: [
2057
- 255,
2058
- 20,
2059
- 147
2060
- ],
2061
- deepskyblue: [
2062
- 0,
2063
- 191,
2064
- 255
2065
- ],
2066
- dimgray: [
2067
- 105,
2068
- 105,
2069
- 105
2070
- ],
2071
- dimgrey: [
2072
- 105,
2073
- 105,
2074
- 105
2075
- ],
2076
- dodgerblue: [
2077
- 30,
2078
- 144,
2079
- 255
2080
- ],
2081
- firebrick: [
2082
- 178,
2083
- 34,
2084
- 34
2085
- ],
2086
- floralwhite: [
2087
- 255,
2088
- 250,
2089
- 240
2090
- ],
2091
- forestgreen: [
2092
- 34,
2093
- 139,
2094
- 34
2095
- ],
2096
- fuchsia: [
2097
- 255,
2098
- 0,
2099
- 255
2100
- ],
2101
- gainsboro: [
2102
- 220,
2103
- 220,
2104
- 220
2105
- ],
2106
- ghostwhite: [
2107
- 248,
2108
- 248,
2109
- 255
2110
- ],
2111
- gold: [
2112
- 255,
2113
- 215,
2114
- 0
2115
- ],
2116
- goldenrod: [
2117
- 218,
2118
- 165,
2119
- 32
2120
- ],
2121
- gray: [
2122
- 128,
2123
- 128,
2124
- 128
2125
- ],
2126
- green: [
2127
- 0,
2128
- 128,
2129
- 0
2130
- ],
2131
- greenyellow: [
2132
- 173,
2133
- 255,
2134
- 47
2135
- ],
2136
- grey: [
2137
- 128,
2138
- 128,
2139
- 128
2140
- ],
2141
- honeydew: [
2142
- 240,
2143
- 255,
2144
- 240
2145
- ],
2146
- hotpink: [
2147
- 255,
2148
- 105,
2149
- 180
2150
- ],
2151
- indianred: [
2152
- 205,
2153
- 92,
2154
- 92
2155
- ],
2156
- indigo: [
2157
- 75,
2158
- 0,
2159
- 130
2160
- ],
2161
- ivory: [
2162
- 255,
2163
- 255,
2164
- 240
2165
- ],
2166
- khaki: [
2167
- 240,
2168
- 230,
2169
- 140
2170
- ],
2171
- lavender: [
2172
- 230,
2173
- 230,
2174
- 250
2175
- ],
2176
- lavenderblush: [
2177
- 255,
2178
- 240,
2179
- 245
2180
- ],
2181
- lawngreen: [
2182
- 124,
2183
- 252,
2184
- 0
2185
- ],
2186
- lemonchiffon: [
2187
- 255,
2188
- 250,
2189
- 205
2190
- ],
2191
- lightblue: [
2192
- 173,
2193
- 216,
2194
- 230
2195
- ],
2196
- lightcoral: [
2197
- 240,
2198
- 128,
2199
- 128
2200
- ],
2201
- lightcyan: [
2202
- 224,
2203
- 255,
2204
- 255
2205
- ],
2206
- lightgoldenrodyellow: [
2207
- 250,
2208
- 250,
2209
- 210
2210
- ],
2211
- lightgray: [
2212
- 211,
2213
- 211,
2214
- 211
2215
- ],
2216
- lightgreen: [
2217
- 144,
2218
- 238,
2219
- 144
2220
- ],
2221
- lightgrey: [
2222
- 211,
2223
- 211,
2224
- 211
2225
- ],
2226
- lightpink: [
2227
- 255,
2228
- 182,
2229
- 193
2230
- ],
2231
- lightsalmon: [
2232
- 255,
2233
- 160,
2234
- 122
2235
- ],
2236
- lightseagreen: [
2237
- 32,
2238
- 178,
2239
- 170
2240
- ],
2241
- lightskyblue: [
2242
- 135,
2243
- 206,
2244
- 250
2245
- ],
2246
- lightslategray: [
2247
- 119,
2248
- 136,
2249
- 153
2250
- ],
2251
- lightslategrey: [
2252
- 119,
2253
- 136,
2254
- 153
2255
- ],
2256
- lightsteelblue: [
2257
- 176,
2258
- 196,
2259
- 222
2260
- ],
2261
- lightyellow: [
2262
- 255,
2263
- 255,
2264
- 224
2265
- ],
2266
- lime: [
2267
- 0,
2268
- 255,
2269
- 0
2270
- ],
2271
- limegreen: [
2272
- 50,
2273
- 205,
2274
- 50
2275
- ],
2276
- linen: [
2277
- 250,
2278
- 240,
2279
- 230
2280
- ],
2281
- magenta: [
2282
- 255,
2283
- 0,
2284
- 255
2285
- ],
2286
- maroon: [
2287
- 128,
2288
- 0,
2289
- 0
2290
- ],
2291
- mediumaquamarine: [
2292
- 102,
2293
- 205,
2294
- 170
2295
- ],
2296
- mediumblue: [
2297
- 0,
2298
- 0,
2299
- 205
2300
- ],
2301
- mediumorchid: [
2302
- 186,
2303
- 85,
2304
- 211
2305
- ],
2306
- mediumpurple: [
2307
- 147,
2308
- 112,
2309
- 219
2310
- ],
2311
- mediumseagreen: [
2312
- 60,
2313
- 179,
2314
- 113
2315
- ],
2316
- mediumslateblue: [
2317
- 123,
2318
- 104,
2319
- 238
2320
- ],
2321
- mediumspringgreen: [
2322
- 0,
2323
- 250,
2324
- 154
2325
- ],
2326
- mediumturquoise: [
2327
- 72,
2328
- 209,
2329
- 204
2330
- ],
2331
- mediumvioletred: [
2332
- 199,
2333
- 21,
2334
- 133
2335
- ],
2336
- midnightblue: [
2337
- 25,
2338
- 25,
2339
- 112
2340
- ],
2341
- mintcream: [
2342
- 245,
2343
- 255,
2344
- 250
2345
- ],
2346
- mistyrose: [
2347
- 255,
2348
- 228,
2349
- 225
2350
- ],
2351
- moccasin: [
2352
- 255,
2353
- 228,
2354
- 181
2355
- ],
2356
- navajowhite: [
2357
- 255,
2358
- 222,
2359
- 173
2360
- ],
2361
- navy: [
2362
- 0,
2363
- 0,
2364
- 128
2365
- ],
2366
- oldlace: [
2367
- 253,
2368
- 245,
2369
- 230
2370
- ],
2371
- olive: [
2372
- 128,
2373
- 128,
2374
- 0
2375
- ],
2376
- olivedrab: [
2377
- 107,
2378
- 142,
2379
- 35
2380
- ],
2381
- orange: [
2382
- 255,
2383
- 165,
2384
- 0
2385
- ],
2386
- orangered: [
2387
- 255,
2388
- 69,
2389
- 0
2390
- ],
2391
- orchid: [
2392
- 218,
2393
- 112,
2394
- 214
2395
- ],
2396
- palegoldenrod: [
2397
- 238,
2398
- 232,
2399
- 170
2400
- ],
2401
- palegreen: [
2402
- 152,
2403
- 251,
2404
- 152
2405
- ],
2406
- paleturquoise: [
2407
- 175,
2408
- 238,
2409
- 238
2410
- ],
2411
- palevioletred: [
2412
- 219,
2413
- 112,
2414
- 147
2415
- ],
2416
- papayawhip: [
2417
- 255,
2418
- 239,
2419
- 213
2420
- ],
2421
- peachpuff: [
2422
- 255,
2423
- 218,
2424
- 185
2425
- ],
2426
- peru: [
2427
- 205,
2428
- 133,
2429
- 63
2430
- ],
2431
- pink: [
2432
- 255,
2433
- 192,
2434
- 203
2435
- ],
2436
- plum: [
2437
- 221,
2438
- 160,
2439
- 221
2440
- ],
2441
- powderblue: [
2442
- 176,
2443
- 224,
2444
- 230
2445
- ],
2446
- purple: [
2447
- 128,
2448
- 0,
2449
- 128
2450
- ],
2451
- rebeccapurple: [
2452
- 102,
2453
- 51,
2454
- 153
2455
- ],
2456
- red: [
2457
- 255,
2458
- 0,
2459
- 0
2460
- ],
2461
- rosybrown: [
2462
- 188,
2463
- 143,
2464
- 143
2465
- ],
2466
- royalblue: [
2467
- 65,
2468
- 105,
2469
- 225
2470
- ],
2471
- saddlebrown: [
2472
- 139,
2473
- 69,
2474
- 19
2475
- ],
2476
- salmon: [
2477
- 250,
2478
- 128,
2479
- 114
2480
- ],
2481
- sandybrown: [
2482
- 244,
2483
- 164,
2484
- 96
2485
- ],
2486
- seagreen: [
2487
- 46,
2488
- 139,
2489
- 87
2490
- ],
2491
- seashell: [
2492
- 255,
2493
- 245,
2494
- 238
2495
- ],
2496
- sienna: [
2497
- 160,
2498
- 82,
2499
- 45
2500
- ],
2501
- silver: [
2502
- 192,
2503
- 192,
2504
- 192
2505
- ],
2506
- skyblue: [
2507
- 135,
2508
- 206,
2509
- 235
2510
- ],
2511
- slateblue: [
2512
- 106,
2513
- 90,
2514
- 205
2515
- ],
2516
- slategray: [
2517
- 112,
2518
- 128,
2519
- 144
2520
- ],
2521
- slategrey: [
2522
- 112,
2523
- 128,
2524
- 144
2525
- ],
2526
- snow: [
2527
- 255,
2528
- 250,
2529
- 250
2530
- ],
2531
- springgreen: [
2532
- 0,
2533
- 255,
2534
- 127
2535
- ],
2536
- steelblue: [
2537
- 70,
2538
- 130,
2539
- 180
2540
- ],
2541
- tan: [
2542
- 210,
2543
- 180,
2544
- 140
2545
- ],
2546
- teal: [
2547
- 0,
2548
- 128,
2549
- 128
2550
- ],
2551
- thistle: [
2552
- 216,
2553
- 191,
2554
- 216
2555
- ],
2556
- tomato: [
2557
- 255,
2558
- 99,
2559
- 71
2560
- ],
2561
- turquoise: [
2562
- 64,
2563
- 224,
2564
- 208
2565
- ],
2566
- violet: [
2567
- 238,
2568
- 130,
2569
- 238
2570
- ],
2571
- wheat: [
2572
- 245,
2573
- 222,
2574
- 179
2575
- ],
2576
- white: [
2577
- 255,
2578
- 255,
2579
- 255
2580
- ],
2581
- whitesmoke: [
2582
- 245,
2583
- 245,
2584
- 245
2585
- ],
2586
- yellow: [
2587
- 255,
2588
- 255,
2589
- 0
2590
- ],
2591
- yellowgreen: [
2592
- 154,
2593
- 205,
2594
- 50
2595
- ]
2596
- };
2597
-
2598
- //#endregion
2599
- //#region src/rules/tss-no-color-name.js
2600
- const RULE_NAME$2 = "tss-no-color-name";
2601
- var tss_no_color_name_default = createRule({
2602
- name: RULE_NAME$2,
2603
- meta: {
2604
- type: "suggestion",
2605
- docs: {
2606
- description: "Enforce the use of color variables instead of color name within TSS",
2607
- recommended: "recommended"
2608
- },
2609
- schema: [],
2610
- messages: { disallowColorName: "Disallowed color name. Use color from `@mui/material/colors` or `theme.palette`." }
2611
- },
2612
- defaultOptions: [],
2613
- create: function(context) {
2614
- return { CallExpression(node) {
2615
- const stylesObj = getStyesObj(node);
2616
- if (!stylesObj) return;
2617
- function checkColorLiteral(value) {
2618
- if (value.type === AST_NODE_TYPES.Literal && typeof value.value === "string" && Object.keys(color_name_default).includes(value.value.toLowerCase())) context.report({
2619
- node: value,
2620
- messageId: "disallowColorName"
2621
- });
2622
- }
2623
- loopStylesObj(stylesObj, checkColorLiteral);
2624
- } };
2625
- }
2626
- });
2627
-
2628
- //#endregion
2629
- //#region src/rules/tss-no-color-value.js
2630
- const RULE_NAME$1 = "tss-no-color-value";
2631
- var tss_no_color_value_default = createRule({
2632
- name: RULE_NAME$1,
2633
- meta: {
2634
- type: "problem",
2635
- docs: {
2636
- description: "Enforce the use of color variables instead of color codes within TSS",
2637
- recommended: "recommended"
2638
- },
2639
- schema: [],
2640
- messages: { preferMuiColor: "Prefer use color from `@mui/material/colors` or `theme.palette`." }
2641
- },
2642
- defaultOptions: [],
2643
- create: function(context) {
2644
- return { CallExpression(node) {
2645
- const stylesObj = getStyesObj(node);
2646
- if (!stylesObj) return;
2647
- function checkColorLiteral(value) {
2648
- if (value.type === AST_NODE_TYPES.Literal && typeof value.value === "string") {
2649
- const colorCodePattern = /#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})|rgb\?\(\s*(\d{1,3}\s*,\s*){2}\d{1,3}(?:\s*,\s*\d*(?:\.\d+)?)?\s*\)/g;
2650
- if (colorCodePattern.test(value.value)) context.report({
2651
- node: value,
2652
- messageId: "preferMuiColor"
2653
- });
2654
- }
2655
- }
2656
- loopStylesObj(stylesObj, checkColorLiteral);
2657
- } };
2658
- }
2659
- });
2660
-
2661
- //#endregion
2662
- //#region src/rules/tss-unused-classes.js
2663
- const RULE_NAME = "tss-unused-classes";
2664
- var tss_unused_classes_default = createRule({
2665
- name: RULE_NAME,
2666
- meta: {
2667
- type: "suggestion",
2668
- docs: {
2669
- description: "Disallow unused classes in tss",
2670
- recommended: "recommended"
2671
- },
2672
- schema: [],
2673
- messages: { unusedClass: "Class `{{ className }}` is unused in JSX" }
2674
- },
2675
- defaultOptions: [],
2676
- create: function rule(context) {
2677
- const usedClasses = {};
2678
- const definedClasses = {};
2679
- return {
2680
- CallExpression(node) {
2681
- const stylesObj = getStyesObj(node);
2682
- if (stylesObj === void 0) return;
2683
- stylesObj.properties.forEach((property) => {
2684
- if (property.computed) return;
2685
- if (property.type === "ExperimentalSpreadProperty" || property.type === AST_NODE_TYPES.SpreadElement) return;
2686
- definedClasses[property.key.value || property.key.name] = property;
2687
- });
2688
- },
2689
- MemberExpression(node) {
2690
- if (node.object.type === AST_NODE_TYPES.Identifier && node.object.name === "classes") {
2691
- const whichClass = getBasicIdentifier(node.property);
2692
- if (whichClass) usedClasses[whichClass] = true;
2693
- return;
2694
- }
2695
- const classIdentifier = getBasicIdentifier(node.property);
2696
- if (!classIdentifier) return;
2697
- if (classIdentifier !== "classes") return;
2698
- const { parent } = node;
2699
- if (parent.type !== AST_NODE_TYPES.MemberExpression) return;
2700
- if (node.object.object && node.object.object.type !== AST_NODE_TYPES.ThisExpression) return;
2701
- const propsIdentifier = getBasicIdentifier(parent.object);
2702
- if (propsIdentifier && propsIdentifier !== "props") return;
2703
- if (!propsIdentifier && parent.object.type !== AST_NODE_TYPES.MemberExpression) return;
2704
- if (parent.parent.type === AST_NODE_TYPES.MemberExpression) return;
2705
- const parentClassIdentifier = getBasicIdentifier(parent.property);
2706
- if (parentClassIdentifier) usedClasses[parentClassIdentifier] = true;
2707
- },
2708
- "Program:exit": () => {
2709
- Object.keys(definedClasses).forEach((definedClassKey) => {
2710
- if (!usedClasses[definedClassKey]) context.report({
2711
- node: definedClasses[definedClassKey],
2712
- messageId: "unusedClass",
2713
- data: { className: definedClassKey }
2714
- });
2715
- });
2716
- }
2717
- };
2718
- }
2719
- });
2720
-
2721
- //#endregion
2722
- //#region src/rules/index.js
2723
- const rules = {
2724
- [RULE_NAME$18]: enforce_mui_icon_alias_default,
2725
- [RULE_NAME$17]: import_monorepo_default,
2726
- [RULE_NAME$16]: intl_id_missing_default,
2727
- [RULE_NAME$15]: intl_id_naming_default,
2728
- [RULE_NAME$14]: intl_id_prefix_default,
2729
- [RULE_NAME$13]: intl_id_unused_default,
2730
- [RULE_NAME$12]: intl_no_default_default,
2731
- [RULE_NAME$11]: no_async_array_methods_default,
2732
- [RULE_NAME$10]: no_extends_error_default,
2733
- [RULE_NAME$9]: no_import_css_default,
2734
- [RULE_NAME$8]: no_then_catch_finally_default,
2735
- [RULE_NAME$7]: no_unnecessary_template_literals_default,
2736
- [RULE_NAME$6]: react_better_exhaustive_deps_default,
2737
- [RULE_NAME$5]: react_hook_use_ref_default,
2738
- [RULE_NAME$4]: react_prefer_sx_prop_default,
2739
- [RULE_NAME$3]: tss_class_naming_default,
2740
- [RULE_NAME$2]: tss_no_color_name_default,
2741
- [RULE_NAME$1]: tss_no_color_value_default,
2742
- [RULE_NAME]: tss_unused_classes_default
2743
- };
2744
-
2745
- //#endregion
2746
- //#region src/index.js
2747
- const plugin = {
2748
- rules: {},
2749
- configs: { recommended: {
2750
- plugins: ["@agilebot"],
2751
- rules: {},
2752
- settings: { react: { version: "18.0.0" } }
2753
- } }
2754
- };
2755
- Object.keys(rules).forEach((name) => {
2756
- const rule = rules[name];
2757
- plugin.rules[name] = rule;
2758
- if (rule.meta && rule.meta.docs && rule.meta.docs.recommended) plugin.configs.recommended.rules[`@agilebot/${name}`] = rule.meta.type === "problem" ? "error" : "warn";
2759
- });
2760
- var src_default = plugin;
2761
-
2762
- //#endregion
2763
- export { src_default as default };