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.
Files changed (62) hide show
  1. package/bin/index.mjs +4318 -0
  2. package/bin/prompt-DVcPfS2N.mjs +847 -0
  3. package/dist/index.cjs +26648 -1606
  4. package/dist/index.d.cts +55 -0
  5. package/dist/index.d.cts.map +1 -0
  6. package/dist/index.d.mts +55 -0
  7. package/dist/index.d.mts.map +1 -0
  8. package/dist/index.mjs +26650 -1584
  9. package/dist/index.mjs.map +1 -0
  10. package/package.json +20 -22
  11. package/src/configs/flat-config.ts +69 -0
  12. package/src/configs/index.ts +6 -0
  13. package/src/{utils/get-child-nodes.ts → core/ast-node/finder/child-nodes.ts} +2 -2
  14. package/src/{utils/getConstructor.ts → core/ast-node/finder/constructor.ts} +1 -1
  15. package/src/core/ast-node/finder/property-name.ts +20 -0
  16. package/src/core/ast-node/finder/public-property.ts +76 -0
  17. package/src/core/cdk-construct/type-checker/is-construct-or-stack.ts +21 -0
  18. package/src/core/cdk-construct/type-checker/is-construct.ts +22 -0
  19. package/src/{utils → core/cdk-construct/type-checker}/is-resource-with-readonly-interface.ts +5 -4
  20. package/src/core/cdk-construct/type-checker/is-resource.ts +17 -0
  21. package/src/core/cdk-construct/type-finder/index.ts +73 -0
  22. package/src/{utils/typecheck/ts-type.ts → core/ts-type/checker/is-array.ts} +2 -8
  23. package/src/core/ts-type/checker/is-class.ts +7 -0
  24. package/src/core/ts-type/checker/is-extends-target-super-class.ts +24 -0
  25. package/src/core/ts-type/checker/private/get-symbol.ts +5 -0
  26. package/src/core/ts-type/finder/array-element.ts +20 -0
  27. package/src/core/ts-type/finder/constructor-property-name.ts +21 -0
  28. package/src/{utils/getGenericTypeArgument.ts → core/ts-type/finder/generics-type-argument.ts} +5 -5
  29. package/src/index.ts +6 -108
  30. package/src/rules/construct-constructor-property.ts +48 -26
  31. package/src/rules/index.ts +34 -0
  32. package/src/rules/no-construct-in-interface.ts +5 -51
  33. package/src/rules/no-construct-in-public-property-of-construct.ts +20 -143
  34. package/src/rules/no-construct-stack-suffix.ts +5 -5
  35. package/src/rules/no-mutable-property-of-props-interface.ts +1 -1
  36. package/src/rules/no-mutable-public-property-of-construct.ts +47 -39
  37. package/src/rules/no-parent-name-construct-id-match.ts +4 -6
  38. package/src/rules/no-unused-props/index.ts +19 -10
  39. package/src/rules/no-unused-props/props-usage-analyzer.ts +207 -190
  40. package/src/rules/no-unused-props/props-usage-tracker.ts +4 -3
  41. package/src/rules/no-unused-props/visitor/direct-props-usage-visitor.ts +145 -0
  42. package/src/rules/no-unused-props/visitor/index.ts +5 -0
  43. package/src/rules/no-unused-props/visitor/instance-variable-usage-visitor.ts +117 -0
  44. package/src/rules/no-unused-props/visitor/interface/node-visitor.ts +9 -0
  45. package/src/rules/no-unused-props/visitor/method-call-collector-visitor.ts +60 -0
  46. package/src/rules/no-unused-props/visitor/props-alias-visitor.ts +81 -0
  47. package/src/rules/no-unused-props/visitor/traverse-nodes.ts +38 -0
  48. package/src/rules/no-variable-construct-id.ts +4 -4
  49. package/src/rules/pascal-case-construct-id.ts +5 -5
  50. package/src/rules/props-name-convention.ts +4 -4
  51. package/src/rules/require-jsdoc.ts +2 -2
  52. package/src/rules/require-passing-this.ts +4 -4
  53. package/src/rules/require-props-default-doc.ts +1 -1
  54. package/bin/migration.mjs +0 -100
  55. package/dist/index.d.ts +0 -47
  56. package/dist/index.d.ts.map +0 -1
  57. package/src/utils/getArrayElementType.ts +0 -14
  58. package/src/utils/getPropertyNames.ts +0 -41
  59. package/src/utils/typecheck/cdk.ts +0 -71
  60. package/src/utils/typecheck/ts-node.ts +0 -7
  61. /package/src/{utils/convertString.ts → shared/converter/to-pascal-case.ts} +0 -0
  62. /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.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": "sh scripts/build.sh",
23
+ "build": "tsdown",
24
24
  "test": "vitest --run",
25
- "lint": "eslint --fix --config eslint.config.js",
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.36.0",
34
- "@secretlint/secretlint-rule-preset-recommend": "^11.2.4",
35
- "@types/node": "^24.9.2",
36
- "@typescript-eslint/rule-tester": "^8.46.2",
37
- "@vitest/coverage-v8": "3.2.4",
38
- "commander": "^14.0.1",
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.38.0",
41
+ "eslint": "^9.39.1",
41
42
  "eslint-plugin-import": "^2.32.0",
42
- "pkgroll": "^2.20.1",
43
- "rolldown": "1.0.0-beta.45",
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.46.2",
47
- "vitest": "^3.2.4"
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
- "typescript": ">=5.0.0"
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
+ });
@@ -0,0 +1,6 @@
1
+ import { recommended, strict } from "./flat-config";
2
+
3
+ export const configs = {
4
+ recommended,
5
+ strict,
6
+ };
@@ -1,9 +1,9 @@
1
1
  import { TSESTree } from "@typescript-eslint/utils";
2
2
 
3
3
  /**
4
- * Safely gets child nodes from a TSESTree.Node
4
+ * find child nodes from a TSESTree.Node
5
5
  */
6
- export const getChildNodes = (node: TSESTree.Node): TSESTree.Node[] => {
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 getConstructor = (
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
+ };
@@ -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 { isResourceType } from "./typecheck/cdk";
9
- import { checkHeritageClauseIsImplements } from "./typecheck/ts-node";
10
- import { isClassType } from "./typecheck/ts-type";
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 (!checkHeritageClauseIsImplements(hc)) return hcAcc;
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 { Symbol, SymbolFlags, Type } from "typescript";
1
+ import { Type } from "typescript";
2
2
 
3
- const getSymbol = (type: Type): Symbol | undefined => {
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,7 @@
1
+ import { SymbolFlags, Type } from "typescript";
2
+
3
+ import { getSymbol } from "./private/get-symbol";
4
+
5
+ export const isClassType = (type: Type): boolean => {
6
+ return getSymbol(type)?.flags === SymbolFlags.Class;
7
+ };
@@ -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,5 @@
1
+ import { Symbol, Type } from "typescript";
2
+
3
+ export const getSymbol = (type: Type): Symbol | undefined => {
4
+ return type.getSymbol?.() ?? type.symbol;
5
+ };
@@ -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
+ };
@@ -1,11 +1,11 @@
1
1
  import { Type } from "typescript";
2
2
 
3
3
  /**
4
- * Extracts the type argument from a generic type reference
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 generic type reference, undefined otherwise
6
+ * @returns The first type argument if it's a generics type reference, undefined otherwise
7
7
  */
8
- export const getGenericTypeArgument = (type: Type): Type | undefined => {
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 (generic types like Array<T>, etc.)
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 generic types have this)
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 &&