@echoes-of-order/eslint-config 1.121.0
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/CHANGELOG.md +1093 -0
- package/configs/.gitkeep +1 -0
- package/configs/admin.js +203 -0
- package/configs/api-client.js +46 -0
- package/configs/backend.js +895 -0
- package/configs/domains.js +123 -0
- package/configs/frontend.js +30 -0
- package/configs/image-server.js +26 -0
- package/configs/ionos-proxy.js +372 -0
- package/configs/nestjs.js +156 -0
- package/configs/node.js +92 -0
- package/configs/react.js +111 -0
- package/configs/wiki.js +42 -0
- package/index.js +39 -0
- package/package.json +85 -0
- package/rules/.gitkeep +1 -0
- package/rules/__tests__/analyze-relation-usage.test.js.disabled +300 -0
- package/rules/__tests__/complexity.test.js.disabled +300 -0
- package/rules/__tests__/enforce-dto-factory-in-services.integration.test.js +226 -0
- package/rules/__tests__/enforce-dto-factory-in-services.test.js +177 -0
- package/rules/__tests__/enforce-entity-dto-create-no-id.integration.test.js +18 -0
- package/rules/__tests__/enforce-function-argument-count.test.js.disabled +300 -0
- package/rules/__tests__/enforce-repository-token-handling.test.js +58 -0
- package/rules/__tests__/english-only-code-strings.test.js.disabled +300 -0
- package/rules/__tests__/eslint-rules.integration.test.ts +350 -0
- package/rules/__tests__/integration-test-controller-response-dto.js +261 -0
- package/rules/__tests__/integration-test-dto-factory-in-services.js +260 -0
- package/rules/__tests__/integration-test-no-entity-type-casting.js +161 -0
- package/rules/__tests__/integration-test-typeorm-naming-conventions.js +501 -0
- package/rules/__tests__/test-config.js +33 -0
- package/rules/admin-controller-security.js +180 -0
- package/rules/analyze-relation-usage.js +687 -0
- package/rules/api-response-dto.js +174 -0
- package/rules/auth-guard-required.js +142 -0
- package/rules/backend-specific.js +36 -0
- package/rules/best-practices.js +421 -0
- package/rules/complexity.js +20 -0
- package/rules/controller-architecture.js +340 -0
- package/rules/controller-naming-conventions.js +190 -0
- package/rules/controller-readonly-restriction.js +148 -0
- package/rules/controller-swagger-complete.js +312 -0
- package/rules/controller-swagger-docs.js +119 -0
- package/rules/controller-swagger-english.js +320 -0
- package/rules/coordinate-naming.js +132 -0
- package/rules/custom-mui-button.js +135 -0
- package/rules/dead-code-detection-backend.js +50 -0
- package/rules/dead-code-detection-frontend.js +48 -0
- package/rules/dead-code-detection.js +71 -0
- package/rules/debug-controller-response-dto.js +79 -0
- package/rules/deprecate.js +8 -0
- package/rules/dto-annotation-property-consistency.js +111 -0
- package/rules/dto-entity-mapping-completeness.js +688 -0
- package/rules/dto-entity-swagger-separation.js +265 -0
- package/rules/dto-entity-type-consistency.js +352 -0
- package/rules/dto-entity-type-matching.js +519 -0
- package/rules/dto-naming-convention.js +98 -0
- package/rules/dto-visibility-modifiers.js +159 -0
- package/rules/enforce-api-versioning.js +122 -0
- package/rules/enforce-app-module-registration.js +179 -0
- package/rules/enforce-basecontroller.js +152 -0
- package/rules/enforce-body-request-dto.js +141 -0
- package/rules/enforce-controller-response-dto.js +349 -0
- package/rules/enforce-custom-error-classes.js +242 -0
- package/rules/enforce-database-transaction-safety.js +179 -0
- package/rules/enforce-dto-constructor.js +95 -0
- package/rules/enforce-dto-create-parameter-types.js +170 -0
- package/rules/enforce-dto-create-pattern.js +274 -0
- package/rules/enforce-dto-entity-creation.js +164 -0
- package/rules/enforce-dto-factory-in-services.js +188 -0
- package/rules/enforce-dto-from-entity-method.js +47 -0
- package/rules/enforce-dto-from-entity.js +314 -0
- package/rules/enforce-dto-naming-conventions.js +212 -0
- package/rules/enforce-dto-naming.js +176 -0
- package/rules/enforce-dto-usage-simple.js +114 -0
- package/rules/enforce-dto-usage.js +407 -0
- package/rules/enforce-eager-translation-loading.js +178 -0
- package/rules/enforce-entity-creation-pattern.js +137 -0
- package/rules/enforce-entity-dto-convert-method.js +157 -0
- package/rules/enforce-entity-dto-create-no-id.js +117 -0
- package/rules/enforce-entity-dto-extends-base.js +141 -0
- package/rules/enforce-entity-dto-from-request-dto-structure.js +113 -0
- package/rules/enforce-entity-dto-fromentity-complex.js +69 -0
- package/rules/enforce-entity-dto-fromentity-simple.js +69 -0
- package/rules/enforce-entity-dto-fromrequestdto-structure.js +262 -0
- package/rules/enforce-entity-dto-methods-restriction.js +159 -0
- package/rules/enforce-entity-dto-no-request-dto.js +102 -0
- package/rules/enforce-entity-dto-optional-auto-fields.js +101 -0
- package/rules/enforce-entity-dto-required-methods.js +248 -0
- package/rules/enforce-entity-factory-pattern.js +180 -0
- package/rules/enforce-entity-instantiation-in-toentity.js +125 -0
- package/rules/enforce-enum-for-playable-entities.js +95 -0
- package/rules/enforce-error-handling.js +257 -0
- package/rules/enforce-explicit-dto-types.js +118 -0
- package/rules/enforce-from-request-dto-usage.js +62 -0
- package/rules/enforce-generic-entity-dto.js +71 -0
- package/rules/enforce-inject-decorator.js +133 -0
- package/rules/enforce-lazy-type-loading.js +170 -0
- package/rules/enforce-module-existence.js +157 -0
- package/rules/enforce-nonentity-dto-create.js +107 -0
- package/rules/enforce-playable-entity-naming.js +108 -0
- package/rules/enforce-repository-token-handling.js +92 -0
- package/rules/enforce-request-dto-no-entity-dto.js +201 -0
- package/rules/enforce-request-dto-required-fields.js +217 -0
- package/rules/enforce-result-pattern.js +45 -0
- package/rules/enforce-service-relation-loading.js +116 -0
- package/rules/enforce-test-coverage.js +96 -0
- package/rules/enforce-toentity-conditional-assignment.js +132 -0
- package/rules/enforce-translations-required.js +203 -0
- package/rules/enforce-typeorm-naming-conventions.js +366 -0
- package/rules/enforce-vite-health-metrics.js +240 -0
- package/rules/entity-required-properties.js +321 -0
- package/rules/entity-to-dto-test.js +73 -0
- package/rules/enum-database-validation.js +149 -0
- package/rules/errors.js +190 -0
- package/rules/es6.js +204 -0
- package/rules/eslint-plugin-no-comments.js +44 -0
- package/rules/filename-class-name-match.js +62 -0
- package/rules/forbid-fromentity-outside-entity-folder.js +237 -0
- package/rules/function-params-newline.js +111 -0
- package/rules/imports.js +264 -0
- package/rules/jest.js +13 -0
- package/rules/jsx.js +16 -0
- package/rules/max-classes-per-file.js +49 -0
- package/rules/multiline-formatting.js +146 -0
- package/rules/no-blank-lines-between-decorators-and-properties.js +95 -0
- package/rules/no-comments.js +62 -0
- package/rules/no-dto-constructors.js +126 -0
- package/rules/no-dto-default-values.js +220 -0
- package/rules/no-dto-duplicates.js +127 -0
- package/rules/no-dto-in-entity.js +99 -0
- package/rules/no-dynamic-import-in-types.js +71 -0
- package/rules/no-dynamic-imports-in-controllers.js +95 -0
- package/rules/no-entity-imports-in-controllers.js +101 -0
- package/rules/no-entity-in-swagger-docs.js +139 -0
- package/rules/no-entity-type-casting.js +104 -0
- package/rules/no-fetch.js +77 -0
- package/rules/no-import-meta-env.js +151 -0
- package/rules/no-inline-styles.js +5 -0
- package/rules/no-magic-values.js +85 -0
- package/rules/no-partial-type.js +168 -0
- package/rules/no-relative-imports.js +31 -0
- package/rules/no-tsyringe.js +181 -0
- package/rules/no-type-assertion.js +175 -0
- package/rules/no-undefined-entity-properties.js +121 -0
- package/rules/node.js +44 -0
- package/rules/perfectionist.js +50 -0
- package/rules/performance-minimal.js +155 -0
- package/rules/performance.js +44 -0
- package/rules/pino-logger-format.js +200 -0
- package/rules/prefer-dto-classes.js +112 -0
- package/rules/prefer-dto-create-method.js +225 -0
- package/rules/promises.js +17 -0
- package/rules/react-hooks.js +15 -0
- package/rules/react.js +28 -0
- package/rules/regexp.js +70 -0
- package/rules/require-dto-response.js +81 -0
- package/rules/require-valid-relations.js +388 -0
- package/rules/result-pattern.js +162 -0
- package/rules/security.js +37 -0
- package/rules/service-architecture.js +148 -0
- package/rules/sonarjs.js +26 -0
- package/rules/strict.js +7 -0
- package/rules/style.js +611 -0
- package/rules/stylistic.js +93 -0
- package/rules/typeorm-column-type-validation.js +224 -0
- package/rules/typescript-advanced.js +113 -0
- package/rules/typescript-core.js +111 -0
- package/rules/typescript.js +146 -0
- package/rules/unicorn.js +168 -0
- package/rules/variables.js +51 -0
- package/rules/websocket-architecture.js +115 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Verbot von Type-Casting für Entity-Assignments
|
|
3
|
+
* @author Echoes of Order Team
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
meta: {
|
|
8
|
+
type: "problem",
|
|
9
|
+
docs: {
|
|
10
|
+
description: "Verbot von Type-Casting für Entity-Assignments. Verwende echte DB-Lookups statt { id: value } as EntityType",
|
|
11
|
+
category: "Best Practices",
|
|
12
|
+
recommended: true,
|
|
13
|
+
},
|
|
14
|
+
fixable: null,
|
|
15
|
+
schema: [],
|
|
16
|
+
messages: {
|
|
17
|
+
noEntityTypeCasting: "Type-Casting für Entity-Assignments ist verboten. Verwende echte DB-Lookups statt '{{ pattern }}' als '{{ entityType }}'",
|
|
18
|
+
suggestDbLookup: "Verwende stattdessen: const entity = await this.getEntityById(id); assignment.entity = entity;",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
create(context) {
|
|
23
|
+
return {
|
|
24
|
+
// Erkennt Assignment-Expressions mit Type-Casting
|
|
25
|
+
AssignmentExpression(node) {
|
|
26
|
+
// Prüfe ob es ein Type-Casting ist
|
|
27
|
+
if (node.right.type === "TSAsExpression") {
|
|
28
|
+
const asExpression = node.right;
|
|
29
|
+
const objectExpression = asExpression.expression;
|
|
30
|
+
const entityType = asExpression.typeAnnotation;
|
|
31
|
+
|
|
32
|
+
// Prüfe ob es ein Object-Literal mit nur 'id' Property ist
|
|
33
|
+
if (objectExpression.type === "ObjectExpression" &&
|
|
34
|
+
objectExpression.properties.length === 1) {
|
|
35
|
+
|
|
36
|
+
const property = objectExpression.properties[0];
|
|
37
|
+
|
|
38
|
+
// Prüfe ob es eine 'id' Property ist
|
|
39
|
+
if (property.type === "Property" &&
|
|
40
|
+
property.key.type === "Identifier" &&
|
|
41
|
+
property.key.name === "id") {
|
|
42
|
+
|
|
43
|
+
// Prüfe ob der Type eine Entity ist (endet mit "Entity")
|
|
44
|
+
if (entityType.type === "TSTypeReference" &&
|
|
45
|
+
entityType.typeName.type === "Identifier" &&
|
|
46
|
+
entityType.typeName.name.endsWith("Entity")) {
|
|
47
|
+
|
|
48
|
+
const entityTypeName = entityType.typeName.name;
|
|
49
|
+
const pattern = `{ id: ${property.value.name || 'value'} }`;
|
|
50
|
+
|
|
51
|
+
context.report({
|
|
52
|
+
node,
|
|
53
|
+
messageId: "noEntityTypeCasting",
|
|
54
|
+
data: {
|
|
55
|
+
pattern,
|
|
56
|
+
entityType: entityTypeName,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
// Erkennt auch Variable-Declarations mit Type-Casting
|
|
66
|
+
VariableDeclarator(node) {
|
|
67
|
+
if (node.init && node.init.type === "TSAsExpression") {
|
|
68
|
+
const asExpression = node.init;
|
|
69
|
+
const objectExpression = asExpression.expression;
|
|
70
|
+
const entityType = asExpression.typeAnnotation;
|
|
71
|
+
|
|
72
|
+
// Gleiche Logik wie bei AssignmentExpression
|
|
73
|
+
if (objectExpression.type === "ObjectExpression" &&
|
|
74
|
+
objectExpression.properties.length === 1) {
|
|
75
|
+
|
|
76
|
+
const property = objectExpression.properties[0];
|
|
77
|
+
|
|
78
|
+
if (property.type === "Property" &&
|
|
79
|
+
property.key.type === "Identifier" &&
|
|
80
|
+
property.key.name === "id") {
|
|
81
|
+
|
|
82
|
+
if (entityType.type === "TSTypeReference" &&
|
|
83
|
+
entityType.typeName.type === "Identifier" &&
|
|
84
|
+
entityType.typeName.name.endsWith("Entity")) {
|
|
85
|
+
|
|
86
|
+
const entityTypeName = entityType.typeName.name;
|
|
87
|
+
const pattern = `{ id: ${property.value.name || 'value'} }`;
|
|
88
|
+
|
|
89
|
+
context.report({
|
|
90
|
+
node,
|
|
91
|
+
messageId: "noEntityTypeCasting",
|
|
92
|
+
data: {
|
|
93
|
+
pattern,
|
|
94
|
+
entityType: entityTypeName,
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint Custom Rule: no-fetch
|
|
3
|
+
*
|
|
4
|
+
* Verbietet die direkte Verwendung von fetch() und erzwingt die Nutzung
|
|
5
|
+
* des ApiClient aus @echoes-of-order/api-client.
|
|
6
|
+
*
|
|
7
|
+
* @see https://eslint.org/docs/developer-guide/working-with-rules
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
meta: {
|
|
12
|
+
type: "problem",
|
|
13
|
+
docs: {
|
|
14
|
+
description: "Verbietet direkte fetch()-Aufrufe und erzwingt ApiClient-Nutzung",
|
|
15
|
+
category: "Best Practices",
|
|
16
|
+
recommended: true,
|
|
17
|
+
},
|
|
18
|
+
messages: {
|
|
19
|
+
noFetch: "Direkte Verwendung von fetch() ist nicht erlaubt. Verwende stattdessen den ApiClient aus @echoes-of-order/api-client oder erstelle einen Repository in /infrastructure/: " +
|
|
20
|
+
"import { ApiClient } from '@echoes-of-order/api-client'; " +
|
|
21
|
+
"const client = new ApiClient(); " +
|
|
22
|
+
"client.get/post/put/delete(url, options).",
|
|
23
|
+
},
|
|
24
|
+
schema: [],
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
create (context) {
|
|
28
|
+
return {
|
|
29
|
+
CallExpression (node) {
|
|
30
|
+
// Prüfe, ob es ein fetch()-Aufruf ist
|
|
31
|
+
if (
|
|
32
|
+
node.callee.type === "Identifier" &&
|
|
33
|
+
node.callee.name === "fetch"
|
|
34
|
+
) {
|
|
35
|
+
const filename = context.getFilename();
|
|
36
|
+
|
|
37
|
+
// Ausnahmen: Api.ts und Test-Dateien
|
|
38
|
+
const isApiFile = filename.includes("/utilities/Api/Api.ts") ||
|
|
39
|
+
filename.includes("\\utilities\\Api\\Api.ts");
|
|
40
|
+
const isTestFile = filename.match(/\.(test|spec)\.(ts|tsx)$/);
|
|
41
|
+
|
|
42
|
+
if (!isApiFile && !isTestFile) {
|
|
43
|
+
context.report({
|
|
44
|
+
node,
|
|
45
|
+
messageId: "noFetch",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Prüfe auch window.fetch() und globalThis.fetch()
|
|
51
|
+
if (
|
|
52
|
+
node.callee.type === "MemberExpression" &&
|
|
53
|
+
node.callee.property.type === "Identifier" &&
|
|
54
|
+
node.callee.property.name === "fetch" &&
|
|
55
|
+
(
|
|
56
|
+
(node.callee.object.type === "Identifier" && node.callee.object.name === "window") ||
|
|
57
|
+
(node.callee.object.type === "Identifier" && node.callee.object.name === "globalThis")
|
|
58
|
+
)
|
|
59
|
+
) {
|
|
60
|
+
const filename = context.getFilename();
|
|
61
|
+
|
|
62
|
+
const isApiFile = filename.includes("/utilities/Api/Api.ts") ||
|
|
63
|
+
filename.includes("\\utilities\\Api\\Api.ts");
|
|
64
|
+
const isTestFile = filename.match(/\.(test|spec)\.(ts|tsx)$/);
|
|
65
|
+
|
|
66
|
+
if (!isApiFile && !isTestFile) {
|
|
67
|
+
context.report({
|
|
68
|
+
node,
|
|
69
|
+
messageId: "noFetch",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
meta: {
|
|
3
|
+
type: "problem",
|
|
4
|
+
docs: {
|
|
5
|
+
description: "Forbid direct usage of import.meta.env, use ConfigProvider instead",
|
|
6
|
+
category: "Best Practices",
|
|
7
|
+
recommended: true,
|
|
8
|
+
},
|
|
9
|
+
fixable: "code",
|
|
10
|
+
schema: [],
|
|
11
|
+
messages: {
|
|
12
|
+
noImportMetaEnv: "Direct usage of import.meta.env is forbidden. Use ConfigProvider methods instead to ensure proper validation and error handling.",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
create (context) {
|
|
16
|
+
let hasConfigProviderImport = false;
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
ImportDeclaration (node) {
|
|
20
|
+
// Check if ConfigProvider is already imported
|
|
21
|
+
if (node.source.value.includes("ConfigProvider")) {
|
|
22
|
+
hasConfigProviderImport = true;
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
MemberExpression (node) {
|
|
27
|
+
if (
|
|
28
|
+
node.object &&
|
|
29
|
+
node.object.type === "MetaProperty" &&
|
|
30
|
+
node.object.meta &&
|
|
31
|
+
node.object.meta.name === "import" &&
|
|
32
|
+
node.object.property &&
|
|
33
|
+
node.object.property.name === "meta" &&
|
|
34
|
+
node.property &&
|
|
35
|
+
node.property.name === "env"
|
|
36
|
+
) {
|
|
37
|
+
context.report({
|
|
38
|
+
node,
|
|
39
|
+
messageId: "noImportMetaEnv",
|
|
40
|
+
fix (fixer) {
|
|
41
|
+
const fixes = [];
|
|
42
|
+
|
|
43
|
+
// Add ConfigProvider import if not present
|
|
44
|
+
if (!hasConfigProviderImport) {
|
|
45
|
+
const program = context.getSourceCode().ast;
|
|
46
|
+
const lastImport = program.body.filter(n => n.type === "ImportDeclaration").slice(-1)[0] || null;
|
|
47
|
+
|
|
48
|
+
if (lastImport) {
|
|
49
|
+
fixes.push(
|
|
50
|
+
fixer.insertTextAfter(
|
|
51
|
+
lastImport,
|
|
52
|
+
'\nimport { ConfigProvider } from "@/config/ConfigProvider";'
|
|
53
|
+
)
|
|
54
|
+
);
|
|
55
|
+
} else {
|
|
56
|
+
fixes.push(
|
|
57
|
+
fixer.insertTextBeforeRange([0, 0], 'import { ConfigProvider } from "@/config/ConfigProvider";\n')
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Check if this is accessing a specific environment variable
|
|
63
|
+
const parent = node.parent;
|
|
64
|
+
if (parent && parent.type === "MemberExpression" && parent.property) {
|
|
65
|
+
const rawName = parent.property.name || parent.property.value;
|
|
66
|
+
const toCamel = (name) => {
|
|
67
|
+
const lower = String(name).toLowerCase();
|
|
68
|
+
if (lower === "node_env") return "NodeEnv";
|
|
69
|
+
if (lower === "db_host") return "DbHost";
|
|
70
|
+
if (lower === "db_port") return "DbPort";
|
|
71
|
+
if (lower === "db_timeout") return "DbTimeout";
|
|
72
|
+
if (lower === "database_url" || lower === "db_url") return "DatabaseUrl";
|
|
73
|
+
if (lower === "log_level") return "LogLevel";
|
|
74
|
+
if (lower === "api_url") return "ApiUrl";
|
|
75
|
+
if (lower === "secret_key") return "SecretKey";
|
|
76
|
+
if (lower === "primary_db_url") return "PrimaryDbUrl";
|
|
77
|
+
if (lower === "replica_db_url") return "ReplicaDbUrl";
|
|
78
|
+
if (lower === "redis_url") return "RedisUrl";
|
|
79
|
+
if (lower === "redis_ttl") return "RedisTtl";
|
|
80
|
+
const parts = String(name).split(/[_-]+/);
|
|
81
|
+
return parts.map((p, i) => i === 0 ? p.charAt(0).toUpperCase() + p.slice(1).toLowerCase() : p.charAt(0).toUpperCase() + p.slice(1).toLowerCase()).join("");
|
|
82
|
+
};
|
|
83
|
+
const envVarName = toCamel(rawName);
|
|
84
|
+
if (envVarName) {
|
|
85
|
+
// Convert to ConfigProvider method call
|
|
86
|
+
const methodName = `get${envVarName}`;
|
|
87
|
+
fixes.push(fixer.replaceText(parent, `ConfigProvider.${methodName}()`));
|
|
88
|
+
} else {
|
|
89
|
+
// Fallback: replace with generic ConfigProvider call
|
|
90
|
+
fixes.push(fixer.replaceText(node, "ConfigProvider"));
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
// Replace the entire import.meta.env with ConfigProvider
|
|
94
|
+
fixes.push(fixer.replaceText(node, "ConfigProvider"));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return fixes;
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
CallExpression (node) {
|
|
104
|
+
if (
|
|
105
|
+
node.callee &&
|
|
106
|
+
node.callee.type === "MemberExpression" &&
|
|
107
|
+
node.callee.object &&
|
|
108
|
+
node.callee.object.type === "MetaProperty" &&
|
|
109
|
+
node.callee.object.meta &&
|
|
110
|
+
node.callee.object.meta.name === "import" &&
|
|
111
|
+
node.callee.object.property &&
|
|
112
|
+
node.callee.object.property.name === "meta" &&
|
|
113
|
+
node.callee.property &&
|
|
114
|
+
node.callee.property.name === "env"
|
|
115
|
+
) {
|
|
116
|
+
context.report({
|
|
117
|
+
node,
|
|
118
|
+
messageId: "noImportMetaEnv",
|
|
119
|
+
fix (fixer) {
|
|
120
|
+
const fixes = [];
|
|
121
|
+
|
|
122
|
+
// Add ConfigProvider import if not present
|
|
123
|
+
if (!hasConfigProviderImport) {
|
|
124
|
+
const program = context.getSourceCode().ast;
|
|
125
|
+
const lastImport = program.body.filter(n => n.type === "ImportDeclaration").slice(-1)[0] || null;
|
|
126
|
+
|
|
127
|
+
if (lastImport) {
|
|
128
|
+
fixes.push(
|
|
129
|
+
fixer.insertTextAfter(
|
|
130
|
+
lastImport,
|
|
131
|
+
'\nimport { ConfigProvider } from "@/config/ConfigProvider";'
|
|
132
|
+
)
|
|
133
|
+
);
|
|
134
|
+
} else {
|
|
135
|
+
fixes.push(
|
|
136
|
+
fixer.insertTextBeforeRange([0, 0], 'import { ConfigProvider } from "@/config/ConfigProvider";\n')
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Replace the call with ConfigProvider
|
|
142
|
+
fixes.push(fixer.replaceText(node.callee, "ConfigProvider"));
|
|
143
|
+
|
|
144
|
+
return fixes;
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
rules: {
|
|
3
|
+
"no-magic-values": {
|
|
4
|
+
meta: {
|
|
5
|
+
type: "problem",
|
|
6
|
+
docs: {
|
|
7
|
+
description: "Disallow magic numbers and magic strings",
|
|
8
|
+
category: "Best Practices",
|
|
9
|
+
recommended: true,
|
|
10
|
+
},
|
|
11
|
+
messages: {
|
|
12
|
+
magicNumber: "Magic Number '{{value}}' found. Use a named constant instead.",
|
|
13
|
+
magicString: "Magic String '{{value}}' found. Use a named constant instead.",
|
|
14
|
+
},
|
|
15
|
+
schema: [
|
|
16
|
+
{
|
|
17
|
+
type: "object",
|
|
18
|
+
properties: {
|
|
19
|
+
ignoreNumbers: {
|
|
20
|
+
type: "array",
|
|
21
|
+
items: { type: "number" },
|
|
22
|
+
description: "Numbers to ignore (e.g., 0, 1, -1)",
|
|
23
|
+
default: [0, 1, -1]
|
|
24
|
+
},
|
|
25
|
+
ignoreStrings: {
|
|
26
|
+
type: "array",
|
|
27
|
+
items: { type: "string" },
|
|
28
|
+
description: "Strings to ignore (e.g., empty string)",
|
|
29
|
+
default: [""]
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
additionalProperties: false
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
create(context) {
|
|
38
|
+
const options = context.options[0] || {};
|
|
39
|
+
const ignoreNumbers = options.ignoreNumbers || [0, 1, -1];
|
|
40
|
+
const ignoreStrings = options.ignoreStrings || [""];
|
|
41
|
+
|
|
42
|
+
function checkLiteral(node) {
|
|
43
|
+
if (node.type === "Literal") {
|
|
44
|
+
// Skip if it's inside a variable declaration (constant definition)
|
|
45
|
+
if (node.parent && node.parent.type === "VariableDeclarator") {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (typeof node.value === "number") {
|
|
50
|
+
// Ignore configured numbers
|
|
51
|
+
if (ignoreNumbers.includes(node.value)) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
context.report({
|
|
56
|
+
node,
|
|
57
|
+
messageId: "magicNumber",
|
|
58
|
+
data: {
|
|
59
|
+
value: node.value,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
} else if (typeof node.value === "string") {
|
|
63
|
+
// Ignore configured strings
|
|
64
|
+
if (ignoreStrings.includes(node.value)) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
context.report({
|
|
69
|
+
node,
|
|
70
|
+
messageId: "magicString",
|
|
71
|
+
data: {
|
|
72
|
+
value: node.value,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
Literal: checkLiteral,
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
const noPartialTypeRule = {
|
|
2
|
+
meta: {
|
|
3
|
+
type: "problem",
|
|
4
|
+
docs: {
|
|
5
|
+
description: "Verbietet die Verwendung von Partial<T> Utility Type",
|
|
6
|
+
category: "Type Safety",
|
|
7
|
+
recommended: true,
|
|
8
|
+
},
|
|
9
|
+
schema: [],
|
|
10
|
+
messages: {
|
|
11
|
+
noPartialType: "Partial<T> ist verboten. Verwende stattdessen explizite optionale Properties oder separate DTOs für Update-Operationen.",
|
|
12
|
+
noPartialTypeInParameter: "Partial<T> in Funktionsparametern ist verboten. Definiere ein explizites Update-DTO mit nur den benötigten Properties.",
|
|
13
|
+
noPartialTypeInReturnType: "Partial<T> in Rückgabetypen ist verboten. Verwende vollständig typisierte DTOs.",
|
|
14
|
+
noPartialTypeInProperty: "Partial<T> in Property-Definitionen ist verboten. Definiere Properties explizit als optional.",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
create(context) {
|
|
18
|
+
const checkTypeAnnotation = (node, messageId) => {
|
|
19
|
+
if (!node.typeAnnotation) return;
|
|
20
|
+
|
|
21
|
+
const typeAnnotation = node.typeAnnotation.typeAnnotation || node.typeAnnotation;
|
|
22
|
+
|
|
23
|
+
// Prüfe auf Partial<T>
|
|
24
|
+
if (
|
|
25
|
+
typeAnnotation.type === "TSTypeReference" &&
|
|
26
|
+
typeAnnotation.typeName &&
|
|
27
|
+
typeAnnotation.typeName.name === "Partial"
|
|
28
|
+
) {
|
|
29
|
+
context.report({
|
|
30
|
+
node: typeAnnotation,
|
|
31
|
+
messageId: messageId || "noPartialType",
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Prüfe auf Partial in Union Types (z.B. SomeType | Partial<OtherType>)
|
|
36
|
+
if (typeAnnotation.type === "TSUnionType") {
|
|
37
|
+
typeAnnotation.types.forEach((type) => {
|
|
38
|
+
if (
|
|
39
|
+
type.type === "TSTypeReference" &&
|
|
40
|
+
type.typeName &&
|
|
41
|
+
type.typeName.name === "Partial"
|
|
42
|
+
) {
|
|
43
|
+
context.report({
|
|
44
|
+
node: type,
|
|
45
|
+
messageId: messageId || "noPartialType",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Prüfe auf Partial in Intersection Types (z.B. SomeType & Partial<OtherType>)
|
|
52
|
+
if (typeAnnotation.type === "TSIntersectionType") {
|
|
53
|
+
typeAnnotation.types.forEach((type) => {
|
|
54
|
+
if (
|
|
55
|
+
type.type === "TSTypeReference" &&
|
|
56
|
+
type.typeName &&
|
|
57
|
+
type.typeName.name === "Partial"
|
|
58
|
+
) {
|
|
59
|
+
context.report({
|
|
60
|
+
node: type,
|
|
61
|
+
messageId: messageId || "noPartialType",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
// Prüfe Funktionsparameter
|
|
70
|
+
"FunctionDeclaration > :matches(Identifier, RestElement)"(node) {
|
|
71
|
+
checkTypeAnnotation(node, "noPartialTypeInParameter");
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// Prüfe Arrow Function Parameter
|
|
75
|
+
"ArrowFunctionExpression > :matches(Identifier, RestElement)"(node) {
|
|
76
|
+
checkTypeAnnotation(node, "noPartialTypeInParameter");
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
// Prüfe Methoden-Parameter
|
|
80
|
+
"MethodDefinition > FunctionExpression > :matches(Identifier, RestElement)"(node) {
|
|
81
|
+
checkTypeAnnotation(node, "noPartialTypeInParameter");
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
// Prüfe Rückgabetypen von Funktionen
|
|
85
|
+
"FunctionDeclaration[returnType]"(node) {
|
|
86
|
+
if (node.returnType) {
|
|
87
|
+
const returnTypeAnnotation = node.returnType.typeAnnotation;
|
|
88
|
+
if (
|
|
89
|
+
returnTypeAnnotation.type === "TSTypeReference" &&
|
|
90
|
+
returnTypeAnnotation.typeName &&
|
|
91
|
+
returnTypeAnnotation.typeName.name === "Partial"
|
|
92
|
+
) {
|
|
93
|
+
context.report({
|
|
94
|
+
node: returnTypeAnnotation,
|
|
95
|
+
messageId: "noPartialTypeInReturnType",
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
// Prüfe Rückgabetypen von Arrow Functions
|
|
102
|
+
"ArrowFunctionExpression[returnType]"(node) {
|
|
103
|
+
if (node.returnType) {
|
|
104
|
+
const returnTypeAnnotation = node.returnType.typeAnnotation;
|
|
105
|
+
if (
|
|
106
|
+
returnTypeAnnotation.type === "TSTypeReference" &&
|
|
107
|
+
returnTypeAnnotation.typeName &&
|
|
108
|
+
returnTypeAnnotation.typeName.name === "Partial"
|
|
109
|
+
) {
|
|
110
|
+
context.report({
|
|
111
|
+
node: returnTypeAnnotation,
|
|
112
|
+
messageId: "noPartialTypeInReturnType",
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
// Prüfe Rückgabetypen von Methoden
|
|
119
|
+
"MethodDefinition[returnType]"(node) {
|
|
120
|
+
if (node.returnType) {
|
|
121
|
+
const returnTypeAnnotation = node.returnType.typeAnnotation;
|
|
122
|
+
if (
|
|
123
|
+
returnTypeAnnotation.type === "TSTypeReference" &&
|
|
124
|
+
returnTypeAnnotation.typeName &&
|
|
125
|
+
returnTypeAnnotation.typeName.name === "Partial"
|
|
126
|
+
) {
|
|
127
|
+
context.report({
|
|
128
|
+
node: returnTypeAnnotation,
|
|
129
|
+
messageId: "noPartialTypeInReturnType",
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
// Prüfe Class Properties
|
|
136
|
+
"PropertyDefinition[typeAnnotation]"(node) {
|
|
137
|
+
checkTypeAnnotation(node, "noPartialTypeInProperty");
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
// Prüfe Variable Declarations
|
|
141
|
+
"VariableDeclarator > Identifier[typeAnnotation]"(node) {
|
|
142
|
+
checkTypeAnnotation(node, "noPartialType");
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
// Prüfe Type Aliases
|
|
146
|
+
"TSTypeAliasDeclaration"(node) {
|
|
147
|
+
if (
|
|
148
|
+
node.typeAnnotation.type === "TSTypeReference" &&
|
|
149
|
+
node.typeAnnotation.typeName &&
|
|
150
|
+
node.typeAnnotation.typeName.name === "Partial"
|
|
151
|
+
) {
|
|
152
|
+
context.report({
|
|
153
|
+
node: node.typeAnnotation,
|
|
154
|
+
messageId: "noPartialType",
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export default {
|
|
163
|
+
rules: {
|
|
164
|
+
"no-partial-type": noPartialTypeRule,
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
meta: {
|
|
3
|
+
type: "problem",
|
|
4
|
+
docs: {
|
|
5
|
+
description: "Disallow relative imports with parent directory references",
|
|
6
|
+
category: "Best Practices",
|
|
7
|
+
recommended: true,
|
|
8
|
+
},
|
|
9
|
+
fixable: null,
|
|
10
|
+
schema: [],
|
|
11
|
+
messages: {
|
|
12
|
+
noRelativeImport: "Relative imports with parent directory references (../) are not allowed. Use absolute imports with @/ prefix instead.",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
create(context) {
|
|
17
|
+
return {
|
|
18
|
+
ImportDeclaration(node) {
|
|
19
|
+
const source = node.source.value;
|
|
20
|
+
|
|
21
|
+
// Check if the import path contains parent directory references
|
|
22
|
+
if (typeof source === "string" && source.includes("../")) {
|
|
23
|
+
context.report({
|
|
24
|
+
node: node.source,
|
|
25
|
+
messageId: "noRelativeImport",
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
},
|
|
31
|
+
};
|