@gem-sdk/eslint-plugin 0.0.3 → 0.0.4
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/README.md +7 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/rules/noExportTypeFromComponent.d.ts +8 -0
- package/dist/rules/noExportTypeFromComponent.d.ts.map +1 -0
- package/dist/rules/noExportTypeFromComponent.js +31 -0
- package/dist/rules/noMultipleComponentExports.d.ts +4 -0
- package/dist/rules/noMultipleComponentExports.d.ts.map +1 -0
- package/dist/rules/noMultipleComponentExports.js +102 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,8 +54,10 @@ Choose the config that matches your tech stack:
|
|
|
54
54
|
"@gem-sdk/no-t-for-variable": "error",
|
|
55
55
|
"@gem-sdk/not-use-store-to-refs": "error",
|
|
56
56
|
"@gem-sdk/not-use-custom-color-class": "warn",
|
|
57
|
+
"@gem-sdk/no-export-type-from-component": "error",
|
|
57
58
|
"@gem-sdk/no-export-type-from-tsx": "error",
|
|
58
|
-
"@gem-sdk/no-import-from-package-path": "error"
|
|
59
|
+
"@gem-sdk/no-import-from-package-path": "error",
|
|
60
|
+
"@gem-sdk/no-multiple-component-exports": "error"
|
|
59
61
|
}
|
|
60
62
|
}
|
|
61
63
|
```
|
|
@@ -72,7 +74,11 @@ Choose the config that matches your tech stack:
|
|
|
72
74
|
| `no-t-for-variable` | error | error | error |
|
|
73
75
|
| `not-use-store-to-refs` | — | error | — |
|
|
74
76
|
| `not-use-custom-color-class` | — | warn | — |
|
|
77
|
+
| `no-export-type-from-component` | — | error | — |
|
|
75
78
|
| `no-export-type-from-tsx` | — | — | error |
|
|
76
79
|
| `no-import-from-package-path` | — | — | error |
|
|
80
|
+
| `no-multiple-component-exports` | — | — | error |
|
|
77
81
|
|
|
78
82
|
`vue` and `react` both extend `recommended`. Vue configs require `vue-eslint-parser`.
|
|
83
|
+
|
|
84
|
+
`no-export-type-from-component` and `no-export-type-from-tsx` enforce the same thing (no `export type`/`export interface` from a component file) — the former also matches `.vue` files, the latter is React-only. They're kept separate so each config only reports once per file.
|
package/dist/index.d.ts
CHANGED
|
@@ -5,10 +5,12 @@ export declare const rules: {
|
|
|
5
5
|
'pure-ui-layer': import("eslint").Rule.RuleModule;
|
|
6
6
|
'module-index-required': import("eslint").Rule.RuleModule;
|
|
7
7
|
'no-t-for-variable': import("eslint").Rule.RuleModule;
|
|
8
|
+
'no-export-type-from-component': import("eslint").Rule.RuleModule;
|
|
8
9
|
'not-use-store-to-refs': import("eslint").Rule.RuleModule;
|
|
9
10
|
'not-use-custom-color-class': import("eslint").Rule.RuleModule;
|
|
10
11
|
'no-export-type-from-tsx': import("eslint").Rule.RuleModule;
|
|
11
12
|
'no-import-from-package-path': import("eslint").Rule.RuleModule;
|
|
13
|
+
'no-multiple-component-exports': import("eslint").Rule.RuleModule;
|
|
12
14
|
};
|
|
13
15
|
export declare const configs: {
|
|
14
16
|
recommended: {
|
|
@@ -33,6 +35,7 @@ export declare const configs: {
|
|
|
33
35
|
readonly '@gem-sdk/no-t-for-variable': 'error';
|
|
34
36
|
readonly '@gem-sdk/not-use-store-to-refs': 'error';
|
|
35
37
|
readonly '@gem-sdk/not-use-custom-color-class': 'warn';
|
|
38
|
+
readonly '@gem-sdk/no-export-type-from-component': 'error';
|
|
36
39
|
};
|
|
37
40
|
};
|
|
38
41
|
react: {
|
|
@@ -46,6 +49,7 @@ export declare const configs: {
|
|
|
46
49
|
readonly '@gem-sdk/no-t-for-variable': 'error';
|
|
47
50
|
readonly '@gem-sdk/no-export-type-from-tsx': 'error';
|
|
48
51
|
readonly '@gem-sdk/no-import-from-package-path': 'error';
|
|
52
|
+
readonly '@gem-sdk/no-multiple-component-exports': 'error';
|
|
49
53
|
};
|
|
50
54
|
};
|
|
51
55
|
};
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,KAAK;;;;;;;;;;;;;CAkBgB,CAAC;AA2CnC,eAAO,MAAM,OAAO;;oCArCR,UAAU;;qBAElB,2BAA2B,EAAE,OAAO;qBACpC,2BAA2B,EAAE,OAAO;qBACpC,iCAAiC,EAAE,OAAO;qBAC1C,wBAAwB,EAAE,OAAO;qBACjC,gCAAgC,EAAE,OAAO;qBACzC,4BAA4B,EAAE,OAAO;;;;oCAQ7B,UAAU;;kDAbW,OAAO;kDACP,OAAO;wDACD,OAAO;+CAChB,OAAO;uDACC,OAAO;mDACX,OAAO;uDAWH,OAAO;4DACF,MAAM;+DACH,OAAO;;;;oCAQzC,UAAU;;kDA1BW,OAAO;kDACP,OAAO;wDACD,OAAO;+CAChB,OAAO;uDACC,OAAO;mDACX,OAAO;yDAwBD,OAAO;6DACH,OAAO;+DACL,OAAO;;;CAIH,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,9 +7,11 @@ exports.configs = exports.rules = void 0;
|
|
|
7
7
|
const moduleImportBoundary_1 = __importDefault(require("./rules/moduleImportBoundary"));
|
|
8
8
|
const moduleIndexRequired_1 = __importDefault(require("./rules/moduleIndexRequired"));
|
|
9
9
|
const moduleStructure_1 = __importDefault(require("./rules/moduleStructure"));
|
|
10
|
+
const noExportTypeFromComponent_1 = __importDefault(require("./rules/noExportTypeFromComponent"));
|
|
10
11
|
const noExportTypeFromTsx_1 = __importDefault(require("./rules/noExportTypeFromTsx"));
|
|
11
12
|
const noImportFromPackagePath_1 = __importDefault(require("./rules/noImportFromPackagePath"));
|
|
12
13
|
const noModuleBarrel_1 = __importDefault(require("./rules/noModuleBarrel"));
|
|
14
|
+
const noMultipleComponentExports_1 = __importDefault(require("./rules/noMultipleComponentExports"));
|
|
13
15
|
const noTForVariable_1 = __importDefault(require("./rules/noTForVariable"));
|
|
14
16
|
const notUseCustomColorClass_1 = __importDefault(require("./rules/notUseCustomColorClass"));
|
|
15
17
|
const notUseStoreToRefs_1 = __importDefault(require("./rules/notUseStoreToRefs"));
|
|
@@ -23,12 +25,15 @@ exports.rules = {
|
|
|
23
25
|
'module-index-required': moduleIndexRequired_1.default,
|
|
24
26
|
// i18n
|
|
25
27
|
'no-t-for-variable': noTForVariable_1.default,
|
|
28
|
+
// Shared component rules — target both .tsx and .vue
|
|
29
|
+
'no-export-type-from-component': noExportTypeFromComponent_1.default,
|
|
26
30
|
// Vue-specific
|
|
27
31
|
'not-use-store-to-refs': notUseStoreToRefs_1.default,
|
|
28
32
|
'not-use-custom-color-class': notUseCustomColorClass_1.default,
|
|
29
33
|
// React/TypeScript-specific
|
|
30
34
|
'no-export-type-from-tsx': noExportTypeFromTsx_1.default,
|
|
31
35
|
'no-import-from-package-path': noImportFromPackagePath_1.default,
|
|
36
|
+
'no-multiple-component-exports': noMultipleComponentExports_1.default,
|
|
32
37
|
};
|
|
33
38
|
/**
|
|
34
39
|
* Shared architecture + i18n rules for both React and Vue repos.
|
|
@@ -53,6 +58,7 @@ const vue = {
|
|
|
53
58
|
...recommended.rules,
|
|
54
59
|
'@gem-sdk/not-use-store-to-refs': 'error',
|
|
55
60
|
'@gem-sdk/not-use-custom-color-class': 'warn',
|
|
61
|
+
'@gem-sdk/no-export-type-from-component': 'error',
|
|
56
62
|
},
|
|
57
63
|
};
|
|
58
64
|
/**
|
|
@@ -64,6 +70,7 @@ const react = {
|
|
|
64
70
|
...recommended.rules,
|
|
65
71
|
'@gem-sdk/no-export-type-from-tsx': 'error',
|
|
66
72
|
'@gem-sdk/no-import-from-package-path': 'error',
|
|
73
|
+
'@gem-sdk/no-multiple-component-exports': 'error',
|
|
67
74
|
},
|
|
68
75
|
};
|
|
69
76
|
exports.configs = { recommended, vue, react };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Rule } from 'eslint';
|
|
2
|
+
/**
|
|
3
|
+
* Disallows exporting types and interfaces from component files (.tsx and .vue).
|
|
4
|
+
* Types should live in a dedicated .ts file.
|
|
5
|
+
*/
|
|
6
|
+
declare const rule: Rule.RuleModule;
|
|
7
|
+
export default rule;
|
|
8
|
+
//# sourceMappingURL=noExportTypeFromComponent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noExportTypeFromComponent.d.ts","sourceRoot":"","sources":["../../src/rules/noExportTypeFromComponent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAInC;;;GAGG;AACH,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UAwBhB,CAAC;eAEa,IAAI"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const COMPONENT_EXTENSIONS = ['.tsx', '.vue'];
|
|
4
|
+
/**
|
|
5
|
+
* Disallows exporting types and interfaces from component files (.tsx and .vue).
|
|
6
|
+
* Types should live in a dedicated .ts file.
|
|
7
|
+
*/
|
|
8
|
+
const rule = {
|
|
9
|
+
meta: {
|
|
10
|
+
type: 'problem',
|
|
11
|
+
docs: {
|
|
12
|
+
description: 'Disallow export type and interface declarations from .tsx and .vue files',
|
|
13
|
+
},
|
|
14
|
+
messages: {
|
|
15
|
+
noTypeExport: 'Exporting types and interfaces from .tsx and .vue files is not allowed. Move them to a .ts file.',
|
|
16
|
+
},
|
|
17
|
+
schema: [],
|
|
18
|
+
},
|
|
19
|
+
create(context) {
|
|
20
|
+
return {
|
|
21
|
+
ExportNamedDeclaration(node) {
|
|
22
|
+
const fileName = context.getFilename();
|
|
23
|
+
if (COMPONENT_EXTENSIONS.some((ext) => fileName.endsWith(ext)) &&
|
|
24
|
+
node.exportKind === 'type') {
|
|
25
|
+
context.report({ node, messageId: 'noTypeExport' });
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
exports.default = rule;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noMultipleComponentExports.d.ts","sourceRoot":"","sources":["../../src/rules/noMultipleComponentExports.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AA8EnC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UA+BhB,CAAC;eAEa,IAAI"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const PASCAL_CASE = /^[A-Z][A-Za-z0-9]*$/;
|
|
4
|
+
function isComponentInit(node) {
|
|
5
|
+
return !!node && (node.type === 'ArrowFunctionExpression' || node.type === 'FunctionExpression');
|
|
6
|
+
}
|
|
7
|
+
function findLocalComponent(body, name) {
|
|
8
|
+
for (const statement of body) {
|
|
9
|
+
if (statement.type === 'VariableDeclaration') {
|
|
10
|
+
for (const declarator of statement.declarations) {
|
|
11
|
+
if (declarator.id.type === 'Identifier' && declarator.id.name === name && isComponentInit(declarator.init)) {
|
|
12
|
+
return { name, node: declarator };
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (statement.type === 'FunctionDeclaration' && statement.id?.name === name) {
|
|
17
|
+
return { name, node: statement };
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Detects components exported at the top level of a file, covering `export const X = () => {}`,
|
|
24
|
+
* `export function X() {}`, `export default`, and `export { X }` re-exports of local declarations.
|
|
25
|
+
*/
|
|
26
|
+
function collectExportedComponents(program) {
|
|
27
|
+
const components = [];
|
|
28
|
+
for (const statement of program.body) {
|
|
29
|
+
if (statement.type === 'ExportNamedDeclaration') {
|
|
30
|
+
const declaration = statement.declaration;
|
|
31
|
+
if (declaration?.type === 'VariableDeclaration') {
|
|
32
|
+
for (const declarator of declaration.declarations) {
|
|
33
|
+
if (declarator.id.type === 'Identifier' &&
|
|
34
|
+
PASCAL_CASE.test(declarator.id.name) &&
|
|
35
|
+
isComponentInit(declarator.init)) {
|
|
36
|
+
components.push({ name: declarator.id.name, node: declarator });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else if (declaration?.type === 'FunctionDeclaration' && declaration.id && PASCAL_CASE.test(declaration.id.name)) {
|
|
41
|
+
components.push({ name: declaration.id.name, node: declaration });
|
|
42
|
+
}
|
|
43
|
+
for (const specifier of statement.specifiers) {
|
|
44
|
+
if (specifier.exported.type !== 'Identifier' || specifier.local.type !== 'Identifier')
|
|
45
|
+
continue;
|
|
46
|
+
const exportedName = specifier.exported.name;
|
|
47
|
+
if (!PASCAL_CASE.test(exportedName))
|
|
48
|
+
continue;
|
|
49
|
+
const local = findLocalComponent(program.body, specifier.local.name);
|
|
50
|
+
if (local)
|
|
51
|
+
components.push({ name: exportedName, node: specifier });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (statement.type === 'ExportDefaultDeclaration') {
|
|
55
|
+
const declaration = statement.declaration;
|
|
56
|
+
if (declaration.type === 'FunctionDeclaration' || declaration.type === 'ArrowFunctionExpression' || declaration.type === 'FunctionExpression') {
|
|
57
|
+
const name = declaration.type === 'FunctionDeclaration' ? declaration.id?.name : undefined;
|
|
58
|
+
if (!name || PASCAL_CASE.test(name)) {
|
|
59
|
+
components.push({ name: name ?? 'default', node: declaration });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else if (declaration.type === 'Identifier') {
|
|
63
|
+
const local = findLocalComponent(program.body, declaration.name);
|
|
64
|
+
if (local)
|
|
65
|
+
components.push(local);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return components;
|
|
70
|
+
}
|
|
71
|
+
const rule = {
|
|
72
|
+
meta: {
|
|
73
|
+
type: 'problem',
|
|
74
|
+
docs: {
|
|
75
|
+
description: 'Disallow defining and exporting more than one component from a single file',
|
|
76
|
+
},
|
|
77
|
+
messages: {
|
|
78
|
+
multipleComponents: 'Only one component should be defined and exported per file. "{{name}}" is an additional exported component — move it to its own file.',
|
|
79
|
+
},
|
|
80
|
+
schema: [],
|
|
81
|
+
},
|
|
82
|
+
create(context) {
|
|
83
|
+
return {
|
|
84
|
+
Program(node) {
|
|
85
|
+
const filename = context.getFilename();
|
|
86
|
+
if (!filename.endsWith('.tsx'))
|
|
87
|
+
return;
|
|
88
|
+
const components = collectExportedComponents(node);
|
|
89
|
+
if (components.length <= 1)
|
|
90
|
+
return;
|
|
91
|
+
for (const component of components.slice(1)) {
|
|
92
|
+
context.report({
|
|
93
|
+
node: component.node,
|
|
94
|
+
messageId: 'multipleComponents',
|
|
95
|
+
data: { name: component.name },
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
exports.default = rule;
|