@wsxjs/eslint-plugin-wsx 0.0.18 → 0.0.20

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 CHANGED
@@ -450,6 +450,106 @@ var noInnerHTML = {
450
450
  }
451
451
  };
452
452
 
453
+ // src/rules/i18n-after-autoregister.ts
454
+ var i18nAfterAutoRegister = {
455
+ meta: {
456
+ type: "problem",
457
+ docs: {
458
+ description: "require @i18n decorator to be after @autoRegister decorator",
459
+ category: "Possible Errors",
460
+ recommended: true
461
+ },
462
+ messages: {
463
+ wrongOrder: "@i18n decorator must be placed after @autoRegister decorator.\n\nCorrect order:\n @autoRegister({ tagName: 'my-component' })\n @i18n('common')\n export class MyComponent extends WebComponent {}\n\nThis is required because decorators execute from bottom to top."
464
+ },
465
+ schema: []
466
+ },
467
+ create(context) {
468
+ const autoRegisterImports = /* @__PURE__ */ new Set();
469
+ const i18nImports = /* @__PURE__ */ new Set();
470
+ return {
471
+ // Track imports to identify decorators
472
+ ImportDeclaration(node) {
473
+ if (node.source.type === "Literal" && typeof node.source.value === "string" && node.source.value === "@wsxjs/wsx-core") {
474
+ node.specifiers.forEach((specifier) => {
475
+ if (specifier.type === "ImportSpecifier") {
476
+ if (specifier.imported.type === "Identifier" && specifier.imported.name === "autoRegister") {
477
+ const localName = specifier.local.type === "Identifier" ? specifier.local.name : null;
478
+ if (localName) {
479
+ autoRegisterImports.add(localName);
480
+ }
481
+ }
482
+ }
483
+ });
484
+ }
485
+ if (node.source.type === "Literal" && typeof node.source.value === "string" && node.source.value === "@wsxjs/wsx-i18next") {
486
+ node.specifiers.forEach((specifier) => {
487
+ if (specifier.type === "ImportSpecifier") {
488
+ if (specifier.imported.type === "Identifier" && specifier.imported.name === "i18n") {
489
+ const localName = specifier.local.type === "Identifier" ? specifier.local.name : null;
490
+ if (localName) {
491
+ i18nImports.add(localName);
492
+ }
493
+ }
494
+ } else if (specifier.type === "ImportDefaultSpecifier") {
495
+ const localName = specifier.local.type === "Identifier" ? specifier.local.name : null;
496
+ if (localName) {
497
+ i18nImports.add(localName);
498
+ }
499
+ }
500
+ });
501
+ }
502
+ },
503
+ // Check class decorators for correct order
504
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
505
+ "ClassDeclaration, ClassExpression"(node) {
506
+ if (!node.decorators || node.decorators.length < 2) {
507
+ return;
508
+ }
509
+ let autoRegisterIndex = -1;
510
+ let i18nIndex = -1;
511
+ node.decorators.forEach((decorator, index) => {
512
+ const isAutoRegister = (() => {
513
+ if (decorator.expression.type === "Identifier") {
514
+ return decorator.expression.name === "autoRegister" || autoRegisterImports.has(decorator.expression.name);
515
+ } else if (decorator.expression.type === "CallExpression") {
516
+ if (decorator.expression.callee.type === "Identifier") {
517
+ return decorator.expression.callee.name === "autoRegister" || autoRegisterImports.has(decorator.expression.callee.name);
518
+ }
519
+ }
520
+ return false;
521
+ })();
522
+ const isI18n = (() => {
523
+ if (decorator.expression.type === "Identifier") {
524
+ return decorator.expression.name === "i18n" || i18nImports.has(decorator.expression.name);
525
+ } else if (decorator.expression.type === "CallExpression") {
526
+ if (decorator.expression.callee.type === "Identifier") {
527
+ return decorator.expression.callee.name === "i18n" || i18nImports.has(decorator.expression.callee.name);
528
+ }
529
+ }
530
+ return false;
531
+ })();
532
+ if (isAutoRegister && autoRegisterIndex === -1) {
533
+ autoRegisterIndex = index;
534
+ }
535
+ if (isI18n && i18nIndex === -1) {
536
+ i18nIndex = index;
537
+ }
538
+ });
539
+ if (autoRegisterIndex !== -1 && i18nIndex !== -1) {
540
+ if (i18nIndex < autoRegisterIndex) {
541
+ const i18nDecorator = node.decorators[i18nIndex];
542
+ context.report({
543
+ node: i18nDecorator,
544
+ messageId: "wrongOrder"
545
+ });
546
+ }
547
+ }
548
+ }
549
+ };
550
+ }
551
+ };
552
+
453
553
  // src/configs/recommended.ts
454
554
  var recommendedConfig = {
455
555
  parser: "@typescript-eslint/parser",
@@ -474,6 +574,7 @@ var recommendedConfig = {
474
574
  "wsx/require-jsx-import-source": "error",
475
575
  "wsx/no-null-render": "error",
476
576
  "wsx/no-inner-html": "error",
577
+ "wsx/i18n-after-autoregister": "error",
477
578
  // TypeScript 规则(推荐)
478
579
  "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
479
580
  "@typescript-eslint/no-explicit-any": "warn",
@@ -642,7 +743,8 @@ var plugin = {
642
743
  "state-requires-initial-value": stateRequiresInitialValue,
643
744
  "require-jsx-import-source": requireJsxImportSource,
644
745
  "no-null-render": noNullRender,
645
- "no-inner-html": noInnerHTML
746
+ "no-inner-html": noInnerHTML,
747
+ "i18n-after-autoregister": i18nAfterAutoRegister
646
748
  },
647
749
  // 配置预设
648
750
  configs: {
package/dist/index.mjs CHANGED
@@ -421,6 +421,106 @@ var noInnerHTML = {
421
421
  }
422
422
  };
423
423
 
424
+ // src/rules/i18n-after-autoregister.ts
425
+ var i18nAfterAutoRegister = {
426
+ meta: {
427
+ type: "problem",
428
+ docs: {
429
+ description: "require @i18n decorator to be after @autoRegister decorator",
430
+ category: "Possible Errors",
431
+ recommended: true
432
+ },
433
+ messages: {
434
+ wrongOrder: "@i18n decorator must be placed after @autoRegister decorator.\n\nCorrect order:\n @autoRegister({ tagName: 'my-component' })\n @i18n('common')\n export class MyComponent extends WebComponent {}\n\nThis is required because decorators execute from bottom to top."
435
+ },
436
+ schema: []
437
+ },
438
+ create(context) {
439
+ const autoRegisterImports = /* @__PURE__ */ new Set();
440
+ const i18nImports = /* @__PURE__ */ new Set();
441
+ return {
442
+ // Track imports to identify decorators
443
+ ImportDeclaration(node) {
444
+ if (node.source.type === "Literal" && typeof node.source.value === "string" && node.source.value === "@wsxjs/wsx-core") {
445
+ node.specifiers.forEach((specifier) => {
446
+ if (specifier.type === "ImportSpecifier") {
447
+ if (specifier.imported.type === "Identifier" && specifier.imported.name === "autoRegister") {
448
+ const localName = specifier.local.type === "Identifier" ? specifier.local.name : null;
449
+ if (localName) {
450
+ autoRegisterImports.add(localName);
451
+ }
452
+ }
453
+ }
454
+ });
455
+ }
456
+ if (node.source.type === "Literal" && typeof node.source.value === "string" && node.source.value === "@wsxjs/wsx-i18next") {
457
+ node.specifiers.forEach((specifier) => {
458
+ if (specifier.type === "ImportSpecifier") {
459
+ if (specifier.imported.type === "Identifier" && specifier.imported.name === "i18n") {
460
+ const localName = specifier.local.type === "Identifier" ? specifier.local.name : null;
461
+ if (localName) {
462
+ i18nImports.add(localName);
463
+ }
464
+ }
465
+ } else if (specifier.type === "ImportDefaultSpecifier") {
466
+ const localName = specifier.local.type === "Identifier" ? specifier.local.name : null;
467
+ if (localName) {
468
+ i18nImports.add(localName);
469
+ }
470
+ }
471
+ });
472
+ }
473
+ },
474
+ // Check class decorators for correct order
475
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
476
+ "ClassDeclaration, ClassExpression"(node) {
477
+ if (!node.decorators || node.decorators.length < 2) {
478
+ return;
479
+ }
480
+ let autoRegisterIndex = -1;
481
+ let i18nIndex = -1;
482
+ node.decorators.forEach((decorator, index) => {
483
+ const isAutoRegister = (() => {
484
+ if (decorator.expression.type === "Identifier") {
485
+ return decorator.expression.name === "autoRegister" || autoRegisterImports.has(decorator.expression.name);
486
+ } else if (decorator.expression.type === "CallExpression") {
487
+ if (decorator.expression.callee.type === "Identifier") {
488
+ return decorator.expression.callee.name === "autoRegister" || autoRegisterImports.has(decorator.expression.callee.name);
489
+ }
490
+ }
491
+ return false;
492
+ })();
493
+ const isI18n = (() => {
494
+ if (decorator.expression.type === "Identifier") {
495
+ return decorator.expression.name === "i18n" || i18nImports.has(decorator.expression.name);
496
+ } else if (decorator.expression.type === "CallExpression") {
497
+ if (decorator.expression.callee.type === "Identifier") {
498
+ return decorator.expression.callee.name === "i18n" || i18nImports.has(decorator.expression.callee.name);
499
+ }
500
+ }
501
+ return false;
502
+ })();
503
+ if (isAutoRegister && autoRegisterIndex === -1) {
504
+ autoRegisterIndex = index;
505
+ }
506
+ if (isI18n && i18nIndex === -1) {
507
+ i18nIndex = index;
508
+ }
509
+ });
510
+ if (autoRegisterIndex !== -1 && i18nIndex !== -1) {
511
+ if (i18nIndex < autoRegisterIndex) {
512
+ const i18nDecorator = node.decorators[i18nIndex];
513
+ context.report({
514
+ node: i18nDecorator,
515
+ messageId: "wrongOrder"
516
+ });
517
+ }
518
+ }
519
+ }
520
+ };
521
+ }
522
+ };
523
+
424
524
  // src/configs/recommended.ts
425
525
  var recommendedConfig = {
426
526
  parser: "@typescript-eslint/parser",
@@ -445,6 +545,7 @@ var recommendedConfig = {
445
545
  "wsx/require-jsx-import-source": "error",
446
546
  "wsx/no-null-render": "error",
447
547
  "wsx/no-inner-html": "error",
548
+ "wsx/i18n-after-autoregister": "error",
448
549
  // TypeScript 规则(推荐)
449
550
  "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
450
551
  "@typescript-eslint/no-explicit-any": "warn",
@@ -613,7 +714,8 @@ var plugin = {
613
714
  "state-requires-initial-value": stateRequiresInitialValue,
614
715
  "require-jsx-import-source": requireJsxImportSource,
615
716
  "no-null-render": noNullRender,
616
- "no-inner-html": noInnerHTML
717
+ "no-inner-html": noInnerHTML,
718
+ "i18n-after-autoregister": i18nAfterAutoRegister
617
719
  },
618
720
  // 配置预设
619
721
  configs: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wsxjs/eslint-plugin-wsx",
3
- "version": "0.0.18",
3
+ "version": "0.0.20",
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.18"
28
+ "@wsxjs/wsx-core": "0.0.20"
29
29
  },
30
30
  "devDependencies": {
31
31
  "tsup": "^8.0.0",
@@ -28,6 +28,7 @@ export const recommendedConfig: WSXConfig = {
28
28
  "wsx/require-jsx-import-source": "error",
29
29
  "wsx/no-null-render": "error",
30
30
  "wsx/no-inner-html": "error",
31
+ "wsx/i18n-after-autoregister": "error",
31
32
 
32
33
  // TypeScript 规则(推荐)
33
34
  "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ import { stateRequiresInitialValue } from "./rules/state-requires-initial-value"
12
12
  import { requireJsxImportSource } from "./rules/require-jsx-import-source";
13
13
  import { noNullRender } from "./rules/no-null-render";
14
14
  import { noInnerHTML } from "./rules/no-inner-html";
15
+ import { i18nAfterAutoRegister } from "./rules/i18n-after-autoregister";
15
16
  import { recommendedConfig } from "./configs/recommended";
16
17
  import { createFlatConfig } from "./configs/flat";
17
18
  import { WSXPlugin } from "./types";
@@ -32,6 +33,7 @@ const plugin: WSXPlugin = {
32
33
  "require-jsx-import-source": requireJsxImportSource,
33
34
  "no-null-render": noNullRender,
34
35
  "no-inner-html": noInnerHTML,
36
+ "i18n-after-autoregister": i18nAfterAutoRegister,
35
37
  },
36
38
 
37
39
  // 配置预设
@@ -0,0 +1,166 @@
1
+ /**
2
+ * ESLint 规则:i18n-after-autoregister
3
+ *
4
+ * 确保 @i18n 装饰器必须在 @autoRegister 装饰器之后
5
+ * 这是因为装饰器的执行顺序是从下到上,@i18n 需要先应用,然后 @autoRegister 才能注册正确的类
6
+ */
7
+
8
+ import { Rule } from "eslint";
9
+ import { WSXRuleModule } from "../types";
10
+
11
+ export const i18nAfterAutoRegister: WSXRuleModule = {
12
+ meta: {
13
+ type: "problem",
14
+ docs: {
15
+ description: "require @i18n decorator to be after @autoRegister decorator",
16
+ category: "Possible Errors",
17
+ recommended: true,
18
+ },
19
+ messages: {
20
+ wrongOrder:
21
+ "@i18n decorator must be placed after @autoRegister decorator.\n" +
22
+ "\n" +
23
+ "Correct order:\n" +
24
+ " @autoRegister({ tagName: 'my-component' })\n" +
25
+ " @i18n('common')\n" +
26
+ " export class MyComponent extends WebComponent {}\n" +
27
+ "\n" +
28
+ "This is required because decorators execute from bottom to top.",
29
+ },
30
+ schema: [],
31
+ },
32
+ create(context: Rule.RuleContext) {
33
+ // Track imported decorator identifiers
34
+ const autoRegisterImports = new Set<string>();
35
+ const i18nImports = new Set<string>();
36
+
37
+ return {
38
+ // Track imports to identify decorators
39
+ ImportDeclaration(node) {
40
+ // Track @autoRegister from @wsxjs/wsx-core
41
+ if (
42
+ node.source.type === "Literal" &&
43
+ typeof node.source.value === "string" &&
44
+ node.source.value === "@wsxjs/wsx-core"
45
+ ) {
46
+ node.specifiers.forEach((specifier) => {
47
+ if (specifier.type === "ImportSpecifier") {
48
+ if (
49
+ specifier.imported.type === "Identifier" &&
50
+ specifier.imported.name === "autoRegister"
51
+ ) {
52
+ const localName =
53
+ specifier.local.type === "Identifier"
54
+ ? specifier.local.name
55
+ : null;
56
+ if (localName) {
57
+ autoRegisterImports.add(localName);
58
+ }
59
+ }
60
+ }
61
+ });
62
+ }
63
+
64
+ // Track @i18n from @wsxjs/wsx-i18next
65
+ if (
66
+ node.source.type === "Literal" &&
67
+ typeof node.source.value === "string" &&
68
+ node.source.value === "@wsxjs/wsx-i18next"
69
+ ) {
70
+ node.specifiers.forEach((specifier) => {
71
+ if (specifier.type === "ImportSpecifier") {
72
+ if (
73
+ specifier.imported.type === "Identifier" &&
74
+ specifier.imported.name === "i18n"
75
+ ) {
76
+ const localName =
77
+ specifier.local.type === "Identifier"
78
+ ? specifier.local.name
79
+ : null;
80
+ if (localName) {
81
+ i18nImports.add(localName);
82
+ }
83
+ }
84
+ } else if (specifier.type === "ImportDefaultSpecifier") {
85
+ // Handle default import: import i18n from '@wsxjs/wsx-i18next'
86
+ const localName =
87
+ specifier.local.type === "Identifier" ? specifier.local.name : null;
88
+ if (localName) {
89
+ i18nImports.add(localName);
90
+ }
91
+ }
92
+ });
93
+ }
94
+ },
95
+
96
+ // Check class decorators for correct order
97
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
98
+ "ClassDeclaration, ClassExpression"(node: any) {
99
+ if (!node.decorators || node.decorators.length < 2) {
100
+ return;
101
+ }
102
+
103
+ // Find positions of @autoRegister and @i18n decorators
104
+ let autoRegisterIndex = -1;
105
+ let i18nIndex = -1;
106
+
107
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
108
+ node.decorators.forEach((decorator: any, index: number) => {
109
+ const isAutoRegister = (() => {
110
+ if (decorator.expression.type === "Identifier") {
111
+ return (
112
+ decorator.expression.name === "autoRegister" ||
113
+ autoRegisterImports.has(decorator.expression.name)
114
+ );
115
+ } else if (decorator.expression.type === "CallExpression") {
116
+ if (decorator.expression.callee.type === "Identifier") {
117
+ return (
118
+ decorator.expression.callee.name === "autoRegister" ||
119
+ autoRegisterImports.has(decorator.expression.callee.name)
120
+ );
121
+ }
122
+ }
123
+ return false;
124
+ })();
125
+
126
+ const isI18n = (() => {
127
+ if (decorator.expression.type === "Identifier") {
128
+ return (
129
+ decorator.expression.name === "i18n" ||
130
+ i18nImports.has(decorator.expression.name)
131
+ );
132
+ } else if (decorator.expression.type === "CallExpression") {
133
+ if (decorator.expression.callee.type === "Identifier") {
134
+ return (
135
+ decorator.expression.callee.name === "i18n" ||
136
+ i18nImports.has(decorator.expression.callee.name)
137
+ );
138
+ }
139
+ }
140
+ return false;
141
+ })();
142
+
143
+ if (isAutoRegister && autoRegisterIndex === -1) {
144
+ autoRegisterIndex = index;
145
+ }
146
+ if (isI18n && i18nIndex === -1) {
147
+ i18nIndex = index;
148
+ }
149
+ });
150
+
151
+ // If both decorators are present, check order
152
+ // Decorators execute from bottom to top, so @i18n should be after @autoRegister (higher index)
153
+ if (autoRegisterIndex !== -1 && i18nIndex !== -1) {
154
+ if (i18nIndex < autoRegisterIndex) {
155
+ // Find the @i18n decorator node to report the error
156
+ const i18nDecorator = node.decorators[i18nIndex];
157
+ context.report({
158
+ node: i18nDecorator,
159
+ messageId: "wrongOrder",
160
+ });
161
+ }
162
+ }
163
+ },
164
+ };
165
+ },
166
+ };