@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 +9 -9
- package/LICENSE.tpl +8 -8
- package/README.md +9 -9
- package/dist/index.js +293 -200
- package/package.json +5 -6
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.
|
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:
|
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:
|
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
|
-
|
582
|
-
|
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
|
-
|
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:
|
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:
|
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:
|
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 =
|
2093
|
-
defaultOptions: [],
|
1981
|
+
var reactPreferNamedPropertyAccess = eslintUtils.createESLintRule({
|
2094
1982
|
meta: {
|
2095
|
-
type: '
|
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
|
-
|
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
|
-
|
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:
|
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:
|
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
|
-
|
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 === '
|
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.
|
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
|
-
"
|
22
|
-
"eslint-
|
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.
|
36
|
-
"typescript-eslint": "^7.
|
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",
|