Skip to content

Commit 2f3a713

Browse files
authored
Generate CLIENT_TYPES_MAP by processing client types from v2 (#313)
1 parent ed9ca3d commit 2f3a713

File tree

6 files changed

+24507
-1
lines changed

6 files changed

+24507
-1
lines changed

.changeset/thick-boats-march.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"aws-sdk-js-codemod": patch
3+
---
4+
5+
Generate CLIENT_TYPES_MAP by processing client types from v2

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"@types/node": "^14.18.33",
5050
"@typescript-eslint/eslint-plugin": "^5.43.0",
5151
"@typescript-eslint/parser": "^5.43.0",
52+
"aws-sdk": "2.1288.0",
5253
"eslint": "^8.27.0",
5354
"eslint-plugin-simple-import-sort": "^8.0.0",
5455
"jest": "^29.3.1",
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { readFile } from "fs/promises";
2+
import jscodeshift, { Identifier, TSArrayType, TSTypeLiteral, TSTypeReference } from "jscodeshift";
3+
import { join } from "path";
4+
5+
const TYPES_TO_SKIP = ["apiVersion", "ClientConfiguration"];
6+
const ElementTypeToNativeTypeMap = {
7+
TSStringKeyword: "string",
8+
TSNumberKeyword: "number",
9+
TSBooleanKeyword: "boolean",
10+
};
11+
12+
export const getClientTypeMap = async (clientName: string): Promise<Record<string, string>> => {
13+
const clientTypesMap = {};
14+
15+
const typesPath = join("node_modules", "aws-sdk", "clients", `${clientName.toLowerCase()}.d.ts`);
16+
const relativeTypesPath = join(__dirname, "..", "..", typesPath);
17+
18+
const typesCode = await readFile(relativeTypesPath, "utf8");
19+
20+
const j = jscodeshift.withParser("ts");
21+
const source = j(typesCode);
22+
23+
source.find(j.TSModuleDeclaration, { id: { name: clientName } }).forEach((moduleDeclaration) => {
24+
const tsTypes = j(moduleDeclaration).find(j.TSTypeAliasDeclaration).nodes();
25+
26+
for (const [type, value] of Object.entries(ElementTypeToNativeTypeMap)) {
27+
tsTypes
28+
.filter((tsType) => tsType.typeAnnotation.type === type)
29+
.forEach((tsType) => {
30+
clientTypesMap[tsType.id.name] = value;
31+
});
32+
}
33+
34+
tsTypes
35+
.filter((tsType) => tsType.typeAnnotation.type === "TSTypeReference")
36+
.forEach((tsType) => {
37+
const name = tsType.id.name;
38+
const typeName = ((tsType.typeAnnotation as TSTypeReference).typeName as Identifier).name;
39+
if (typeName === "Date") {
40+
clientTypesMap[name] = typeName;
41+
} else if (typeName === "EventStream") {
42+
// Exception for SelectObjectContentEventStream
43+
clientTypesMap[name] = "AsyncIterable<KEY>";
44+
} else {
45+
console.log("TSTypeReference with unsupported type:", name, typeName);
46+
}
47+
});
48+
49+
tsTypes
50+
.filter((tsType) => tsType.typeAnnotation.type === "TSUnionType")
51+
.forEach((tsType) => {
52+
const name = tsType.id.name;
53+
if (name.endsWith("Blob")) {
54+
clientTypesMap[name] = "Uint8Array";
55+
}
56+
});
57+
58+
tsTypes
59+
.filter((tsType) => tsType.typeAnnotation.type === "TSArrayType")
60+
.forEach((tsType) => {
61+
const name = tsType.id.name;
62+
const elementType = (tsType.typeAnnotation as TSArrayType).elementType;
63+
if (elementType.type === "TSTypeReference") {
64+
const typeName = elementType.typeName;
65+
if (typeName.type === "Identifier") {
66+
if (clientTypesMap[typeName.name]) {
67+
clientTypesMap[name] = `Array<${clientTypesMap[typeName.name]}>`;
68+
} else {
69+
// Assume it's an interface which would be available in v3.
70+
clientTypesMap[name] = `Array<${typeName.name}>`;
71+
}
72+
} else {
73+
console.log("TSArrayType TSTypeReference without Identifier type:", name);
74+
}
75+
} else if (Object.keys(ElementTypeToNativeTypeMap).includes(elementType.type)) {
76+
clientTypesMap[name] = `Array<${ElementTypeToNativeTypeMap[elementType.type]}>`;
77+
} else {
78+
console.log("TSArrayType with unsupported elemental type:", name);
79+
}
80+
});
81+
82+
tsTypes
83+
.filter((tsType) => tsType.typeAnnotation.type === "TSTypeLiteral")
84+
.forEach((tsType) => {
85+
const name = tsType.id.name;
86+
const member = (tsType.typeAnnotation as TSTypeLiteral).members[0];
87+
if (member.type === "TSIndexSignature") {
88+
if (member.typeAnnotation) {
89+
if (member.typeAnnotation.typeAnnotation) {
90+
const typeAnnotation = member.typeAnnotation.typeAnnotation;
91+
if (typeAnnotation.type === "TSTypeReference") {
92+
const typeName = typeAnnotation.typeName;
93+
if (typeName.type === "Identifier") {
94+
if (clientTypesMap[typeName.name]) {
95+
clientTypesMap[name] = `Record<string, ${clientTypesMap[typeName.name]}>`;
96+
} else {
97+
// Assume it's an interface which would be available in v3.
98+
clientTypesMap[name] = `Record<string, ${typeName.name}>`;
99+
}
100+
} else {
101+
console.log("TSTypeLiteral TSTypeReference without Identifier type:", name);
102+
}
103+
} else if (Object.keys(ElementTypeToNativeTypeMap).includes(typeAnnotation.type)) {
104+
clientTypesMap[name] = `Record<string, ${
105+
ElementTypeToNativeTypeMap[typeAnnotation.type]
106+
}>`;
107+
} else {
108+
console.log("TSTypeLiteral with unsupported typeAnnotation type:", name);
109+
}
110+
}
111+
}
112+
}
113+
});
114+
115+
tsTypes.forEach((tsType) => {
116+
const name = tsType.id.name;
117+
const type = tsType.typeAnnotation.type;
118+
if (!TYPES_TO_SKIP.includes(name) && !clientTypesMap[name] && type !== "TSUnionType") {
119+
console.log("Unsupported type:", name);
120+
}
121+
});
122+
});
123+
124+
return Object.entries(clientTypesMap)
125+
.sort(([key1], [key2]) => key1.localeCompare(key2))
126+
.reduce((obj, [key, value]) => {
127+
obj[key] = value;
128+
return obj;
129+
}, {});
130+
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { writeFile } from "fs/promises";
2+
import { join } from "path";
3+
import { format } from "prettier";
4+
5+
import { CLIENT_NAMES } from "../../src/transforms/v2-to-v3/config";
6+
import { getClientTypeMap } from "./getClientTypeMap";
7+
8+
const codegenComment = `// This file is generated by scripts/generateClientTypesMap/index.ts
9+
// Do not edit this file directly. Instead, edit the script and run it to regenerate this file.`;
10+
11+
const filePath = join("src", "transforms", "v2-to-v3", "config", "CLIENT_TYPES_MAP.ts");
12+
const relativeFilePath = join(__dirname, "..", "..", filePath);
13+
14+
(async () => {
15+
let fileContent = codegenComment;
16+
17+
fileContent += `\n\n/* eslint-disable @typescript-eslint/naming-convention */`;
18+
fileContent += `\nexport const CLIENT_TYPES_MAP: Record<string, Record<string, string>> = `;
19+
20+
const clientTypesMap = {};
21+
22+
for (const clientName of CLIENT_NAMES) {
23+
clientTypesMap[clientName] = await getClientTypeMap(clientName);
24+
}
25+
26+
fileContent += JSON.stringify(clientTypesMap);
27+
fileContent += `;\n`;
28+
29+
await writeFile(relativeFilePath, format(fileContent, { parser: "typescript", printWidth: 100 }));
30+
})();

0 commit comments

Comments
 (0)