@fictjs/eslint-plugin 0.0.14 → 0.1.0
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.cjs +472 -75
- package/dist/index.d.cts +30 -8
- package/dist/index.d.ts +30 -8
- package/dist/index.js +468 -75
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
default: () => index_default,
|
|
24
|
+
noComputedPropsKey: () => no_computed_props_key_default,
|
|
24
25
|
noDirectMutation: () => no_direct_mutation_default,
|
|
25
26
|
noEmptyEffect: () => no_empty_effect_default,
|
|
26
27
|
noInlineFunctions: () => no_inline_functions_default,
|
|
@@ -29,13 +30,49 @@ __export(index_exports, {
|
|
|
29
30
|
noStateDestructureWrite: () => no_state_destructure_write_default,
|
|
30
31
|
noStateInLoop: () => no_state_in_loop_default,
|
|
31
32
|
noStateOutsideComponent: () => no_state_outside_component_default,
|
|
33
|
+
noThirdPartyPropsSpread: () => no_third_party_props_spread_default,
|
|
34
|
+
noUnsafePropsSpread: () => no_unsafe_props_spread_default,
|
|
35
|
+
noUnsupportedPropsDestructure: () => no_unsupported_props_destructure_default,
|
|
32
36
|
requireComponentReturn: () => require_component_return_default,
|
|
33
37
|
requireListKey: () => require_list_key_default
|
|
34
38
|
});
|
|
35
39
|
module.exports = __toCommonJS(index_exports);
|
|
36
40
|
|
|
37
|
-
// src/rules/no-
|
|
41
|
+
// src/rules/no-computed-props-key.ts
|
|
38
42
|
var rule = {
|
|
43
|
+
meta: {
|
|
44
|
+
type: "suggestion",
|
|
45
|
+
docs: {
|
|
46
|
+
description: "Disallow computed keys in JSX props object spreads",
|
|
47
|
+
recommended: true
|
|
48
|
+
},
|
|
49
|
+
messages: {
|
|
50
|
+
computedKey: "Computed props key may break fine-grained reactivity; use explicit props."
|
|
51
|
+
},
|
|
52
|
+
schema: []
|
|
53
|
+
},
|
|
54
|
+
create(context) {
|
|
55
|
+
return {
|
|
56
|
+
JSXSpreadAttribute(node) {
|
|
57
|
+
const expr = node.argument;
|
|
58
|
+
if (!expr || expr.type !== "ObjectExpression") return;
|
|
59
|
+
for (const prop of expr.properties ?? []) {
|
|
60
|
+
if (prop.type === "Property" && prop.computed) {
|
|
61
|
+
context.report({
|
|
62
|
+
node: prop,
|
|
63
|
+
messageId: "computedKey"
|
|
64
|
+
});
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
var no_computed_props_key_default = rule;
|
|
73
|
+
|
|
74
|
+
// src/rules/no-direct-mutation.ts
|
|
75
|
+
var rule2 = {
|
|
39
76
|
meta: {
|
|
40
77
|
type: "suggestion",
|
|
41
78
|
docs: {
|
|
@@ -87,10 +124,10 @@ function isDeepAccess(node) {
|
|
|
87
124
|
}
|
|
88
125
|
return depth > 1;
|
|
89
126
|
}
|
|
90
|
-
var no_direct_mutation_default =
|
|
127
|
+
var no_direct_mutation_default = rule2;
|
|
91
128
|
|
|
92
129
|
// src/rules/no-empty-effect.ts
|
|
93
|
-
var
|
|
130
|
+
var rule3 = {
|
|
94
131
|
meta: {
|
|
95
132
|
type: "suggestion",
|
|
96
133
|
docs: {
|
|
@@ -215,10 +252,10 @@ var rule2 = {
|
|
|
215
252
|
};
|
|
216
253
|
}
|
|
217
254
|
};
|
|
218
|
-
var no_empty_effect_default =
|
|
255
|
+
var no_empty_effect_default = rule3;
|
|
219
256
|
|
|
220
257
|
// src/rules/no-inline-functions.ts
|
|
221
|
-
var
|
|
258
|
+
var rule4 = {
|
|
222
259
|
meta: {
|
|
223
260
|
type: "suggestion",
|
|
224
261
|
docs: {
|
|
@@ -268,10 +305,10 @@ var rule3 = {
|
|
|
268
305
|
};
|
|
269
306
|
}
|
|
270
307
|
};
|
|
271
|
-
var no_inline_functions_default =
|
|
308
|
+
var no_inline_functions_default = rule4;
|
|
272
309
|
|
|
273
310
|
// src/rules/no-memo-side-effects.ts
|
|
274
|
-
var
|
|
311
|
+
var rule5 = {
|
|
275
312
|
meta: {
|
|
276
313
|
type: "problem",
|
|
277
314
|
docs: {
|
|
@@ -331,10 +368,10 @@ var rule4 = {
|
|
|
331
368
|
};
|
|
332
369
|
}
|
|
333
370
|
};
|
|
334
|
-
var no_memo_side_effects_default =
|
|
371
|
+
var no_memo_side_effects_default = rule5;
|
|
335
372
|
|
|
336
373
|
// src/rules/no-nested-components.ts
|
|
337
|
-
var
|
|
374
|
+
var rule6 = {
|
|
338
375
|
meta: {
|
|
339
376
|
type: "problem",
|
|
340
377
|
docs: {
|
|
@@ -425,66 +462,7 @@ var rule5 = {
|
|
|
425
462
|
};
|
|
426
463
|
}
|
|
427
464
|
};
|
|
428
|
-
var no_nested_components_default =
|
|
429
|
-
|
|
430
|
-
// src/rules/no-state-in-loop.ts
|
|
431
|
-
var rule6 = {
|
|
432
|
-
meta: {
|
|
433
|
-
type: "problem",
|
|
434
|
-
docs: {
|
|
435
|
-
description: "Disallow $state declarations inside loops",
|
|
436
|
-
recommended: true
|
|
437
|
-
},
|
|
438
|
-
messages: {
|
|
439
|
-
noStateInLoop: "$state should not be declared inside a loop. Move it outside the loop."
|
|
440
|
-
},
|
|
441
|
-
schema: []
|
|
442
|
-
},
|
|
443
|
-
create(context) {
|
|
444
|
-
let loopDepth = 0;
|
|
445
|
-
return {
|
|
446
|
-
ForStatement() {
|
|
447
|
-
loopDepth++;
|
|
448
|
-
},
|
|
449
|
-
"ForStatement:exit"() {
|
|
450
|
-
loopDepth--;
|
|
451
|
-
},
|
|
452
|
-
ForInStatement() {
|
|
453
|
-
loopDepth++;
|
|
454
|
-
},
|
|
455
|
-
"ForInStatement:exit"() {
|
|
456
|
-
loopDepth--;
|
|
457
|
-
},
|
|
458
|
-
ForOfStatement() {
|
|
459
|
-
loopDepth++;
|
|
460
|
-
},
|
|
461
|
-
"ForOfStatement:exit"() {
|
|
462
|
-
loopDepth--;
|
|
463
|
-
},
|
|
464
|
-
WhileStatement() {
|
|
465
|
-
loopDepth++;
|
|
466
|
-
},
|
|
467
|
-
"WhileStatement:exit"() {
|
|
468
|
-
loopDepth--;
|
|
469
|
-
},
|
|
470
|
-
DoWhileStatement() {
|
|
471
|
-
loopDepth++;
|
|
472
|
-
},
|
|
473
|
-
"DoWhileStatement:exit"() {
|
|
474
|
-
loopDepth--;
|
|
475
|
-
},
|
|
476
|
-
CallExpression(node) {
|
|
477
|
-
if (loopDepth > 0 && node.callee.type === "Identifier" && node.callee.name === "$state") {
|
|
478
|
-
context.report({
|
|
479
|
-
node,
|
|
480
|
-
messageId: "noStateInLoop"
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
};
|
|
487
|
-
var no_state_in_loop_default = rule6;
|
|
465
|
+
var no_nested_components_default = rule6;
|
|
488
466
|
|
|
489
467
|
// src/rules/no-state-destructure-write.ts
|
|
490
468
|
var rule7 = {
|
|
@@ -576,6 +554,65 @@ var rule7 = {
|
|
|
576
554
|
};
|
|
577
555
|
var no_state_destructure_write_default = rule7;
|
|
578
556
|
|
|
557
|
+
// src/rules/no-state-in-loop.ts
|
|
558
|
+
var rule8 = {
|
|
559
|
+
meta: {
|
|
560
|
+
type: "problem",
|
|
561
|
+
docs: {
|
|
562
|
+
description: "Disallow $state declarations inside loops",
|
|
563
|
+
recommended: true
|
|
564
|
+
},
|
|
565
|
+
messages: {
|
|
566
|
+
noStateInLoop: "$state should not be declared inside a loop. Move it outside the loop."
|
|
567
|
+
},
|
|
568
|
+
schema: []
|
|
569
|
+
},
|
|
570
|
+
create(context) {
|
|
571
|
+
let loopDepth = 0;
|
|
572
|
+
return {
|
|
573
|
+
ForStatement() {
|
|
574
|
+
loopDepth++;
|
|
575
|
+
},
|
|
576
|
+
"ForStatement:exit"() {
|
|
577
|
+
loopDepth--;
|
|
578
|
+
},
|
|
579
|
+
ForInStatement() {
|
|
580
|
+
loopDepth++;
|
|
581
|
+
},
|
|
582
|
+
"ForInStatement:exit"() {
|
|
583
|
+
loopDepth--;
|
|
584
|
+
},
|
|
585
|
+
ForOfStatement() {
|
|
586
|
+
loopDepth++;
|
|
587
|
+
},
|
|
588
|
+
"ForOfStatement:exit"() {
|
|
589
|
+
loopDepth--;
|
|
590
|
+
},
|
|
591
|
+
WhileStatement() {
|
|
592
|
+
loopDepth++;
|
|
593
|
+
},
|
|
594
|
+
"WhileStatement:exit"() {
|
|
595
|
+
loopDepth--;
|
|
596
|
+
},
|
|
597
|
+
DoWhileStatement() {
|
|
598
|
+
loopDepth++;
|
|
599
|
+
},
|
|
600
|
+
"DoWhileStatement:exit"() {
|
|
601
|
+
loopDepth--;
|
|
602
|
+
},
|
|
603
|
+
CallExpression(node) {
|
|
604
|
+
if (loopDepth > 0 && node.callee.type === "Identifier" && node.callee.name === "$state") {
|
|
605
|
+
context.report({
|
|
606
|
+
node,
|
|
607
|
+
messageId: "noStateInLoop"
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
var no_state_in_loop_default = rule8;
|
|
615
|
+
|
|
579
616
|
// src/rules/no-state-outside-component.ts
|
|
580
617
|
var isUpperCaseName = (name) => !!name && /^[A-Z]/.test(name);
|
|
581
618
|
var isHookName = (name) => !!name && /^use[A-Z]/.test(name);
|
|
@@ -643,7 +680,7 @@ var isDirectStateDeclaration = (node, ancestors) => {
|
|
|
643
680
|
if (parent.init !== node) return false;
|
|
644
681
|
return parent.id?.type === "Identifier";
|
|
645
682
|
};
|
|
646
|
-
var
|
|
683
|
+
var rule9 = {
|
|
647
684
|
meta: {
|
|
648
685
|
type: "problem",
|
|
649
686
|
docs: {
|
|
@@ -692,10 +729,358 @@ var rule8 = {
|
|
|
692
729
|
};
|
|
693
730
|
}
|
|
694
731
|
};
|
|
695
|
-
var no_state_outside_component_default =
|
|
732
|
+
var no_state_outside_component_default = rule9;
|
|
733
|
+
|
|
734
|
+
// src/rules/no-third-party-props-spread.ts
|
|
735
|
+
var rule10 = {
|
|
736
|
+
meta: {
|
|
737
|
+
type: "suggestion",
|
|
738
|
+
docs: {
|
|
739
|
+
description: "Warn on third-party object spreads in JSX props",
|
|
740
|
+
recommended: true
|
|
741
|
+
},
|
|
742
|
+
messages: {
|
|
743
|
+
thirdPartySpread: "Spreading third-party objects into props may hide reactive changes; prefer explicit props or map fields."
|
|
744
|
+
},
|
|
745
|
+
schema: [
|
|
746
|
+
{
|
|
747
|
+
type: "object",
|
|
748
|
+
properties: {
|
|
749
|
+
includeCallExpressions: {
|
|
750
|
+
type: "boolean",
|
|
751
|
+
description: "Also warn when JSX spread comes from a third-party call expression."
|
|
752
|
+
},
|
|
753
|
+
allow: {
|
|
754
|
+
type: "array",
|
|
755
|
+
items: { type: "string" },
|
|
756
|
+
description: "Module specifiers to treat as internal."
|
|
757
|
+
},
|
|
758
|
+
internalPrefixes: {
|
|
759
|
+
type: "array",
|
|
760
|
+
items: { type: "string" },
|
|
761
|
+
description: 'Import path prefixes to treat as internal (e.g. "@/", "~/" ).'
|
|
762
|
+
}
|
|
763
|
+
},
|
|
764
|
+
additionalProperties: false
|
|
765
|
+
}
|
|
766
|
+
]
|
|
767
|
+
},
|
|
768
|
+
create(context) {
|
|
769
|
+
const options = context.options[0] || {};
|
|
770
|
+
const allow = new Set(options.allow ?? []);
|
|
771
|
+
const internalPrefixes = options.internalPrefixes ?? [];
|
|
772
|
+
const includeCallExpressions = options.includeCallExpressions === true;
|
|
773
|
+
const thirdPartyImports = /* @__PURE__ */ new Set();
|
|
774
|
+
const isThirdPartySource = (source) => {
|
|
775
|
+
if (allow.has(source)) return false;
|
|
776
|
+
if (source.startsWith(".") || source.startsWith("/")) return false;
|
|
777
|
+
if (internalPrefixes.some((prefix) => source.startsWith(prefix))) return false;
|
|
778
|
+
return true;
|
|
779
|
+
};
|
|
780
|
+
const isComponentName = (name) => {
|
|
781
|
+
if (name.type === "JSXIdentifier") {
|
|
782
|
+
return /^[A-Z]/.test(name.name);
|
|
783
|
+
}
|
|
784
|
+
if (name.type === "JSXMemberExpression") {
|
|
785
|
+
return true;
|
|
786
|
+
}
|
|
787
|
+
return false;
|
|
788
|
+
};
|
|
789
|
+
const unwrapExpression = (expr) => {
|
|
790
|
+
let current = expr;
|
|
791
|
+
while (current) {
|
|
792
|
+
if (current.type === "ChainExpression") {
|
|
793
|
+
current = current.expression;
|
|
794
|
+
continue;
|
|
795
|
+
}
|
|
796
|
+
if (current.type === "TSAsExpression" || current.type === "TSTypeAssertion" || current.type === "TSNonNullExpression") {
|
|
797
|
+
current = current.expression;
|
|
798
|
+
continue;
|
|
799
|
+
}
|
|
800
|
+
break;
|
|
801
|
+
}
|
|
802
|
+
return current;
|
|
803
|
+
};
|
|
804
|
+
const getRootIdentifierName = (expr) => {
|
|
805
|
+
let current = unwrapExpression(expr);
|
|
806
|
+
while (current) {
|
|
807
|
+
if (current.type === "Identifier") return current.name;
|
|
808
|
+
if (current.type === "MemberExpression" || current.type === "OptionalMemberExpression") {
|
|
809
|
+
current = unwrapExpression(current.object);
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
if (includeCallExpressions && (current.type === "CallExpression" || current.type === "OptionalCallExpression")) {
|
|
813
|
+
current = unwrapExpression(current.callee);
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
816
|
+
return null;
|
|
817
|
+
}
|
|
818
|
+
return null;
|
|
819
|
+
};
|
|
820
|
+
return {
|
|
821
|
+
ImportDeclaration(node) {
|
|
822
|
+
if (!node.source?.value || typeof node.source.value !== "string") return;
|
|
823
|
+
if (!isThirdPartySource(node.source.value)) return;
|
|
824
|
+
for (const spec of node.specifiers ?? []) {
|
|
825
|
+
if (spec.local?.name) {
|
|
826
|
+
thirdPartyImports.add(spec.local.name);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
},
|
|
830
|
+
JSXOpeningElement(node) {
|
|
831
|
+
if (!isComponentName(node.name)) return;
|
|
832
|
+
for (const attr of node.attributes ?? []) {
|
|
833
|
+
if (attr.type !== "JSXSpreadAttribute") continue;
|
|
834
|
+
const expr = attr.argument;
|
|
835
|
+
if (!expr) continue;
|
|
836
|
+
if (expr.type === "CallExpression" && expr.callee?.type === "Identifier" && expr.callee.name === "mergeProps") {
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
const root = getRootIdentifierName(expr);
|
|
840
|
+
if (root && thirdPartyImports.has(root)) {
|
|
841
|
+
context.report({
|
|
842
|
+
node: attr,
|
|
843
|
+
messageId: "thirdPartySpread"
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
var no_third_party_props_spread_default = rule10;
|
|
852
|
+
|
|
853
|
+
// src/rules/no-unsafe-props-spread.ts
|
|
854
|
+
var rule11 = {
|
|
855
|
+
meta: {
|
|
856
|
+
type: "suggestion",
|
|
857
|
+
docs: {
|
|
858
|
+
description: "Warn on JSX spread sources that are too dynamic to keep props reactive",
|
|
859
|
+
recommended: true
|
|
860
|
+
},
|
|
861
|
+
messages: {
|
|
862
|
+
unsafeSpread: "JSX spread source is too dynamic to keep props reactive. Consider passing explicit props or using mergeProps(() => source)."
|
|
863
|
+
},
|
|
864
|
+
schema: [
|
|
865
|
+
{
|
|
866
|
+
type: "object",
|
|
867
|
+
properties: {
|
|
868
|
+
accessorNames: {
|
|
869
|
+
type: "array",
|
|
870
|
+
items: { type: "string" },
|
|
871
|
+
description: "Identifier names to treat as accessor functions."
|
|
872
|
+
},
|
|
873
|
+
accessorModules: {
|
|
874
|
+
type: "array",
|
|
875
|
+
items: { type: "string" },
|
|
876
|
+
description: "Module specifiers whose imports are accessor functions."
|
|
877
|
+
}
|
|
878
|
+
},
|
|
879
|
+
additionalProperties: false
|
|
880
|
+
}
|
|
881
|
+
]
|
|
882
|
+
},
|
|
883
|
+
create(context) {
|
|
884
|
+
const options = context.options[0] || {};
|
|
885
|
+
const accessorVars = new Set(options.accessorNames ?? []);
|
|
886
|
+
const accessorModules = new Set(options.accessorModules ?? []);
|
|
887
|
+
const isComponentName = (name) => {
|
|
888
|
+
if (name.type === "JSXIdentifier") {
|
|
889
|
+
return /^[A-Z]/.test(name.name);
|
|
890
|
+
}
|
|
891
|
+
if (name.type === "JSXMemberExpression") {
|
|
892
|
+
return true;
|
|
893
|
+
}
|
|
894
|
+
return false;
|
|
895
|
+
};
|
|
896
|
+
const recordAccessorVar = (node) => {
|
|
897
|
+
if (!node.init || node.id?.type !== "Identifier") return;
|
|
898
|
+
if (node.init.type !== "CallExpression") return;
|
|
899
|
+
if (node.init.callee?.type !== "Identifier") return;
|
|
900
|
+
const callee = node.init.callee.name;
|
|
901
|
+
if (callee === "$state" || callee === "$memo" || callee === "prop") {
|
|
902
|
+
accessorVars.add(node.id.name);
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
const isSafeAccessorExpr = (expr) => {
|
|
906
|
+
if (expr.type === "Identifier") return true;
|
|
907
|
+
if (expr.type === "CallExpression" && expr.callee?.type === "Identifier" && expr.arguments.length === 0 && accessorVars.has(expr.callee.name)) {
|
|
908
|
+
return true;
|
|
909
|
+
}
|
|
910
|
+
return false;
|
|
911
|
+
};
|
|
912
|
+
const isObviouslyDynamic = (expr) => expr.type === "ConditionalExpression" || expr.type === "LogicalExpression" || expr.type === "SequenceExpression" || expr.type === "AssignmentExpression" || expr.type === "UpdateExpression" || expr.type === "AwaitExpression" || expr.type === "NewExpression" || expr.type === "YieldExpression";
|
|
913
|
+
const hasUnsafeObjectLiteral = (obj) => {
|
|
914
|
+
for (const prop of obj.properties ?? []) {
|
|
915
|
+
if (prop.type === "SpreadElement") return true;
|
|
916
|
+
if (prop.type === "Property") {
|
|
917
|
+
if (prop.computed) return true;
|
|
918
|
+
if (prop.kind === "get" || prop.kind === "set") return true;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
return false;
|
|
922
|
+
};
|
|
923
|
+
const shouldWarnForSpreadExpr = (expr) => {
|
|
924
|
+
if (isSafeAccessorExpr(expr)) return false;
|
|
925
|
+
if (expr.type === "CallExpression" && expr.callee?.type === "Identifier" && expr.callee.name === "mergeProps") {
|
|
926
|
+
return false;
|
|
927
|
+
}
|
|
928
|
+
if (expr.type === "ObjectExpression") {
|
|
929
|
+
return hasUnsafeObjectLiteral(expr);
|
|
930
|
+
}
|
|
931
|
+
if (isObviouslyDynamic(expr)) return true;
|
|
932
|
+
if (expr.type === "CallExpression") return true;
|
|
933
|
+
if (expr.type === "MemberExpression") return true;
|
|
934
|
+
return false;
|
|
935
|
+
};
|
|
936
|
+
return {
|
|
937
|
+
ImportDeclaration(node) {
|
|
938
|
+
if (!node.source?.value || typeof node.source.value !== "string") return;
|
|
939
|
+
if (!accessorModules.has(node.source.value)) return;
|
|
940
|
+
for (const spec of node.specifiers ?? []) {
|
|
941
|
+
if (spec.local?.name) {
|
|
942
|
+
accessorVars.add(spec.local.name);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
},
|
|
946
|
+
VariableDeclarator: recordAccessorVar,
|
|
947
|
+
JSXOpeningElement(node) {
|
|
948
|
+
if (!isComponentName(node.name)) return;
|
|
949
|
+
for (const attr of node.attributes ?? []) {
|
|
950
|
+
if (attr.type !== "JSXSpreadAttribute") continue;
|
|
951
|
+
const expr = attr.argument;
|
|
952
|
+
if (!expr) continue;
|
|
953
|
+
if (shouldWarnForSpreadExpr(expr)) {
|
|
954
|
+
context.report({
|
|
955
|
+
node: attr,
|
|
956
|
+
messageId: "unsafeSpread"
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
var no_unsafe_props_spread_default = rule11;
|
|
965
|
+
|
|
966
|
+
// src/rules/no-unsupported-props-destructure.ts
|
|
967
|
+
var rule12 = {
|
|
968
|
+
meta: {
|
|
969
|
+
type: "suggestion",
|
|
970
|
+
docs: {
|
|
971
|
+
description: "Warn on unsupported props destructuring patterns in components",
|
|
972
|
+
recommended: true
|
|
973
|
+
},
|
|
974
|
+
messages: {
|
|
975
|
+
computedKey: "Computed property in props pattern cannot be made reactive.",
|
|
976
|
+
arrayRest: "Array rest in props destructuring falls back to non-reactive binding.",
|
|
977
|
+
nestedRest: "Nested props rest destructuring falls back to non-reactive binding; access props directly or use prop.",
|
|
978
|
+
nonFirstParam: "Props destructuring is only supported in the first parameter.",
|
|
979
|
+
fallback: "Props destructuring falls back to non-reactive binding."
|
|
980
|
+
},
|
|
981
|
+
schema: []
|
|
982
|
+
},
|
|
983
|
+
create(context) {
|
|
984
|
+
const isComponentName = (name) => /^[A-Z]/.test(name);
|
|
985
|
+
const getPropsPattern = (fnNode) => {
|
|
986
|
+
const params = fnNode?.params ?? [];
|
|
987
|
+
const first = params[0];
|
|
988
|
+
if (params.length > 1) {
|
|
989
|
+
params.slice(1).forEach((param) => {
|
|
990
|
+
if (!param) return;
|
|
991
|
+
const target = param.type === "AssignmentPattern" ? param.left : param;
|
|
992
|
+
if (target?.type === "ObjectPattern" || target?.type === "ArrayPattern") {
|
|
993
|
+
context.report({
|
|
994
|
+
node: target,
|
|
995
|
+
messageId: "nonFirstParam"
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
if (!first) return null;
|
|
1001
|
+
if (first.type === "ObjectPattern") return first;
|
|
1002
|
+
if (first.type === "AssignmentPattern" && first.left?.type === "ObjectPattern") {
|
|
1003
|
+
return first.left;
|
|
1004
|
+
}
|
|
1005
|
+
if (first.type === "ArrayPattern") {
|
|
1006
|
+
context.report({
|
|
1007
|
+
node: first,
|
|
1008
|
+
messageId: "fallback"
|
|
1009
|
+
});
|
|
1010
|
+
return null;
|
|
1011
|
+
}
|
|
1012
|
+
return null;
|
|
1013
|
+
};
|
|
1014
|
+
const reportOnce = (node, messageId) => {
|
|
1015
|
+
context.report({
|
|
1016
|
+
node,
|
|
1017
|
+
messageId
|
|
1018
|
+
});
|
|
1019
|
+
};
|
|
1020
|
+
const walkPattern = (pattern, depth) => {
|
|
1021
|
+
if (!pattern) return;
|
|
1022
|
+
if (pattern.type === "ObjectPattern") {
|
|
1023
|
+
for (const prop of pattern.properties ?? []) {
|
|
1024
|
+
if (prop.type === "RestElement") {
|
|
1025
|
+
if (depth > 0) {
|
|
1026
|
+
reportOnce(prop, "nestedRest");
|
|
1027
|
+
}
|
|
1028
|
+
continue;
|
|
1029
|
+
}
|
|
1030
|
+
if (prop.type !== "Property") {
|
|
1031
|
+
reportOnce(prop, "fallback");
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
if (prop.computed) {
|
|
1035
|
+
reportOnce(prop, "computedKey");
|
|
1036
|
+
continue;
|
|
1037
|
+
}
|
|
1038
|
+
const value = prop.value;
|
|
1039
|
+
if (value.type === "Identifier") {
|
|
1040
|
+
continue;
|
|
1041
|
+
}
|
|
1042
|
+
if (value.type === "AssignmentPattern" && value.left?.type === "Identifier") {
|
|
1043
|
+
continue;
|
|
1044
|
+
}
|
|
1045
|
+
if (value.type === "ObjectPattern") {
|
|
1046
|
+
walkPattern(value, depth + 1);
|
|
1047
|
+
continue;
|
|
1048
|
+
}
|
|
1049
|
+
if (value.type === "ArrayPattern") {
|
|
1050
|
+
const hasRest = (value.elements ?? []).some((el) => el?.type === "RestElement");
|
|
1051
|
+
reportOnce(value, hasRest ? "arrayRest" : "fallback");
|
|
1052
|
+
continue;
|
|
1053
|
+
}
|
|
1054
|
+
reportOnce(value, "fallback");
|
|
1055
|
+
}
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
if (pattern.type === "ArrayPattern") {
|
|
1059
|
+
reportOnce(pattern, "fallback");
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
return {
|
|
1063
|
+
FunctionDeclaration(node) {
|
|
1064
|
+
if (!node.id?.name || !isComponentName(node.id.name)) return;
|
|
1065
|
+
const pattern = getPropsPattern(node);
|
|
1066
|
+
if (pattern) walkPattern(pattern, 0);
|
|
1067
|
+
},
|
|
1068
|
+
VariableDeclarator(node) {
|
|
1069
|
+
if (!node.id || node.id.type !== "Identifier" || !isComponentName(node.id.name)) return;
|
|
1070
|
+
if (!node.init) return;
|
|
1071
|
+
if (node.init.type !== "ArrowFunctionExpression" && node.init.type !== "FunctionExpression") {
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
const pattern = getPropsPattern(node.init);
|
|
1075
|
+
if (pattern) walkPattern(pattern, 0);
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
var no_unsupported_props_destructure_default = rule12;
|
|
696
1081
|
|
|
697
1082
|
// src/rules/require-component-return.ts
|
|
698
|
-
var
|
|
1083
|
+
var rule13 = {
|
|
699
1084
|
meta: {
|
|
700
1085
|
type: "problem",
|
|
701
1086
|
docs: {
|
|
@@ -783,10 +1168,10 @@ var rule9 = {
|
|
|
783
1168
|
};
|
|
784
1169
|
}
|
|
785
1170
|
};
|
|
786
|
-
var require_component_return_default =
|
|
1171
|
+
var require_component_return_default = rule13;
|
|
787
1172
|
|
|
788
1173
|
// src/rules/require-list-key.ts
|
|
789
|
-
var
|
|
1174
|
+
var rule14 = {
|
|
790
1175
|
meta: {
|
|
791
1176
|
type: "problem",
|
|
792
1177
|
docs: {
|
|
@@ -877,7 +1262,7 @@ var rule10 = {
|
|
|
877
1262
|
};
|
|
878
1263
|
}
|
|
879
1264
|
};
|
|
880
|
-
var require_list_key_default =
|
|
1265
|
+
var require_list_key_default = rule14;
|
|
881
1266
|
|
|
882
1267
|
// src/index.ts
|
|
883
1268
|
var plugin = {
|
|
@@ -889,10 +1274,14 @@ var plugin = {
|
|
|
889
1274
|
"no-state-in-loop": no_state_in_loop_default,
|
|
890
1275
|
"no-direct-mutation": no_direct_mutation_default,
|
|
891
1276
|
"no-empty-effect": no_empty_effect_default,
|
|
1277
|
+
"no-computed-props-key": no_computed_props_key_default,
|
|
892
1278
|
"no-inline-functions": no_inline_functions_default,
|
|
893
1279
|
"no-state-destructure-write": no_state_destructure_write_default,
|
|
894
1280
|
"no-state-outside-component": no_state_outside_component_default,
|
|
895
1281
|
"no-nested-components": no_nested_components_default,
|
|
1282
|
+
"no-third-party-props-spread": no_third_party_props_spread_default,
|
|
1283
|
+
"no-unsafe-props-spread": no_unsafe_props_spread_default,
|
|
1284
|
+
"no-unsupported-props-destructure": no_unsupported_props_destructure_default,
|
|
896
1285
|
"require-list-key": require_list_key_default,
|
|
897
1286
|
"no-memo-side-effects": no_memo_side_effects_default,
|
|
898
1287
|
"require-component-return": require_component_return_default
|
|
@@ -905,12 +1294,16 @@ var plugin = {
|
|
|
905
1294
|
"fict/no-direct-mutation": "warn",
|
|
906
1295
|
"fict/no-empty-effect": "warn",
|
|
907
1296
|
// FICT-E001
|
|
1297
|
+
"fict/no-computed-props-key": "warn",
|
|
908
1298
|
"fict/no-inline-functions": "warn",
|
|
909
1299
|
// FICT-X003
|
|
910
1300
|
"fict/no-state-destructure-write": "error",
|
|
911
1301
|
"fict/no-state-outside-component": "error",
|
|
912
1302
|
"fict/no-nested-components": "error",
|
|
913
1303
|
// FICT-C003
|
|
1304
|
+
"fict/no-third-party-props-spread": "warn",
|
|
1305
|
+
"fict/no-unsafe-props-spread": "warn",
|
|
1306
|
+
"fict/no-unsupported-props-destructure": "warn",
|
|
914
1307
|
"fict/require-list-key": "error",
|
|
915
1308
|
// FICT-J002
|
|
916
1309
|
"fict/no-memo-side-effects": "warn",
|
|
@@ -924,6 +1317,7 @@ var plugin = {
|
|
|
924
1317
|
var index_default = plugin;
|
|
925
1318
|
// Annotate the CommonJS export names for ESM import in node:
|
|
926
1319
|
0 && (module.exports = {
|
|
1320
|
+
noComputedPropsKey,
|
|
927
1321
|
noDirectMutation,
|
|
928
1322
|
noEmptyEffect,
|
|
929
1323
|
noInlineFunctions,
|
|
@@ -932,6 +1326,9 @@ var index_default = plugin;
|
|
|
932
1326
|
noStateDestructureWrite,
|
|
933
1327
|
noStateInLoop,
|
|
934
1328
|
noStateOutsideComponent,
|
|
1329
|
+
noThirdPartyPropsSpread,
|
|
1330
|
+
noUnsafePropsSpread,
|
|
1331
|
+
noUnsupportedPropsDestructure,
|
|
935
1332
|
requireComponentReturn,
|
|
936
1333
|
requireListKey
|
|
937
1334
|
});
|