@darraghor/eslint-plugin-nestjs-typed 6.16.0 → 6.17.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/README.md +1 -0
- package/dist/configs/recommended.js +2 -1
- package/dist/rules/index.d.ts +1 -0
- package/dist/rules/index.js +3 -1
- package/dist/rules/useDependencyInjection/useDependencyInjection.d.ts +2 -0
- package/dist/rules/useDependencyInjection/useDependencyInjection.js +132 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,6 +63,7 @@ Awesome! [Click here](https://github.com/darraghoriordan/eslint-plugin-nestjs-ty
|
|
|
63
63
|
| Nest Modules and Dependency Injection | [`provided-injected-should-match-factory-parameters`](./src/docs/rules/provided-injected-should-match-factory-parameters.md) | ✅ |
|
|
64
64
|
| | [`injectable-should-be-provided`](./src/docs/rules/injectable-should-be-provided.md) | ✅ |
|
|
65
65
|
| | [`use-injectable-provided-token`](./src/docs/rules/use-injectable-provided-token.md) | ✅ |
|
|
66
|
+
| | [`use-dependency-injection`](./src/docs/rules/use-dependency-injection.md) | ❌ |
|
|
66
67
|
| | | |
|
|
67
68
|
| Nest Swagger | [`api-property-matches-property-optionality`](./src/docs/rules/api-property-matches-property-optionality.md) | ✅ |
|
|
68
69
|
| | [`controllers-should-supply-api-tags`](./src/docs/rules/controllers-should-supply-api-tags.md) | ✅ |
|
|
@@ -24,9 +24,10 @@ export const rules = {
|
|
|
24
24
|
"@darraghor/nestjs-typed/no-duplicate-decorators": "error",
|
|
25
25
|
"@darraghor/nestjs-typed/use-injectable-provided-token": "error",
|
|
26
26
|
"@darraghor/nestjs-typed/api-property-should-have-api-extra-models": "error",
|
|
27
|
+
"@darraghor/nestjs-typed/use-dependency-injection": "off",
|
|
27
28
|
};
|
|
28
29
|
export default {
|
|
29
30
|
extends: ["./configs/base"],
|
|
30
31
|
rules,
|
|
31
32
|
};
|
|
32
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
33
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVjb21tZW5kZWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29uZmlncy9yZWNvbW1lbmRlZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxNQUFNLENBQUMsTUFBTSxLQUFLLEdBQWdDO0lBQzlDLDJFQUEyRSxFQUN2RSxPQUFPO0lBQ1gsdURBQXVELEVBQUU7UUFDckQsT0FBTztRQUNQO1lBQ0ksR0FBRyxFQUFFLENBQUMsYUFBYSxDQUFDO1lBQ3BCLGVBQWUsRUFBRSxDQUFDLGNBQWMsRUFBRSxRQUFRLEVBQUUsUUFBUSxDQUFDO1NBQ3hEO0tBQ0o7SUFDRCxtRUFBbUUsRUFDL0QsT0FBTztJQUNYLGdFQUFnRSxFQUFFLE9BQU87SUFDekUsNERBQTRELEVBQUUsT0FBTztJQUNyRSwwREFBMEQsRUFBRSxPQUFPO0lBQ25FLHVFQUF1RSxFQUNuRSxPQUFPO0lBQ1gsOERBQThELEVBQUUsT0FBTztJQUN2RSxrRUFBa0UsRUFBRSxPQUFPO0lBQzNFLCtFQUErRSxFQUMzRSxPQUFPO0lBQ1gsa0VBQWtFLEVBQUUsT0FBTztJQUMzRSx3REFBd0QsRUFBRSxPQUFPO0lBQ2pFLDhEQUE4RCxFQUFFLE9BQU87SUFDdkUsdURBQXVELEVBQUUsS0FBSztJQUM5RCxpRUFBaUUsRUFBRSxLQUFLO0lBQ3hFLHFEQUFxRCxFQUFFLEtBQUs7SUFDNUQsaURBQWlELEVBQUUsT0FBTztJQUMxRCx1REFBdUQsRUFBRSxPQUFPO0lBQ2hFLG1FQUFtRSxFQUMvRCxPQUFPO0lBQ1gsa0RBQWtELEVBQUUsS0FBSztDQUM1RCxDQUFDO0FBQ0YsZUFBZTtJQUNYLE9BQU8sRUFBRSxDQUFDLGdCQUFnQixDQUFDO0lBQzNCLEtBQUs7Q0FDUixDQUFDIn0=
|
package/dist/rules/index.d.ts
CHANGED
|
@@ -33,5 +33,6 @@ declare const allRules: {
|
|
|
33
33
|
}], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
34
34
|
"use-injectable-provided-token": import("@typescript-eslint/utils/ts-eslint").RuleModule<"useInjectableProvidedToken", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
35
35
|
"api-property-should-have-api-extra-models": import("@typescript-eslint/utils/ts-eslint").RuleModule<"shouldUseApiExtraModels", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
36
|
+
"use-dependency-injection": import("@typescript-eslint/utils/ts-eslint").RuleModule<"useDependencyInjection", [], unknown, import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
36
37
|
};
|
|
37
38
|
export default allRules;
|
package/dist/rules/index.js
CHANGED
|
@@ -18,6 +18,7 @@ import noDuplicateDecorators from "./noDuplicateDecorators/noDuplicateDecorators
|
|
|
18
18
|
import useCorrectEndpointNamingConvention from "./useCorrectEndpointNamingConvention/useCorrectEndpointNamingConvention.js";
|
|
19
19
|
import useInjectableProvidedToken from "./useInjectableProvidedToken/useInjectableProvidedToken.js";
|
|
20
20
|
import apiPropertyShouldHaveApiExtraModels from "./apiPropertyShouldHaveApiExtraModels/apiPropertyShouldHaveApiExtraModels.js";
|
|
21
|
+
import useDependencyInjection from "./useDependencyInjection/useDependencyInjection.js";
|
|
21
22
|
const allRules = {
|
|
22
23
|
"all-properties-have-explicit-defined": allPropertiesHaveExplicitDefined,
|
|
23
24
|
"api-property-matches-property-optionality": apiPropertyMatchesPropertyOptionality,
|
|
@@ -39,6 +40,7 @@ const allRules = {
|
|
|
39
40
|
"use-correct-endpoint-naming-convention": useCorrectEndpointNamingConvention,
|
|
40
41
|
"use-injectable-provided-token": useInjectableProvidedToken,
|
|
41
42
|
"api-property-should-have-api-extra-models": apiPropertyShouldHaveApiExtraModels,
|
|
43
|
+
"use-dependency-injection": useDependencyInjection,
|
|
42
44
|
};
|
|
43
45
|
export default allRules;
|
|
44
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
46
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcnVsZXMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTywwQkFBMEIsTUFBTSw2REFBNkQsQ0FBQztBQUNyRyxPQUFPLDRDQUE0QyxNQUFNLDRFQUE0RSxDQUFDO0FBQ3RJLE9BQU8scUNBQXFDLE1BQU0sa0ZBQWtGLENBQUM7QUFDckksT0FBTyw2QkFBNkIsTUFBTSxrRUFBa0UsQ0FBQztBQUM3RyxPQUFPLGtDQUFrQyxNQUFNLDRFQUE0RSxDQUFDO0FBQzVILE9BQU8sNEJBQTRCLE1BQU0sZ0VBQWdFLENBQUM7QUFDMUcsT0FBTyx1Q0FBdUMsTUFBTSxzRkFBc0YsQ0FBQztBQUMzSSxPQUFPLGdDQUFnQyxNQUFNLDRFQUE0RSxDQUFDO0FBQzFILE9BQU8sdUNBQXVDLE1BQU0sOEVBQThFLENBQUM7QUFDbkksT0FBTyxtQ0FBbUMsTUFBTSxpRkFBaUYsQ0FBQztBQUNsSSxPQUFPLGtDQUFrQyxNQUFNLDRFQUE0RSxDQUFDO0FBQzVILE9BQU8sMkJBQTJCLE1BQU0sOERBQThELENBQUM7QUFDdkcsT0FBTyxnQ0FBZ0MsTUFBTSx3RUFBd0UsQ0FBQztBQUN0SCxPQUFPLHlCQUF5QixNQUFNLDBEQUEwRCxDQUFDO0FBQ2pHLE9BQU8sbUNBQW1DLE1BQU0sOEVBQThFLENBQUM7QUFDL0gsT0FBTyx3QkFBd0IsTUFBTSx3REFBd0QsQ0FBQztBQUM5RixPQUFPLHFCQUFxQixNQUFNLGtEQUFrRCxDQUFDO0FBQ3JGLE9BQU8sa0NBQWtDLE1BQU0sNEVBQTRFLENBQUM7QUFDNUgsT0FBTywwQkFBMEIsTUFBTSw0REFBNEQsQ0FBQztBQUNwRyxPQUFPLG1DQUFtQyxNQUFNLDhFQUE4RSxDQUFDO0FBQy9ILE9BQU8sc0JBQXNCLE1BQU0sb0RBQW9ELENBQUM7QUFDeEYsTUFBTSxRQUFRLEdBQUc7SUFDYixzQ0FBc0MsRUFBRSxnQ0FBZ0M7SUFDeEUsMkNBQTJDLEVBQ3ZDLHFDQUFxQztJQUN6QywrQkFBK0IsRUFBRSwwQkFBMEI7SUFDM0QseUJBQXlCLEVBQUUscUJBQXFCO0lBQ2hELG1EQUFtRCxFQUMvQyw0Q0FBNEM7SUFDaEQsb0NBQW9DLEVBQUUsNkJBQTZCO0lBQ25FLHdDQUF3QyxFQUNwQyxrQ0FBa0M7SUFDdEMseUNBQXlDLEVBQ3JDLG1DQUFtQztJQUN2QyxrQ0FBa0MsRUFBRSw0QkFBNEI7SUFDaEUsK0NBQStDLEVBQzNDLHVDQUF1QztJQUMzQyxzQ0FBc0MsRUFBRSxnQ0FBZ0M7SUFDeEUsMENBQTBDLEVBQ3RDLHVDQUF1QztJQUMzQyx1REFBdUQsRUFDbkQsbUNBQW1DO0lBQ3ZDLDBDQUEwQyxFQUN0QyxrQ0FBa0M7SUFDdEMsZ0NBQWdDLEVBQUUsMkJBQTJCO0lBQzdELCtCQUErQixFQUFFLHlCQUF5QjtJQUMxRCw2QkFBNkIsRUFBRSx3QkFBd0I7SUFDdkQsd0NBQXdDLEVBQ3BDLGtDQUFrQztJQUN0QywrQkFBK0IsRUFBRSwwQkFBMEI7SUFDM0QsMkNBQTJDLEVBQ3ZDLG1DQUFtQztJQUN2QywwQkFBMEIsRUFBRSxzQkFBc0I7Q0FDckQsQ0FBQztBQUVGLGVBQWUsUUFBUSxDQUFDIn0=
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
import { createRule } from "../../utils/createRule.js";
|
|
3
|
+
import { typedTokenHelpers } from "../../utils/typedTokenHelpers.js";
|
|
4
|
+
// Decorators that indicate a class should use dependency injection
|
|
5
|
+
const INJECTABLE_DECORATORS = [
|
|
6
|
+
"Injectable",
|
|
7
|
+
"Controller",
|
|
8
|
+
"Component", // Legacy NestJS decorator
|
|
9
|
+
"Service", // Common alias
|
|
10
|
+
];
|
|
11
|
+
const rule = createRule({
|
|
12
|
+
name: "use-dependency-injection",
|
|
13
|
+
meta: {
|
|
14
|
+
docs: {
|
|
15
|
+
description: "Enforce dependency injection through constructor parameters rather than property initialization. Services and controllers should not instantiate their dependencies directly.",
|
|
16
|
+
},
|
|
17
|
+
messages: {
|
|
18
|
+
useDependencyInjection: "Dependencies must be provided through the class constructor, not instantiated directly. This violates the dependency injection principle.",
|
|
19
|
+
},
|
|
20
|
+
schema: [],
|
|
21
|
+
hasSuggestions: false,
|
|
22
|
+
type: "problem",
|
|
23
|
+
},
|
|
24
|
+
defaultOptions: [],
|
|
25
|
+
create(context) {
|
|
26
|
+
let isInjectableClass = false;
|
|
27
|
+
let currentConstructor = null;
|
|
28
|
+
const programImports = new Set();
|
|
29
|
+
const programVariables = new Set();
|
|
30
|
+
return {
|
|
31
|
+
// Track all imports at the program level
|
|
32
|
+
ImportDeclaration(node) {
|
|
33
|
+
node.specifiers.forEach((specifier) => {
|
|
34
|
+
if (specifier.local?.name) {
|
|
35
|
+
programImports.add(specifier.local.name);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
// Track top-level variable declarations (like const bunyan = require('bunyan'))
|
|
40
|
+
"Program > VariableDeclaration"(node) {
|
|
41
|
+
node.declarations.forEach((declaration) => {
|
|
42
|
+
if (declaration.id &&
|
|
43
|
+
declaration.id.type ===
|
|
44
|
+
TSESTree.AST_NODE_TYPES.Identifier) {
|
|
45
|
+
programVariables.add(declaration.id.name);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
// Track when we enter a class that should use DI
|
|
50
|
+
ClassDeclaration(node) {
|
|
51
|
+
isInjectableClass = typedTokenHelpers.nodeHasDecoratorsNamed(node, INJECTABLE_DECORATORS);
|
|
52
|
+
},
|
|
53
|
+
// Track when we exit the class
|
|
54
|
+
"ClassDeclaration:exit"() {
|
|
55
|
+
isInjectableClass = false;
|
|
56
|
+
},
|
|
57
|
+
// Track when we enter a constructor
|
|
58
|
+
MethodDefinition(node) {
|
|
59
|
+
if (node.key.type === TSESTree.AST_NODE_TYPES.Identifier &&
|
|
60
|
+
node.key.name === "constructor") {
|
|
61
|
+
currentConstructor = node;
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
// Track when we exit a constructor
|
|
65
|
+
"MethodDefinition:exit"(node) {
|
|
66
|
+
if (node.key.type === TSESTree.AST_NODE_TYPES.Identifier &&
|
|
67
|
+
node.key.name === "constructor") {
|
|
68
|
+
currentConstructor = null;
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
// Check property definitions with initializers
|
|
72
|
+
PropertyDefinition(node) {
|
|
73
|
+
if (!isInjectableClass || !node.value) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// Check for new expressions: private logger = new Logger()
|
|
77
|
+
if (node.value.type === TSESTree.AST_NODE_TYPES.NewExpression) {
|
|
78
|
+
context.report({
|
|
79
|
+
node: node.value,
|
|
80
|
+
messageId: "useDependencyInjection",
|
|
81
|
+
});
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// Check for require calls: private logger = require('bunyan')
|
|
85
|
+
const isRequireCall = node.value.type ===
|
|
86
|
+
TSESTree.AST_NODE_TYPES.CallExpression &&
|
|
87
|
+
node.value.callee.type ===
|
|
88
|
+
TSESTree.AST_NODE_TYPES.Identifier &&
|
|
89
|
+
node.value.callee.name === "require";
|
|
90
|
+
if (isRequireCall) {
|
|
91
|
+
context.report({
|
|
92
|
+
node: node.value,
|
|
93
|
+
messageId: "useDependencyInjection",
|
|
94
|
+
});
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// Check for identifiers from imports/variables: private logger = bunyan
|
|
98
|
+
const isImportedIdentifier = node.value.type === TSESTree.AST_NODE_TYPES.Identifier &&
|
|
99
|
+
(programImports.has(node.value.name) ||
|
|
100
|
+
programVariables.has(node.value.name));
|
|
101
|
+
if (isImportedIdentifier) {
|
|
102
|
+
context.report({
|
|
103
|
+
node: node.value,
|
|
104
|
+
messageId: "useDependencyInjection",
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
// Check assignments in constructor: this.logger = new Logger()
|
|
109
|
+
AssignmentExpression(node) {
|
|
110
|
+
if (!isInjectableClass ||
|
|
111
|
+
!currentConstructor ||
|
|
112
|
+
node.operator !== "=") {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// Check if it's assigning to a class member (this.property)
|
|
116
|
+
const isThisMemberAssignment = node.left.type ===
|
|
117
|
+
TSESTree.AST_NODE_TYPES.MemberExpression &&
|
|
118
|
+
node.left.object.type ===
|
|
119
|
+
TSESTree.AST_NODE_TYPES.ThisExpression;
|
|
120
|
+
const isNewExpression = node.right.type === TSESTree.AST_NODE_TYPES.NewExpression;
|
|
121
|
+
if (isThisMemberAssignment && isNewExpression) {
|
|
122
|
+
context.report({
|
|
123
|
+
node: node.right,
|
|
124
|
+
messageId: "useDependencyInjection",
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
export default rule;
|
|
132
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXNlRGVwZW5kZW5jeUluamVjdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9ydWxlcy91c2VEZXBlbmRlbmN5SW5qZWN0aW9uL3VzZURlcGVuZGVuY3lJbmplY3Rpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFDLFFBQVEsRUFBQyxNQUFNLDBCQUEwQixDQUFDO0FBQ2xELE9BQU8sRUFBQyxVQUFVLEVBQUMsTUFBTSwyQkFBMkIsQ0FBQztBQUNyRCxPQUFPLEVBQUMsaUJBQWlCLEVBQUMsTUFBTSxrQ0FBa0MsQ0FBQztBQUVuRSxtRUFBbUU7QUFDbkUsTUFBTSxxQkFBcUIsR0FBRztJQUMxQixZQUFZO0lBQ1osWUFBWTtJQUNaLFdBQVcsRUFBRSwwQkFBMEI7SUFDdkMsU0FBUyxFQUFFLGVBQWU7Q0FDN0IsQ0FBQztBQUVGLE1BQU0sSUFBSSxHQUFHLFVBQVUsQ0FBK0I7SUFDbEQsSUFBSSxFQUFFLDBCQUEwQjtJQUNoQyxJQUFJLEVBQUU7UUFDRixJQUFJLEVBQUU7WUFDRixXQUFXLEVBQ1AsK0tBQStLO1NBQ3RMO1FBQ0QsUUFBUSxFQUFFO1lBQ04sc0JBQXNCLEVBQ2xCLDJJQUEySTtTQUNsSjtRQUNELE1BQU0sRUFBRSxFQUFFO1FBQ1YsY0FBYyxFQUFFLEtBQUs7UUFDckIsSUFBSSxFQUFFLFNBQVM7S0FDbEI7SUFDRCxjQUFjLEVBQUUsRUFBRTtJQUVsQixNQUFNLENBQUMsT0FBTztRQUNWLElBQUksaUJBQWlCLEdBQUcsS0FBSyxDQUFDO1FBQzlCLElBQUksa0JBQWtCLEdBQXFDLElBQUksQ0FBQztRQUNoRSxNQUFNLGNBQWMsR0FBZ0IsSUFBSSxHQUFHLEVBQVUsQ0FBQztRQUN0RCxNQUFNLGdCQUFnQixHQUFnQixJQUFJLEdBQUcsRUFBVSxDQUFDO1FBRXhELE9BQU87WUFDSCx5Q0FBeUM7WUFDekMsaUJBQWlCLENBQUMsSUFBZ0M7Z0JBQzlDLElBQUksQ0FBQyxVQUFVLENB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
|