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
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
import { IPropsUsageTracker } from "../props-usage-tracker";
|
|
4
|
+
|
|
5
|
+
import { INodeVisitor } from "./interface/node-visitor";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Visitor that tracks direct usage of the props parameter.
|
|
9
|
+
*
|
|
10
|
+
* This visitor detects various patterns where props is accessed directly:
|
|
11
|
+
* - `props.bucketName` - Property access
|
|
12
|
+
* - `const { bucketName } = props` - Destructuring assignment
|
|
13
|
+
* - `this.bucketName = props.bucketName` - Assignment expression
|
|
14
|
+
* - `console.log(props)` - Whole object usage (marks all properties as used)
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* constructor(scope: Construct, id: string, props: MyConstructProps) {
|
|
19
|
+
* super(scope, id);
|
|
20
|
+
* const name = props.bucketName; // <- Detected by visitMemberExpression
|
|
21
|
+
* const { enableVersioning } = props; // <- Detected by visitVariableDeclarator
|
|
22
|
+
* this.name = props.bucketName; // <- Detected by visitAssignmentExpression
|
|
23
|
+
* console.log(props); // <- Detected by visitIdentifier (marks all)
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export class DirectPropsUsageVisitor implements INodeVisitor {
|
|
28
|
+
constructor(
|
|
29
|
+
private readonly tracker: IPropsUsageTracker,
|
|
30
|
+
private readonly propsParamName: string
|
|
31
|
+
) {}
|
|
32
|
+
|
|
33
|
+
visitMemberExpression(node: TSESTree.MemberExpression): void {
|
|
34
|
+
this.tracker.markAsUsedForMemberExpression(node, this.propsParamName);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
visitVariableDeclarator(node: TSESTree.VariableDeclarator): void {
|
|
38
|
+
this.tracker.markAsUsedForVariableDeclarator(node, this.propsParamName);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
visitAssignmentExpression(node: TSESTree.AssignmentExpression): void {
|
|
42
|
+
this.tracker.markAsUsedForAssignmentExpression(node, this.propsParamName);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
visitIdentifier(node: TSESTree.Identifier): void {
|
|
46
|
+
/**
|
|
47
|
+
* Handles cases where the props identifier is used as a whole value.
|
|
48
|
+
*
|
|
49
|
+
* This method detects when `props` (or the configured propsParamName) is used
|
|
50
|
+
* in contexts where all properties should be marked as used, because we cannot
|
|
51
|
+
* statically determine which properties will be accessed at runtime.
|
|
52
|
+
*
|
|
53
|
+
* Handled patterns:
|
|
54
|
+
*
|
|
55
|
+
* 1. **External function call** (`someFunction(props)` or `console.log(props)`):
|
|
56
|
+
* - Marks ALL properties as used
|
|
57
|
+
* - Excludes `this.method(props)` which is handled by analyzePrivateMethodsCalledFromConstructor
|
|
58
|
+
*
|
|
59
|
+
* AST structure:
|
|
60
|
+
* CallExpression
|
|
61
|
+
* ├── callee: Identifier (someFunction) or MemberExpression (console.log)
|
|
62
|
+
* └── arguments: [Identifier (name: "props")]
|
|
63
|
+
*
|
|
64
|
+
* 2. **Return statement** (`return props`):
|
|
65
|
+
* - Marks ALL properties as used
|
|
66
|
+
*
|
|
67
|
+
* AST structure:
|
|
68
|
+
* ReturnStatement
|
|
69
|
+
* └── argument: Identifier (name: "props")
|
|
70
|
+
*
|
|
71
|
+
* 3. **Array element** (`[props]` or `[a, props, b]`):
|
|
72
|
+
* - Marks ALL properties as used
|
|
73
|
+
*
|
|
74
|
+
* AST structure:
|
|
75
|
+
* ArrayExpression
|
|
76
|
+
* └── elements: [..., Identifier (name: "props"), ...]
|
|
77
|
+
*
|
|
78
|
+
* 4. **Object property value** (`{ key: props }`):
|
|
79
|
+
* - Marks ALL properties as used
|
|
80
|
+
*
|
|
81
|
+
* AST structure:
|
|
82
|
+
* Property
|
|
83
|
+
* ├── key: Identifier (name: "key")
|
|
84
|
+
* └── value: Identifier (name: "props")
|
|
85
|
+
*
|
|
86
|
+
* The following cases are intentionally excluded (handled elsewhere):
|
|
87
|
+
* - `props.xxx` (MemberExpression) - handled by visitMemberExpression
|
|
88
|
+
* - `const { xxx } = props` (VariableDeclarator with ObjectPattern) - handled by visitVariableDeclarator
|
|
89
|
+
* - `this.xxx = props.xxx` (AssignmentExpression) - handled by visitAssignmentExpression
|
|
90
|
+
* - `this.method(props)` (CallExpression with ThisExpression callee) - handled by analyzePrivateMethodsCalledFromConstructor
|
|
91
|
+
* - `const a = props` (alias registration) - handled by PropsAliasVisitor
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
// NOTE: Check if props object is used as a whole (e.g., console.log(props))
|
|
95
|
+
if (node.name !== this.propsParamName) return;
|
|
96
|
+
|
|
97
|
+
const parent = node.parent;
|
|
98
|
+
if (!parent) return;
|
|
99
|
+
|
|
100
|
+
switch (parent.type) {
|
|
101
|
+
// NOTE: Pattern 1: External function call
|
|
102
|
+
case AST_NODE_TYPES.CallExpression: {
|
|
103
|
+
if (!parent.arguments.includes(node)) return;
|
|
104
|
+
if (
|
|
105
|
+
parent.callee.type === AST_NODE_TYPES.MemberExpression &&
|
|
106
|
+
parent.callee.object.type === AST_NODE_TYPES.ThisExpression
|
|
107
|
+
) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
this.tracker.markAllAsUsed();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// NOTE: Pattern 2: Return statement
|
|
115
|
+
case AST_NODE_TYPES.ReturnStatement: {
|
|
116
|
+
// NOTE: return props - props as a whole
|
|
117
|
+
if (parent.argument === node) {
|
|
118
|
+
this.tracker.markAllAsUsed();
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// NOTE: Pattern 3: Array element
|
|
124
|
+
case AST_NODE_TYPES.ArrayExpression: {
|
|
125
|
+
// NOTE: [props] - props as a whole
|
|
126
|
+
if (parent.elements.includes(node)) {
|
|
127
|
+
this.tracker.markAllAsUsed();
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// NOTE: Pattern 4: Object property value
|
|
133
|
+
case AST_NODE_TYPES.Property: {
|
|
134
|
+
// NOTE: { key: props } - props as a whole
|
|
135
|
+
if (parent.value === node) {
|
|
136
|
+
this.tracker.markAllAsUsed();
|
|
137
|
+
}
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
default: {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { DirectPropsUsageVisitor } from "./direct-props-usage-visitor";
|
|
2
|
+
export { InstanceVariableUsageVisitor } from "./instance-variable-usage-visitor";
|
|
3
|
+
export { MethodCallCollectorVisitor } from "./method-call-collector-visitor";
|
|
4
|
+
export { PropsAliasVisitor } from "./props-alias-visitor";
|
|
5
|
+
export { traverseNodes } from "./traverse-nodes";
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
import { IPropsUsageTracker } from "../props-usage-tracker";
|
|
4
|
+
|
|
5
|
+
import { INodeVisitor } from "./interface/node-visitor";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Visitor that tracks props usage through instance variables.
|
|
9
|
+
*
|
|
10
|
+
* When props is assigned to an instance variable (e.g., `this.myProps = props`),
|
|
11
|
+
* this visitor tracks property access on that instance variable throughout the class.
|
|
12
|
+
*
|
|
13
|
+
* Handles two patterns:
|
|
14
|
+
* 1. Property access: `this.myProps.bucketName` - marks specific property as used
|
|
15
|
+
* 2. Whole object usage: `console.log(this.myProps)` - marks all properties as used
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* class MyConstruct extends Construct {
|
|
20
|
+
* private myProps: MyConstructProps;
|
|
21
|
+
*
|
|
22
|
+
* constructor(scope: Construct, id: string, props: MyConstructProps) {
|
|
23
|
+
* super(scope, id);
|
|
24
|
+
* this.myProps = props;
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* someMethod() {
|
|
28
|
+
* console.log(this.myProps.bucketName); // <- 'bucketName' is marked as used
|
|
29
|
+
* }
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class InstanceVariableUsageVisitor implements INodeVisitor {
|
|
34
|
+
constructor(
|
|
35
|
+
private readonly tracker: IPropsUsageTracker,
|
|
36
|
+
private readonly instanceVarName: string
|
|
37
|
+
) {}
|
|
38
|
+
|
|
39
|
+
visitMemberExpression(node: TSESTree.MemberExpression): void {
|
|
40
|
+
// ===========================================================================
|
|
41
|
+
// Pattern 1: Property access - this.instanceVarName.propertyName
|
|
42
|
+
// ===========================================================================
|
|
43
|
+
// Matches code like: `console.log(this.myProps.bucketName)` or `return this.myProps.enableVersioning`
|
|
44
|
+
//
|
|
45
|
+
// AST structure:
|
|
46
|
+
// MemberExpression (node)
|
|
47
|
+
// ├── object: MemberExpression (this.myProps)
|
|
48
|
+
// │ ├── object: ThisExpression
|
|
49
|
+
// │ └── property: Identifier (name: "myProps" === instanceVarName)
|
|
50
|
+
// └── property: Identifier (name: "bucketName" - the property being accessed)
|
|
51
|
+
//
|
|
52
|
+
// When this pattern matches, we mark the accessed property (e.g., "bucketName") as used.
|
|
53
|
+
if (
|
|
54
|
+
node.type === AST_NODE_TYPES.MemberExpression &&
|
|
55
|
+
node.object.type === AST_NODE_TYPES.MemberExpression &&
|
|
56
|
+
node.object.object.type === AST_NODE_TYPES.ThisExpression &&
|
|
57
|
+
node.object.property.type === AST_NODE_TYPES.Identifier &&
|
|
58
|
+
node.object.property.name === this.instanceVarName &&
|
|
59
|
+
node.property.type === AST_NODE_TYPES.Identifier
|
|
60
|
+
) {
|
|
61
|
+
this.tracker.markAsUsed(node.property.name);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ===========================================================================
|
|
66
|
+
// Pattern 2: Whole object usage - this.instanceVarName used as a value
|
|
67
|
+
// ===========================================================================
|
|
68
|
+
// Matches code like: `console.log(this.myProps)` or `return this.myProps`
|
|
69
|
+
//
|
|
70
|
+
// AST structure:
|
|
71
|
+
// MemberExpression (node)
|
|
72
|
+
// ├── object: ThisExpression
|
|
73
|
+
// └── property: Identifier (name: "myProps" === instanceVarName)
|
|
74
|
+
//
|
|
75
|
+
// However, we need to exclude certain cases where `this.myProps` appears
|
|
76
|
+
// but isn't actually being "used as a value":
|
|
77
|
+
//
|
|
78
|
+
// Exclusion A: Property access (handled by Pattern 1 above)
|
|
79
|
+
// Code: `this.myProps.bucketName`
|
|
80
|
+
// The `this.myProps` part has parent MemberExpression where it's the object
|
|
81
|
+
//
|
|
82
|
+
// Exclusion B: Assignment left-hand side
|
|
83
|
+
// Code: `this.myProps = props`
|
|
84
|
+
// The `this.myProps` is being assigned to, not used as a value
|
|
85
|
+
if (
|
|
86
|
+
node.type === AST_NODE_TYPES.MemberExpression &&
|
|
87
|
+
node.object.type === AST_NODE_TYPES.ThisExpression &&
|
|
88
|
+
node.property.type === AST_NODE_TYPES.Identifier &&
|
|
89
|
+
node.property.name === this.instanceVarName
|
|
90
|
+
) {
|
|
91
|
+
const parent = node.parent;
|
|
92
|
+
|
|
93
|
+
// NOTE: Exclusion A - Skip if this is part of a property access (this.myProps.xxx)
|
|
94
|
+
// The parent MemberExpression's object being this node means we're accessing a property
|
|
95
|
+
if (
|
|
96
|
+
parent?.type === AST_NODE_TYPES.MemberExpression &&
|
|
97
|
+
parent.object === node
|
|
98
|
+
) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// NOTE: Exclusion B - Skip if this is the left side of an assignment (this.myProps = ...)
|
|
103
|
+
if (
|
|
104
|
+
parent?.type === AST_NODE_TYPES.AssignmentExpression &&
|
|
105
|
+
parent.left === node
|
|
106
|
+
) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// NOTE: If we reach here, `this.myProps` is used as a whole value
|
|
111
|
+
// Examples: console.log(this.myProps), return this.myProps, [this.myProps], etc.
|
|
112
|
+
// Since we can't know which properties will be accessed at runtime, mark all as used
|
|
113
|
+
this.tracker.markAllAsUsed();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
export interface INodeVisitor {
|
|
4
|
+
visitMemberExpression?(node: TSESTree.MemberExpression): void;
|
|
5
|
+
visitVariableDeclarator?(node: TSESTree.VariableDeclarator): void;
|
|
6
|
+
visitAssignmentExpression?(node: TSESTree.AssignmentExpression): void;
|
|
7
|
+
visitIdentifier?(node: TSESTree.Identifier): void;
|
|
8
|
+
visitCallExpression?(node: TSESTree.CallExpression): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
import { INodeVisitor } from "./interface/node-visitor";
|
|
4
|
+
|
|
5
|
+
type MethodCallInfo = {
|
|
6
|
+
methodName: string;
|
|
7
|
+
propsArgIndices: number[];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Visitor that collects method calls where props is passed as an argument.
|
|
12
|
+
*
|
|
13
|
+
* This visitor detects patterns like:
|
|
14
|
+
* - `this.someMethod(props)`
|
|
15
|
+
* - `this.someMethod(a, props, b)` (props at any position)
|
|
16
|
+
*
|
|
17
|
+
* AST structure for `this.someMethod(props)`:
|
|
18
|
+
* CallExpression
|
|
19
|
+
* ├── callee: MemberExpression
|
|
20
|
+
* │ ├── object: ThisExpression
|
|
21
|
+
* │ └── property: Identifier (name: "someMethod")
|
|
22
|
+
* └── arguments: [Identifier (name: "props")]
|
|
23
|
+
*
|
|
24
|
+
* The collected information is used by analyzePrivateMethodsCalledFromConstructor
|
|
25
|
+
* to analyze the method bodies and track props usage within them.
|
|
26
|
+
*/
|
|
27
|
+
export class MethodCallCollectorVisitor implements INodeVisitor {
|
|
28
|
+
private readonly _result: MethodCallInfo[] = [];
|
|
29
|
+
|
|
30
|
+
constructor(private readonly propsParamName: string) {}
|
|
31
|
+
|
|
32
|
+
visitCallExpression(node: TSESTree.CallExpression): void {
|
|
33
|
+
// NOTE: Check for this.methodName(...) pattern
|
|
34
|
+
if (
|
|
35
|
+
node.callee.type !== AST_NODE_TYPES.MemberExpression ||
|
|
36
|
+
node.callee.object.type !== AST_NODE_TYPES.ThisExpression ||
|
|
37
|
+
node.callee.property.type !== AST_NODE_TYPES.Identifier
|
|
38
|
+
) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const methodName = node.callee.property.name;
|
|
43
|
+
const propsArgIndices = node.arguments.reduce<number[]>(
|
|
44
|
+
(acc, arg, index) =>
|
|
45
|
+
arg.type === AST_NODE_TYPES.Identifier &&
|
|
46
|
+
arg.name === this.propsParamName
|
|
47
|
+
? [...acc, index]
|
|
48
|
+
: acc,
|
|
49
|
+
[]
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
if (propsArgIndices.length) {
|
|
53
|
+
this._result.push({ methodName, propsArgIndices });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get result(): MethodCallInfo[] {
|
|
58
|
+
return this._result;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
import { IPropsUsageTracker } from "../props-usage-tracker";
|
|
4
|
+
|
|
5
|
+
import { INodeVisitor } from "./interface/node-visitor";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Visitor that tracks props usage through variable aliases.
|
|
9
|
+
*
|
|
10
|
+
* When props is assigned to another variable (alias), this visitor:
|
|
11
|
+
* 1. Registers the alias when `const myProps = props` is detected
|
|
12
|
+
* 2. Tracks property access on the alias like `myProps.bucketName`
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* constructor(scope: Construct, id: string, props: MyConstructProps) {
|
|
17
|
+
* super(scope, id);
|
|
18
|
+
* const myProps = props; // <- Alias 'myProps' is registered
|
|
19
|
+
* console.log(myProps.bucketName); // <- 'bucketName' is marked as used
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export class PropsAliasVisitor implements INodeVisitor {
|
|
24
|
+
private readonly aliases = new Set<string>();
|
|
25
|
+
|
|
26
|
+
constructor(
|
|
27
|
+
private readonly tracker: IPropsUsageTracker,
|
|
28
|
+
private readonly propsParamName: string
|
|
29
|
+
) {}
|
|
30
|
+
|
|
31
|
+
visitMemberExpression(node: TSESTree.MemberExpression): void {
|
|
32
|
+
this.tracker.markAsUsedForMemberExpression(node, this.propsParamName);
|
|
33
|
+
/**
|
|
34
|
+
* NOTE: Check if the object is an alias of props
|
|
35
|
+
* ```ts
|
|
36
|
+
* const myProps = props;
|
|
37
|
+
* console.log(myProps.bucketName); // <- detect this access
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
if (
|
|
41
|
+
node.object.type === AST_NODE_TYPES.Identifier &&
|
|
42
|
+
this.aliases.has(node.object.name) &&
|
|
43
|
+
node.property.type === AST_NODE_TYPES.Identifier
|
|
44
|
+
) {
|
|
45
|
+
this.tracker.markAsUsed(node.property.name);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
visitIdentifier(node: TSESTree.Identifier): void {
|
|
50
|
+
/**
|
|
51
|
+
* Handles alias registration for props.
|
|
52
|
+
*
|
|
53
|
+
* This method detects when props is assigned to a simple variable,
|
|
54
|
+
* which creates an alias that should be tracked for property access.
|
|
55
|
+
*
|
|
56
|
+
* Handled pattern:
|
|
57
|
+
*
|
|
58
|
+
* **Variable assignment** (`const myProps = props`):
|
|
59
|
+
* - Registers 'myProps' as an alias of props
|
|
60
|
+
* - Later access like `myProps.bucketName` will be detected by visitMemberExpression
|
|
61
|
+
*
|
|
62
|
+
* AST structure:
|
|
63
|
+
* VariableDeclarator
|
|
64
|
+
* ├── id: Identifier (name: "myProps" - the alias to register)
|
|
65
|
+
* └── init: Identifier (name: "props")
|
|
66
|
+
*/
|
|
67
|
+
if (node.name !== this.propsParamName) return;
|
|
68
|
+
|
|
69
|
+
const parent = node.parent;
|
|
70
|
+
if (!parent) return;
|
|
71
|
+
|
|
72
|
+
// NOTE: const myProps = props - track 'myProps' as an alias of props
|
|
73
|
+
if (
|
|
74
|
+
parent.type === AST_NODE_TYPES.VariableDeclarator &&
|
|
75
|
+
parent.init === node &&
|
|
76
|
+
parent.id.type === AST_NODE_TYPES.Identifier
|
|
77
|
+
) {
|
|
78
|
+
this.aliases.add(parent.id.name);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
import { findChildNodes } from "../../../core/ast-node/finder/child-nodes";
|
|
4
|
+
|
|
5
|
+
import { INodeVisitor } from "./interface/node-visitor";
|
|
6
|
+
|
|
7
|
+
export const traverseNodes = (
|
|
8
|
+
node: TSESTree.Node,
|
|
9
|
+
visitor: INodeVisitor
|
|
10
|
+
): void => {
|
|
11
|
+
switch (node.type) {
|
|
12
|
+
case AST_NODE_TYPES.MemberExpression: {
|
|
13
|
+
visitor.visitMemberExpression?.(node);
|
|
14
|
+
break;
|
|
15
|
+
}
|
|
16
|
+
case AST_NODE_TYPES.VariableDeclarator: {
|
|
17
|
+
visitor.visitVariableDeclarator?.(node);
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
case AST_NODE_TYPES.AssignmentExpression: {
|
|
21
|
+
visitor.visitAssignmentExpression?.(node);
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
case AST_NODE_TYPES.Identifier: {
|
|
25
|
+
visitor.visitIdentifier?.(node);
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
case AST_NODE_TYPES.CallExpression: {
|
|
29
|
+
visitor.visitCallExpression?.(node);
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// NOTE: Recursively visit child nodes
|
|
35
|
+
for (const child of findChildNodes(node)) {
|
|
36
|
+
traverseNodes(child, visitor);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
@@ -5,9 +5,9 @@ import {
|
|
|
5
5
|
TSESTree,
|
|
6
6
|
} from "@typescript-eslint/utils";
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
8
|
+
import { isConstructType } from "../core/cdk-construct/type-checker/is-construct";
|
|
9
|
+
import { findConstructorPropertyNames } from "../core/ts-type/finder/constructor-property-name";
|
|
10
|
+
import { createRule } from "../shared/create-rule";
|
|
11
11
|
|
|
12
12
|
type Context = TSESLint.RuleContext<"invalidConstructId", []>;
|
|
13
13
|
|
|
@@ -37,7 +37,7 @@ export const noVariableConstructId = createRule({
|
|
|
37
37
|
|
|
38
38
|
if (!isConstructType(type) || node.arguments.length < 2) return;
|
|
39
39
|
|
|
40
|
-
const constructorPropertyNames =
|
|
40
|
+
const constructorPropertyNames = findConstructorPropertyNames(type);
|
|
41
41
|
if (constructorPropertyNames[1] !== "id") return;
|
|
42
42
|
|
|
43
43
|
validateConstructId(node, context);
|
|
@@ -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 QUOTE_TYPE = {
|
|
14
14
|
SINGLE: "'",
|
|
@@ -48,7 +48,7 @@ export const pascalCaseConstructId = createRule({
|
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
const constructorPropertyNames =
|
|
51
|
+
const constructorPropertyNames = findConstructorPropertyNames(type);
|
|
52
52
|
if (constructorPropertyNames[1] !== "id") return;
|
|
53
53
|
|
|
54
54
|
validateConstructId(node, context);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { AST_NODE_TYPES, ESLintUtils } from "@typescript-eslint/utils";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { findConstructor } from "../core/ast-node/finder/constructor";
|
|
4
|
+
import { isConstructType } from "../core/cdk-construct/type-checker/is-construct";
|
|
5
|
+
import { createRule } from "../shared/create-rule";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Enforces a naming convention for props interfaces in Construct classes
|
|
@@ -34,7 +34,7 @@ export const propsNameConvention = createRule({
|
|
|
34
34
|
if (!isConstructType(type)) return;
|
|
35
35
|
|
|
36
36
|
// NOTE: check constructor parameter
|
|
37
|
-
const constructor =
|
|
37
|
+
const constructor = findConstructor(node);
|
|
38
38
|
if (!constructor) return;
|
|
39
39
|
|
|
40
40
|
const propsParam = constructor.value.params?.[2];
|
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
ESLintUtils,
|
|
5
5
|
} from "@typescript-eslint/utils";
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { isConstructType } from "../core/cdk-construct/type-checker/is-construct";
|
|
8
|
+
import { createRule } from "../shared/create-rule";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Require JSDoc comments for interface properties and public properties in Construct classes
|
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
TSESLint,
|
|
5
5
|
} from "@typescript-eslint/utils";
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
7
|
+
import { isConstructType } from "../core/cdk-construct/type-checker/is-construct";
|
|
8
|
+
import { findConstructorPropertyNames } from "../core/ts-type/finder/constructor-property-name";
|
|
9
|
+
import { createRule } from "../shared/create-rule";
|
|
10
10
|
|
|
11
11
|
type Option = {
|
|
12
12
|
allowNonThisAndDisallowScope?: boolean;
|
|
@@ -63,7 +63,7 @@ export const requirePassingThis = createRule({
|
|
|
63
63
|
if (argument.type === AST_NODE_TYPES.ThisExpression) return;
|
|
64
64
|
|
|
65
65
|
// NOTE: If the first argument is not `scope`, it's valid
|
|
66
|
-
const constructorPropertyNames =
|
|
66
|
+
const constructorPropertyNames = findConstructorPropertyNames(type);
|
|
67
67
|
if (constructorPropertyNames[0] !== "scope") return;
|
|
68
68
|
|
|
69
69
|
// NOTE: If `allowNonThisAndDisallowScope` is false, require `this` for all cases
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AST_NODE_TYPES, AST_TOKEN_TYPES } from "@typescript-eslint/utils";
|
|
2
2
|
|
|
3
|
-
import { createRule } from "../
|
|
3
|
+
import { createRule } from "../shared/create-rule";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Requires @default JSDoc documentation for optional properties in interfaces ending with 'Props'
|