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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-cdk-plugin",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.6",
|
|
4
4
|
"description": "eslint plugin for AWS CDK projects",
|
|
5
5
|
"main": "./dist/index.mjs",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -20,44 +20,42 @@
|
|
|
20
20
|
"migrate-cdk-plugin": "./bin/migration.mjs"
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
|
-
"build": "
|
|
23
|
+
"build": "tsdown",
|
|
24
24
|
"test": "vitest --run",
|
|
25
|
-
"
|
|
25
|
+
"test:integration": "sh scripts/integration-test.sh",
|
|
26
|
+
"lint": "eslint --fix",
|
|
26
27
|
"check": "tsc --noEmit",
|
|
27
28
|
"pack": "pnpm run build && npm pack",
|
|
28
29
|
"docs": "pnpm run --filter=@eslint-cdk-plugin/docs",
|
|
29
|
-
"example:flat-config": "pnpm run --filter=@eslint-cdk-plugin/example-flat-config"
|
|
30
|
-
"prebuild": "rolldown -c"
|
|
30
|
+
"example:flat-config": "pnpm run --filter=@eslint-cdk-plugin/example-flat-config"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@eslint/js": "^9.
|
|
34
|
-
"@secretlint/secretlint-rule-preset-recommend": "^11.2.
|
|
35
|
-
"@types/node": "^24.
|
|
36
|
-
"@typescript-eslint/
|
|
37
|
-
"@
|
|
38
|
-
"
|
|
33
|
+
"@eslint/js": "^9.39.1",
|
|
34
|
+
"@secretlint/secretlint-rule-preset-recommend": "^11.2.5",
|
|
35
|
+
"@types/node": "^24.10.0",
|
|
36
|
+
"@typescript-eslint/parser": "^8.48.1",
|
|
37
|
+
"@typescript-eslint/rule-tester": "^8.48.1",
|
|
38
|
+
"@typescript-eslint/utils": "^8.48.1",
|
|
39
|
+
"commander": "^14.0.2",
|
|
39
40
|
"consola": "^3.4.2",
|
|
40
|
-
"eslint": "^9.
|
|
41
|
+
"eslint": "^9.39.1",
|
|
41
42
|
"eslint-plugin-import": "^2.32.0",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"secretlint": "^11.2.4",
|
|
43
|
+
"secretlint": "^11.2.5",
|
|
44
|
+
"tsdown": "^0.17.0",
|
|
45
45
|
"typescript": "^5.9.3",
|
|
46
|
-
"typescript-eslint": "^8.
|
|
47
|
-
"vitest": "^
|
|
48
|
-
},
|
|
49
|
-
"dependencies": {
|
|
50
|
-
"@typescript-eslint/parser": "^8.46.2",
|
|
51
|
-
"@typescript-eslint/utils": "^8.46.2"
|
|
46
|
+
"typescript-eslint": "^8.48.1",
|
|
47
|
+
"vitest": "^4.0.6"
|
|
52
48
|
},
|
|
53
49
|
"peerDependencies": {
|
|
54
|
-
"
|
|
50
|
+
"eslint": "^8.57.0 || ^9.0.0",
|
|
51
|
+
"typescript": ">=4.8.4 <6.0.0"
|
|
55
52
|
},
|
|
56
53
|
"engines": {
|
|
57
54
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
|
58
55
|
},
|
|
59
56
|
"packageManager": "pnpm@10.10.0",
|
|
60
57
|
"files": [
|
|
58
|
+
"bin",
|
|
61
59
|
"dist",
|
|
62
60
|
"src",
|
|
63
61
|
"!src/__tests__",
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import tsParser from "@typescript-eslint/parser";
|
|
2
|
+
import { FlatConfig } from "@typescript-eslint/utils/ts-eslint";
|
|
3
|
+
|
|
4
|
+
import { name, version } from "../../package.json";
|
|
5
|
+
import { rules } from "../rules";
|
|
6
|
+
|
|
7
|
+
const cdk = {
|
|
8
|
+
meta: { name, version },
|
|
9
|
+
rules,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const createFlatConfig = (
|
|
13
|
+
rules: FlatConfig.Rules
|
|
14
|
+
): {
|
|
15
|
+
languageOptions: FlatConfig.LanguageOptions;
|
|
16
|
+
plugins: FlatConfig.Plugins;
|
|
17
|
+
rules: FlatConfig.Rules;
|
|
18
|
+
} => {
|
|
19
|
+
return {
|
|
20
|
+
plugins: {
|
|
21
|
+
cdk,
|
|
22
|
+
},
|
|
23
|
+
languageOptions: {
|
|
24
|
+
parser: tsParser,
|
|
25
|
+
parserOptions: {
|
|
26
|
+
projectService: true,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
rules,
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const recommended = createFlatConfig({
|
|
34
|
+
"cdk/construct-constructor-property": "error",
|
|
35
|
+
"cdk/no-construct-in-interface": "error",
|
|
36
|
+
"cdk/no-construct-in-public-property-of-construct": "error",
|
|
37
|
+
"cdk/no-construct-stack-suffix": "error",
|
|
38
|
+
"cdk/no-mutable-property-of-props-interface": "warn",
|
|
39
|
+
"cdk/no-mutable-public-property-of-construct": "warn",
|
|
40
|
+
"cdk/no-parent-name-construct-id-match": [
|
|
41
|
+
"error",
|
|
42
|
+
{ disallowContainingParentName: false },
|
|
43
|
+
],
|
|
44
|
+
"cdk/no-unused-props": "error",
|
|
45
|
+
"cdk/no-variable-construct-id": "error",
|
|
46
|
+
"cdk/pascal-case-construct-id": "error",
|
|
47
|
+
"cdk/require-passing-this": ["error", { allowNonThisAndDisallowScope: true }],
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export const strict = createFlatConfig({
|
|
51
|
+
"cdk/construct-constructor-property": "error",
|
|
52
|
+
"cdk/no-construct-in-interface": "error",
|
|
53
|
+
"cdk/no-construct-in-public-property-of-construct": "error",
|
|
54
|
+
"cdk/no-construct-stack-suffix": "error",
|
|
55
|
+
"cdk/no-import-private": "error",
|
|
56
|
+
"cdk/no-mutable-property-of-props-interface": "error",
|
|
57
|
+
"cdk/no-mutable-public-property-of-construct": "error",
|
|
58
|
+
"cdk/no-parent-name-construct-id-match": [
|
|
59
|
+
"error",
|
|
60
|
+
{ disallowContainingParentName: true },
|
|
61
|
+
],
|
|
62
|
+
"cdk/no-unused-props": "error",
|
|
63
|
+
"cdk/no-variable-construct-id": "error",
|
|
64
|
+
"cdk/pascal-case-construct-id": "error",
|
|
65
|
+
"cdk/props-name-convention": "error",
|
|
66
|
+
"cdk/require-jsdoc": "error",
|
|
67
|
+
"cdk/require-passing-this": "error",
|
|
68
|
+
"cdk/require-props-default-doc": "error",
|
|
69
|
+
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { TSESTree } from "@typescript-eslint/utils";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* find child nodes from a TSESTree.Node
|
|
5
5
|
*/
|
|
6
|
-
export const
|
|
6
|
+
export const findChildNodes = (node: TSESTree.Node): TSESTree.Node[] => {
|
|
7
7
|
return Object.entries(node).reduce<TSESTree.Node[]>((acc, [key, value]) => {
|
|
8
8
|
if (["parent", "range", "loc"].includes(key)) return acc; // Keys to skip to avoid circular references and unnecessary properties
|
|
9
9
|
if (isESTreeNode(value)) return [...acc, value];
|
|
@@ -5,7 +5,7 @@ import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/utils";
|
|
|
5
5
|
* @param node The class declaration
|
|
6
6
|
* @returns The constructor method definition or undefined if not found
|
|
7
7
|
*/
|
|
8
|
-
export const
|
|
8
|
+
export const findConstructor = (
|
|
9
9
|
node: TSESTree.ClassDeclaration
|
|
10
10
|
): TSESTree.MethodDefinition | undefined => {
|
|
11
11
|
return node.body.body.find(
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Retrieves the property names from an array of properties.
|
|
5
|
+
*
|
|
6
|
+
* @param properties An array of properties to extract names from.
|
|
7
|
+
* @returns An array of property names.
|
|
8
|
+
*/
|
|
9
|
+
export const findPropertyNames = (
|
|
10
|
+
properties: (TSESTree.Property | TSESTree.RestElement)[]
|
|
11
|
+
): string[] => {
|
|
12
|
+
return properties.reduce<string[]>(
|
|
13
|
+
(acc, prop) =>
|
|
14
|
+
prop.type === AST_NODE_TYPES.Property &&
|
|
15
|
+
prop.key.type === AST_NODE_TYPES.Identifier
|
|
16
|
+
? [...acc, prop.key.name]
|
|
17
|
+
: acc,
|
|
18
|
+
[]
|
|
19
|
+
);
|
|
20
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { AST_NODE_TYPES, TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
import { findConstructor } from "./constructor";
|
|
4
|
+
|
|
5
|
+
export type PublicProperty = {
|
|
6
|
+
/**
|
|
7
|
+
* Name of the public property
|
|
8
|
+
*/
|
|
9
|
+
name: string;
|
|
10
|
+
/**
|
|
11
|
+
* AST node representing the public property
|
|
12
|
+
*/
|
|
13
|
+
node:
|
|
14
|
+
| TSESTree.PropertyDefinitionComputedName
|
|
15
|
+
| TSESTree.PropertyDefinitionNonComputedName
|
|
16
|
+
| TSESTree.TSParameterProperty;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const findPublicPropertiesInClass = (
|
|
20
|
+
node: TSESTree.ClassDeclaration
|
|
21
|
+
): PublicProperty[] => {
|
|
22
|
+
const constructorProperties = findPropertiesInConstructor(node);
|
|
23
|
+
const classElementProperties = findPropertiesInClassElement(node);
|
|
24
|
+
return [...constructorProperties, ...classElementProperties];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const findPropertiesInConstructor = (
|
|
28
|
+
node: TSESTree.ClassDeclaration
|
|
29
|
+
) => {
|
|
30
|
+
const constructor = findConstructor(node);
|
|
31
|
+
if (!constructor) return [];
|
|
32
|
+
return constructor.value.params.flatMap(
|
|
33
|
+
(property) => findPublicProperty(property) ?? []
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const findPropertiesInClassElement = (node: TSESTree.ClassDeclaration): PublicProperty[] => {
|
|
38
|
+
return node.body.body.flatMap(
|
|
39
|
+
(property) => findPublicProperty(property) ?? []
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const findPublicProperty = (
|
|
44
|
+
property: TSESTree.Parameter | TSESTree.ClassElement
|
|
45
|
+
): PublicProperty | undefined => {
|
|
46
|
+
switch (property.type) {
|
|
47
|
+
// NOTE: get from constructor
|
|
48
|
+
case AST_NODE_TYPES.TSParameterProperty: {
|
|
49
|
+
if (property.parameter.type !== AST_NODE_TYPES.Identifier) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (["private", "protected"].includes(property.accessibility ?? "")) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (!property.parameter.typeAnnotation) return;
|
|
56
|
+
return {
|
|
57
|
+
name: property.parameter.name,
|
|
58
|
+
node: property,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// NOTE: get from class element
|
|
62
|
+
case AST_NODE_TYPES.PropertyDefinition: {
|
|
63
|
+
if (property.key.type !== AST_NODE_TYPES.Identifier) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (["private", "protected"].includes(property.accessibility ?? "")) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (!property.typeAnnotation) return;
|
|
70
|
+
return {
|
|
71
|
+
name: property.key.name,
|
|
72
|
+
node: property,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Type } from "typescript";
|
|
2
|
+
|
|
3
|
+
import { isExtendsFromTargetSuperClass } from "../../ts-type/checker/is-extends-target-super-class";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Check if the type extends Construct or Stack
|
|
7
|
+
* @param type - The type to check
|
|
8
|
+
* @param ignoredClasses - Classes that inherit from Construct Class or Stack Class but do not want to be treated as Construct Class or Stack Class
|
|
9
|
+
* @returns True if the type extends Construct or Stack, otherwise false
|
|
10
|
+
*/
|
|
11
|
+
export const isConstructOrStackType = (
|
|
12
|
+
type: Type,
|
|
13
|
+
ignoredClasses: readonly string[] = ["App", "Stage", "CfnOutput"] as const
|
|
14
|
+
): boolean => {
|
|
15
|
+
if (ignoredClasses.includes(type.symbol?.name ?? "")) return false;
|
|
16
|
+
return isExtendsFromTargetSuperClass(
|
|
17
|
+
type,
|
|
18
|
+
["Construct", "Stack"],
|
|
19
|
+
isConstructOrStackType
|
|
20
|
+
);
|
|
21
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Type } from "typescript";
|
|
2
|
+
|
|
3
|
+
import { isExtendsFromTargetSuperClass } from "../../ts-type/checker/is-extends-target-super-class";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Check if the type extends Construct
|
|
7
|
+
* @param type - The type to check
|
|
8
|
+
* @param ignoredClasses - Classes that inherit from Construct Class but do not want to be treated as Construct Class
|
|
9
|
+
* @returns True if the type extends Construct, otherwise false
|
|
10
|
+
*/
|
|
11
|
+
export const isConstructType = (
|
|
12
|
+
type: Type,
|
|
13
|
+
ignoredClasses: readonly string[] = [
|
|
14
|
+
"App",
|
|
15
|
+
"Stage",
|
|
16
|
+
"CfnOutput",
|
|
17
|
+
"Stack",
|
|
18
|
+
] as const
|
|
19
|
+
): boolean => {
|
|
20
|
+
if (ignoredClasses.includes(type.symbol?.name ?? "")) return false;
|
|
21
|
+
return isExtendsFromTargetSuperClass(type, ["Construct"], isConstructType);
|
|
22
|
+
};
|
package/src/{utils → core/cdk-construct/type-checker}/is-resource-with-readonly-interface.ts
RENAMED
|
@@ -2,12 +2,13 @@ import {
|
|
|
2
2
|
isClassDeclaration,
|
|
3
3
|
isIdentifier,
|
|
4
4
|
isPropertyAccessExpression,
|
|
5
|
+
SyntaxKind,
|
|
5
6
|
Type,
|
|
6
7
|
} from "typescript";
|
|
7
8
|
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
import {
|
|
9
|
+
import { isClassType } from "../../ts-type/checker/is-class";
|
|
10
|
+
|
|
11
|
+
import { isResourceType } from "./is-resource";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Checks if the type is an AWS resource Construct that implements a read-only resource interface
|
|
@@ -140,7 +141,7 @@ const getDirectImplementedInterfaceNames = (type: Type): string[] => {
|
|
|
140
141
|
if (!heritageClauses) return acc;
|
|
141
142
|
|
|
142
143
|
return heritageClauses.reduce<string[]>((hcAcc, hc) => {
|
|
143
|
-
if (
|
|
144
|
+
if (hc.token !== SyntaxKind.ImplementsKeyword) return hcAcc;
|
|
144
145
|
|
|
145
146
|
return hc.types.reduce<string[]>((typeAcc, type) => {
|
|
146
147
|
const expression = type.expression;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Type } from "typescript";
|
|
2
|
+
|
|
3
|
+
import { isExtendsFromTargetSuperClass } from "../../ts-type/checker/is-extends-target-super-class";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Check if the type extends Resource
|
|
7
|
+
* @param type - The type to check
|
|
8
|
+
* @param ignoredClasses - Classes that inherit from Resource Class but do not want to be treated as Resource Class
|
|
9
|
+
* @returns True if the type extends Resource, otherwise false
|
|
10
|
+
*/
|
|
11
|
+
export const isResourceType = (
|
|
12
|
+
type: Type,
|
|
13
|
+
ignoredClasses: readonly string[] = [] // App, Stage, CfnOutput, Stack are not extended Resource, so no need to ignore them
|
|
14
|
+
): boolean => {
|
|
15
|
+
if (ignoredClasses.includes(type.symbol?.name ?? "")) return false;
|
|
16
|
+
return isExtendsFromTargetSuperClass(type, ["Resource"], isResourceType);
|
|
17
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Type } from "typescript";
|
|
2
|
+
|
|
3
|
+
import { findArrayElementType } from "../../../core/ts-type/finder/array-element";
|
|
4
|
+
import { findGenericsTypeArgument } from "../../../core/ts-type/finder/generics-type-argument";
|
|
5
|
+
import { isClassType } from "../../ts-type/checker/is-class";
|
|
6
|
+
import { isResourceWithReadonlyInterface } from "../type-checker/is-resource-with-readonly-interface";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Recursively find the ts.Type to obtain type of CDK Construct class
|
|
10
|
+
*/
|
|
11
|
+
export const findTypeOfCdkConstruct = (type: Type): Type | undefined => {
|
|
12
|
+
if (isClassType(type) && isResourceWithReadonlyInterface(type)) {
|
|
13
|
+
return type;
|
|
14
|
+
}
|
|
15
|
+
return (
|
|
16
|
+
findFromArray(type) ??
|
|
17
|
+
findFromGenerics(type) ??
|
|
18
|
+
findFromUnion(type) ??
|
|
19
|
+
findFromIntersection(type)
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Find Construct type from an array type (e.g. s3.Bucket[])
|
|
25
|
+
*/
|
|
26
|
+
const findFromArray = (
|
|
27
|
+
type: Type
|
|
28
|
+
): Type | undefined => {
|
|
29
|
+
const arrElementType = findArrayElementType(type);
|
|
30
|
+
if (!arrElementType) return undefined;
|
|
31
|
+
|
|
32
|
+
return findTypeOfCdkConstruct(arrElementType);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Find Construct type from a generics type (e.g. Array<s3.Bucket>, Promise<s3.Bucket[]>)
|
|
37
|
+
*/
|
|
38
|
+
const findFromGenerics = (
|
|
39
|
+
type: Type
|
|
40
|
+
): Type | undefined => {
|
|
41
|
+
const genericsArgument = findGenericsTypeArgument(type);
|
|
42
|
+
if (!genericsArgument) return undefined;
|
|
43
|
+
|
|
44
|
+
return findTypeOfCdkConstruct(genericsArgument);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Find Construct type from a union type (e.g. s3.Bucket | string)
|
|
49
|
+
*/
|
|
50
|
+
const findFromUnion = (
|
|
51
|
+
type: Type
|
|
52
|
+
): Type | undefined => {
|
|
53
|
+
if (!type.isUnion()) return undefined;
|
|
54
|
+
|
|
55
|
+
for (const unionType of type.types) {
|
|
56
|
+
const foundType = findTypeOfCdkConstruct(unionType);
|
|
57
|
+
if (foundType) return foundType;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Find Construct type from an intersection type (e.g. s3.Bucket & { customProp: string })
|
|
63
|
+
*/
|
|
64
|
+
const findFromIntersection = (
|
|
65
|
+
type: Type
|
|
66
|
+
): Type | undefined => {
|
|
67
|
+
if (!type.isIntersection()) return undefined;
|
|
68
|
+
|
|
69
|
+
for (const intersectionType of type.types) {
|
|
70
|
+
const foundType = findTypeOfCdkConstruct(intersectionType);
|
|
71
|
+
if (foundType) return foundType;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Type } from "typescript";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
return type.getSymbol?.() ?? type.symbol;
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
export const isClassType = (type: Type): boolean => {
|
|
8
|
-
return getSymbol(type)?.flags === SymbolFlags.Class;
|
|
9
|
-
};
|
|
3
|
+
import { getSymbol } from "./private/get-symbol";
|
|
10
4
|
|
|
11
5
|
export const isArrayType = (type: Type): boolean => {
|
|
12
6
|
const symbol = getSymbol(type);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Type } from "typescript";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Check if the type extends target super class
|
|
5
|
+
* @param type - The type to check
|
|
6
|
+
* @param targetSuperClasses - The target super classes
|
|
7
|
+
* @returns True if the type extends target super class, otherwise false
|
|
8
|
+
*/
|
|
9
|
+
export const isExtendsFromTargetSuperClass = (
|
|
10
|
+
type: Type,
|
|
11
|
+
targetSuperClasses: string[],
|
|
12
|
+
typeCheckFunction: (type: Type) => boolean
|
|
13
|
+
): boolean => {
|
|
14
|
+
if (!type.symbol) return false;
|
|
15
|
+
|
|
16
|
+
// NOTE: Check if the current type ends in target super class
|
|
17
|
+
if (targetSuperClasses.some((suffix) => type.symbol.name === suffix)) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// NOTE: Check the base type
|
|
22
|
+
const baseTypes = type.getBaseTypes() ?? [];
|
|
23
|
+
return baseTypes.some((baseType) => typeCheckFunction(baseType));
|
|
24
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Type } from "typescript";
|
|
2
|
+
|
|
3
|
+
import { isArrayType } from "../checker/is-array";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Find the element type of an array type (e.g. for s3.Bucket[], returns s3.Bucket)
|
|
8
|
+
* @param type - The type to check
|
|
9
|
+
* @returns The element type if the type is an array, undefined otherwise
|
|
10
|
+
*/
|
|
11
|
+
export const findArrayElementType = (type: Type): Type | undefined => {
|
|
12
|
+
if (!isArrayType(type)) return undefined;
|
|
13
|
+
|
|
14
|
+
// Get type arguments for Array<T>
|
|
15
|
+
if ("typeArguments" in type && Array.isArray(type.typeArguments)) {
|
|
16
|
+
return (type.typeArguments as Type[])[0];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return undefined;
|
|
20
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { isClassDeclaration, isConstructorDeclaration, Type } from "typescript";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Parses type to get the property names of the class constructor.
|
|
6
|
+
* @returns The property names of the class constructor.
|
|
7
|
+
*/
|
|
8
|
+
export const findConstructorPropertyNames = (type: Type): string[] => {
|
|
9
|
+
const declarations = type.symbol?.declarations;
|
|
10
|
+
if (!declarations?.length) return [];
|
|
11
|
+
|
|
12
|
+
const classDeclaration = declarations[0];
|
|
13
|
+
if (!isClassDeclaration(classDeclaration)) return [];
|
|
14
|
+
|
|
15
|
+
const constructor = classDeclaration.members.find((member) =>
|
|
16
|
+
isConstructorDeclaration(member)
|
|
17
|
+
);
|
|
18
|
+
if (!constructor?.parameters.length) return [];
|
|
19
|
+
|
|
20
|
+
return constructor.parameters.map((param) => param.name.getText());
|
|
21
|
+
};
|
package/src/{utils/getGenericTypeArgument.ts → core/ts-type/finder/generics-type-argument.ts}
RENAMED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Type } from "typescript";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Extracts the type argument from a
|
|
4
|
+
* Extracts the type argument from a generics type reference (e.g. Array<s3.Bucket>, returns s3.Bucket)
|
|
5
5
|
* @param type - The type to check
|
|
6
|
-
* @returns The first type argument if it's a
|
|
6
|
+
* @returns The first type argument if it's a generics type reference, undefined otherwise
|
|
7
7
|
*/
|
|
8
|
-
export const
|
|
8
|
+
export const findGenericsTypeArgument = (type: Type): Type | undefined => {
|
|
9
9
|
// NOTE: Check for type alias (e.g. Readonly<T>, Partial<T>)
|
|
10
10
|
if (
|
|
11
11
|
"aliasSymbol" in type &&
|
|
@@ -16,7 +16,7 @@ export const getGenericTypeArgument = (type: Type): Type | undefined => {
|
|
|
16
16
|
return type.aliasTypeArguments[0];
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
// NOTE: Check if type has typeArguments (
|
|
19
|
+
// NOTE: Check if type has typeArguments (generics types like Array<T>, etc.)
|
|
20
20
|
// This works for TypeReference types
|
|
21
21
|
if (
|
|
22
22
|
"typeArguments" in type &&
|
|
@@ -26,7 +26,7 @@ export const getGenericTypeArgument = (type: Type): Type | undefined => {
|
|
|
26
26
|
return type.typeArguments[0] as Type;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
// NOTE: Alternative approach: check for target property (some
|
|
29
|
+
// NOTE: Alternative approach: check for target property (some generics types have this)
|
|
30
30
|
if (
|
|
31
31
|
"target" in type &&
|
|
32
32
|
type.target &&
|