@wsxjs/eslint-plugin-wsx 0.0.5

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.
@@ -0,0 +1,55 @@
1
+ /**
2
+ * ESLint 规则:no-react-imports
3
+ *
4
+ * 禁止在 WSX 文件中导入 React 相关模块
5
+ */
6
+
7
+ import { Rule } from "eslint";
8
+ import { WSXRuleModule } from "../types";
9
+
10
+ export const noReactImports: WSXRuleModule = {
11
+ meta: {
12
+ type: "problem",
13
+ docs: {
14
+ description: "disallow React imports in WSX files",
15
+ category: "Best Practices",
16
+ recommended: true,
17
+ },
18
+ fixable: "code",
19
+ messages: {
20
+ noReactImport: "Do not import React in WSX files. Use 'h' function instead",
21
+ },
22
+ schema: [], // 无配置选项
23
+ },
24
+ create(context: Rule.RuleContext) {
25
+ const reactModules = [
26
+ "react",
27
+ "react-dom",
28
+ "react-dom/client",
29
+ "react-hooks",
30
+ "@types/react",
31
+ "@types/react-dom",
32
+ ];
33
+
34
+ return {
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ ImportDeclaration(node: any) {
37
+ const source = node.source.value;
38
+ if (
39
+ typeof source === "string" &&
40
+ reactModules.some(
41
+ (module) => source === module || source.startsWith(module + "/")
42
+ )
43
+ ) {
44
+ context.report({
45
+ node,
46
+ messageId: "noReactImport",
47
+ fix(fixer) {
48
+ return fixer.remove(node);
49
+ },
50
+ });
51
+ }
52
+ },
53
+ };
54
+ },
55
+ };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * ESLint 规则:render-method-required
3
+ *
4
+ * 确保继承 WebComponent 的类实现 render() 方法
5
+ */
6
+
7
+ import { Rule } from "eslint";
8
+ import { WSXRuleModule } from "../types";
9
+
10
+ export const renderMethodRequired: WSXRuleModule = {
11
+ meta: {
12
+ type: "problem",
13
+ docs: {
14
+ description: "require WSX components to implement render method",
15
+ category: "Possible Errors",
16
+ recommended: true,
17
+ },
18
+ messages: {
19
+ missingRenderMethod:
20
+ "WSX component '{{componentName}}' must implement a render() method",
21
+ },
22
+ schema: [], // 无配置选项
23
+ },
24
+ create(context: Rule.RuleContext) {
25
+ return {
26
+ ClassDeclaration(node: import("estree").ClassDeclaration) {
27
+ // 检查是否继承自 WebComponent
28
+ const isWebComponent =
29
+ node.superClass &&
30
+ node.superClass.type === "Identifier" &&
31
+ node.superClass.name === "WebComponent";
32
+
33
+ if (!isWebComponent) return;
34
+
35
+ const componentName = node.id?.name || "Unknown";
36
+ const hasRenderMethod = node.body.body.some(
37
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
+ (member: any) =>
39
+ member.type === "MethodDefinition" &&
40
+ member.key.type === "Identifier" &&
41
+ member.key.name === "render" &&
42
+ member.value.body !== null
43
+ );
44
+
45
+ if (!hasRenderMethod) {
46
+ context.report({
47
+ node,
48
+ messageId: "missingRenderMethod",
49
+ data: { componentName },
50
+ });
51
+ }
52
+ },
53
+ };
54
+ },
55
+ };
@@ -0,0 +1,97 @@
1
+ /**
2
+ * ESLint 规则:web-component-naming
3
+ *
4
+ * 强制 Web Component 命名规范(必须包含连字符)
5
+ */
6
+
7
+ import { Rule } from "eslint";
8
+ import { WSXRuleModule } from "../types";
9
+
10
+ export const webComponentNaming: WSXRuleModule = {
11
+ meta: {
12
+ type: "suggestion",
13
+ docs: {
14
+ description: "enforce Web Component naming conventions",
15
+ category: "Stylistic Issues",
16
+ recommended: true,
17
+ },
18
+ messages: {
19
+ tagNameNeedsHyphen:
20
+ "Web Component tag name '{{tagName}}' must contain at least one hyphen",
21
+ tagNameReserved: "Tag name '{{tagName}}' conflicts with HTML standard elements",
22
+ },
23
+ schema: [], // 无配置选项
24
+ },
25
+ create(context: Rule.RuleContext) {
26
+ const htmlElements = new Set([
27
+ "div",
28
+ "span",
29
+ "p",
30
+ "a",
31
+ "button",
32
+ "input",
33
+ "form",
34
+ "img",
35
+ "h1",
36
+ "h2",
37
+ "h3",
38
+ "ul",
39
+ "li",
40
+ "table",
41
+ "tr",
42
+ "td",
43
+ "th",
44
+ "section",
45
+ "article",
46
+ "header",
47
+ "footer",
48
+ ]);
49
+
50
+ return {
51
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
+ Decorator(node: any) {
53
+ if (
54
+ node.expression.type === "CallExpression" &&
55
+ node.expression.callee.type === "Identifier" &&
56
+ node.expression.callee.name === "autoRegister"
57
+ ) {
58
+ const args = node.expression.arguments;
59
+ if (args.length > 0 && args[0].type === "ObjectExpression") {
60
+ const tagNameProp = args[0].properties.find(
61
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
+ (prop: any) =>
63
+ prop.type === "Property" &&
64
+ prop.key &&
65
+ prop.key.type === "Identifier" &&
66
+ prop.key.name === "tagName"
67
+ );
68
+
69
+ if (
70
+ tagNameProp &&
71
+ tagNameProp.type === "Property" &&
72
+ tagNameProp.value.type === "Literal"
73
+ ) {
74
+ const tagName = tagNameProp.value.value;
75
+
76
+ if (typeof tagName === "string") {
77
+ if (htmlElements.has(tagName)) {
78
+ context.report({
79
+ node: tagNameProp.value,
80
+ messageId: "tagNameReserved",
81
+ data: { tagName },
82
+ });
83
+ } else if (!tagName.includes("-")) {
84
+ context.report({
85
+ node: tagNameProp.value,
86
+ messageId: "tagNameNeedsHyphen",
87
+ data: { tagName },
88
+ });
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
94
+ },
95
+ };
96
+ },
97
+ };
package/src/types.ts ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * TypeScript 类型定义 - WSX ESLint 插件
3
+ */
4
+
5
+ import { Rule } from "eslint";
6
+
7
+ export interface WSXRuleContext extends Rule.RuleContext {
8
+ // WSX 特定的上下文扩展
9
+ report: (descriptor: Rule.ReportDescriptor) => void;
10
+ }
11
+
12
+ export interface WSXRuleModule extends Rule.RuleModule {
13
+ // WSX 特定的规则模块扩展
14
+ create: (context: WSXRuleContext) => Rule.RuleListener;
15
+ }
16
+
17
+ export interface WSXConfig {
18
+ parser?: string;
19
+ parserOptions?: {
20
+ ecmaVersion?: string | number;
21
+ sourceType?: "script" | "module";
22
+ ecmaFeatures?: {
23
+ jsx?: boolean;
24
+ };
25
+ jsxPragma?: string;
26
+ jsxFragmentName?: string;
27
+ };
28
+ plugins?: string[];
29
+ rules?: Record<string, unknown>;
30
+ globals?: Record<string, "readonly" | "writable">;
31
+ settings?: Record<string, unknown>;
32
+ }
33
+
34
+ export interface WSXPluginMeta {
35
+ name: string;
36
+ version: string;
37
+ }
38
+
39
+ export interface WSXPlugin {
40
+ meta: WSXPluginMeta;
41
+ rules: Record<string, WSXRuleModule>;
42
+ configs: Record<string, WSXConfig>;
43
+ [key: string]: unknown;
44
+ }