@agilebot/eslint-plugin 0.3.9 → 0.3.11

Sign up to get free protection for your applications and to get access to all the features.
package/LICENSE CHANGED
@@ -1,9 +1,9 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2024 Agilebot, Inc.
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
-
7
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
-
9
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Agilebot, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/LICENSE.tpl CHANGED
@@ -1,8 +1,8 @@
1
- /**
2
- * @license %(name)s v%(version)s
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
- */
1
+ /**
2
+ * @license %(name)s v%(version)s
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
+ */
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
- # @agilebot/eslint-plugin
2
-
3
- Agilebot extended ESLint rules. For @agilebot/eslint-config.
4
-
5
- ### Usage
6
-
7
- ```bash
8
- npm install --save-dev eslint @agilebot/eslint-plugin
9
- ```
1
+ # @agilebot/eslint-plugin
2
+
3
+ Agilebot extended ESLint rules. For @agilebot/eslint-config.
4
+
5
+ ### Usage
6
+
7
+ ```bash
8
+ npm install --save-dev eslint @agilebot/eslint-plugin
9
+ ```
package/dist/index.js CHANGED
@@ -1,17 +1,16 @@
1
- /**
2
- * @license @agilebot/eslint-plugin v0.3.9
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
- */
1
+ /**
2
+ * @license @agilebot/eslint-plugin v0.3.11
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
9
  'use strict';
10
10
 
11
11
  var eslintUtils = require('@agilebot/eslint-utils');
12
12
  var fs = require('node:fs');
13
13
  var path = require('node:path');
14
- var utils = require('@typescript-eslint/utils');
15
14
 
16
15
  function _interopNamespaceDefault(e) {
17
16
  var n = Object.create(null);
@@ -33,12 +32,12 @@ function _interopNamespaceDefault(e) {
33
32
  var fs__namespace = /*#__PURE__*/_interopNamespaceDefault(fs);
34
33
  var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
35
34
 
36
- var enforceMuiIconAlias = {
35
+ var enforceMuiIconAlias = eslintUtils.createESLintRule({
37
36
  meta: {
38
37
  type: 'problem',
39
38
  docs: {
40
39
  description: 'Enforce alias for @mui/icons-material imports',
41
- recommended: true
40
+ recommended: 'recommended'
42
41
  },
43
42
  fixable: 'code',
44
43
  schema: [],
@@ -46,6 +45,7 @@ var enforceMuiIconAlias = {
46
45
  iconAlias: 'Import for {{ name }} should be aliased.'
47
46
  }
48
47
  },
48
+ defaultOptions: [],
49
49
  create(context) {
50
50
  return {
51
51
  ImportDeclaration(node) {
@@ -70,124 +70,7 @@ var enforceMuiIconAlias = {
70
70
  }
71
71
  };
72
72
  }
73
- };
74
-
75
- var funcNaming = {
76
- meta: {
77
- docs: {
78
- description: 'Enforce function naming convention'
79
- },
80
- schema: [{
81
- type: 'object',
82
- properties: {
83
- format: {
84
- "enum": ['camelCase', 'PascalCase']
85
- }
86
- }
87
- }],
88
- messages: {
89
- invalidFuncNaming: 'Invalid function naming for non-React component, expected {{format}}',
90
- invalidReactFCNaming: 'Invalid naming convention for React functional component, expected PascalCase'
91
- }
92
- },
93
- create(context) {
94
- if (!context.options[0]) {
95
- throw new Error('Missing options');
96
- }
97
- const format = context.options[0].format;
98
- function validate({
99
- node,
100
- fnName
101
- }) {
102
- let isPass;
103
- switch (format) {
104
- case 'camelCase':
105
- if (!eslintUtils.isCamelCase(fnName)) {
106
- isPass = false;
107
- }
108
- break;
109
- case 'PascalCase':
110
- if (!eslintUtils.isPascalCase(fnName)) {
111
- isPass = false;
112
- }
113
- break;
114
- }
115
- if (isPass === false) {
116
- context.report({
117
- node: node,
118
- messageId: 'invalidFuncNaming',
119
- data: {
120
- format
121
- }
122
- });
123
- }
124
- }
125
- function checkJSXElement(node) {
126
- if (!node) {
127
- return false;
128
- }
129
- if (node.type === 'JSXElement' || node.type === 'JSXFragment') {
130
- return true;
131
- }
132
- if (node.type === 'BlockStatement') {
133
- for (const statement of node.body) {
134
- if (statement.type === 'ReturnStatement') {
135
- if (checkJSXElement(statement.argument)) {
136
- return true;
137
- }
138
- } else if (checkJSXElement(statement)) {
139
- return true;
140
- }
141
- }
142
- }
143
- if (node.type === 'ArrowFunctionExpression' || node.type === 'FunctionExpression') {
144
- return checkJSXElement(node.body);
145
- }
146
- return false;
147
- }
148
- return {
149
- MethodDefinition(node) {
150
- if (node.kind === 'method') ;
151
- },
152
- FunctionDeclaration(node) {
153
- if (node.type === 'FunctionDeclaration' && node.id) {
154
- const fnName = node.id.name;
155
- const isReactComponent = checkJSXElement(node.body);
156
- if (!isReactComponent) {
157
- validate({
158
- node,
159
- fnName
160
- });
161
- }
162
- }
163
- },
164
- VariableDeclarator(node) {
165
- if (node.id && node.init && ['FunctionExpression', 'ArrowFunctionExpression'].includes(node.init.type)) {
166
- const fnName = node.id.name;
167
- let isReactComponent = checkJSXElement(node.init.body);
168
- if (node.id.typeAnnotation && node.id.typeAnnotation.typeAnnotation) {
169
- const typeAnnotation = node.id.typeAnnotation.typeAnnotation;
170
- if (typeAnnotation.type === 'TSTypeReference' && typeAnnotation.typeName.type === 'Identifier') {
171
- const typeName = typeAnnotation.typeName.name;
172
- if (['FC', 'React.FC', 'FunctionComponent', 'React.FunctionComponent'].includes(typeName)) {
173
- isReactComponent = true;
174
- }
175
- }
176
- }
177
- if (!isReactComponent) {
178
- validate({
179
- node,
180
- fnName
181
- });
182
- }
183
- }
184
- },
185
- Property(node) {
186
- if (node.value.type === 'FunctionExpression') ;
187
- }
188
- };
189
- }
190
- };
73
+ });
191
74
 
192
75
  function getSetting(context, name) {
193
76
  return context.settings["agilebot/".concat(name)];
@@ -202,12 +85,12 @@ function warnOnce(message) {
202
85
  console.warn("Warning: ".concat(message));
203
86
  }
204
87
 
205
- var importMonorepo = {
88
+ var importMonorepo = eslintUtils.createESLintRule({
206
89
  meta: {
207
90
  type: 'problem',
208
91
  docs: {
209
92
  description: 'Enforce import styles for monorepo',
210
- recommended: true
93
+ recommended: 'recommended'
211
94
  },
212
95
  fixable: 'code',
213
96
  schema: [],
@@ -215,6 +98,7 @@ var importMonorepo = {
215
98
  monorepoImport: 'Import for {{ module }} should not contains src folder.'
216
99
  }
217
100
  },
101
+ defaultOptions: [],
218
102
  create(context) {
219
103
  return {
220
104
  ImportDeclaration(node) {
@@ -247,7 +131,7 @@ var importMonorepo = {
247
131
  }
248
132
  };
249
133
  }
250
- };
134
+ });
251
135
 
252
136
  function findFormatMessageAttrNode(node, attrName) {
253
137
  if (node.type === 'CallExpression' && (node.callee.name === 'formatMessage' || node.callee.name === '$t') && node.arguments.length > 0 && node.arguments[0].properties) {
@@ -317,13 +201,12 @@ function getIntlIds(context) {
317
201
  return results;
318
202
  }
319
203
 
320
- var intlIdMissing = {
204
+ var intlIdMissing = eslintUtils.createESLintRule({
321
205
  meta: {
206
+ type: 'problem',
322
207
  docs: {
323
- description: 'Validates intl message ids are in locale file',
324
- category: 'Intl'
208
+ description: 'Validates intl message ids are in locale file'
325
209
  },
326
- fixable: undefined,
327
210
  schema: [],
328
211
  messages: {
329
212
  missingId: 'Missing id: {{value}}',
@@ -331,6 +214,7 @@ var intlIdMissing = {
331
214
  disallowInvoke: 'Do not invoke intl by {{value}}'
332
215
  }
333
216
  },
217
+ defaultOptions: [],
334
218
  create: function (context) {
335
219
  const translatedIds = getIntlIds(context);
336
220
  const translatedIdSet = new Set(translatedIds);
@@ -403,19 +287,19 @@ var intlIdMissing = {
403
287
  }
404
288
  };
405
289
  }
406
- };
290
+ });
407
291
 
408
- var intlIdNaming = {
292
+ var intlIdNaming = eslintUtils.createESLintRule({
409
293
  meta: {
294
+ type: 'problem',
410
295
  docs: {
411
- description: 'Validates intl message ids naming convention',
412
- category: 'Intl'
296
+ description: 'Validates intl message ids naming convention'
413
297
  },
414
- fixable: undefined,
415
298
  schema: [{
416
299
  type: 'object',
417
300
  properties: {
418
301
  format: {
302
+ type: 'string',
419
303
  "enum": ['camelCase', 'PascalCase']
420
304
  }
421
305
  }
@@ -424,6 +308,7 @@ var intlIdNaming = {
424
308
  invalidIdNaming: "Invalid id naming, expected {{format}}"
425
309
  }
426
310
  },
311
+ defaultOptions: [],
427
312
  create: function (context) {
428
313
  if (!context.options[0]) {
429
314
  throw new Error('Missing options');
@@ -495,15 +380,14 @@ var intlIdNaming = {
495
380
  }
496
381
  };
497
382
  }
498
- };
383
+ });
499
384
 
500
- var intlIdPrefix = {
385
+ var intlIdPrefix = eslintUtils.createESLintRule({
501
386
  meta: {
387
+ type: 'problem',
502
388
  docs: {
503
- description: 'Validates intl message ids has correct prefixes',
504
- category: 'Intl'
389
+ description: 'Validates intl message ids has correct prefixes'
505
390
  },
506
- fixable: undefined,
507
391
  schema: [{
508
392
  type: 'array',
509
393
  items: {
@@ -514,6 +398,7 @@ var intlIdPrefix = {
514
398
  invalidIdPrefix: 'Invalid id prefix: {{value}}'
515
399
  }
516
400
  },
401
+ defaultOptions: [],
517
402
  create: function (context) {
518
403
  if (context.options[0].length === 0) {
519
404
  throw new Error('Prefixes are required in settings');
@@ -569,18 +454,19 @@ var intlIdPrefix = {
569
454
  }
570
455
  };
571
456
  }
572
- };
457
+ });
573
458
 
574
459
  const usedIds = new Map();
575
- var intlIdUnused = {
460
+ var intlIdUnused = eslintUtils.createESLintRule({
576
461
  meta: {
462
+ type: 'problem',
577
463
  docs: {
578
- description: 'Finds unused intl message ids in locale file',
579
- category: 'Intl'
464
+ description: 'Finds unused intl message ids in locale file'
580
465
  },
581
- fixable: undefined,
582
- schema: []
466
+ schema: [],
467
+ messages: {}
583
468
  },
469
+ defaultOptions: [],
584
470
  create: function (context) {
585
471
  const projectRoot = getSetting(context, 'project-root');
586
472
  if (!projectRoot) {
@@ -645,20 +531,20 @@ var intlIdUnused = {
645
531
  }
646
532
  };
647
533
  }
648
- };
534
+ });
649
535
 
650
- var intlNoDefault = {
536
+ var intlNoDefault = eslintUtils.createESLintRule({
651
537
  meta: {
538
+ type: 'problem',
652
539
  docs: {
653
- description: 'Validates defaultMessage is not used with react-intl',
654
- category: 'Intl'
540
+ description: 'Validates defaultMessage is not used with react-intl'
655
541
  },
656
- fixable: undefined,
657
542
  schema: [],
658
543
  messages: {
659
544
  noDefaultMessage: 'Do not use defaultMessage'
660
545
  }
661
546
  },
547
+ defaultOptions: [],
662
548
  create: function (context) {
663
549
  function processAttrNode(node) {
664
550
  context.report({
@@ -687,21 +573,21 @@ var intlNoDefault = {
687
573
  }
688
574
  };
689
575
  }
690
- };
576
+ });
691
577
 
692
- var noAsyncArrayMethods = {
578
+ var noAsyncArrayMethods = eslintUtils.createESLintRule({
693
579
  meta: {
580
+ type: 'problem',
694
581
  docs: {
695
582
  description: 'No async callback for Array methods forEach, map, filter, reduce, some, every, etc.',
696
- category: 'Array',
697
- recommended: true
583
+ recommended: 'recommended'
698
584
  },
699
- fixable: undefined,
700
585
  schema: [],
701
586
  messages: {
702
587
  noAsyncArrayMethods: "No async function in method '{{ methodName }}'"
703
588
  }
704
589
  },
590
+ defaultOptions: [],
705
591
  create: function (context) {
706
592
  return {
707
593
  ExpressionStatement: function (node) {
@@ -729,21 +615,21 @@ var noAsyncArrayMethods = {
729
615
  }
730
616
  };
731
617
  }
732
- };
618
+ });
733
619
 
734
- var noImportCss = {
620
+ var noImportCss = eslintUtils.createESLintRule({
735
621
  meta: {
736
622
  type: 'problem',
737
623
  docs: {
738
624
  description: 'Prevent importing CSS',
739
- recommended: true
625
+ recommended: 'recommended'
740
626
  },
741
- fixable: 'code',
742
627
  schema: [],
743
628
  messages: {
744
629
  noImportCSS: 'Do not import CSS files. Use CSS-in-JS instead.'
745
630
  }
746
631
  },
632
+ defaultOptions: [],
747
633
  create(context) {
748
634
  return {
749
635
  ImportDeclaration(node) {
@@ -760,9 +646,9 @@ var noImportCss = {
760
646
  }
761
647
  };
762
648
  }
763
- };
649
+ });
764
650
 
765
- var noThenCatchFinally = {
651
+ var noThenCatchFinally = eslintUtils.createESLintRule({
766
652
  meta: {
767
653
  type: 'suggestion',
768
654
  docs: {
@@ -784,6 +670,7 @@ var noThenCatchFinally = {
784
670
  forbiddenThenCatchFinally: "then()/catch()/finally() is forbidden when invoke {{ name }}()."
785
671
  }
786
672
  },
673
+ defaultOptions: [],
787
674
  create(context) {
788
675
  const configuration = context.options[0] || {};
789
676
  const restrictedFunctions = configuration.restrictedFunctions || [];
@@ -814,14 +701,14 @@ var noThenCatchFinally = {
814
701
  }
815
702
  };
816
703
  }
817
- };
704
+ });
818
705
 
819
- var noUnnecessaryTemplateLiterals = {
706
+ var noUnnecessaryTemplateLiterals = eslintUtils.createESLintRule({
820
707
  meta: {
821
708
  type: 'problem',
822
709
  docs: {
823
710
  description: 'Check if a template string contains only one ${}',
824
- recommended: true
711
+ recommended: 'recommended'
825
712
  },
826
713
  fixable: 'code',
827
714
  schema: [],
@@ -829,6 +716,7 @@ var noUnnecessaryTemplateLiterals = {
829
716
  unnecessaryTemplateString: 'Unnecessary template string with only one ${}.'
830
717
  }
831
718
  },
719
+ defaultOptions: [],
832
720
  create(context) {
833
721
  return {
834
722
  TemplateLiteral(node) {
@@ -845,7 +733,7 @@ var noUnnecessaryTemplateLiterals = {
845
733
  }
846
734
  };
847
735
  }
848
- };
736
+ });
849
737
 
850
738
  var reactBetterExhaustiveDeps = {
851
739
  meta: {
@@ -2039,19 +1927,20 @@ function isAncestorNodeOf(a, b) {
2039
1927
  }
2040
1928
 
2041
1929
  const Components = require('eslint-plugin-react/lib/util/Components');
2042
- var reactHookUseRef = {
1930
+ var reactHookUseRef = eslintUtils.createESLintRule({
2043
1931
  meta: {
1932
+ type: 'suggestion',
2044
1933
  docs: {
2045
1934
  description: 'Ensure naming of useRef hook value.',
2046
- recommended: true
1935
+ recommended: 'recommended'
2047
1936
  },
2048
1937
  schema: [],
2049
- type: 'suggestion',
2050
1938
  hasSuggestions: true,
2051
1939
  messages: {
2052
1940
  useRefName: 'useRef call is not end with "Ref"'
2053
1941
  }
2054
1942
  },
1943
+ defaultOptions: [],
2055
1944
  create: Components.detect((context, component, util) => ({
2056
1945
  CallExpression(node) {
2057
1946
  const isImmediateReturn = node.parent && node.parent.type === 'ReturnStatement';
@@ -2070,7 +1959,7 @@ var reactHookUseRef = {
2070
1959
  }
2071
1960
  }
2072
1961
  }))
2073
- };
1962
+ });
2074
1963
 
2075
1964
  function* updateImportStatement(context, fixer, key) {
2076
1965
  const sourceCode = context.sourceCode;
@@ -2089,10 +1978,9 @@ function* updateImportStatement(context, fixer, key) {
2089
1978
  }
2090
1979
  yield fixer.insertTextAfter([...importNode.specifiers].pop(), ", ".concat(key));
2091
1980
  }
2092
- var reactPreferNamedPropertyAccess = utils.ESLintUtils.RuleCreator.withoutDocs({
2093
- defaultOptions: [],
1981
+ var reactPreferNamedPropertyAccess = eslintUtils.createESLintRule({
2094
1982
  meta: {
2095
- type: 'layout',
1983
+ type: 'problem',
2096
1984
  fixable: 'code',
2097
1985
  docs: {
2098
1986
  description: 'Enforce importing each member of React namespace separately instead of accessing them through React namespace',
@@ -2104,6 +1992,7 @@ var reactPreferNamedPropertyAccess = utils.ESLintUtils.RuleCreator.withoutDocs({
2104
1992
  },
2105
1993
  schema: []
2106
1994
  },
1995
+ defaultOptions: [],
2107
1996
  create(context) {
2108
1997
  return {
2109
1998
  TSQualifiedName(node) {
@@ -2155,12 +2044,12 @@ var reactPreferNamedPropertyAccess = utils.ESLintUtils.RuleCreator.withoutDocs({
2155
2044
  }
2156
2045
  });
2157
2046
 
2158
- var reactPreferSxProp = {
2047
+ var reactPreferSxProp = eslintUtils.createESLintRule({
2159
2048
  meta: {
2049
+ type: 'problem',
2160
2050
  docs: {
2161
2051
  description: 'Prefer using sx prop instead of inline styles',
2162
- category: 'Best Practices',
2163
- recommended: true
2052
+ recommended: 'recommended'
2164
2053
  },
2165
2054
  messages: {
2166
2055
  preferSxProp: 'Avoid using inline styles, use sx prop or tss-react or styled-component instead'
@@ -2178,6 +2067,7 @@ var reactPreferSxProp = {
2178
2067
  }
2179
2068
  }]
2180
2069
  },
2070
+ defaultOptions: [],
2181
2071
  create(context) {
2182
2072
  const configuration = context.options[0] || {};
2183
2073
  const allowedFor = configuration.allowedFor || [];
@@ -2222,7 +2112,7 @@ var reactPreferSxProp = {
2222
2112
  }
2223
2113
  };
2224
2114
  }
2225
- };
2115
+ });
2226
2116
 
2227
2117
  function getBasicIdentifier(node) {
2228
2118
  if (node.type === 'Identifier') {
@@ -2313,18 +2203,19 @@ function loopStylesObj(node, callback) {
2313
2203
  }
2314
2204
  }
2315
2205
 
2316
- var tssClassNaming = {
2206
+ var tssClassNaming = eslintUtils.createESLintRule({
2317
2207
  meta: {
2318
2208
  type: 'problem',
2319
2209
  docs: {
2320
2210
  description: 'Enforce camelCase class names in TSS',
2321
- category: 'Best Practices',
2322
- recommended: true
2211
+ recommended: 'recommended'
2323
2212
  },
2213
+ schema: [],
2324
2214
  messages: {
2325
2215
  camelCase: 'Class `{{ className }}` must be camelCase in TSS.'
2326
2216
  }
2327
2217
  },
2218
+ defaultOptions: [],
2328
2219
  create: function rule(context) {
2329
2220
  return {
2330
2221
  CallExpression(node) {
@@ -2353,7 +2244,7 @@ var tssClassNaming = {
2353
2244
  }
2354
2245
  };
2355
2246
  }
2356
- };
2247
+ });
2357
2248
 
2358
2249
  var colors = {
2359
2250
  aliceblue: [240, 248, 255],
@@ -2506,17 +2397,19 @@ var colors = {
2506
2397
  yellowgreen: [154, 205, 50]
2507
2398
  };
2508
2399
 
2509
- var tssNoColorName = {
2400
+ var tssNoColorName = eslintUtils.createESLintRule({
2510
2401
  meta: {
2511
2402
  type: 'suggestion',
2512
2403
  docs: {
2513
2404
  description: 'Enforce the use of color variables instead of color name within TSS',
2514
- recommended: true
2405
+ recommended: 'recommended'
2515
2406
  },
2407
+ schema: [],
2516
2408
  messages: {
2517
2409
  disallowColorName: 'Disallowed color name. Use color from `@mui/material/colors` or `theme.palette`.'
2518
2410
  }
2519
2411
  },
2412
+ defaultOptions: [],
2520
2413
  create: function (context) {
2521
2414
  return {
2522
2415
  CallExpression(node) {
@@ -2536,19 +2429,21 @@ var tssNoColorName = {
2536
2429
  }
2537
2430
  };
2538
2431
  }
2539
- };
2432
+ });
2540
2433
 
2541
- var tssNoColorValue = {
2434
+ var tssNoColorValue = eslintUtils.createESLintRule({
2542
2435
  meta: {
2543
2436
  type: 'problem',
2544
2437
  docs: {
2545
2438
  description: 'Enforce the use of color variables instead of color codes within TSS',
2546
- recommended: true
2439
+ recommended: 'recommended'
2547
2440
  },
2441
+ schema: [],
2548
2442
  messages: {
2549
2443
  preferMuiColor: 'Prefer use color from `@mui/material/colors` or `theme.palette`.'
2550
2444
  }
2551
2445
  },
2446
+ defaultOptions: [],
2552
2447
  create: function (context) {
2553
2448
  return {
2554
2449
  CallExpression(node) {
@@ -2571,20 +2466,21 @@ var tssNoColorValue = {
2571
2466
  }
2572
2467
  };
2573
2468
  }
2574
- };
2469
+ });
2575
2470
 
2576
- var tssUnusedClasses = {
2471
+ var tssUnusedClasses = eslintUtils.createESLintRule({
2577
2472
  meta: {
2578
2473
  type: 'suggestion',
2579
2474
  docs: {
2580
2475
  description: 'Disallow unused classes in tss',
2581
- category: 'Best Practices',
2582
- recommended: true
2476
+ recommended: 'recommended'
2583
2477
  },
2478
+ schema: [],
2584
2479
  messages: {
2585
2480
  unusedClass: 'Class `{{ className }}` is unused in JSX'
2586
2481
  }
2587
2482
  },
2483
+ defaultOptions: [],
2588
2484
  create: function rule(context) {
2589
2485
  const usedClasses = {};
2590
2486
  const definedClasses = {};
@@ -2660,12 +2556,208 @@ var tssUnusedClasses = {
2660
2556
  }
2661
2557
  };
2662
2558
  }
2663
- };
2559
+ });
2560
+
2561
+ const reactGlobalFuncs = new Set(['createContext', 'forwardRef', 'lazy', 'memo', 'combineProviders']);
2562
+ const reactFCTypes = new Set(['FC', 'FunctionComponent', 'VFC', 'VoidFunctionComponent']);
2563
+ const otherTypes = new Set(['StoryObj', 'StoryFn']);
2564
+ const defaultExcludes = ['^(__dirname|__filename)$', '(.*)Event$'];
2565
+ var varNaming = eslintUtils.createESLintRule({
2566
+ meta: {
2567
+ type: 'problem',
2568
+ docs: {
2569
+ description: 'Enforce variable and function naming convention',
2570
+ recommended: 'recommended'
2571
+ },
2572
+ schema: [{
2573
+ type: 'object',
2574
+ properties: {
2575
+ funcFormat: {
2576
+ type: 'array',
2577
+ items: {
2578
+ "enum": ['camelCase', 'PascalCase', 'UPPER_CASE']
2579
+ }
2580
+ },
2581
+ varFormat: {
2582
+ type: 'array',
2583
+ items: {
2584
+ "enum": ['camelCase', 'PascalCase', 'UPPER_CASE']
2585
+ }
2586
+ },
2587
+ exclude: {
2588
+ type: 'array',
2589
+ items: {
2590
+ type: 'string'
2591
+ }
2592
+ }
2593
+ }
2594
+ }],
2595
+ messages: {
2596
+ invalidFuncNaming: 'Invalid function naming for non-React component, expected {{formats}}',
2597
+ invalidReactFCNaming: 'Invalid naming convention for React functional component, expected PascalCase',
2598
+ invalidVarNaming: 'Invalid variable naming, expected {{formats}}'
2599
+ }
2600
+ },
2601
+ defaultOptions: [],
2602
+ create(context) {
2603
+ if (!context.options[0]) {
2604
+ context.options[0] = {};
2605
+ }
2606
+ const funcFormat = context.options[0].funcFormat || ['camelCase'];
2607
+ const varFormat = context.options[0].varFormat || ['camelCase', 'UPPER_CASE'];
2608
+ let exclude = context.options[0].exclude || [];
2609
+ exclude = [...defaultExcludes, ...exclude];
2610
+ function validate(type, {
2611
+ node,
2612
+ name
2613
+ }) {
2614
+ let isPass = false;
2615
+ let formats;
2616
+ let messageId;
2617
+ switch (type) {
2618
+ case 'func':
2619
+ formats = funcFormat;
2620
+ messageId = 'invalidFuncNaming';
2621
+ break;
2622
+ case 'var':
2623
+ formats = varFormat;
2624
+ messageId = 'invalidVarNaming';
2625
+ break;
2626
+ }
2627
+ for (const format of formats) {
2628
+ switch (format) {
2629
+ case 'camelCase':
2630
+ if (eslintUtils.isCamelCase(name)) {
2631
+ isPass = true;
2632
+ }
2633
+ break;
2634
+ case 'PascalCase':
2635
+ if (eslintUtils.isPascalCase(name)) {
2636
+ isPass = true;
2637
+ }
2638
+ break;
2639
+ case 'UPPER_CASE':
2640
+ if (eslintUtils.isUpperCase(name)) {
2641
+ isPass = true;
2642
+ }
2643
+ break;
2644
+ }
2645
+ }
2646
+ if (!isPass) {
2647
+ context.report({
2648
+ node: node,
2649
+ messageId: messageId,
2650
+ data: {
2651
+ formats: formats.join(', ')
2652
+ }
2653
+ });
2654
+ }
2655
+ }
2656
+ function checkJSXElement(node) {
2657
+ if (!node) {
2658
+ return false;
2659
+ }
2660
+ if (node.type === 'JSXElement' || node.type === 'JSXFragment') {
2661
+ return true;
2662
+ }
2663
+ if (node.type === 'BlockStatement') {
2664
+ for (const statement of node.body) {
2665
+ if (statement.type === 'ReturnStatement') {
2666
+ if (checkJSXElement(statement.argument)) {
2667
+ return true;
2668
+ }
2669
+ } else if (checkJSXElement(statement)) {
2670
+ return true;
2671
+ }
2672
+ }
2673
+ }
2674
+ if (node.type === 'ArrowFunctionExpression' || node.type === 'FunctionExpression') {
2675
+ return checkJSXElement(node.body);
2676
+ }
2677
+ return false;
2678
+ }
2679
+ return {
2680
+ FunctionDeclaration(node) {
2681
+ if (node.type === 'FunctionDeclaration' && node.id) {
2682
+ const fnName = node.id.name;
2683
+ const isReactComponent = checkJSXElement(node.body);
2684
+ if (!isReactComponent) {
2685
+ validate('func', {
2686
+ node,
2687
+ name: fnName
2688
+ });
2689
+ }
2690
+ }
2691
+ },
2692
+ VariableDeclarator(node) {
2693
+ if (node.id && node.init && ['FunctionExpression', 'ArrowFunctionExpression'].includes(node.init.type)) {
2694
+ const fnName = node.id.name;
2695
+ let isReactComponent = checkJSXElement(node.init.body);
2696
+ if (node.id.typeAnnotation && node.id.typeAnnotation.typeAnnotation) {
2697
+ const typeAnnotation = node.id.typeAnnotation.typeAnnotation;
2698
+ if (typeAnnotation.type === 'TSTypeReference' && typeAnnotation.typeName.type === 'Identifier') {
2699
+ const typeName = typeAnnotation.typeName.name;
2700
+ const typeNameLast = typeName.split('.').pop();
2701
+ if (reactFCTypes.has(typeNameLast)) {
2702
+ isReactComponent = true;
2703
+ }
2704
+ }
2705
+ }
2706
+ if (!isReactComponent) {
2707
+ validate('func', {
2708
+ node,
2709
+ name: fnName
2710
+ });
2711
+ }
2712
+ } else if (node.id) {
2713
+ const varName = node.id.name;
2714
+ for (const excludeRegex of exclude) {
2715
+ if (new RegExp(excludeRegex).test(varName)) {
2716
+ return;
2717
+ }
2718
+ }
2719
+ if (node.id.typeAnnotation && node.id.typeAnnotation.typeAnnotation) {
2720
+ const typeAnnotation = node.id.typeAnnotation.typeAnnotation;
2721
+ if (typeAnnotation.type === 'TSTypeReference' && typeAnnotation.typeName.type === 'Identifier') {
2722
+ const typeName = typeAnnotation.typeName.name;
2723
+ const typeNameLast = typeName.split('.').pop();
2724
+ if (otherTypes.has(typeNameLast)) {
2725
+ return;
2726
+ }
2727
+ }
2728
+ }
2729
+ if (node.id.parent && node.id.parent.init) {
2730
+ let calleeName;
2731
+ let shouldCheckReact = false;
2732
+ if (node.id.parent.init.type === 'CallExpression') {
2733
+ shouldCheckReact = true;
2734
+ calleeName = node.id.parent.init.callee.name;
2735
+ } else if (node.id.parent.init.type === 'TSAsExpression' && node.id.parent.init.expression && node.id.parent.init.expression.callee) {
2736
+ shouldCheckReact = true;
2737
+ calleeName = node.id.parent.init.expression.callee.name;
2738
+ }
2739
+ if (shouldCheckReact) {
2740
+ if (!calleeName) {
2741
+ return;
2742
+ }
2743
+ if (reactGlobalFuncs.has(calleeName) || reactGlobalFuncs.has(calleeName.split('.').pop())) {
2744
+ return;
2745
+ }
2746
+ }
2747
+ }
2748
+ validate('var', {
2749
+ node,
2750
+ name: varName
2751
+ });
2752
+ }
2753
+ }
2754
+ };
2755
+ }
2756
+ });
2664
2757
 
2665
2758
  var ruleFiles = /*#__PURE__*/Object.freeze({
2666
2759
  __proto__: null,
2667
2760
  rules_enforce_mui_icon_alias: enforceMuiIconAlias,
2668
- rules_func_naming: funcNaming,
2669
2761
  rules_import_monorepo: importMonorepo,
2670
2762
  rules_intl_id_missing: intlIdMissing,
2671
2763
  rules_intl_id_naming: intlIdNaming,
@@ -2683,7 +2775,8 @@ var ruleFiles = /*#__PURE__*/Object.freeze({
2683
2775
  rules_tss_class_naming: tssClassNaming,
2684
2776
  rules_tss_no_color_name: tssNoColorName,
2685
2777
  rules_tss_no_color_value: tssNoColorValue,
2686
- rules_tss_unused_classes: tssUnusedClasses
2778
+ rules_tss_unused_classes: tssUnusedClasses,
2779
+ rules_var_naming: varNaming
2687
2780
  });
2688
2781
 
2689
2782
  const plugin = {
@@ -2706,7 +2799,7 @@ Object.keys(ruleFiles).forEach(key => {
2706
2799
  const rule = ruleFiles[key];
2707
2800
  plugin.rules[finalKey] = rule;
2708
2801
  if (rule.meta && rule.meta.docs && rule.meta.docs.recommended) {
2709
- plugin.configs.recommended.rules["@agilebot/".concat(finalKey)] = rule.meta.type === 'suggestion' ? 'warn' : 'error';
2802
+ plugin.configs.recommended.rules["@agilebot/".concat(finalKey)] = rule.meta.type === 'problem' ? 'error' : 'warn';
2710
2803
  }
2711
2804
  });
2712
2805
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agilebot/eslint-plugin",
3
- "version": "0.3.9",
3
+ "version": "0.3.11",
4
4
  "description": "Agilebot's ESLint plugin",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -18,9 +18,8 @@
18
18
  "node": "^18.18.0 || >=20.0.0"
19
19
  },
20
20
  "dependencies": {
21
- "@typescript-eslint/utils": "~7.9.0",
22
- "eslint-plugin-react": "^7.34.1",
23
- "@agilebot/eslint-utils": "0.3.9"
21
+ "eslint-plugin-react": "^7.35.0",
22
+ "@agilebot/eslint-utils": "0.3.11"
24
23
  },
25
24
  "peerDependencies": {
26
25
  "eslint": "^7.0.0 || ^8.0.0"
@@ -32,8 +31,8 @@
32
31
  "@types/color-name": "^1.1.4",
33
32
  "@types/estree": "^1.0.5",
34
33
  "color-name": "^2.0.0",
35
- "eslint-vitest-rule-tester": "^0.3.2",
36
- "typescript-eslint": "^7.11.0"
34
+ "eslint-vitest-rule-tester": "^0.3.3",
35
+ "typescript-eslint": "^7.16.0"
37
36
  },
38
37
  "scripts": {
39
38
  "build": "rollup -c rollup.config.mjs",