@agilebot/eslint-plugin 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +5 -20
- package/dist/index.js +520 -439
- package/package.json +4 -3
package/dist/index.d.ts
CHANGED
@@ -1,20 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
export default plugin;
|
2
|
+
declare namespace plugin {
|
3
|
+
let rules: import('eslint').Linter.RulesRecord;
|
4
|
+
namespace configs {
|
4
5
|
namespace recommended {
|
5
6
|
export let plugins: string[];
|
6
|
-
let rules_1:
|
7
|
-
'@agilebot/no-unnecessary-template-literals': string;
|
8
|
-
'@agilebot/no-async-array-methods': string;
|
9
|
-
'@agilebot/react-prefer-named-property-access': string;
|
10
|
-
'@agilebot/react-hook-use-ref': string;
|
11
|
-
'@agilebot/react-prefer-sx-prop': string;
|
12
|
-
'@agilebot/tss-unused-classes': string;
|
13
|
-
'@agilebot/tss-no-color-value': string;
|
14
|
-
'@agilebot/tss-class-naming': string;
|
15
|
-
'@agilebot/import-enforce-icon-alias': string;
|
16
|
-
'@agilebot/import-monorepo': string;
|
17
|
-
};
|
7
|
+
let rules_1: import('eslint').Linter.RulesRecord;
|
18
8
|
export { rules_1 as rules };
|
19
9
|
export namespace settings {
|
20
10
|
namespace react {
|
@@ -24,8 +14,3 @@ declare namespace _default {
|
|
24
14
|
}
|
25
15
|
}
|
26
16
|
}
|
27
|
-
export default _default;
|
28
|
-
/**
|
29
|
-
* @type {import('eslint').Linter.RulesRecord}
|
30
|
-
*/
|
31
|
-
declare const rules: import('eslint').Linter.RulesRecord;
|
package/dist/index.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license @agilebot/eslint-plugin v0.3.
|
2
|
+
* @license @agilebot/eslint-plugin v0.3.4
|
3
3
|
*
|
4
4
|
* Copyright (c) Agilebot, Inc. and its affiliates.
|
5
5
|
*
|
@@ -41,7 +41,10 @@ var enforceIconAlias = {
|
|
41
41
|
recommended: true
|
42
42
|
},
|
43
43
|
fixable: 'code',
|
44
|
-
schema: []
|
44
|
+
schema: [],
|
45
|
+
messages: {
|
46
|
+
iconAlias: 'Import for {{ name }} should be aliased.'
|
47
|
+
}
|
45
48
|
},
|
46
49
|
create(context) {
|
47
50
|
return {
|
@@ -59,7 +62,10 @@ var enforceIconAlias = {
|
|
59
62
|
if (specifier.imported.name === specifier.local.name) {
|
60
63
|
context.report({
|
61
64
|
node,
|
62
|
-
|
65
|
+
messageId: 'iconAlias',
|
66
|
+
data: {
|
67
|
+
name: node.source.value
|
68
|
+
},
|
63
69
|
fix: fixer =>
|
64
70
|
fixer.replaceText(
|
65
71
|
specifier,
|
@@ -86,7 +92,10 @@ var monorepo = {
|
|
86
92
|
recommended: true
|
87
93
|
},
|
88
94
|
fixable: 'code',
|
89
|
-
schema: []
|
95
|
+
schema: [],
|
96
|
+
messages: {
|
97
|
+
monorepoImport: 'Import for {{ module }} should not contains src folder.'
|
98
|
+
}
|
90
99
|
},
|
91
100
|
create(context) {
|
92
101
|
return {
|
@@ -110,7 +119,10 @@ var monorepo = {
|
|
110
119
|
if (values[2] === 'src') {
|
111
120
|
context.report({
|
112
121
|
node,
|
113
|
-
|
122
|
+
messageId: 'monorepoImport',
|
123
|
+
data: {
|
124
|
+
module: node.source.value
|
125
|
+
},
|
114
126
|
fix: fixer => {
|
115
127
|
const correctedPath = values
|
116
128
|
.filter((_, index) => index !== 2)
|
@@ -240,8 +252,7 @@ var idMissing = {
|
|
240
252
|
meta: {
|
241
253
|
docs: {
|
242
254
|
description: 'Validates intl message ids are in locale file',
|
243
|
-
category: 'Intl'
|
244
|
-
recommended: true
|
255
|
+
category: 'Intl'
|
245
256
|
},
|
246
257
|
fixable: undefined,
|
247
258
|
schema: []
|
@@ -322,8 +333,7 @@ var idPrefix = {
|
|
322
333
|
meta: {
|
323
334
|
docs: {
|
324
335
|
description: 'Validates intl message ids has correct prefixes',
|
325
|
-
category: 'Intl'
|
326
|
-
recommended: true
|
336
|
+
category: 'Intl'
|
327
337
|
},
|
328
338
|
fixable: undefined,
|
329
339
|
schema: [
|
@@ -400,8 +410,7 @@ var idUnused = {
|
|
400
410
|
meta: {
|
401
411
|
docs: {
|
402
412
|
description: 'Finds unused intl message ids in locale file',
|
403
|
-
category: 'Intl'
|
404
|
-
recommended: true
|
413
|
+
category: 'Intl'
|
405
414
|
},
|
406
415
|
fixable: undefined,
|
407
416
|
schema: []
|
@@ -483,8 +492,7 @@ var noDefault = {
|
|
483
492
|
meta: {
|
484
493
|
docs: {
|
485
494
|
description: 'Validates defaultMessage is not used with react-intl',
|
486
|
-
category: 'Intl'
|
487
|
-
recommended: true
|
495
|
+
category: 'Intl'
|
488
496
|
},
|
489
497
|
fixable: undefined,
|
490
498
|
schema: []
|
@@ -521,387 +529,56 @@ var noDefault = {
|
|
521
529
|
}
|
522
530
|
};
|
523
531
|
|
524
|
-
|
525
|
-
if (node.type === 'Identifier') {
|
526
|
-
return node.name;
|
527
|
-
}
|
528
|
-
if (node.type === 'Literal') {
|
529
|
-
return node.value;
|
530
|
-
}
|
531
|
-
if (node.type === 'TemplateLiteral') {
|
532
|
-
if (node.expressions.length > 0) {
|
533
|
-
return null;
|
534
|
-
}
|
535
|
-
return node.quasis[0].value.raw;
|
536
|
-
}
|
537
|
-
return null;
|
538
|
-
}
|
539
|
-
function getBaseIdentifier(node) {
|
540
|
-
switch (node.type) {
|
541
|
-
case 'Identifier': {
|
542
|
-
return node;
|
543
|
-
}
|
544
|
-
case 'CallExpression': {
|
545
|
-
return getBaseIdentifier(node.callee);
|
546
|
-
}
|
547
|
-
case 'MemberExpression': {
|
548
|
-
return getBaseIdentifier(node.object);
|
549
|
-
}
|
550
|
-
}
|
551
|
-
return null;
|
552
|
-
}
|
553
|
-
function getStyesObj(node) {
|
554
|
-
const isMakeStyles = node.callee.name === 'makeStyles';
|
555
|
-
const isModernApi =
|
556
|
-
node.callee.type === 'MemberExpression' &&
|
557
|
-
node.callee.property.name === 'create' &&
|
558
|
-
getBaseIdentifier(node.callee.object) &&
|
559
|
-
getBaseIdentifier(node.callee.object).name === 'tss';
|
560
|
-
if (!isMakeStyles && !isModernApi) {
|
561
|
-
return;
|
562
|
-
}
|
563
|
-
const styles = (() => {
|
564
|
-
if (isMakeStyles) {
|
565
|
-
return node.parent.arguments[0];
|
566
|
-
}
|
567
|
-
if (isModernApi) {
|
568
|
-
return node.callee.parent.arguments[0];
|
569
|
-
}
|
570
|
-
})();
|
571
|
-
if (!styles) {
|
572
|
-
return;
|
573
|
-
}
|
574
|
-
switch (styles.type) {
|
575
|
-
case 'ObjectExpression':
|
576
|
-
return styles;
|
577
|
-
case 'ArrowFunctionExpression':
|
578
|
-
{
|
579
|
-
const { body } = styles;
|
580
|
-
switch (body.type) {
|
581
|
-
case 'ObjectExpression':
|
582
|
-
return body;
|
583
|
-
case 'BlockStatement': {
|
584
|
-
let stylesObj;
|
585
|
-
body.body.forEach(bodyNode => {
|
586
|
-
if (
|
587
|
-
bodyNode.type === 'ReturnStatement' &&
|
588
|
-
bodyNode.argument.type === 'ObjectExpression'
|
589
|
-
) {
|
590
|
-
stylesObj = bodyNode.argument;
|
591
|
-
}
|
592
|
-
});
|
593
|
-
return stylesObj;
|
594
|
-
}
|
595
|
-
}
|
596
|
-
}
|
597
|
-
break;
|
598
|
-
}
|
599
|
-
}
|
600
|
-
|
601
|
-
var classNaming = {
|
602
|
-
meta: {
|
603
|
-
type: 'problem'
|
604
|
-
},
|
605
|
-
create: function rule(context) {
|
606
|
-
return {
|
607
|
-
CallExpression(node) {
|
608
|
-
const stylesObj = getStyesObj(node);
|
609
|
-
if (stylesObj === undefined) {
|
610
|
-
return;
|
611
|
-
}
|
612
|
-
stylesObj.properties.forEach(property => {
|
613
|
-
if (property.computed) {
|
614
|
-
return;
|
615
|
-
}
|
616
|
-
if (
|
617
|
-
property.type === 'ExperimentalSpreadProperty' ||
|
618
|
-
property.type === 'SpreadElement'
|
619
|
-
) {
|
620
|
-
return;
|
621
|
-
}
|
622
|
-
const className = property.key.value || property.key.name;
|
623
|
-
if (!eslintUtils.isCamelCase(className)) {
|
624
|
-
context.report({
|
625
|
-
node: property,
|
626
|
-
message: `Class \`${className}\` must be camelCase in TSS.`
|
627
|
-
});
|
628
|
-
}
|
629
|
-
});
|
630
|
-
}
|
631
|
-
};
|
632
|
-
}
|
633
|
-
};
|
634
|
-
|
635
|
-
var noColorValue = {
|
532
|
+
var betterExhaustiveDeps = {
|
636
533
|
meta: {
|
637
|
-
type: '
|
534
|
+
type: 'suggestion',
|
638
535
|
docs: {
|
639
536
|
description:
|
640
|
-
'
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
537
|
+
'verifies the list of dependencies for Hooks like useEffect and similar',
|
538
|
+
url: 'https://github.com/facebook/react/issues/14920'
|
539
|
+
},
|
540
|
+
fixable: 'code',
|
541
|
+
hasSuggestions: true,
|
542
|
+
schema: [
|
543
|
+
{
|
544
|
+
type: 'object',
|
545
|
+
additionalProperties: false,
|
546
|
+
enableDangerousAutofixThisMayCauseInfiniteLoops: false,
|
547
|
+
properties: {
|
548
|
+
additionalHooks: {
|
549
|
+
type: 'string'
|
550
|
+
},
|
551
|
+
enableDangerousAutofixThisMayCauseInfiniteLoops: {
|
552
|
+
type: 'boolean'
|
553
|
+
},
|
554
|
+
staticHooks: {
|
555
|
+
type: 'object',
|
556
|
+
additionalProperties: {
|
557
|
+
oneOf: [
|
558
|
+
{
|
559
|
+
type: 'boolean'
|
560
|
+
},
|
561
|
+
{
|
562
|
+
type: 'array',
|
563
|
+
items: {
|
564
|
+
type: 'boolean'
|
565
|
+
}
|
566
|
+
},
|
567
|
+
{
|
568
|
+
type: 'object',
|
569
|
+
additionalProperties: {
|
570
|
+
type: 'boolean'
|
571
|
+
}
|
675
572
|
}
|
676
|
-
|
677
|
-
}
|
573
|
+
]
|
574
|
+
}
|
575
|
+
},
|
576
|
+
checkMemoizedVariableIsStatic: {
|
577
|
+
type: 'boolean'
|
678
578
|
}
|
679
579
|
}
|
680
|
-
loopStylesObj(stylesObj);
|
681
580
|
}
|
682
|
-
|
683
|
-
}
|
684
|
-
};
|
685
|
-
|
686
|
-
var unusedClasses = {
|
687
|
-
meta: {
|
688
|
-
type: 'problem'
|
689
|
-
},
|
690
|
-
create: function rule(context) {
|
691
|
-
const usedClasses = {};
|
692
|
-
const definedClasses = {};
|
693
|
-
return {
|
694
|
-
CallExpression(node) {
|
695
|
-
const stylesObj = getStyesObj(node);
|
696
|
-
if (stylesObj === undefined) {
|
697
|
-
return;
|
698
|
-
}
|
699
|
-
stylesObj.properties.forEach(property => {
|
700
|
-
if (property.computed) {
|
701
|
-
return;
|
702
|
-
}
|
703
|
-
if (
|
704
|
-
property.type === 'ExperimentalSpreadProperty' ||
|
705
|
-
property.type === 'SpreadElement'
|
706
|
-
) {
|
707
|
-
return;
|
708
|
-
}
|
709
|
-
definedClasses[property.key.value || property.key.name] = property;
|
710
|
-
});
|
711
|
-
},
|
712
|
-
MemberExpression(node) {
|
713
|
-
if (
|
714
|
-
node.object.type === 'Identifier' &&
|
715
|
-
node.object.name === 'classes'
|
716
|
-
) {
|
717
|
-
const whichClass = getBasicIdentifier(node.property);
|
718
|
-
if (whichClass) {
|
719
|
-
usedClasses[whichClass] = true;
|
720
|
-
}
|
721
|
-
return;
|
722
|
-
}
|
723
|
-
const classIdentifier = getBasicIdentifier(node.property);
|
724
|
-
if (!classIdentifier) {
|
725
|
-
return;
|
726
|
-
}
|
727
|
-
if (classIdentifier !== 'classes') {
|
728
|
-
return;
|
729
|
-
}
|
730
|
-
const { parent } = node;
|
731
|
-
if (parent.type !== 'MemberExpression') {
|
732
|
-
return;
|
733
|
-
}
|
734
|
-
if (
|
735
|
-
node.object.object &&
|
736
|
-
node.object.object.type !== 'ThisExpression'
|
737
|
-
) {
|
738
|
-
return;
|
739
|
-
}
|
740
|
-
const propsIdentifier = getBasicIdentifier(parent.object);
|
741
|
-
if (propsIdentifier && propsIdentifier !== 'props') {
|
742
|
-
return;
|
743
|
-
}
|
744
|
-
if (!propsIdentifier && parent.object.type !== 'MemberExpression') {
|
745
|
-
return;
|
746
|
-
}
|
747
|
-
if (parent.parent.type === 'MemberExpression') {
|
748
|
-
return;
|
749
|
-
}
|
750
|
-
const parentClassIdentifier = getBasicIdentifier(parent.property);
|
751
|
-
if (parentClassIdentifier) {
|
752
|
-
usedClasses[parentClassIdentifier] = true;
|
753
|
-
}
|
754
|
-
},
|
755
|
-
'Program:exit': () => {
|
756
|
-
Object.keys(definedClasses).forEach(definedClassKey => {
|
757
|
-
if (!usedClasses[definedClassKey]) {
|
758
|
-
context.report({
|
759
|
-
node: definedClasses[definedClassKey],
|
760
|
-
message: `Class \`${definedClassKey}\` is unused`
|
761
|
-
});
|
762
|
-
}
|
763
|
-
});
|
764
|
-
}
|
765
|
-
};
|
766
|
-
}
|
767
|
-
};
|
768
|
-
|
769
|
-
var noAsyncArrayMethods = {
|
770
|
-
meta: {
|
771
|
-
docs: {
|
772
|
-
description:
|
773
|
-
'No async callback for Array methods forEach, map, filter, reduce, some, every, etc.',
|
774
|
-
category: 'Array',
|
775
|
-
recommended: true
|
776
|
-
},
|
777
|
-
fixable: undefined,
|
778
|
-
schema: []
|
779
|
-
},
|
780
|
-
create: function (context) {
|
781
|
-
return {
|
782
|
-
ExpressionStatement: function (node) {
|
783
|
-
const notAllowedArrayMethods = [
|
784
|
-
'forEach',
|
785
|
-
'filter',
|
786
|
-
'some',
|
787
|
-
'every',
|
788
|
-
'map',
|
789
|
-
'reduce',
|
790
|
-
'reduceRight',
|
791
|
-
'flatMap',
|
792
|
-
'find',
|
793
|
-
'findIndex',
|
794
|
-
'findLast',
|
795
|
-
'findLastIndex'
|
796
|
-
];
|
797
|
-
const { callee } = node.expression;
|
798
|
-
if (!callee || !callee.property || !callee.property.name) {
|
799
|
-
return;
|
800
|
-
}
|
801
|
-
if (notAllowedArrayMethods.includes(callee.property.name)) {
|
802
|
-
const functionArguments = node.expression.arguments.find(n => {
|
803
|
-
return ['ArrowFunctionExpression', 'FunctionExpression'].includes(
|
804
|
-
n.type
|
805
|
-
);
|
806
|
-
});
|
807
|
-
if (functionArguments && functionArguments.async) {
|
808
|
-
context.report({
|
809
|
-
node,
|
810
|
-
message: `No async function in method '${callee.property.name}'`
|
811
|
-
});
|
812
|
-
}
|
813
|
-
}
|
814
|
-
}
|
815
|
-
};
|
816
|
-
}
|
817
|
-
};
|
818
|
-
|
819
|
-
var noUnnecessaryTemplateLiterals = {
|
820
|
-
meta: {
|
821
|
-
type: 'problem',
|
822
|
-
docs: {
|
823
|
-
description: 'Check if a template string contains only one ${}',
|
824
|
-
recommended: true
|
825
|
-
},
|
826
|
-
fixable: 'code',
|
827
|
-
schema: []
|
828
|
-
},
|
829
|
-
create(context) {
|
830
|
-
return {
|
831
|
-
TemplateLiteral(node) {
|
832
|
-
const code = context.sourceCode.getText(node);
|
833
|
-
if (
|
834
|
-
code.startsWith('`${') &&
|
835
|
-
code.endsWith('}`') &&
|
836
|
-
code.split('${').length === 2
|
837
|
-
) {
|
838
|
-
context.report({
|
839
|
-
node,
|
840
|
-
message: 'Unnecessary template string with only one ${}.',
|
841
|
-
fix(fixer) {
|
842
|
-
return fixer.replaceText(
|
843
|
-
node,
|
844
|
-
code.substring(3, code.length - 2)
|
845
|
-
);
|
846
|
-
}
|
847
|
-
});
|
848
|
-
}
|
849
|
-
}
|
850
|
-
};
|
851
|
-
}
|
852
|
-
};
|
853
|
-
|
854
|
-
var betterExhaustiveDeps = {
|
855
|
-
meta: {
|
856
|
-
type: 'suggestion',
|
857
|
-
docs: {
|
858
|
-
description:
|
859
|
-
'verifies the list of dependencies for Hooks like useEffect and similar',
|
860
|
-
recommended: true,
|
861
|
-
url: 'https://github.com/facebook/react/issues/14920'
|
862
|
-
},
|
863
|
-
fixable: 'code',
|
864
|
-
hasSuggestions: true,
|
865
|
-
schema: [
|
866
|
-
{
|
867
|
-
type: 'object',
|
868
|
-
additionalProperties: false,
|
869
|
-
enableDangerousAutofixThisMayCauseInfiniteLoops: false,
|
870
|
-
properties: {
|
871
|
-
additionalHooks: {
|
872
|
-
type: 'string'
|
873
|
-
},
|
874
|
-
enableDangerousAutofixThisMayCauseInfiniteLoops: {
|
875
|
-
type: 'boolean'
|
876
|
-
},
|
877
|
-
staticHooks: {
|
878
|
-
type: 'object',
|
879
|
-
additionalProperties: {
|
880
|
-
oneOf: [
|
881
|
-
{
|
882
|
-
type: 'boolean'
|
883
|
-
},
|
884
|
-
{
|
885
|
-
type: 'array',
|
886
|
-
items: {
|
887
|
-
type: 'boolean'
|
888
|
-
}
|
889
|
-
},
|
890
|
-
{
|
891
|
-
type: 'object',
|
892
|
-
additionalProperties: {
|
893
|
-
type: 'boolean'
|
894
|
-
}
|
895
|
-
}
|
896
|
-
]
|
897
|
-
}
|
898
|
-
},
|
899
|
-
checkMemoizedVariableIsStatic: {
|
900
|
-
type: 'boolean'
|
901
|
-
}
|
902
|
-
}
|
903
|
-
}
|
904
|
-
]
|
581
|
+
]
|
905
582
|
},
|
906
583
|
create(context) {
|
907
584
|
const additionalHooks =
|
@@ -2394,7 +2071,7 @@ var hookUseRef = {
|
|
2394
2071
|
meta: {
|
2395
2072
|
docs: {
|
2396
2073
|
description: 'Ensure naming of useRef hook value.',
|
2397
|
-
recommended:
|
2074
|
+
recommended: true
|
2398
2075
|
},
|
2399
2076
|
schema: [],
|
2400
2077
|
type: 'suggestion',
|
@@ -2455,7 +2132,8 @@ var preferNamedPropertyAccess = utils.ESLintUtils.RuleCreator.withoutDocs({
|
|
2455
2132
|
fixable: 'code',
|
2456
2133
|
docs: {
|
2457
2134
|
description:
|
2458
|
-
'Enforce importing each member of React namespace separately instead of accessing them through React namespace'
|
2135
|
+
'Enforce importing each member of React namespace separately instead of accessing them through React namespace',
|
2136
|
+
recommended: 'recommended'
|
2459
2137
|
},
|
2460
2138
|
messages: {
|
2461
2139
|
illegalReactPropertyAccess:
|
@@ -2509,7 +2187,7 @@ var preferSxProp = {
|
|
2509
2187
|
docs: {
|
2510
2188
|
description: 'Prefer using sx prop instead of inline styles',
|
2511
2189
|
category: 'Best Practices',
|
2512
|
-
recommended:
|
2190
|
+
recommended: true
|
2513
2191
|
},
|
2514
2192
|
messages: {
|
2515
2193
|
preferSxProp:
|
@@ -2582,55 +2260,458 @@ var preferSxProp = {
|
|
2582
2260
|
}
|
2583
2261
|
};
|
2584
2262
|
|
2585
|
-
|
2586
|
-
|
2587
|
-
|
2588
|
-
|
2589
|
-
|
2590
|
-
|
2591
|
-
|
2592
|
-
|
2593
|
-
|
2594
|
-
|
2595
|
-
rules_react_prefer_named_property_access: preferNamedPropertyAccess,
|
2596
|
-
rules_react_prefer_sx_prop: preferSxProp,
|
2597
|
-
rules_tss_class_naming: classNaming,
|
2598
|
-
rules_tss_no_color_value: noColorValue,
|
2599
|
-
rules_tss_unused_classes: unusedClasses,
|
2600
|
-
rules_unprefixed_no_async_array_methods: noAsyncArrayMethods,
|
2601
|
-
rules_unprefixed_no_unnecessary_template_literals: noUnnecessaryTemplateLiterals
|
2602
|
-
});
|
2603
|
-
|
2604
|
-
const rules = {};
|
2605
|
-
Object.keys(ruleFiles).forEach(key => {
|
2606
|
-
const ruleKey = key.replace(/^rules_/, '').replace(/^unprefixed_/, '');
|
2607
|
-
const finalKey = ruleKey.replace(/_/g, '-');
|
2608
|
-
rules[finalKey] = ruleFiles[key];
|
2609
|
-
});
|
2610
|
-
var index = {
|
2611
|
-
rules,
|
2612
|
-
configs: {
|
2613
|
-
recommended: {
|
2614
|
-
plugins: ['@agilebot'],
|
2615
|
-
rules: {
|
2616
|
-
'@agilebot/no-unnecessary-template-literals': 'error',
|
2617
|
-
'@agilebot/no-async-array-methods': 'error',
|
2618
|
-
'@agilebot/react-prefer-named-property-access': 'error',
|
2619
|
-
'@agilebot/react-hook-use-ref': 'warn',
|
2620
|
-
'@agilebot/react-prefer-sx-prop': 'error',
|
2621
|
-
'@agilebot/tss-unused-classes': 'warn',
|
2622
|
-
'@agilebot/tss-no-color-value': 'error',
|
2623
|
-
'@agilebot/tss-class-naming': 'error',
|
2624
|
-
'@agilebot/import-enforce-icon-alias': 'error',
|
2625
|
-
'@agilebot/import-monorepo': 'error'
|
2626
|
-
},
|
2627
|
-
settings: {
|
2628
|
-
react: {
|
2629
|
-
version: '18.0.0'
|
2630
|
-
}
|
2631
|
-
}
|
2263
|
+
function getBasicIdentifier(node) {
|
2264
|
+
if (node.type === 'Identifier') {
|
2265
|
+
return node.name;
|
2266
|
+
}
|
2267
|
+
if (node.type === 'Literal') {
|
2268
|
+
return node.value;
|
2269
|
+
}
|
2270
|
+
if (node.type === 'TemplateLiteral') {
|
2271
|
+
if (node.expressions.length > 0) {
|
2272
|
+
return null;
|
2632
2273
|
}
|
2274
|
+
return node.quasis[0].value.raw;
|
2633
2275
|
}
|
2634
|
-
|
2276
|
+
return null;
|
2277
|
+
}
|
2278
|
+
function getBaseIdentifier(node) {
|
2279
|
+
switch (node.type) {
|
2280
|
+
case 'Identifier': {
|
2281
|
+
return node;
|
2282
|
+
}
|
2283
|
+
case 'CallExpression': {
|
2284
|
+
return getBaseIdentifier(node.callee);
|
2285
|
+
}
|
2286
|
+
case 'MemberExpression': {
|
2287
|
+
return getBaseIdentifier(node.object);
|
2288
|
+
}
|
2289
|
+
}
|
2290
|
+
return null;
|
2291
|
+
}
|
2292
|
+
function getStyesObj(node) {
|
2293
|
+
const isMakeStyles = node.callee.name === 'makeStyles';
|
2294
|
+
const isModernApi =
|
2295
|
+
node.callee.type === 'MemberExpression' &&
|
2296
|
+
node.callee.property.name === 'create' &&
|
2297
|
+
getBaseIdentifier(node.callee.object) &&
|
2298
|
+
getBaseIdentifier(node.callee.object).name === 'tss';
|
2299
|
+
if (!isMakeStyles && !isModernApi) {
|
2300
|
+
return;
|
2301
|
+
}
|
2302
|
+
const styles = (() => {
|
2303
|
+
if (isMakeStyles) {
|
2304
|
+
return node.parent.arguments[0];
|
2305
|
+
}
|
2306
|
+
if (isModernApi) {
|
2307
|
+
return node.callee.parent.arguments[0];
|
2308
|
+
}
|
2309
|
+
})();
|
2310
|
+
if (!styles) {
|
2311
|
+
return;
|
2312
|
+
}
|
2313
|
+
switch (styles.type) {
|
2314
|
+
case 'ObjectExpression':
|
2315
|
+
return styles;
|
2316
|
+
case 'ArrowFunctionExpression':
|
2317
|
+
{
|
2318
|
+
const { body } = styles;
|
2319
|
+
switch (body.type) {
|
2320
|
+
case 'ObjectExpression':
|
2321
|
+
return body;
|
2322
|
+
case 'BlockStatement': {
|
2323
|
+
let stylesObj;
|
2324
|
+
body.body.forEach(bodyNode => {
|
2325
|
+
if (
|
2326
|
+
bodyNode.type === 'ReturnStatement' &&
|
2327
|
+
bodyNode.argument.type === 'ObjectExpression'
|
2328
|
+
) {
|
2329
|
+
stylesObj = bodyNode.argument;
|
2330
|
+
}
|
2331
|
+
});
|
2332
|
+
return stylesObj;
|
2333
|
+
}
|
2334
|
+
}
|
2335
|
+
}
|
2336
|
+
break;
|
2337
|
+
}
|
2338
|
+
}
|
2339
|
+
|
2340
|
+
var classNaming = {
|
2341
|
+
meta: {
|
2342
|
+
type: 'problem',
|
2343
|
+
docs: {
|
2344
|
+
description: 'Enforce camelCase class names in TSS',
|
2345
|
+
category: 'Best Practices',
|
2346
|
+
recommended: true
|
2347
|
+
}
|
2348
|
+
},
|
2349
|
+
create: function rule(context) {
|
2350
|
+
return {
|
2351
|
+
CallExpression(node) {
|
2352
|
+
const stylesObj = getStyesObj(node);
|
2353
|
+
if (stylesObj === undefined) {
|
2354
|
+
return;
|
2355
|
+
}
|
2356
|
+
stylesObj.properties.forEach(property => {
|
2357
|
+
if (property.computed) {
|
2358
|
+
return;
|
2359
|
+
}
|
2360
|
+
if (
|
2361
|
+
property.type === 'ExperimentalSpreadProperty' ||
|
2362
|
+
property.type === 'SpreadElement'
|
2363
|
+
) {
|
2364
|
+
return;
|
2365
|
+
}
|
2366
|
+
const className = property.key.value || property.key.name;
|
2367
|
+
if (!eslintUtils.isCamelCase(className)) {
|
2368
|
+
context.report({
|
2369
|
+
node: property,
|
2370
|
+
message: `Class \`${className}\` must be camelCase in TSS.`
|
2371
|
+
});
|
2372
|
+
}
|
2373
|
+
});
|
2374
|
+
}
|
2375
|
+
};
|
2376
|
+
}
|
2377
|
+
};
|
2378
|
+
|
2379
|
+
var noColorValue = {
|
2380
|
+
meta: {
|
2381
|
+
type: 'problem',
|
2382
|
+
docs: {
|
2383
|
+
description:
|
2384
|
+
'Enforce the use of color variables instead of color codes within TSS',
|
2385
|
+
recommended: true
|
2386
|
+
}
|
2387
|
+
},
|
2388
|
+
create: function (context) {
|
2389
|
+
const parserOptions = context.parserOptions;
|
2390
|
+
if (!parserOptions || !parserOptions.project) {
|
2391
|
+
return {};
|
2392
|
+
}
|
2393
|
+
return {
|
2394
|
+
CallExpression(node) {
|
2395
|
+
const stylesObj = getStyesObj(node);
|
2396
|
+
if (!stylesObj) {
|
2397
|
+
return;
|
2398
|
+
}
|
2399
|
+
function checkColorLiteral(value) {
|
2400
|
+
if (value.type === 'Literal' && typeof value.value === 'string') {
|
2401
|
+
const colorCodePattern =
|
2402
|
+
/#(?:[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;
|
2403
|
+
const isColorCode = colorCodePattern.test(value.value);
|
2404
|
+
if (isColorCode) {
|
2405
|
+
context.report({
|
2406
|
+
node: value,
|
2407
|
+
message: 'Use color variables instead of color codes in TSS.'
|
2408
|
+
});
|
2409
|
+
}
|
2410
|
+
}
|
2411
|
+
}
|
2412
|
+
function loopStylesObj(obj) {
|
2413
|
+
if (obj && obj.type === 'ObjectExpression') {
|
2414
|
+
obj.properties.forEach(property => {
|
2415
|
+
if (property.type === 'Property' && property.value) {
|
2416
|
+
if (property.value.type === 'ObjectExpression') {
|
2417
|
+
loopStylesObj(property.value);
|
2418
|
+
} else {
|
2419
|
+
checkColorLiteral(property.value);
|
2420
|
+
}
|
2421
|
+
}
|
2422
|
+
});
|
2423
|
+
}
|
2424
|
+
}
|
2425
|
+
loopStylesObj(stylesObj);
|
2426
|
+
}
|
2427
|
+
};
|
2428
|
+
}
|
2429
|
+
};
|
2430
|
+
|
2431
|
+
var unusedClasses = {
|
2432
|
+
meta: {
|
2433
|
+
type: 'suggestion',
|
2434
|
+
docs: {
|
2435
|
+
description: 'Disallow unused classes in tss',
|
2436
|
+
category: 'Best Practices',
|
2437
|
+
recommended: true
|
2438
|
+
}
|
2439
|
+
},
|
2440
|
+
create: function rule(context) {
|
2441
|
+
const usedClasses = {};
|
2442
|
+
const definedClasses = {};
|
2443
|
+
return {
|
2444
|
+
CallExpression(node) {
|
2445
|
+
const stylesObj = getStyesObj(node);
|
2446
|
+
if (stylesObj === undefined) {
|
2447
|
+
return;
|
2448
|
+
}
|
2449
|
+
stylesObj.properties.forEach(property => {
|
2450
|
+
if (property.computed) {
|
2451
|
+
return;
|
2452
|
+
}
|
2453
|
+
if (
|
2454
|
+
property.type === 'ExperimentalSpreadProperty' ||
|
2455
|
+
property.type === 'SpreadElement'
|
2456
|
+
) {
|
2457
|
+
return;
|
2458
|
+
}
|
2459
|
+
definedClasses[property.key.value || property.key.name] = property;
|
2460
|
+
});
|
2461
|
+
},
|
2462
|
+
MemberExpression(node) {
|
2463
|
+
if (
|
2464
|
+
node.object.type === 'Identifier' &&
|
2465
|
+
node.object.name === 'classes'
|
2466
|
+
) {
|
2467
|
+
const whichClass = getBasicIdentifier(node.property);
|
2468
|
+
if (whichClass) {
|
2469
|
+
usedClasses[whichClass] = true;
|
2470
|
+
}
|
2471
|
+
return;
|
2472
|
+
}
|
2473
|
+
const classIdentifier = getBasicIdentifier(node.property);
|
2474
|
+
if (!classIdentifier) {
|
2475
|
+
return;
|
2476
|
+
}
|
2477
|
+
if (classIdentifier !== 'classes') {
|
2478
|
+
return;
|
2479
|
+
}
|
2480
|
+
const { parent } = node;
|
2481
|
+
if (parent.type !== 'MemberExpression') {
|
2482
|
+
return;
|
2483
|
+
}
|
2484
|
+
if (
|
2485
|
+
node.object.object &&
|
2486
|
+
node.object.object.type !== 'ThisExpression'
|
2487
|
+
) {
|
2488
|
+
return;
|
2489
|
+
}
|
2490
|
+
const propsIdentifier = getBasicIdentifier(parent.object);
|
2491
|
+
if (propsIdentifier && propsIdentifier !== 'props') {
|
2492
|
+
return;
|
2493
|
+
}
|
2494
|
+
if (!propsIdentifier && parent.object.type !== 'MemberExpression') {
|
2495
|
+
return;
|
2496
|
+
}
|
2497
|
+
if (parent.parent.type === 'MemberExpression') {
|
2498
|
+
return;
|
2499
|
+
}
|
2500
|
+
const parentClassIdentifier = getBasicIdentifier(parent.property);
|
2501
|
+
if (parentClassIdentifier) {
|
2502
|
+
usedClasses[parentClassIdentifier] = true;
|
2503
|
+
}
|
2504
|
+
},
|
2505
|
+
'Program:exit': () => {
|
2506
|
+
Object.keys(definedClasses).forEach(definedClassKey => {
|
2507
|
+
if (!usedClasses[definedClassKey]) {
|
2508
|
+
context.report({
|
2509
|
+
node: definedClasses[definedClassKey],
|
2510
|
+
message: `Class \`${definedClassKey}\` is unused`
|
2511
|
+
});
|
2512
|
+
}
|
2513
|
+
});
|
2514
|
+
}
|
2515
|
+
};
|
2516
|
+
}
|
2517
|
+
};
|
2518
|
+
|
2519
|
+
var noAsyncArrayMethods = {
|
2520
|
+
meta: {
|
2521
|
+
docs: {
|
2522
|
+
description:
|
2523
|
+
'No async callback for Array methods forEach, map, filter, reduce, some, every, etc.',
|
2524
|
+
category: 'Array',
|
2525
|
+
recommended: true
|
2526
|
+
},
|
2527
|
+
fixable: undefined,
|
2528
|
+
schema: []
|
2529
|
+
},
|
2530
|
+
create: function (context) {
|
2531
|
+
return {
|
2532
|
+
ExpressionStatement: function (node) {
|
2533
|
+
const notAllowedArrayMethods = [
|
2534
|
+
'forEach',
|
2535
|
+
'filter',
|
2536
|
+
'some',
|
2537
|
+
'every',
|
2538
|
+
'map',
|
2539
|
+
'reduce',
|
2540
|
+
'reduceRight',
|
2541
|
+
'flatMap',
|
2542
|
+
'find',
|
2543
|
+
'findIndex',
|
2544
|
+
'findLast',
|
2545
|
+
'findLastIndex'
|
2546
|
+
];
|
2547
|
+
const { callee } = node.expression;
|
2548
|
+
if (!callee || !callee.property || !callee.property.name) {
|
2549
|
+
return;
|
2550
|
+
}
|
2551
|
+
if (notAllowedArrayMethods.includes(callee.property.name)) {
|
2552
|
+
const functionArguments = node.expression.arguments.find(n => {
|
2553
|
+
return ['ArrowFunctionExpression', 'FunctionExpression'].includes(
|
2554
|
+
n.type
|
2555
|
+
);
|
2556
|
+
});
|
2557
|
+
if (functionArguments && functionArguments.async) {
|
2558
|
+
context.report({
|
2559
|
+
node,
|
2560
|
+
message: `No async function in method '${callee.property.name}'`
|
2561
|
+
});
|
2562
|
+
}
|
2563
|
+
}
|
2564
|
+
}
|
2565
|
+
};
|
2566
|
+
}
|
2567
|
+
};
|
2568
|
+
|
2569
|
+
var noThenCatchFinally = {
|
2570
|
+
meta: {
|
2571
|
+
type: 'suggestion',
|
2572
|
+
docs: {
|
2573
|
+
description:
|
2574
|
+
'Disallow the use of then()/catch()/finally() when invoking specific functions.'
|
2575
|
+
},
|
2576
|
+
schema: [
|
2577
|
+
{
|
2578
|
+
type: 'object',
|
2579
|
+
properties: {
|
2580
|
+
restrictedFunctions: {
|
2581
|
+
type: 'array',
|
2582
|
+
uniqueItems: true,
|
2583
|
+
items: { type: 'string' }
|
2584
|
+
}
|
2585
|
+
}
|
2586
|
+
}
|
2587
|
+
],
|
2588
|
+
messages: {
|
2589
|
+
forbiddenThenCatchFinally: `then()/catch()/finally() is forbidden when invoke {{ name }}().`
|
2590
|
+
}
|
2591
|
+
},
|
2592
|
+
create(context) {
|
2593
|
+
const configuration = context.options[0] || {};
|
2594
|
+
const restrictedFunctions = configuration.restrictedFunctions || [];
|
2595
|
+
function isTopLevelScoped() {
|
2596
|
+
return context.getScope().block.type === 'Program';
|
2597
|
+
}
|
2598
|
+
function isThenCatchFinally(node) {
|
2599
|
+
return (
|
2600
|
+
node.property &&
|
2601
|
+
(node.property.name === 'then' ||
|
2602
|
+
node.property.name === 'catch' ||
|
2603
|
+
node.property.name === 'finally')
|
2604
|
+
);
|
2605
|
+
}
|
2606
|
+
return {
|
2607
|
+
'CallExpression > MemberExpression.callee'(node) {
|
2608
|
+
if (isTopLevelScoped()) {
|
2609
|
+
return;
|
2610
|
+
}
|
2611
|
+
if (!isThenCatchFinally(node)) {
|
2612
|
+
return;
|
2613
|
+
}
|
2614
|
+
const callExpression = node.object;
|
2615
|
+
if (
|
2616
|
+
callExpression.type === 'CallExpression' &&
|
2617
|
+
callExpression.callee.type === 'Identifier' &&
|
2618
|
+
restrictedFunctions.includes(callExpression.callee.name)
|
2619
|
+
) {
|
2620
|
+
context.report({
|
2621
|
+
node: node.property,
|
2622
|
+
messageId: 'forbiddenThenCatchFinally',
|
2623
|
+
data: {
|
2624
|
+
name: callExpression.callee.name
|
2625
|
+
}
|
2626
|
+
});
|
2627
|
+
}
|
2628
|
+
}
|
2629
|
+
};
|
2630
|
+
}
|
2631
|
+
};
|
2632
|
+
|
2633
|
+
var noUnnecessaryTemplateLiterals = {
|
2634
|
+
meta: {
|
2635
|
+
type: 'problem',
|
2636
|
+
docs: {
|
2637
|
+
description: 'Check if a template string contains only one ${}',
|
2638
|
+
recommended: true
|
2639
|
+
},
|
2640
|
+
fixable: 'code',
|
2641
|
+
schema: [],
|
2642
|
+
messages: {
|
2643
|
+
unnecessaryTemplateString:
|
2644
|
+
'Unnecessary template string with only one ${}.'
|
2645
|
+
}
|
2646
|
+
},
|
2647
|
+
create(context) {
|
2648
|
+
return {
|
2649
|
+
TemplateLiteral(node) {
|
2650
|
+
const code = context.sourceCode.getText(node);
|
2651
|
+
if (
|
2652
|
+
code.startsWith('`${') &&
|
2653
|
+
code.endsWith('}`') &&
|
2654
|
+
code.split('${').length === 2
|
2655
|
+
) {
|
2656
|
+
context.report({
|
2657
|
+
node,
|
2658
|
+
messageId: 'unnecessaryTemplateString',
|
2659
|
+
fix(fixer) {
|
2660
|
+
return fixer.replaceText(
|
2661
|
+
node,
|
2662
|
+
code.substring(3, code.length - 2)
|
2663
|
+
);
|
2664
|
+
}
|
2665
|
+
});
|
2666
|
+
}
|
2667
|
+
}
|
2668
|
+
};
|
2669
|
+
}
|
2670
|
+
};
|
2671
|
+
|
2672
|
+
var ruleFiles = /*#__PURE__*/Object.freeze({
|
2673
|
+
__proto__: null,
|
2674
|
+
rules_import_enforce_icon_alias: enforceIconAlias,
|
2675
|
+
rules_import_monorepo: monorepo,
|
2676
|
+
rules_intl_id_missing: idMissing,
|
2677
|
+
rules_intl_id_prefix: idPrefix,
|
2678
|
+
rules_intl_id_unused: idUnused,
|
2679
|
+
rules_intl_no_default: noDefault,
|
2680
|
+
rules_react_better_exhaustive_deps: betterExhaustiveDeps,
|
2681
|
+
rules_react_hook_use_ref: hookUseRef,
|
2682
|
+
rules_react_prefer_named_property_access: preferNamedPropertyAccess,
|
2683
|
+
rules_react_prefer_sx_prop: preferSxProp,
|
2684
|
+
rules_tss_class_naming: classNaming,
|
2685
|
+
rules_tss_no_color_value: noColorValue,
|
2686
|
+
rules_tss_unused_classes: unusedClasses,
|
2687
|
+
rules_unprefixed_no_async_array_methods: noAsyncArrayMethods,
|
2688
|
+
rules_unprefixed_no_then_catch_finally: noThenCatchFinally,
|
2689
|
+
rules_unprefixed_no_unnecessary_template_literals: noUnnecessaryTemplateLiterals
|
2690
|
+
});
|
2691
|
+
|
2692
|
+
const plugin = {
|
2693
|
+
rules: {},
|
2694
|
+
configs: {
|
2695
|
+
recommended: {
|
2696
|
+
plugins: ['@agilebot'],
|
2697
|
+
rules: {},
|
2698
|
+
settings: {
|
2699
|
+
react: {
|
2700
|
+
version: '18.0.0'
|
2701
|
+
}
|
2702
|
+
}
|
2703
|
+
}
|
2704
|
+
}
|
2705
|
+
};
|
2706
|
+
Object.keys(ruleFiles).forEach(key => {
|
2707
|
+
const ruleKey = key.replace(/^rules_/, '').replace(/^unprefixed_/, '');
|
2708
|
+
const finalKey = ruleKey.replace(/_/g, '-');
|
2709
|
+
const rule = ruleFiles[key];
|
2710
|
+
plugin.rules[finalKey] = rule;
|
2711
|
+
if (rule.meta && rule.meta.docs && rule.meta.docs.recommended) {
|
2712
|
+
plugin.configs.recommended.rules[`@agilebot/${finalKey}`] =
|
2713
|
+
rule.meta.type === 'suggestion' ? 'warn' : 'error';
|
2714
|
+
}
|
2715
|
+
});
|
2635
2716
|
|
2636
|
-
module.exports =
|
2717
|
+
module.exports = plugin;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@agilebot/eslint-plugin",
|
3
|
-
"version": "0.3.
|
3
|
+
"version": "0.3.4",
|
4
4
|
"description": "Agilebot's ESLint plugin",
|
5
5
|
"main": "dist/index.js",
|
6
6
|
"types": "dist/index.d.ts",
|
@@ -20,7 +20,7 @@
|
|
20
20
|
"dependencies": {
|
21
21
|
"@typescript-eslint/utils": "~7.7.0",
|
22
22
|
"eslint-plugin-react": "^7.34.1",
|
23
|
-
"@agilebot/eslint-utils": "0.3.
|
23
|
+
"@agilebot/eslint-utils": "0.3.4"
|
24
24
|
},
|
25
25
|
"peerDependencies": {
|
26
26
|
"eslint": "^7.0.0 || ^8.0.0"
|
@@ -29,7 +29,8 @@
|
|
29
29
|
"dist"
|
30
30
|
],
|
31
31
|
"devDependencies": {
|
32
|
-
"@types/estree": "^1.0.5"
|
32
|
+
"@types/estree": "^1.0.5",
|
33
|
+
"eslint-vitest-rule-tester": "^0.3.2"
|
33
34
|
},
|
34
35
|
"scripts": {
|
35
36
|
"build": "rollup -c rollup.config.mjs && nr dts",
|