@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.
package/dist/index.mjs ADDED
@@ -0,0 +1,358 @@
1
+ // src/rules/render-method-required.ts
2
+ var renderMethodRequired = {
3
+ meta: {
4
+ type: "problem",
5
+ docs: {
6
+ description: "require WSX components to implement render method",
7
+ category: "Possible Errors",
8
+ recommended: true
9
+ },
10
+ messages: {
11
+ missingRenderMethod: "WSX component '{{componentName}}' must implement a render() method"
12
+ },
13
+ schema: []
14
+ // 无配置选项
15
+ },
16
+ create(context) {
17
+ return {
18
+ ClassDeclaration(node) {
19
+ const isWebComponent = node.superClass && node.superClass.type === "Identifier" && node.superClass.name === "WebComponent";
20
+ if (!isWebComponent) return;
21
+ const componentName = node.id?.name || "Unknown";
22
+ const hasRenderMethod = node.body.body.some(
23
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
+ (member) => member.type === "MethodDefinition" && member.key.type === "Identifier" && member.key.name === "render" && member.value.body !== null
25
+ );
26
+ if (!hasRenderMethod) {
27
+ context.report({
28
+ node,
29
+ messageId: "missingRenderMethod",
30
+ data: { componentName }
31
+ });
32
+ }
33
+ }
34
+ };
35
+ }
36
+ };
37
+
38
+ // src/rules/no-react-imports.ts
39
+ var noReactImports = {
40
+ meta: {
41
+ type: "problem",
42
+ docs: {
43
+ description: "disallow React imports in WSX files",
44
+ category: "Best Practices",
45
+ recommended: true
46
+ },
47
+ fixable: "code",
48
+ messages: {
49
+ noReactImport: "Do not import React in WSX files. Use 'h' function instead"
50
+ },
51
+ schema: []
52
+ // 无配置选项
53
+ },
54
+ create(context) {
55
+ const reactModules = [
56
+ "react",
57
+ "react-dom",
58
+ "react-dom/client",
59
+ "react-hooks",
60
+ "@types/react",
61
+ "@types/react-dom"
62
+ ];
63
+ return {
64
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
65
+ ImportDeclaration(node) {
66
+ const source = node.source.value;
67
+ if (typeof source === "string" && reactModules.some(
68
+ (module) => source === module || source.startsWith(module + "/")
69
+ )) {
70
+ context.report({
71
+ node,
72
+ messageId: "noReactImport",
73
+ fix(fixer) {
74
+ return fixer.remove(node);
75
+ }
76
+ });
77
+ }
78
+ }
79
+ };
80
+ }
81
+ };
82
+
83
+ // src/rules/web-component-naming.ts
84
+ var webComponentNaming = {
85
+ meta: {
86
+ type: "suggestion",
87
+ docs: {
88
+ description: "enforce Web Component naming conventions",
89
+ category: "Stylistic Issues",
90
+ recommended: true
91
+ },
92
+ messages: {
93
+ tagNameNeedsHyphen: "Web Component tag name '{{tagName}}' must contain at least one hyphen",
94
+ tagNameReserved: "Tag name '{{tagName}}' conflicts with HTML standard elements"
95
+ },
96
+ schema: []
97
+ // 无配置选项
98
+ },
99
+ create(context) {
100
+ const htmlElements = /* @__PURE__ */ new Set([
101
+ "div",
102
+ "span",
103
+ "p",
104
+ "a",
105
+ "button",
106
+ "input",
107
+ "form",
108
+ "img",
109
+ "h1",
110
+ "h2",
111
+ "h3",
112
+ "ul",
113
+ "li",
114
+ "table",
115
+ "tr",
116
+ "td",
117
+ "th",
118
+ "section",
119
+ "article",
120
+ "header",
121
+ "footer"
122
+ ]);
123
+ return {
124
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
125
+ Decorator(node) {
126
+ if (node.expression.type === "CallExpression" && node.expression.callee.type === "Identifier" && node.expression.callee.name === "autoRegister") {
127
+ const args = node.expression.arguments;
128
+ if (args.length > 0 && args[0].type === "ObjectExpression") {
129
+ const tagNameProp = args[0].properties.find(
130
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
131
+ (prop) => prop.type === "Property" && prop.key && prop.key.type === "Identifier" && prop.key.name === "tagName"
132
+ );
133
+ if (tagNameProp && tagNameProp.type === "Property" && tagNameProp.value.type === "Literal") {
134
+ const tagName = tagNameProp.value.value;
135
+ if (typeof tagName === "string") {
136
+ if (htmlElements.has(tagName)) {
137
+ context.report({
138
+ node: tagNameProp.value,
139
+ messageId: "tagNameReserved",
140
+ data: { tagName }
141
+ });
142
+ } else if (!tagName.includes("-")) {
143
+ context.report({
144
+ node: tagNameProp.value,
145
+ messageId: "tagNameNeedsHyphen",
146
+ data: { tagName }
147
+ });
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }
153
+ }
154
+ };
155
+ }
156
+ };
157
+
158
+ // src/configs/recommended.ts
159
+ var recommendedConfig = {
160
+ parser: "@typescript-eslint/parser",
161
+ parserOptions: {
162
+ ecmaVersion: "latest",
163
+ sourceType: "module",
164
+ ecmaFeatures: {
165
+ jsx: true
166
+ },
167
+ jsxPragma: "h",
168
+ jsxFragmentName: "Fragment"
169
+ },
170
+ plugins: ["wsx"],
171
+ rules: {
172
+ // WSX 特定规则(移除 valid-jsx-pragma,由 Vite 处理)
173
+ "wsx/render-method-required": "error",
174
+ "wsx/no-react-imports": "error",
175
+ "wsx/web-component-naming": "warn",
176
+ // TypeScript 规则(推荐)
177
+ "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
178
+ "@typescript-eslint/no-explicit-any": "warn",
179
+ "@typescript-eslint/explicit-function-return-type": "off",
180
+ "@typescript-eslint/explicit-module-boundary-types": "off",
181
+ "@typescript-eslint/no-non-null-assertion": "warn",
182
+ // 通用规则
183
+ "no-console": ["warn", { allow: ["warn", "error"] }],
184
+ "no-debugger": "error",
185
+ "no-unused-vars": "off",
186
+ // 使用 TypeScript 版本
187
+ "no-undef": "off",
188
+ // TypeScript 处理
189
+ "prefer-const": "error",
190
+ "no-var": "error",
191
+ "no-duplicate-imports": "error",
192
+ "no-trailing-spaces": "error",
193
+ "eol-last": "error",
194
+ "comma-dangle": ["error", "always-multiline"],
195
+ semi: ["error", "always"],
196
+ quotes: ["error", "double", { avoidEscape: true, allowTemplateLiterals: true }],
197
+ // 禁用 React 相关规则
198
+ "react/react-in-jsx-scope": "off",
199
+ "react/prop-types": "off",
200
+ "react/jsx-uses-react": "off",
201
+ "react/jsx-uses-vars": "off",
202
+ "react/jsx-key": "off",
203
+ "react/jsx-no-duplicate-props": "off",
204
+ "react/jsx-no-undef": "off",
205
+ "react/no-array-index-key": "off",
206
+ "react/no-unescaped-entities": "off"
207
+ },
208
+ globals: {
209
+ // 浏览器环境
210
+ window: "readonly",
211
+ document: "readonly",
212
+ console: "readonly",
213
+ // Node.js 环境
214
+ process: "readonly",
215
+ Buffer: "readonly",
216
+ __dirname: "readonly",
217
+ __filename: "readonly",
218
+ global: "readonly",
219
+ module: "readonly",
220
+ require: "readonly",
221
+ exports: "readonly",
222
+ // Web Components API
223
+ HTMLElement: "readonly",
224
+ customElements: "readonly",
225
+ CustomEvent: "readonly",
226
+ ShadowRoot: "readonly",
227
+ HTMLSlotElement: "readonly",
228
+ CSSStyleSheet: "readonly",
229
+ // WSX 特定
230
+ h: "readonly",
231
+ Fragment: "readonly"
232
+ },
233
+ settings: {
234
+ // 不需要 React 设置
235
+ }
236
+ };
237
+
238
+ // src/configs/flat.ts
239
+ var flatConfig = {
240
+ name: "wsx/recommended",
241
+ files: ["**/*.wsx"],
242
+ languageOptions: {
243
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
244
+ parser: "@typescript-eslint/parser",
245
+ ecmaVersion: "latest",
246
+ sourceType: "module",
247
+ parserOptions: {
248
+ ecmaFeatures: {
249
+ jsx: true
250
+ },
251
+ jsxPragma: "h",
252
+ jsxFragmentName: "Fragment"
253
+ },
254
+ globals: {
255
+ // Browser environment
256
+ window: "readonly",
257
+ document: "readonly",
258
+ console: "readonly",
259
+ // Node.js environment
260
+ process: "readonly",
261
+ Buffer: "readonly",
262
+ __dirname: "readonly",
263
+ __filename: "readonly",
264
+ global: "readonly",
265
+ module: "readonly",
266
+ require: "readonly",
267
+ exports: "readonly",
268
+ // Web Components API
269
+ HTMLElement: "readonly",
270
+ customElements: "readonly",
271
+ CustomEvent: "readonly",
272
+ ShadowRoot: "readonly",
273
+ HTMLSlotElement: "readonly",
274
+ CSSStyleSheet: "readonly",
275
+ // WSX specific
276
+ h: "readonly",
277
+ Fragment: "readonly"
278
+ }
279
+ },
280
+ plugins: {
281
+ wsx: {
282
+ rules: {
283
+ "render-method-required": renderMethodRequired,
284
+ "no-react-imports": noReactImports,
285
+ "web-component-naming": webComponentNaming
286
+ }
287
+ }
288
+ },
289
+ rules: {
290
+ // WSX specific rules
291
+ "wsx/render-method-required": "error",
292
+ "wsx/no-react-imports": "error",
293
+ "wsx/web-component-naming": "warn",
294
+ // TypeScript rules (recommended)
295
+ "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
296
+ "@typescript-eslint/no-explicit-any": "warn",
297
+ "@typescript-eslint/explicit-function-return-type": "off",
298
+ "@typescript-eslint/explicit-module-boundary-types": "off",
299
+ "@typescript-eslint/no-non-null-assertion": "warn",
300
+ // General rules
301
+ "no-console": ["warn", { allow: ["warn", "error"] }],
302
+ "no-debugger": "error",
303
+ "no-unused-vars": "off",
304
+ // Use TypeScript version
305
+ "no-undef": "off",
306
+ // TypeScript handles this
307
+ "prefer-const": "error",
308
+ "no-var": "error",
309
+ "no-duplicate-imports": "error",
310
+ "no-trailing-spaces": "error",
311
+ "eol-last": "error",
312
+ "comma-dangle": ["error", "always-multiline"],
313
+ semi: ["error", "always"],
314
+ quotes: ["error", "double", { avoidEscape: true, allowTemplateLiterals: true }]
315
+ },
316
+ settings: {
317
+ // No React settings needed
318
+ }
319
+ };
320
+ function createFlatConfig(plugin2) {
321
+ return {
322
+ ...flatConfig,
323
+ plugins: {
324
+ wsx: plugin2
325
+ }
326
+ };
327
+ }
328
+
329
+ // src/index.ts
330
+ var plugin = {
331
+ // 插件元信息
332
+ meta: {
333
+ name: "@wsxjs/eslint-plugin-wsx",
334
+ version: "0.0.2"
335
+ },
336
+ // 核心规则(移除 valid-jsx-pragma)
337
+ rules: {
338
+ "render-method-required": renderMethodRequired,
339
+ "no-react-imports": noReactImports,
340
+ "web-component-naming": webComponentNaming
341
+ },
342
+ // 配置预设
343
+ configs: {
344
+ recommended: recommendedConfig
345
+ }
346
+ };
347
+ var flat = {
348
+ recommended: createFlatConfig(plugin)
349
+ };
350
+ var rules = plugin.rules;
351
+ var configs = plugin.configs;
352
+ var index_default = plugin;
353
+ export {
354
+ configs,
355
+ index_default as default,
356
+ flat,
357
+ rules
358
+ };
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@wsxjs/eslint-plugin-wsx",
3
+ "version": "0.0.5",
4
+ "description": "ESLint plugin for WSX Framework",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "src",
18
+ "!**/__tests__",
19
+ "!**/test"
20
+ ],
21
+ "keywords": [
22
+ "eslint",
23
+ "plugin",
24
+ "wsx",
25
+ "web-components"
26
+ ],
27
+ "dependencies": {
28
+ "@wsxjs/wsx-core": "0.0.5"
29
+ },
30
+ "devDependencies": {
31
+ "tsup": "^8.0.0",
32
+ "typescript": "^5.0.0",
33
+ "jest": "^29.0.0",
34
+ "@types/jest": "^29.0.0",
35
+ "ts-jest": "^29.0.0",
36
+ "@types/node": "^20.0.0",
37
+ "@types/estree": "^1.0.0",
38
+ "@typescript-eslint/utils": "^6.0.0",
39
+ "@typescript-eslint/rule-tester": "^6.0.0",
40
+ "@types/eslint": "^8.0.0",
41
+ "eslint": "^8.0.0"
42
+ },
43
+ "peerDependencies": {
44
+ "eslint": ">=8.0.0 || ^9.0.0"
45
+ },
46
+ "scripts": {
47
+ "build": "tsup src/index.ts --format cjs,esm --dts --cjs-interop",
48
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
49
+ "test": "jest",
50
+ "test:watch": "jest --watch",
51
+ "test:coverage": "jest --coverage",
52
+ "typecheck": "tsc --noEmit",
53
+ "clean": "rm -rf dist coverage"
54
+ }
55
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * ESLint Plugin WSX - Flat Config for ESLint 9+
3
+ *
4
+ * Modern flat config format for WSX framework
5
+ */
6
+
7
+ import type { Linter } from "eslint";
8
+ import { renderMethodRequired } from "../rules/render-method-required";
9
+ import { noReactImports } from "../rules/no-react-imports";
10
+ import { webComponentNaming } from "../rules/web-component-naming";
11
+
12
+ export const flatConfig: Linter.FlatConfig = {
13
+ name: "wsx/recommended",
14
+ files: ["**/*.wsx"],
15
+ languageOptions: {
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ parser: "@typescript-eslint/parser" as any,
18
+ ecmaVersion: "latest",
19
+ sourceType: "module",
20
+ parserOptions: {
21
+ ecmaFeatures: {
22
+ jsx: true,
23
+ },
24
+ jsxPragma: "h",
25
+ jsxFragmentName: "Fragment",
26
+ },
27
+ globals: {
28
+ // Browser environment
29
+ window: "readonly",
30
+ document: "readonly",
31
+ console: "readonly",
32
+
33
+ // Node.js environment
34
+ process: "readonly",
35
+ Buffer: "readonly",
36
+ __dirname: "readonly",
37
+ __filename: "readonly",
38
+ global: "readonly",
39
+ module: "readonly",
40
+ require: "readonly",
41
+ exports: "readonly",
42
+
43
+ // Web Components API
44
+ HTMLElement: "readonly",
45
+ customElements: "readonly",
46
+ CustomEvent: "readonly",
47
+ ShadowRoot: "readonly",
48
+ HTMLSlotElement: "readonly",
49
+ CSSStyleSheet: "readonly",
50
+
51
+ // WSX specific
52
+ h: "readonly",
53
+ Fragment: "readonly",
54
+ },
55
+ },
56
+ plugins: {
57
+ wsx: {
58
+ rules: {
59
+ "render-method-required": renderMethodRequired,
60
+ "no-react-imports": noReactImports,
61
+ "web-component-naming": webComponentNaming,
62
+ },
63
+ },
64
+ },
65
+ rules: {
66
+ // WSX specific rules
67
+ "wsx/render-method-required": "error",
68
+ "wsx/no-react-imports": "error",
69
+ "wsx/web-component-naming": "warn",
70
+
71
+ // TypeScript rules (recommended)
72
+ "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
73
+ "@typescript-eslint/no-explicit-any": "warn",
74
+ "@typescript-eslint/explicit-function-return-type": "off",
75
+ "@typescript-eslint/explicit-module-boundary-types": "off",
76
+ "@typescript-eslint/no-non-null-assertion": "warn",
77
+
78
+ // General rules
79
+ "no-console": ["warn", { allow: ["warn", "error"] }],
80
+ "no-debugger": "error",
81
+ "no-unused-vars": "off", // Use TypeScript version
82
+ "no-undef": "off", // TypeScript handles this
83
+ "prefer-const": "error",
84
+ "no-var": "error",
85
+ "no-duplicate-imports": "error",
86
+ "no-trailing-spaces": "error",
87
+ "eol-last": "error",
88
+ "comma-dangle": ["error", "always-multiline"],
89
+ semi: ["error", "always"],
90
+ quotes: ["error", "double", { avoidEscape: true, allowTemplateLiterals: true }],
91
+ },
92
+ settings: {
93
+ // No React settings needed
94
+ },
95
+ };
96
+
97
+ // Helper function to create a flat config with the plugin
98
+ export function createFlatConfig(plugin: Record<string, unknown>): Linter.FlatConfig {
99
+ return {
100
+ ...flatConfig,
101
+ plugins: {
102
+ wsx: plugin,
103
+ },
104
+ };
105
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * ESLint Plugin WSX - 推荐配置
3
+ *
4
+ * 为 WSX 文件提供推荐的 ESLint 配置
5
+ */
6
+
7
+ import { WSXConfig } from "../types";
8
+
9
+ export const recommendedConfig: WSXConfig = {
10
+ parser: "@typescript-eslint/parser",
11
+ parserOptions: {
12
+ ecmaVersion: "latest",
13
+ sourceType: "module",
14
+ ecmaFeatures: {
15
+ jsx: true,
16
+ },
17
+ jsxPragma: "h",
18
+ jsxFragmentName: "Fragment",
19
+ },
20
+ plugins: ["wsx"],
21
+ rules: {
22
+ // WSX 特定规则(移除 valid-jsx-pragma,由 Vite 处理)
23
+ "wsx/render-method-required": "error",
24
+ "wsx/no-react-imports": "error",
25
+ "wsx/web-component-naming": "warn",
26
+
27
+ // TypeScript 规则(推荐)
28
+ "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
29
+ "@typescript-eslint/no-explicit-any": "warn",
30
+ "@typescript-eslint/explicit-function-return-type": "off",
31
+ "@typescript-eslint/explicit-module-boundary-types": "off",
32
+ "@typescript-eslint/no-non-null-assertion": "warn",
33
+
34
+ // 通用规则
35
+ "no-console": ["warn", { allow: ["warn", "error"] }],
36
+ "no-debugger": "error",
37
+ "no-unused-vars": "off", // 使用 TypeScript 版本
38
+ "no-undef": "off", // TypeScript 处理
39
+ "prefer-const": "error",
40
+ "no-var": "error",
41
+ "no-duplicate-imports": "error",
42
+ "no-trailing-spaces": "error",
43
+ "eol-last": "error",
44
+ "comma-dangle": ["error", "always-multiline"],
45
+ semi: ["error", "always"],
46
+ quotes: ["error", "double", { avoidEscape: true, allowTemplateLiterals: true }],
47
+
48
+ // 禁用 React 相关规则
49
+ "react/react-in-jsx-scope": "off",
50
+ "react/prop-types": "off",
51
+ "react/jsx-uses-react": "off",
52
+ "react/jsx-uses-vars": "off",
53
+ "react/jsx-key": "off",
54
+ "react/jsx-no-duplicate-props": "off",
55
+ "react/jsx-no-undef": "off",
56
+ "react/no-array-index-key": "off",
57
+ "react/no-unescaped-entities": "off",
58
+ },
59
+ globals: {
60
+ // 浏览器环境
61
+ window: "readonly",
62
+ document: "readonly",
63
+ console: "readonly",
64
+
65
+ // Node.js 环境
66
+ process: "readonly",
67
+ Buffer: "readonly",
68
+ __dirname: "readonly",
69
+ __filename: "readonly",
70
+ global: "readonly",
71
+ module: "readonly",
72
+ require: "readonly",
73
+ exports: "readonly",
74
+
75
+ // Web Components API
76
+ HTMLElement: "readonly",
77
+ customElements: "readonly",
78
+ CustomEvent: "readonly",
79
+ ShadowRoot: "readonly",
80
+ HTMLSlotElement: "readonly",
81
+ CSSStyleSheet: "readonly",
82
+
83
+ // WSX 特定
84
+ h: "readonly",
85
+ Fragment: "readonly",
86
+ },
87
+ settings: {
88
+ // 不需要 React 设置
89
+ },
90
+ };
package/src/index.ts ADDED
@@ -0,0 +1,46 @@
1
+ /**
2
+ * ESLint Plugin for WSX (Web Components JSX) - TypeScript 版本
3
+ *
4
+ * 提供针对 WSX 框架的专用 ESLint 规则和配置
5
+ * 注意:不包含 valid-jsx-pragma 规则,因为 Vite 插件已处理 JSX pragma
6
+ */
7
+
8
+ import { renderMethodRequired } from "./rules/render-method-required";
9
+ import { noReactImports } from "./rules/no-react-imports";
10
+ import { webComponentNaming } from "./rules/web-component-naming";
11
+ import { recommendedConfig } from "./configs/recommended";
12
+ import { createFlatConfig } from "./configs/flat";
13
+ import { WSXPlugin } from "./types";
14
+
15
+ const plugin: WSXPlugin = {
16
+ // 插件元信息
17
+ meta: {
18
+ name: "@wsxjs/eslint-plugin-wsx",
19
+ version: "0.0.2",
20
+ },
21
+
22
+ // 核心规则(移除 valid-jsx-pragma)
23
+ rules: {
24
+ "render-method-required": renderMethodRequired,
25
+ "no-react-imports": noReactImports,
26
+ "web-component-naming": webComponentNaming,
27
+ },
28
+
29
+ // 配置预设
30
+ configs: {
31
+ recommended: recommendedConfig,
32
+ },
33
+ };
34
+
35
+ // Export for ESLint 9 flat config
36
+ export const flat = {
37
+ recommended: createFlatConfig(plugin),
38
+ };
39
+
40
+ // Export individual rules for manual configuration
41
+ export const rules = plugin.rules;
42
+
43
+ // Export configs
44
+ export const configs = plugin.configs;
45
+
46
+ export default plugin;