@wsxjs/eslint-plugin-wsx 0.0.21 → 0.0.23
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.js +115 -1
- package/dist/index.mjs +115 -1
- package/package.json +2 -2
- package/src/configs/recommended.ts +1 -0
- package/src/index.ts +2 -0
- package/src/rules/lifecycle-must-call-super.ts +155 -0
package/dist/index.js
CHANGED
|
@@ -669,6 +669,118 @@ var noDuplicateKeys = {
|
|
|
669
669
|
}
|
|
670
670
|
};
|
|
671
671
|
|
|
672
|
+
// src/rules/lifecycle-must-call-super.ts
|
|
673
|
+
var lifecycleMustCallSuper = {
|
|
674
|
+
meta: {
|
|
675
|
+
type: "problem",
|
|
676
|
+
docs: {
|
|
677
|
+
description: "Enforce that lifecycle methods (onConnected, onDisconnected, onRendered) call super",
|
|
678
|
+
category: "Possible Errors",
|
|
679
|
+
recommended: true
|
|
680
|
+
},
|
|
681
|
+
messages: {
|
|
682
|
+
mustCallSuper: "Lifecycle method '{{methodName}}' must call 'super.{{methodName}}()' to ensure proper initialization."
|
|
683
|
+
},
|
|
684
|
+
schema: []
|
|
685
|
+
},
|
|
686
|
+
defaultOptions: [],
|
|
687
|
+
create(context) {
|
|
688
|
+
const lifecycleMethods = /* @__PURE__ */ new Set(["onConnected", "onDisconnected", "onRendered"]);
|
|
689
|
+
function checkForSuperCall(node, methodName, visited = /* @__PURE__ */ new WeakSet()) {
|
|
690
|
+
if (visited.has(node)) {
|
|
691
|
+
return false;
|
|
692
|
+
}
|
|
693
|
+
visited.add(node);
|
|
694
|
+
if (node.type === "CallExpression") {
|
|
695
|
+
const callee = node.callee;
|
|
696
|
+
if (callee.type === "MemberExpression" && callee.object.type === "Super" && callee.property.type === "Identifier" && callee.property.name === methodName) {
|
|
697
|
+
return true;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
const nodeAny = node;
|
|
701
|
+
const keysToCheck = [
|
|
702
|
+
"body",
|
|
703
|
+
"consequent",
|
|
704
|
+
"alternate",
|
|
705
|
+
"block",
|
|
706
|
+
"handler",
|
|
707
|
+
"finalizer",
|
|
708
|
+
"argument",
|
|
709
|
+
"callee",
|
|
710
|
+
"expression",
|
|
711
|
+
"declarations",
|
|
712
|
+
"init",
|
|
713
|
+
"test",
|
|
714
|
+
"update",
|
|
715
|
+
"cases",
|
|
716
|
+
"discriminant",
|
|
717
|
+
"object",
|
|
718
|
+
"property",
|
|
719
|
+
"elements",
|
|
720
|
+
"properties",
|
|
721
|
+
"value",
|
|
722
|
+
"key"
|
|
723
|
+
];
|
|
724
|
+
for (const key of keysToCheck) {
|
|
725
|
+
const child = nodeAny[key];
|
|
726
|
+
if (Array.isArray(child)) {
|
|
727
|
+
for (const item of child) {
|
|
728
|
+
if (item && typeof item === "object" && "type" in item) {
|
|
729
|
+
if (checkForSuperCall(
|
|
730
|
+
item,
|
|
731
|
+
methodName,
|
|
732
|
+
visited
|
|
733
|
+
)) {
|
|
734
|
+
return true;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
} else if (child && typeof child === "object" && "type" in child) {
|
|
739
|
+
if (checkForSuperCall(child, methodName, visited)) {
|
|
740
|
+
return true;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
return false;
|
|
745
|
+
}
|
|
746
|
+
return {
|
|
747
|
+
MethodDefinition(node) {
|
|
748
|
+
if (node.accessibility !== "protected") {
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
const methodName = node.key.type === "Identifier" ? node.key.name : null;
|
|
752
|
+
if (!methodName || !lifecycleMethods.has(methodName)) {
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
const methodValue = node.value;
|
|
756
|
+
if (!methodValue || methodValue.type !== "FunctionExpression") {
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
const body = methodValue.body;
|
|
760
|
+
if (!body || body.type !== "BlockStatement") {
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
let hasSuperCall = false;
|
|
764
|
+
for (const statement of body.body) {
|
|
765
|
+
if (checkForSuperCall(statement, methodName)) {
|
|
766
|
+
hasSuperCall = true;
|
|
767
|
+
break;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
if (!hasSuperCall) {
|
|
771
|
+
context.report({
|
|
772
|
+
node: node.key,
|
|
773
|
+
messageId: "mustCallSuper",
|
|
774
|
+
data: {
|
|
775
|
+
methodName
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
|
|
672
784
|
// src/configs/recommended.ts
|
|
673
785
|
var recommendedConfig = {
|
|
674
786
|
parser: "@typescript-eslint/parser",
|
|
@@ -695,6 +807,7 @@ var recommendedConfig = {
|
|
|
695
807
|
"wsx/no-inner-html": "error",
|
|
696
808
|
"wsx/i18n-after-autoregister": "error",
|
|
697
809
|
"wsx/no-duplicate-keys": "error",
|
|
810
|
+
"wsx/lifecycle-must-call-super": "error",
|
|
698
811
|
// TypeScript 规则(推荐)
|
|
699
812
|
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
|
|
700
813
|
"@typescript-eslint/no-explicit-any": "warn",
|
|
@@ -865,7 +978,8 @@ var plugin = {
|
|
|
865
978
|
"no-null-render": noNullRender,
|
|
866
979
|
"no-inner-html": noInnerHTML,
|
|
867
980
|
"i18n-after-autoregister": i18nAfterAutoRegister,
|
|
868
|
-
"no-duplicate-keys": noDuplicateKeys
|
|
981
|
+
"no-duplicate-keys": noDuplicateKeys,
|
|
982
|
+
"lifecycle-must-call-super": lifecycleMustCallSuper
|
|
869
983
|
},
|
|
870
984
|
// 配置预设
|
|
871
985
|
configs: {
|
package/dist/index.mjs
CHANGED
|
@@ -640,6 +640,118 @@ var noDuplicateKeys = {
|
|
|
640
640
|
}
|
|
641
641
|
};
|
|
642
642
|
|
|
643
|
+
// src/rules/lifecycle-must-call-super.ts
|
|
644
|
+
var lifecycleMustCallSuper = {
|
|
645
|
+
meta: {
|
|
646
|
+
type: "problem",
|
|
647
|
+
docs: {
|
|
648
|
+
description: "Enforce that lifecycle methods (onConnected, onDisconnected, onRendered) call super",
|
|
649
|
+
category: "Possible Errors",
|
|
650
|
+
recommended: true
|
|
651
|
+
},
|
|
652
|
+
messages: {
|
|
653
|
+
mustCallSuper: "Lifecycle method '{{methodName}}' must call 'super.{{methodName}}()' to ensure proper initialization."
|
|
654
|
+
},
|
|
655
|
+
schema: []
|
|
656
|
+
},
|
|
657
|
+
defaultOptions: [],
|
|
658
|
+
create(context) {
|
|
659
|
+
const lifecycleMethods = /* @__PURE__ */ new Set(["onConnected", "onDisconnected", "onRendered"]);
|
|
660
|
+
function checkForSuperCall(node, methodName, visited = /* @__PURE__ */ new WeakSet()) {
|
|
661
|
+
if (visited.has(node)) {
|
|
662
|
+
return false;
|
|
663
|
+
}
|
|
664
|
+
visited.add(node);
|
|
665
|
+
if (node.type === "CallExpression") {
|
|
666
|
+
const callee = node.callee;
|
|
667
|
+
if (callee.type === "MemberExpression" && callee.object.type === "Super" && callee.property.type === "Identifier" && callee.property.name === methodName) {
|
|
668
|
+
return true;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
const nodeAny = node;
|
|
672
|
+
const keysToCheck = [
|
|
673
|
+
"body",
|
|
674
|
+
"consequent",
|
|
675
|
+
"alternate",
|
|
676
|
+
"block",
|
|
677
|
+
"handler",
|
|
678
|
+
"finalizer",
|
|
679
|
+
"argument",
|
|
680
|
+
"callee",
|
|
681
|
+
"expression",
|
|
682
|
+
"declarations",
|
|
683
|
+
"init",
|
|
684
|
+
"test",
|
|
685
|
+
"update",
|
|
686
|
+
"cases",
|
|
687
|
+
"discriminant",
|
|
688
|
+
"object",
|
|
689
|
+
"property",
|
|
690
|
+
"elements",
|
|
691
|
+
"properties",
|
|
692
|
+
"value",
|
|
693
|
+
"key"
|
|
694
|
+
];
|
|
695
|
+
for (const key of keysToCheck) {
|
|
696
|
+
const child = nodeAny[key];
|
|
697
|
+
if (Array.isArray(child)) {
|
|
698
|
+
for (const item of child) {
|
|
699
|
+
if (item && typeof item === "object" && "type" in item) {
|
|
700
|
+
if (checkForSuperCall(
|
|
701
|
+
item,
|
|
702
|
+
methodName,
|
|
703
|
+
visited
|
|
704
|
+
)) {
|
|
705
|
+
return true;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
} else if (child && typeof child === "object" && "type" in child) {
|
|
710
|
+
if (checkForSuperCall(child, methodName, visited)) {
|
|
711
|
+
return true;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
return false;
|
|
716
|
+
}
|
|
717
|
+
return {
|
|
718
|
+
MethodDefinition(node) {
|
|
719
|
+
if (node.accessibility !== "protected") {
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
const methodName = node.key.type === "Identifier" ? node.key.name : null;
|
|
723
|
+
if (!methodName || !lifecycleMethods.has(methodName)) {
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
const methodValue = node.value;
|
|
727
|
+
if (!methodValue || methodValue.type !== "FunctionExpression") {
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
const body = methodValue.body;
|
|
731
|
+
if (!body || body.type !== "BlockStatement") {
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
let hasSuperCall = false;
|
|
735
|
+
for (const statement of body.body) {
|
|
736
|
+
if (checkForSuperCall(statement, methodName)) {
|
|
737
|
+
hasSuperCall = true;
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
if (!hasSuperCall) {
|
|
742
|
+
context.report({
|
|
743
|
+
node: node.key,
|
|
744
|
+
messageId: "mustCallSuper",
|
|
745
|
+
data: {
|
|
746
|
+
methodName
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
|
|
643
755
|
// src/configs/recommended.ts
|
|
644
756
|
var recommendedConfig = {
|
|
645
757
|
parser: "@typescript-eslint/parser",
|
|
@@ -666,6 +778,7 @@ var recommendedConfig = {
|
|
|
666
778
|
"wsx/no-inner-html": "error",
|
|
667
779
|
"wsx/i18n-after-autoregister": "error",
|
|
668
780
|
"wsx/no-duplicate-keys": "error",
|
|
781
|
+
"wsx/lifecycle-must-call-super": "error",
|
|
669
782
|
// TypeScript 规则(推荐)
|
|
670
783
|
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
|
|
671
784
|
"@typescript-eslint/no-explicit-any": "warn",
|
|
@@ -836,7 +949,8 @@ var plugin = {
|
|
|
836
949
|
"no-null-render": noNullRender,
|
|
837
950
|
"no-inner-html": noInnerHTML,
|
|
838
951
|
"i18n-after-autoregister": i18nAfterAutoRegister,
|
|
839
|
-
"no-duplicate-keys": noDuplicateKeys
|
|
952
|
+
"no-duplicate-keys": noDuplicateKeys,
|
|
953
|
+
"lifecycle-must-call-super": lifecycleMustCallSuper
|
|
840
954
|
},
|
|
841
955
|
// 配置预设
|
|
842
956
|
configs: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wsxjs/eslint-plugin-wsx",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
4
4
|
"description": "ESLint plugin for WSXJS",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"web-components"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@wsxjs/wsx-core": "0.0.
|
|
28
|
+
"@wsxjs/wsx-core": "0.0.23"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"tsup": "^8.0.0",
|
|
@@ -30,6 +30,7 @@ export const recommendedConfig: WSXConfig = {
|
|
|
30
30
|
"wsx/no-inner-html": "error",
|
|
31
31
|
"wsx/i18n-after-autoregister": "error",
|
|
32
32
|
"wsx/no-duplicate-keys": "error",
|
|
33
|
+
"wsx/lifecycle-must-call-super": "error",
|
|
33
34
|
|
|
34
35
|
// TypeScript 规则(推荐)
|
|
35
36
|
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ import { noNullRender } from "./rules/no-null-render";
|
|
|
14
14
|
import { noInnerHTML } from "./rules/no-inner-html";
|
|
15
15
|
import { i18nAfterAutoRegister } from "./rules/i18n-after-autoregister";
|
|
16
16
|
import { noDuplicateKeys } from "./rules/no-duplicate-keys";
|
|
17
|
+
import { lifecycleMustCallSuper } from "./rules/lifecycle-must-call-super";
|
|
17
18
|
import { recommendedConfig } from "./configs/recommended";
|
|
18
19
|
import { createFlatConfig } from "./configs/flat";
|
|
19
20
|
import { WSXPlugin } from "./types";
|
|
@@ -36,6 +37,7 @@ const plugin: WSXPlugin = {
|
|
|
36
37
|
"no-inner-html": noInnerHTML,
|
|
37
38
|
"i18n-after-autoregister": i18nAfterAutoRegister,
|
|
38
39
|
"no-duplicate-keys": noDuplicateKeys,
|
|
40
|
+
"lifecycle-must-call-super": lifecycleMustCallSuper,
|
|
39
41
|
},
|
|
40
42
|
|
|
41
43
|
// 配置预设
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint 规则:lifecycle-must-call-super
|
|
3
|
+
*
|
|
4
|
+
* 确保生命周期方法(onConnected, onDisconnected, onRendered)调用 super
|
|
5
|
+
* 这些方法在 BaseComponent 中是可选的,但如果被重写,应该调用 super
|
|
6
|
+
* 以确保正确的初始化和清理
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Rule } from "eslint";
|
|
10
|
+
import { WSXRuleModule } from "../types";
|
|
11
|
+
|
|
12
|
+
export const lifecycleMustCallSuper: WSXRuleModule = {
|
|
13
|
+
meta: {
|
|
14
|
+
type: "problem",
|
|
15
|
+
docs: {
|
|
16
|
+
description:
|
|
17
|
+
"Enforce that lifecycle methods (onConnected, onDisconnected, onRendered) call super",
|
|
18
|
+
category: "Possible Errors",
|
|
19
|
+
recommended: true,
|
|
20
|
+
},
|
|
21
|
+
messages: {
|
|
22
|
+
mustCallSuper:
|
|
23
|
+
"Lifecycle method '{{methodName}}' must call 'super.{{methodName}}()' to ensure proper initialization.",
|
|
24
|
+
},
|
|
25
|
+
schema: [],
|
|
26
|
+
},
|
|
27
|
+
defaultOptions: [],
|
|
28
|
+
create(context: Rule.RuleContext) {
|
|
29
|
+
const lifecycleMethods = new Set(["onConnected", "onDisconnected", "onRendered"]);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 递归检查节点中是否有 super.methodName() 调用
|
|
33
|
+
* 使用 visited 集合避免重复检查同一个节点(防止循环引用导致的无限递归)
|
|
34
|
+
*/
|
|
35
|
+
function checkForSuperCall(
|
|
36
|
+
node: import("estree").Node,
|
|
37
|
+
methodName: string,
|
|
38
|
+
visited = new WeakSet<import("estree").Node>()
|
|
39
|
+
): boolean {
|
|
40
|
+
// 避免重复检查同一个节点
|
|
41
|
+
if (visited.has(node)) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
visited.add(node);
|
|
45
|
+
|
|
46
|
+
if (node.type === "CallExpression") {
|
|
47
|
+
const callee = node.callee;
|
|
48
|
+
if (
|
|
49
|
+
callee.type === "MemberExpression" &&
|
|
50
|
+
callee.object.type === "Super" &&
|
|
51
|
+
callee.property.type === "Identifier" &&
|
|
52
|
+
callee.property.name === methodName
|
|
53
|
+
) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 递归检查子节点(只检查常见的子节点类型,避免遍历所有属性)
|
|
59
|
+
const nodeAny = node as unknown as Record<string, unknown>;
|
|
60
|
+
const keysToCheck = [
|
|
61
|
+
"body",
|
|
62
|
+
"consequent",
|
|
63
|
+
"alternate",
|
|
64
|
+
"block",
|
|
65
|
+
"handler",
|
|
66
|
+
"finalizer",
|
|
67
|
+
"argument",
|
|
68
|
+
"callee",
|
|
69
|
+
"expression",
|
|
70
|
+
"declarations",
|
|
71
|
+
"init",
|
|
72
|
+
"test",
|
|
73
|
+
"update",
|
|
74
|
+
"cases",
|
|
75
|
+
"discriminant",
|
|
76
|
+
"object",
|
|
77
|
+
"property",
|
|
78
|
+
"elements",
|
|
79
|
+
"properties",
|
|
80
|
+
"value",
|
|
81
|
+
"key",
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
for (const key of keysToCheck) {
|
|
85
|
+
const child = nodeAny[key];
|
|
86
|
+
if (Array.isArray(child)) {
|
|
87
|
+
for (const item of child) {
|
|
88
|
+
if (item && typeof item === "object" && "type" in item) {
|
|
89
|
+
if (
|
|
90
|
+
checkForSuperCall(
|
|
91
|
+
item as import("estree").Node,
|
|
92
|
+
methodName,
|
|
93
|
+
visited
|
|
94
|
+
)
|
|
95
|
+
) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} else if (child && typeof child === "object" && "type" in child) {
|
|
101
|
+
if (checkForSuperCall(child as import("estree").Node, methodName, visited)) {
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
MethodDefinition(node: import("estree").MethodDefinition) {
|
|
112
|
+
// 只检查 protected 方法
|
|
113
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
114
|
+
if ((node as any).accessibility !== "protected") {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const methodName = node.key.type === "Identifier" ? node.key.name : null;
|
|
119
|
+
if (!methodName || !lifecycleMethods.has(methodName)) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 检查方法体中是否调用了 super
|
|
124
|
+
const methodValue = node.value;
|
|
125
|
+
if (!methodValue || methodValue.type !== "FunctionExpression") {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const body = methodValue.body;
|
|
130
|
+
if (!body || body.type !== "BlockStatement") {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 检查是否有 super 调用
|
|
135
|
+
let hasSuperCall = false;
|
|
136
|
+
for (const statement of body.body) {
|
|
137
|
+
if (checkForSuperCall(statement, methodName)) {
|
|
138
|
+
hasSuperCall = true;
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!hasSuperCall) {
|
|
144
|
+
context.report({
|
|
145
|
+
node: node.key,
|
|
146
|
+
messageId: "mustCallSuper",
|
|
147
|
+
data: {
|
|
148
|
+
methodName,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
};
|