eslint-cdk-plugin 3.4.4 → 3.4.6
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/bin/index.mjs +4318 -0
- package/bin/prompt-DVcPfS2N.mjs +847 -0
- package/dist/index.cjs +26648 -1606
- package/dist/index.d.cts +55 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +55 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +26650 -1584
- package/dist/index.mjs.map +1 -0
- package/package.json +20 -22
- package/src/configs/flat-config.ts +69 -0
- package/src/configs/index.ts +6 -0
- package/src/{utils/get-child-nodes.ts → core/ast-node/finder/child-nodes.ts} +2 -2
- package/src/{utils/getConstructor.ts → core/ast-node/finder/constructor.ts} +1 -1
- package/src/core/ast-node/finder/property-name.ts +20 -0
- package/src/core/ast-node/finder/public-property.ts +76 -0
- package/src/core/cdk-construct/type-checker/is-construct-or-stack.ts +21 -0
- package/src/core/cdk-construct/type-checker/is-construct.ts +22 -0
- package/src/{utils → core/cdk-construct/type-checker}/is-resource-with-readonly-interface.ts +5 -4
- package/src/core/cdk-construct/type-checker/is-resource.ts +17 -0
- package/src/core/cdk-construct/type-finder/index.ts +73 -0
- package/src/{utils/typecheck/ts-type.ts → core/ts-type/checker/is-array.ts} +2 -8
- package/src/core/ts-type/checker/is-class.ts +7 -0
- package/src/core/ts-type/checker/is-extends-target-super-class.ts +24 -0
- package/src/core/ts-type/checker/private/get-symbol.ts +5 -0
- package/src/core/ts-type/finder/array-element.ts +20 -0
- package/src/core/ts-type/finder/constructor-property-name.ts +21 -0
- package/src/{utils/getGenericTypeArgument.ts → core/ts-type/finder/generics-type-argument.ts} +5 -5
- package/src/index.ts +6 -108
- package/src/rules/construct-constructor-property.ts +48 -26
- package/src/rules/index.ts +34 -0
- package/src/rules/no-construct-in-interface.ts +5 -51
- package/src/rules/no-construct-in-public-property-of-construct.ts +20 -143
- package/src/rules/no-construct-stack-suffix.ts +5 -5
- package/src/rules/no-mutable-property-of-props-interface.ts +1 -1
- package/src/rules/no-mutable-public-property-of-construct.ts +47 -39
- package/src/rules/no-parent-name-construct-id-match.ts +4 -6
- package/src/rules/no-unused-props/index.ts +19 -10
- package/src/rules/no-unused-props/props-usage-analyzer.ts +207 -190
- package/src/rules/no-unused-props/props-usage-tracker.ts +4 -3
- package/src/rules/no-unused-props/visitor/direct-props-usage-visitor.ts +145 -0
- package/src/rules/no-unused-props/visitor/index.ts +5 -0
- package/src/rules/no-unused-props/visitor/instance-variable-usage-visitor.ts +117 -0
- package/src/rules/no-unused-props/visitor/interface/node-visitor.ts +9 -0
- package/src/rules/no-unused-props/visitor/method-call-collector-visitor.ts +60 -0
- package/src/rules/no-unused-props/visitor/props-alias-visitor.ts +81 -0
- package/src/rules/no-unused-props/visitor/traverse-nodes.ts +38 -0
- package/src/rules/no-variable-construct-id.ts +4 -4
- package/src/rules/pascal-case-construct-id.ts +5 -5
- package/src/rules/props-name-convention.ts +4 -4
- package/src/rules/require-jsdoc.ts +2 -2
- package/src/rules/require-passing-this.ts +4 -4
- package/src/rules/require-props-default-doc.ts +1 -1
- package/bin/migration.mjs +0 -100
- package/dist/index.d.ts +0 -47
- package/dist/index.d.ts.map +0 -1
- package/src/utils/getArrayElementType.ts +0 -14
- package/src/utils/getPropertyNames.ts +0 -41
- package/src/utils/typecheck/cdk.ts +0 -71
- package/src/utils/typecheck/ts-node.ts +0 -7
- /package/src/{utils/convertString.ts → shared/converter/to-pascal-case.ts} +0 -0
- /package/src/{utils/createRule.ts → shared/create-rule.ts} +0 -0
package/src/index.ts
CHANGED
|
@@ -1,112 +1,7 @@
|
|
|
1
|
-
import tsParser from "@typescript-eslint/parser";
|
|
2
1
|
import { FlatConfig } from "@typescript-eslint/utils/ts-eslint";
|
|
3
2
|
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
import { constructConstructorProperty } from "./rules/construct-constructor-property";
|
|
7
|
-
import { noConstructInInterface } from "./rules/no-construct-in-interface";
|
|
8
|
-
import { noConstructInPublicPropertyOfConstruct } from "./rules/no-construct-in-public-property-of-construct";
|
|
9
|
-
import { noConstructStackSuffix } from "./rules/no-construct-stack-suffix";
|
|
10
|
-
import { noImportPrivate } from "./rules/no-import-private";
|
|
11
|
-
import { noMutablePropertyOfPropsInterface } from "./rules/no-mutable-property-of-props-interface";
|
|
12
|
-
import { noMutablePublicPropertyOfConstruct } from "./rules/no-mutable-public-property-of-construct";
|
|
13
|
-
import { noParentNameConstructIdMatch } from "./rules/no-parent-name-construct-id-match";
|
|
14
|
-
import { noUnusedProps } from "./rules/no-unused-props";
|
|
15
|
-
import { noVariableConstructId } from "./rules/no-variable-construct-id";
|
|
16
|
-
import { pascalCaseConstructId } from "./rules/pascal-case-construct-id";
|
|
17
|
-
import { propsNameConvention } from "./rules/props-name-convention";
|
|
18
|
-
import { requireJSDoc } from "./rules/require-jsdoc";
|
|
19
|
-
import { requirePassingThis } from "./rules/require-passing-this";
|
|
20
|
-
import { requirePropsDefaultDoc } from "./rules/require-props-default-doc";
|
|
21
|
-
|
|
22
|
-
const rules = {
|
|
23
|
-
"construct-constructor-property": constructConstructorProperty,
|
|
24
|
-
"no-construct-in-interface": noConstructInInterface,
|
|
25
|
-
"no-construct-in-public-property-of-construct":
|
|
26
|
-
noConstructInPublicPropertyOfConstruct,
|
|
27
|
-
"no-construct-stack-suffix": noConstructStackSuffix,
|
|
28
|
-
"no-import-private": noImportPrivate,
|
|
29
|
-
"no-mutable-property-of-props-interface": noMutablePropertyOfPropsInterface,
|
|
30
|
-
"no-mutable-public-property-of-construct": noMutablePublicPropertyOfConstruct,
|
|
31
|
-
"no-parent-name-construct-id-match": noParentNameConstructIdMatch,
|
|
32
|
-
"no-unused-props": noUnusedProps,
|
|
33
|
-
"no-variable-construct-id": noVariableConstructId,
|
|
34
|
-
"pascal-case-construct-id": pascalCaseConstructId,
|
|
35
|
-
"props-name-convention": propsNameConvention,
|
|
36
|
-
"require-jsdoc": requireJSDoc,
|
|
37
|
-
"require-passing-this": requirePassingThis,
|
|
38
|
-
"require-props-default-doc": requirePropsDefaultDoc,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const cdkPlugin = {
|
|
42
|
-
meta: { name, version },
|
|
43
|
-
rules,
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const createFlatConfig = (
|
|
47
|
-
rules: FlatConfig.Rules
|
|
48
|
-
): {
|
|
49
|
-
languageOptions: FlatConfig.LanguageOptions;
|
|
50
|
-
plugins: FlatConfig.Plugins;
|
|
51
|
-
rules: FlatConfig.Rules;
|
|
52
|
-
} => {
|
|
53
|
-
return {
|
|
54
|
-
languageOptions: {
|
|
55
|
-
parser: tsParser,
|
|
56
|
-
parserOptions: {
|
|
57
|
-
projectService: true,
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
plugins: {
|
|
61
|
-
cdk: cdkPlugin,
|
|
62
|
-
},
|
|
63
|
-
rules,
|
|
64
|
-
};
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const recommended = createFlatConfig({
|
|
68
|
-
"cdk/construct-constructor-property": "error",
|
|
69
|
-
"cdk/no-construct-in-interface": "error",
|
|
70
|
-
"cdk/no-construct-in-public-property-of-construct": "error",
|
|
71
|
-
"cdk/no-construct-stack-suffix": "error",
|
|
72
|
-
"cdk/no-mutable-property-of-props-interface": "warn",
|
|
73
|
-
"cdk/no-mutable-public-property-of-construct": "warn",
|
|
74
|
-
"cdk/no-parent-name-construct-id-match": [
|
|
75
|
-
"error",
|
|
76
|
-
{ disallowContainingParentName: false },
|
|
77
|
-
],
|
|
78
|
-
// TODO: Enable this rule at v4.0.0
|
|
79
|
-
// "cdk/no-unused-props": "error",
|
|
80
|
-
"cdk/no-variable-construct-id": "error",
|
|
81
|
-
"cdk/pascal-case-construct-id": "error",
|
|
82
|
-
"cdk/require-passing-this": ["error", { allowNonThisAndDisallowScope: true }],
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
const strict = createFlatConfig({
|
|
86
|
-
"cdk/construct-constructor-property": "error",
|
|
87
|
-
"cdk/no-construct-in-interface": "error",
|
|
88
|
-
"cdk/no-construct-in-public-property-of-construct": "error",
|
|
89
|
-
"cdk/no-construct-stack-suffix": "error",
|
|
90
|
-
"cdk/no-import-private": "error",
|
|
91
|
-
"cdk/no-mutable-property-of-props-interface": "error",
|
|
92
|
-
"cdk/no-mutable-public-property-of-construct": "error",
|
|
93
|
-
"cdk/no-parent-name-construct-id-match": [
|
|
94
|
-
"error",
|
|
95
|
-
{ disallowContainingParentName: true },
|
|
96
|
-
],
|
|
97
|
-
"cdk/no-unused-props": "error",
|
|
98
|
-
"cdk/no-variable-construct-id": "error",
|
|
99
|
-
"cdk/pascal-case-construct-id": "error",
|
|
100
|
-
"cdk/props-name-convention": "error",
|
|
101
|
-
"cdk/require-jsdoc": "error",
|
|
102
|
-
"cdk/require-passing-this": "error",
|
|
103
|
-
"cdk/require-props-default-doc": "error",
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
const configs = {
|
|
107
|
-
recommended,
|
|
108
|
-
strict,
|
|
109
|
-
};
|
|
3
|
+
import { configs } from "./configs";
|
|
4
|
+
import { rules } from "./rules";
|
|
110
5
|
|
|
111
6
|
export { configs, rules };
|
|
112
7
|
|
|
@@ -120,7 +15,10 @@ export interface EslintCdkPlugin {
|
|
|
120
15
|
|
|
121
16
|
const eslintCdkPlugin: EslintCdkPlugin = {
|
|
122
17
|
rules,
|
|
123
|
-
configs
|
|
18
|
+
configs: {
|
|
19
|
+
recommended: configs.recommended,
|
|
20
|
+
strict: configs.strict,
|
|
21
|
+
},
|
|
124
22
|
};
|
|
125
23
|
|
|
126
24
|
export default eslintCdkPlugin;
|
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
TSESTree,
|
|
7
7
|
} from "@typescript-eslint/utils";
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { findConstructor } from "../core/ast-node/finder/constructor";
|
|
10
|
+
import { isConstructType } from "../core/cdk-construct/type-checker/is-construct";
|
|
11
|
+
import { createRule } from "../shared/create-rule";
|
|
12
12
|
|
|
13
13
|
type Context = TSESLint.RuleContext<
|
|
14
14
|
| "invalidConstructorProperty"
|
|
@@ -17,6 +17,12 @@ type Context = TSESLint.RuleContext<
|
|
|
17
17
|
[]
|
|
18
18
|
>;
|
|
19
19
|
|
|
20
|
+
type ConstructorProperties = [
|
|
21
|
+
TSESTree.Parameter,
|
|
22
|
+
TSESTree.Parameter,
|
|
23
|
+
TSESTree.Parameter | undefined
|
|
24
|
+
];
|
|
25
|
+
|
|
20
26
|
/**
|
|
21
27
|
* Enforces that constructors of classes extending Construct have the property names 'scope, id' or 'scope, id, props'
|
|
22
28
|
* @param context - The rule context provided by ESLint
|
|
@@ -48,36 +54,46 @@ export const constructConstructorProperty = createRule({
|
|
|
48
54
|
const type = parserServices.getTypeAtLocation(node);
|
|
49
55
|
if (!isConstructType(type)) return;
|
|
50
56
|
|
|
51
|
-
const constructor =
|
|
57
|
+
const constructor = findConstructor(node);
|
|
52
58
|
if (!constructor) return;
|
|
53
59
|
|
|
54
|
-
|
|
60
|
+
const params = checkNumOfConstructorProperty(constructor, context);
|
|
61
|
+
if (params) {
|
|
62
|
+
checkFirstParamIsScope(params[0], context, parserServices);
|
|
63
|
+
checkSecondParamIsId(params[1], context);
|
|
64
|
+
checkThirdParamIsProps(params[2], context);
|
|
65
|
+
}
|
|
55
66
|
},
|
|
56
67
|
};
|
|
57
68
|
},
|
|
58
69
|
});
|
|
59
70
|
|
|
60
71
|
/**
|
|
61
|
-
*
|
|
72
|
+
* Checks if the number of constructor properties is valid (at least 2)
|
|
62
73
|
*/
|
|
63
|
-
const
|
|
74
|
+
const checkNumOfConstructorProperty = (
|
|
64
75
|
constructor: TSESTree.MethodDefinition,
|
|
65
|
-
context: Context
|
|
66
|
-
|
|
67
|
-
): void => {
|
|
76
|
+
context: Context
|
|
77
|
+
): ConstructorProperties | undefined => {
|
|
68
78
|
const params = constructor.value.params;
|
|
69
|
-
|
|
70
|
-
// NOTE: Check if the constructor has at least 2 parameters
|
|
71
79
|
if (params.length < 2) {
|
|
72
80
|
context.report({
|
|
73
81
|
node: constructor.value,
|
|
74
82
|
messageId: "invalidConstructorProperty",
|
|
75
83
|
});
|
|
76
|
-
return;
|
|
84
|
+
return undefined;
|
|
77
85
|
}
|
|
86
|
+
return [params[0], params[1], params[2]];
|
|
87
|
+
};
|
|
78
88
|
|
|
79
|
-
|
|
80
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Checks if the first parameter is named "scope" and of type Construct
|
|
91
|
+
*/
|
|
92
|
+
const checkFirstParamIsScope = (
|
|
93
|
+
firstParam: ConstructorProperties[0],
|
|
94
|
+
context: Context,
|
|
95
|
+
parserServices: ParserServicesWithTypeInformation
|
|
96
|
+
) => {
|
|
81
97
|
if (
|
|
82
98
|
firstParam.type !== AST_NODE_TYPES.Identifier ||
|
|
83
99
|
firstParam.name !== "scope"
|
|
@@ -92,9 +108,15 @@ const validateConstructorProperty = (
|
|
|
92
108
|
messageId: "invalidConstructorType",
|
|
93
109
|
});
|
|
94
110
|
}
|
|
111
|
+
};
|
|
95
112
|
|
|
96
|
-
|
|
97
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Checks if the second parameter is named "id" and of type string
|
|
115
|
+
*/
|
|
116
|
+
const checkSecondParamIsId = (
|
|
117
|
+
secondParam: ConstructorProperties[1],
|
|
118
|
+
context: Context
|
|
119
|
+
) => {
|
|
98
120
|
if (
|
|
99
121
|
secondParam.type !== AST_NODE_TYPES.Identifier ||
|
|
100
122
|
secondParam.name !== "id"
|
|
@@ -103,7 +125,6 @@ const validateConstructorProperty = (
|
|
|
103
125
|
node: secondParam,
|
|
104
126
|
messageId: "invalidConstructorProperty",
|
|
105
127
|
});
|
|
106
|
-
return;
|
|
107
128
|
} else if (
|
|
108
129
|
secondParam.typeAnnotation?.typeAnnotation.type !==
|
|
109
130
|
AST_NODE_TYPES.TSStringKeyword
|
|
@@ -112,15 +133,17 @@ const validateConstructorProperty = (
|
|
|
112
133
|
node: secondParam,
|
|
113
134
|
messageId: "invalidConstructorIdType",
|
|
114
135
|
});
|
|
115
|
-
return;
|
|
116
136
|
}
|
|
137
|
+
};
|
|
117
138
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
139
|
+
/**
|
|
140
|
+
* Checks if the third parameter is named "props"
|
|
141
|
+
*/
|
|
142
|
+
const checkThirdParamIsProps = (
|
|
143
|
+
thirdParam: ConstructorProperties[2],
|
|
144
|
+
context: Context
|
|
145
|
+
) => {
|
|
146
|
+
if (!thirdParam) return;
|
|
124
147
|
if (
|
|
125
148
|
thirdParam.type !== AST_NODE_TYPES.Identifier ||
|
|
126
149
|
thirdParam.name !== "props"
|
|
@@ -129,6 +152,5 @@ const validateConstructorProperty = (
|
|
|
129
152
|
node: thirdParam,
|
|
130
153
|
messageId: "invalidConstructorProperty",
|
|
131
154
|
});
|
|
132
|
-
return;
|
|
133
155
|
}
|
|
134
156
|
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { constructConstructorProperty } from "./construct-constructor-property";
|
|
2
|
+
import { noConstructInInterface } from "./no-construct-in-interface";
|
|
3
|
+
import { noConstructInPublicPropertyOfConstruct } from "./no-construct-in-public-property-of-construct";
|
|
4
|
+
import { noConstructStackSuffix } from "./no-construct-stack-suffix";
|
|
5
|
+
import { noImportPrivate } from "./no-import-private";
|
|
6
|
+
import { noMutablePropertyOfPropsInterface } from "./no-mutable-property-of-props-interface";
|
|
7
|
+
import { noMutablePublicPropertyOfConstruct } from "./no-mutable-public-property-of-construct";
|
|
8
|
+
import { noParentNameConstructIdMatch } from "./no-parent-name-construct-id-match";
|
|
9
|
+
import { noUnusedProps } from "./no-unused-props";
|
|
10
|
+
import { noVariableConstructId } from "./no-variable-construct-id";
|
|
11
|
+
import { pascalCaseConstructId } from "./pascal-case-construct-id";
|
|
12
|
+
import { propsNameConvention } from "./props-name-convention";
|
|
13
|
+
import { requireJSDoc } from "./require-jsdoc";
|
|
14
|
+
import { requirePassingThis } from "./require-passing-this";
|
|
15
|
+
import { requirePropsDefaultDoc } from "./require-props-default-doc";
|
|
16
|
+
|
|
17
|
+
export const rules = {
|
|
18
|
+
"construct-constructor-property": constructConstructorProperty,
|
|
19
|
+
"no-construct-in-interface": noConstructInInterface,
|
|
20
|
+
"no-construct-in-public-property-of-construct":
|
|
21
|
+
noConstructInPublicPropertyOfConstruct,
|
|
22
|
+
"no-construct-stack-suffix": noConstructStackSuffix,
|
|
23
|
+
"no-import-private": noImportPrivate,
|
|
24
|
+
"no-mutable-property-of-props-interface": noMutablePropertyOfPropsInterface,
|
|
25
|
+
"no-mutable-public-property-of-construct": noMutablePublicPropertyOfConstruct,
|
|
26
|
+
"no-parent-name-construct-id-match": noParentNameConstructIdMatch,
|
|
27
|
+
"no-unused-props": noUnusedProps,
|
|
28
|
+
"no-variable-construct-id": noVariableConstructId,
|
|
29
|
+
"pascal-case-construct-id": pascalCaseConstructId,
|
|
30
|
+
"props-name-convention": propsNameConvention,
|
|
31
|
+
"require-jsdoc": requireJSDoc,
|
|
32
|
+
"require-passing-this": requirePassingThis,
|
|
33
|
+
"require-props-default-doc": requirePropsDefaultDoc,
|
|
34
|
+
};
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { getGenericTypeArgument } from "../utils/getGenericTypeArgument";
|
|
6
|
-
import { isResourceWithReadonlyInterface } from "../utils/is-resource-with-readonly-interface";
|
|
7
|
-
import { isClassType } from "../utils/typecheck/ts-type";
|
|
3
|
+
import { findTypeOfCdkConstruct } from "../core/cdk-construct/type-finder";
|
|
4
|
+
import { createRule } from "../shared/create-rule";
|
|
8
5
|
|
|
9
6
|
/**
|
|
10
7
|
* Enforces the use of interface types instead of CDK Construct types in interface properties
|
|
@@ -38,58 +35,15 @@ export const noConstructInInterface = createRule({
|
|
|
38
35
|
}
|
|
39
36
|
|
|
40
37
|
const type = parserServices.getTypeAtLocation(property);
|
|
38
|
+
const result = findTypeOfCdkConstruct(type);
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
if (isClassType(type) && isResourceWithReadonlyInterface(type)) {
|
|
40
|
+
if (result) {
|
|
44
41
|
context.report({
|
|
45
42
|
node: property,
|
|
46
43
|
messageId: "invalidInterfaceProperty",
|
|
47
44
|
data: {
|
|
48
45
|
propertyName: property.key.name,
|
|
49
|
-
typeName:
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// NOTE: Check if it's an array of class types
|
|
56
|
-
const elementType = getArrayElementType(type);
|
|
57
|
-
if (
|
|
58
|
-
elementType &&
|
|
59
|
-
isClassType(elementType) &&
|
|
60
|
-
isResourceWithReadonlyInterface(elementType)
|
|
61
|
-
) {
|
|
62
|
-
context.report({
|
|
63
|
-
node: property,
|
|
64
|
-
messageId: "invalidInterfaceProperty",
|
|
65
|
-
data: {
|
|
66
|
-
propertyName: property.key.name,
|
|
67
|
-
typeName: `${elementType.symbol.name}[]`,
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// NOTE: Check if it's a generic type wrapping a class type
|
|
74
|
-
const genericArgument = getGenericTypeArgument(type);
|
|
75
|
-
if (
|
|
76
|
-
genericArgument &&
|
|
77
|
-
isClassType(genericArgument) &&
|
|
78
|
-
isResourceWithReadonlyInterface(genericArgument)
|
|
79
|
-
) {
|
|
80
|
-
const wrapperName = (() => {
|
|
81
|
-
if (type.aliasSymbol) return type.aliasSymbol.name; // For type aliases like Readonly<T>, Partial<T>
|
|
82
|
-
if (type.symbol?.name) return type.symbol.name; // For other generic types like Array<T>
|
|
83
|
-
return undefined;
|
|
84
|
-
})();
|
|
85
|
-
context.report({
|
|
86
|
-
node: property,
|
|
87
|
-
messageId: "invalidInterfaceProperty",
|
|
88
|
-
data: {
|
|
89
|
-
propertyName: property.key.name,
|
|
90
|
-
typeName: wrapperName
|
|
91
|
-
? `${wrapperName}<${genericArgument.symbol.name}>`
|
|
92
|
-
: genericArgument.symbol.name,
|
|
46
|
+
typeName: result.symbol.name,
|
|
93
47
|
},
|
|
94
48
|
});
|
|
95
49
|
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
|
-
AST_NODE_TYPES,
|
|
3
2
|
ESLintUtils,
|
|
4
3
|
ParserServicesWithTypeInformation,
|
|
5
4
|
TSESLint,
|
|
6
5
|
TSESTree,
|
|
7
6
|
} from "@typescript-eslint/utils";
|
|
8
|
-
import { Type } from "typescript";
|
|
9
7
|
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { isResourceWithReadonlyInterface } from "../utils/is-resource-with-readonly-interface";
|
|
15
|
-
import { isConstructOrStackType } from "../utils/typecheck/cdk";
|
|
16
|
-
import { isClassType } from "../utils/typecheck/ts-type";
|
|
8
|
+
import { findPublicPropertiesInClass } from "../core/ast-node/finder/public-property";
|
|
9
|
+
import { isConstructOrStackType } from "../core/cdk-construct/type-checker/is-construct-or-stack";
|
|
10
|
+
import { findTypeOfCdkConstruct } from "../core/cdk-construct/type-finder";
|
|
11
|
+
import { createRule } from "../shared/create-rule";
|
|
17
12
|
|
|
18
13
|
type Context = TSESLint.RuleContext<"invalidPublicPropertyOfConstruct", []>;
|
|
19
14
|
|
|
15
|
+
type PublicProperty = {
|
|
16
|
+
name: string;
|
|
17
|
+
node: TSESTree.Parameter | TSESTree.ClassElement;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
20
|
/**
|
|
21
21
|
* Disallow Construct types in public property of Construct
|
|
22
22
|
* @param context - The rule context provided by ESLint
|
|
@@ -42,152 +42,29 @@ export const noConstructInPublicPropertyOfConstruct = createRule({
|
|
|
42
42
|
ClassDeclaration(node) {
|
|
43
43
|
const type = parserServices.getTypeAtLocation(node);
|
|
44
44
|
if (!isConstructOrStackType(type)) return;
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
// NOTE: Check constructor parameter properties
|
|
50
|
-
const constructor = getConstructor(node);
|
|
51
|
-
if (
|
|
52
|
-
!constructor ||
|
|
53
|
-
constructor.value.type !== AST_NODE_TYPES.FunctionExpression
|
|
54
|
-
) {
|
|
55
|
-
return;
|
|
45
|
+
const publicProperties = findPublicPropertiesInClass(node);
|
|
46
|
+
for (const publicProperty of publicProperties) {
|
|
47
|
+
validatePublicProperty(publicProperty, context, parserServices);
|
|
56
48
|
}
|
|
57
|
-
|
|
58
|
-
validateConstructorParameterProperty(
|
|
59
|
-
constructor,
|
|
60
|
-
context,
|
|
61
|
-
parserServices
|
|
62
|
-
);
|
|
63
49
|
},
|
|
64
50
|
};
|
|
65
51
|
},
|
|
66
52
|
});
|
|
67
53
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
* - if it is a Construct type, report an error
|
|
71
|
-
*/
|
|
72
|
-
const validatePublicPropertyOfConstruct = (
|
|
73
|
-
node: TSESTree.ClassDeclaration,
|
|
54
|
+
const validatePublicProperty = (
|
|
55
|
+
publicProperty: PublicProperty,
|
|
74
56
|
context: Context,
|
|
75
57
|
parserServices: ParserServicesWithTypeInformation
|
|
76
58
|
) => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
property.key.type !== AST_NODE_TYPES.Identifier
|
|
81
|
-
) {
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// NOTE: Skip private and protected fields
|
|
86
|
-
if (["private", "protected"].includes(property.accessibility ?? "")) {
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// NOTE: Skip fields without type annotation
|
|
91
|
-
if (!property.typeAnnotation) continue;
|
|
92
|
-
|
|
93
|
-
const type = parserServices.getTypeAtLocation(property);
|
|
94
|
-
checkAndReportConstructType(type, property, property.key.name, context);
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* check the constructor parameter property
|
|
100
|
-
* - if it is a Construct type, report an error
|
|
101
|
-
*/
|
|
102
|
-
const validateConstructorParameterProperty = (
|
|
103
|
-
constructor: TSESTree.MethodDefinition,
|
|
104
|
-
context: Context,
|
|
105
|
-
parserServices: ParserServicesWithTypeInformation
|
|
106
|
-
) => {
|
|
107
|
-
for (const param of constructor.value.params) {
|
|
108
|
-
if (
|
|
109
|
-
param.type !== AST_NODE_TYPES.TSParameterProperty ||
|
|
110
|
-
param.parameter.type !== AST_NODE_TYPES.Identifier
|
|
111
|
-
) {
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// NOTE: Skip private and protected parameters
|
|
116
|
-
if (["private", "protected"].includes(param.accessibility ?? "")) {
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// NOTE: Skip parameters without type annotation
|
|
121
|
-
if (!param.parameter.typeAnnotation) continue;
|
|
122
|
-
|
|
123
|
-
const type = parserServices.getTypeAtLocation(param);
|
|
124
|
-
checkAndReportConstructType(type, param, param.parameter.name, context);
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Common validation logic for checking if a type is a Construct type
|
|
130
|
-
*/
|
|
131
|
-
const checkAndReportConstructType = (
|
|
132
|
-
type: Type,
|
|
133
|
-
node: TSESTree.Node,
|
|
134
|
-
propertyName: string,
|
|
135
|
-
context: Context
|
|
136
|
-
): void => {
|
|
137
|
-
// NOTE: Check if it's a direct class type
|
|
138
|
-
if (isClassType(type) && isResourceWithReadonlyInterface(type)) {
|
|
139
|
-
context.report({
|
|
140
|
-
node,
|
|
141
|
-
messageId: "invalidPublicPropertyOfConstruct",
|
|
142
|
-
data: {
|
|
143
|
-
propertyName,
|
|
144
|
-
typeName: type.symbol.name,
|
|
145
|
-
},
|
|
146
|
-
});
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// NOTE: Check if it's an array of class types
|
|
151
|
-
const elementType = getArrayElementType(type);
|
|
152
|
-
if (
|
|
153
|
-
elementType &&
|
|
154
|
-
isClassType(elementType) &&
|
|
155
|
-
isResourceWithReadonlyInterface(elementType)
|
|
156
|
-
) {
|
|
157
|
-
context.report({
|
|
158
|
-
node,
|
|
159
|
-
messageId: "invalidPublicPropertyOfConstruct",
|
|
160
|
-
data: {
|
|
161
|
-
propertyName,
|
|
162
|
-
typeName: `${elementType.symbol.name}[]`,
|
|
163
|
-
},
|
|
164
|
-
});
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// NOTE: Check if it's a generic type wrapping a class type
|
|
169
|
-
const genericArgument = getGenericTypeArgument(type);
|
|
170
|
-
if (
|
|
171
|
-
genericArgument &&
|
|
172
|
-
isClassType(genericArgument) &&
|
|
173
|
-
isResourceWithReadonlyInterface(genericArgument)
|
|
174
|
-
) {
|
|
175
|
-
const wrapperName = (() => {
|
|
176
|
-
if ("aliasSymbol" in type && type.aliasSymbol) {
|
|
177
|
-
return type.aliasSymbol.name; // For type aliases like Readonly<T>, Partial<T>
|
|
178
|
-
}
|
|
179
|
-
if (type.symbol?.name) return type.symbol.name; // For other generic types like Array<T>
|
|
180
|
-
return undefined;
|
|
181
|
-
})();
|
|
182
|
-
|
|
59
|
+
const type = parserServices.getTypeAtLocation(publicProperty.node);
|
|
60
|
+
const constructType = findTypeOfCdkConstruct(type);
|
|
61
|
+
if (constructType) {
|
|
183
62
|
context.report({
|
|
184
|
-
node,
|
|
63
|
+
node: publicProperty.node,
|
|
185
64
|
messageId: "invalidPublicPropertyOfConstruct",
|
|
186
65
|
data: {
|
|
187
|
-
propertyName,
|
|
188
|
-
typeName:
|
|
189
|
-
? `${wrapperName}<${genericArgument.symbol.name}>`
|
|
190
|
-
: genericArgument.symbol.name,
|
|
66
|
+
propertyName: publicProperty.name,
|
|
67
|
+
typeName: constructType.symbol.name,
|
|
191
68
|
},
|
|
192
69
|
});
|
|
193
70
|
}
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
TSESTree,
|
|
6
6
|
} from "@typescript-eslint/utils";
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
8
|
+
import { isConstructOrStackType } from "../core/cdk-construct/type-checker/is-construct-or-stack";
|
|
9
|
+
import { findConstructorPropertyNames } from "../core/ts-type/finder/constructor-property-name";
|
|
10
|
+
import { toPascalCase } from "../shared/converter/to-pascal-case";
|
|
11
|
+
import { createRule } from "../shared/create-rule";
|
|
12
12
|
|
|
13
13
|
const SUFFIX_TYPE = {
|
|
14
14
|
CONSTRUCT: "Construct",
|
|
@@ -72,7 +72,7 @@ export const noConstructStackSuffix = createRule({
|
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
const constructorPropertyNames =
|
|
75
|
+
const constructorPropertyNames = findConstructorPropertyNames(type);
|
|
76
76
|
if (constructorPropertyNames[1] !== "id") return;
|
|
77
77
|
|
|
78
78
|
validateConstructId(node, context);
|