@itzworking/devkit 0.0.198-canary.20250707-e7f7621

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 (49) hide show
  1. package/README.md +15 -0
  2. package/dist-cjs/generate-api-clients/api-type-spec-utils.js +160 -0
  3. package/dist-cjs/generate-api-clients/copy-api-client.js +17 -0
  4. package/dist-cjs/generate-api-clients/generate-api-client-class.js +161 -0
  5. package/dist-cjs/generate-api-clients/generate-api-clients.js +70 -0
  6. package/dist-cjs/generate-api-clients/generate-hook.js +71 -0
  7. package/dist-cjs/generate-api-clients/handlebars-helpers.js +14 -0
  8. package/dist-cjs/generate-api-clients/index.js +4 -0
  9. package/dist-cjs/generate-api-clients/parse-discovered-apis.js +125 -0
  10. package/dist-cjs/generate-api-clients/sync-api-clients.js +84 -0
  11. package/dist-cjs/generate-api-clients/template-loader.js +24 -0
  12. package/dist-cjs/generate-api-clients/templates/api-client.hbs +25 -0
  13. package/dist-cjs/generate-api-clients/templates/api-client.ts.template +131 -0
  14. package/dist-cjs/generate-api-clients/templates/hook-mutation.hbs +39 -0
  15. package/dist-cjs/generate-api-clients/templates/hook-query.hbs +28 -0
  16. package/dist-cjs/generate-api-clients/types.js +2 -0
  17. package/dist-cjs/generate-api-clients/utils.js +89 -0
  18. package/dist-cjs/index.js +4 -0
  19. package/dist-es/generate-api-clients/api-type-spec-utils.js +155 -0
  20. package/dist-es/generate-api-clients/copy-api-client.js +12 -0
  21. package/dist-es/generate-api-clients/generate-api-client-class.js +156 -0
  22. package/dist-es/generate-api-clients/generate-api-clients.js +65 -0
  23. package/dist-es/generate-api-clients/generate-hook.js +66 -0
  24. package/dist-es/generate-api-clients/handlebars-helpers.js +9 -0
  25. package/dist-es/generate-api-clients/index.js +1 -0
  26. package/dist-es/generate-api-clients/parse-discovered-apis.js +120 -0
  27. package/dist-es/generate-api-clients/sync-api-clients.js +79 -0
  28. package/dist-es/generate-api-clients/template-loader.js +20 -0
  29. package/dist-es/generate-api-clients/templates/api-client.hbs +25 -0
  30. package/dist-es/generate-api-clients/templates/api-client.ts.template +131 -0
  31. package/dist-es/generate-api-clients/templates/hook-mutation.hbs +39 -0
  32. package/dist-es/generate-api-clients/templates/hook-query.hbs +28 -0
  33. package/dist-es/generate-api-clients/types.js +1 -0
  34. package/dist-es/generate-api-clients/utils.js +79 -0
  35. package/dist-es/index.js +1 -0
  36. package/dist-types/generate-api-clients/api-type-spec-utils.d.ts +4 -0
  37. package/dist-types/generate-api-clients/copy-api-client.d.ts +5 -0
  38. package/dist-types/generate-api-clients/generate-api-client-class.d.ts +11 -0
  39. package/dist-types/generate-api-clients/generate-api-clients.d.ts +2 -0
  40. package/dist-types/generate-api-clients/generate-hook.d.ts +11 -0
  41. package/dist-types/generate-api-clients/handlebars-helpers.d.ts +1 -0
  42. package/dist-types/generate-api-clients/index.d.ts +1 -0
  43. package/dist-types/generate-api-clients/parse-discovered-apis.d.ts +8 -0
  44. package/dist-types/generate-api-clients/sync-api-clients.d.ts +1 -0
  45. package/dist-types/generate-api-clients/template-loader.d.ts +5 -0
  46. package/dist-types/generate-api-clients/types.d.ts +113 -0
  47. package/dist-types/generate-api-clients/utils.d.ts +21 -0
  48. package/dist-types/index.d.ts +1 -0
  49. package/package.json +44 -0
package/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # ITz Working (AWS Serverless, TypeScript)
2
+
3
+ > **ITz Working because you built it!**
4
+
5
+ Framework for building AWS Serverless applications with TypeScript.
6
+
7
+ - [Intro](#intro)
8
+ - [Key features](#key-features)
9
+ - [Usage](#usage)
10
+
11
+ ## Intro
12
+
13
+ ## Key features
14
+
15
+ ## Usage
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractTypeDefinition = exports.setTypeKind = void 0;
4
+ const ts_morph_1 = require("ts-morph");
5
+ function isDecoratedClass(classDecl) {
6
+ let current = classDecl;
7
+ while (current) {
8
+ const base = current.getBaseClass();
9
+ if (!base)
10
+ break;
11
+ if (base.getName() === "DecoratedClass") {
12
+ return true;
13
+ }
14
+ current = base;
15
+ }
16
+ return false;
17
+ }
18
+ function isZodSchema(varDecl) {
19
+ if (!varDecl)
20
+ return false;
21
+ const initializer = varDecl.getInitializer();
22
+ if (!initializer)
23
+ return false;
24
+ if (initializer.getKind() === ts_morph_1.SyntaxKind.CallExpression) {
25
+ const callExpr = initializer.asKind(ts_morph_1.SyntaxKind.CallExpression);
26
+ if (callExpr) {
27
+ const expr = callExpr.getExpression();
28
+ if (expr.getKind() === ts_morph_1.SyntaxKind.PropertyAccessExpression ||
29
+ expr.getKind() === ts_morph_1.SyntaxKind.Identifier) {
30
+ const exprText = expr.getText();
31
+ if (exprText.startsWith("z.")) {
32
+ return true;
33
+ }
34
+ }
35
+ }
36
+ }
37
+ return false;
38
+ }
39
+ const setTypeKind = (type, sourceFile) => {
40
+ if (!type)
41
+ return;
42
+ let kind = type.kind;
43
+ const importSourceFile = type.importPath
44
+ ? sourceFile
45
+ .getImportDeclarations()
46
+ .find((d) => d.getModuleSpecifierValue() === type.importPath)
47
+ ?.getModuleSpecifierSourceFile()
48
+ : sourceFile;
49
+ const inputClass = importSourceFile?.getClass(type.typeName);
50
+ if (isDecoratedClass(inputClass)) {
51
+ kind = "decorated-class";
52
+ }
53
+ else {
54
+ const inputVar = importSourceFile?.getVariableDeclaration(type.typeName);
55
+ if (isZodSchema(inputVar)) {
56
+ kind = "zod-schema";
57
+ }
58
+ }
59
+ type.kind = kind;
60
+ };
61
+ exports.setTypeKind = setTypeKind;
62
+ const extractTypeDefinition = (type, sourceFile) => {
63
+ if (!type?.importPath) {
64
+ return;
65
+ }
66
+ const importSourceFile = sourceFile
67
+ .getImportDeclarations()
68
+ .find((d) => d.getModuleSpecifierValue() === type.importPath)
69
+ ?.getModuleSpecifierSourceFile();
70
+ if (!importSourceFile) {
71
+ throw new Error(`Could not find import source file for path: ${type.importPath}`);
72
+ }
73
+ const allowedImportLibraries = [
74
+ "@itzworking/decorated-class",
75
+ "zod",
76
+ "zod/v4",
77
+ ];
78
+ const allowedImports = [];
79
+ const inlinedFiles = [];
80
+ const filesToProcess = [importSourceFile];
81
+ const processedFiles = new Set();
82
+ while (filesToProcess.length > 0) {
83
+ const currentSourceFile = filesToProcess.pop();
84
+ if (currentSourceFile === undefined) {
85
+ console.log("Source file is undefined, skipping...");
86
+ continue;
87
+ }
88
+ const filePath = currentSourceFile.getFilePath();
89
+ if (processedFiles.has(filePath)) {
90
+ continue;
91
+ }
92
+ processedFiles.add(filePath);
93
+ for (const importDeclarations of currentSourceFile.getImportDeclarations()) {
94
+ const moduleSpecifier = importDeclarations
95
+ .getModuleSpecifier()
96
+ .getLiteralValue();
97
+ if (allowedImportLibraries.includes(moduleSpecifier)) {
98
+ const namespaceImport = importDeclarations
99
+ .getNamespaceImport()
100
+ ?.getText();
101
+ if (namespaceImport) {
102
+ const currentNamespaceImport = allowedImports.find((i) => i.namespaceImport && i.importPath === moduleSpecifier);
103
+ if (!currentNamespaceImport) {
104
+ allowedImports.push({
105
+ namespaceImport: true,
106
+ importPath: moduleSpecifier,
107
+ importNames: new Set([namespaceImport]),
108
+ });
109
+ }
110
+ else {
111
+ currentNamespaceImport.importNames.add(namespaceImport);
112
+ }
113
+ }
114
+ else {
115
+ let currentImport = allowedImports.find((i) => !i.namespaceImport && i.importPath === moduleSpecifier);
116
+ if (!currentImport) {
117
+ currentImport = {
118
+ namespaceImport: false,
119
+ importPath: moduleSpecifier,
120
+ importNames: new Set(),
121
+ };
122
+ allowedImports.push(currentImport);
123
+ }
124
+ for (const namedImport of importDeclarations.getNamedImports()) {
125
+ currentImport.importNames.add(namedImport.getName());
126
+ }
127
+ }
128
+ }
129
+ else {
130
+ const importedSourceFile = importDeclarations.getModuleSpecifierSourceFile();
131
+ const importedFilePath = importedSourceFile?.getFilePath();
132
+ if (!importedSourceFile || !importedFilePath) {
133
+ continue;
134
+ }
135
+ if (!processedFiles.has(importedFilePath)) {
136
+ filesToProcess.push(importedSourceFile);
137
+ }
138
+ }
139
+ }
140
+ let text = currentSourceFile.getFullText();
141
+ const importDeclarations = currentSourceFile.getImportDeclarations();
142
+ for (const importDecl of importDeclarations.reverse()) {
143
+ const start = importDecl.getStart();
144
+ const end = importDecl.getEnd();
145
+ text = (text.slice(0, start) + text.slice(end)).trim();
146
+ }
147
+ inlinedFiles.push({
148
+ filePath: filePath,
149
+ text,
150
+ });
151
+ }
152
+ type.typeDefinition = {
153
+ allowedImports: allowedImports.map((i) => ({
154
+ ...i,
155
+ importNames: Array.from(i.importNames),
156
+ })),
157
+ inlinedFiles: inlinedFiles.reverse(),
158
+ };
159
+ };
160
+ exports.extractTypeDefinition = extractTypeDefinition;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.copyApiClient = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const utils_1 = require("./utils");
6
+ const template_loader_1 = require("./template-loader");
7
+ const path = tslib_1.__importStar(require("path"));
8
+ const copyApiClient = (options) => {
9
+ const apiClientContent = (0, template_loader_1.loadTemplate)("api-client.ts.template");
10
+ (0, utils_1.writeFile)({
11
+ filePath: path.join(options.outputDirectory, "api-client.ts"),
12
+ content: apiClientContent,
13
+ allowOverwrite: options.allowOverwrite,
14
+ verbose: options.verbose,
15
+ });
16
+ };
17
+ exports.copyApiClient = copyApiClient;
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateApiClientClass = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const utils_1 = require("./utils");
6
+ const handlebars_1 = tslib_1.__importDefault(require("handlebars"));
7
+ const template_loader_1 = require("./template-loader");
8
+ const pathWithQuotes = (apiPath) => {
9
+ return `${apiPath.includes("{") ? "`" : '"'}${apiPath}${apiPath.includes("{") ? "`" : '"'}`.replace(/{/g, "${");
10
+ };
11
+ handlebars_1.default.registerHelper("pathWithQuotes", pathWithQuotes);
12
+ const prepareMethods = (apis) => {
13
+ const methods = [];
14
+ for (const api of apis) {
15
+ const pathParams = (api.fullPath.match(/\{[^}]+}/g) || []).map((p) => p.replace(/[{}]/g, ""));
16
+ let params = "";
17
+ if (api.method === "GET") {
18
+ if (pathParams.length > 0) {
19
+ params += pathParams.map((p) => `${p}: string`).join(", ");
20
+ }
21
+ if (api.inputType) {
22
+ if (params)
23
+ params += ", ";
24
+ params += `body: ${api.inputType}`;
25
+ }
26
+ }
27
+ else {
28
+ if (pathParams.length > 0 || api.inputType) {
29
+ params += "{ ";
30
+ if (pathParams.length > 0) {
31
+ params += pathParams.join(", ") + (api.inputType ? ", " : "");
32
+ }
33
+ if (api.inputType) {
34
+ params += `body`;
35
+ }
36
+ params += " }: { ";
37
+ if (pathParams.length > 0) {
38
+ params +=
39
+ pathParams.map((p) => `${p}: string`).join(", ") +
40
+ (api.inputType ? ", " : "");
41
+ }
42
+ if (api.inputType) {
43
+ params += `body: ${api.inputType.typeName}`;
44
+ }
45
+ params += " }";
46
+ }
47
+ }
48
+ methods.push({
49
+ name: (0, utils_1.toPropertyName)(api.actionName),
50
+ params,
51
+ returnType: api.outputType ? api.outputType.typeName : "void",
52
+ method: api.method.toLowerCase(),
53
+ fullPath: api.fullPath,
54
+ inputType: api.inputType,
55
+ outputType: api.outputType,
56
+ });
57
+ }
58
+ return methods;
59
+ };
60
+ const prepareTypeImports = (apis) => {
61
+ const imports = [];
62
+ for (const api of apis) {
63
+ const typeSpecs = [api.inputType, api.outputType];
64
+ for (const typeSpec of typeSpecs) {
65
+ if (!typeSpec || !typeSpec.importPath)
66
+ continue;
67
+ for (const allowedImport of typeSpec.typeDefinition.allowedImports) {
68
+ let currentImport = imports.find((i) => i.importPath === allowedImport.importPath);
69
+ if (!currentImport) {
70
+ currentImport = {
71
+ importPath: allowedImport.importPath,
72
+ importNames: new Set(),
73
+ };
74
+ imports.push(currentImport);
75
+ }
76
+ if (allowedImport.namespaceImport) {
77
+ const namespaceNames = allowedImport.importNames;
78
+ const conflictingNamespaceNames = [];
79
+ if (namespaceNames.length > 1) {
80
+ conflictingNamespaceNames.push(...namespaceNames);
81
+ }
82
+ if (currentImport.namespaceImport &&
83
+ currentImport.namespaceImport !== namespaceNames[0]) {
84
+ conflictingNamespaceNames.push(currentImport.namespaceImport);
85
+ conflictingNamespaceNames.push(...namespaceNames);
86
+ }
87
+ if (conflictingNamespaceNames.length > 0) {
88
+ throw new Error(`Conflicting namespace imports found for module "${allowedImport.importPath}": ` +
89
+ `[${conflictingNamespaceNames.join(", ")}]. ` +
90
+ `All namespace imports from the same module must use the same name. ` +
91
+ `Please consolidate to use a single namespace import: import * as [name] from "${allowedImport.importPath}"`);
92
+ }
93
+ const namespaceName = namespaceNames[0];
94
+ const conflictingImport = imports.find((imp) => imp.importPath !== allowedImport.importPath &&
95
+ imp.namespaceImport === namespaceName);
96
+ if (conflictingImport) {
97
+ throw new Error(`Namespace name "${namespaceName}" is used by imports from: ` +
98
+ `"${conflictingImport.importPath}" and "${allowedImport.importPath}". ` +
99
+ `Cannot use the same namespace name for different modules. ` +
100
+ `Please use a different namespace name for one of the imports.`);
101
+ }
102
+ currentImport.namespaceImport = namespaceName;
103
+ }
104
+ else {
105
+ for (const importName of allowedImport.importNames) {
106
+ currentImport.importNames.add(importName);
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+ return imports.map((i) => ({
113
+ namespaceImport: i.namespaceImport,
114
+ importPath: i.importPath,
115
+ importNames: Array.from(i.importNames).sort(),
116
+ }));
117
+ };
118
+ const generateApiClientClass = ({ basePath, apis, }, options) => {
119
+ const className = (0, utils_1.toClassName)(basePath) + "Api";
120
+ const imports = (0, utils_1.prepareImports)(apis, options);
121
+ const methods = prepareMethods(apis);
122
+ const templateSource = (0, template_loader_1.loadTemplate)("api-client.hbs");
123
+ const template = handlebars_1.default.compile(templateSource);
124
+ let apiTypesFile = "";
125
+ if (options.typeResolutionMode === "inline-types") {
126
+ const typeImports = prepareTypeImports(apis);
127
+ for (const typeImport of typeImports) {
128
+ if (typeImport.namespaceImport) {
129
+ apiTypesFile += `import * as ${typeImport.namespaceImport} from "${typeImport.importPath}";\n`;
130
+ }
131
+ if (typeImport.importNames.length > 0) {
132
+ apiTypesFile += `import { ${typeImport.importNames.join(", ")} } from "${typeImport.importPath}";\n`;
133
+ }
134
+ }
135
+ const inlinedFilePaths = new Set();
136
+ for (const api of apis) {
137
+ const typeSpecs = [api.inputType, api.outputType];
138
+ for (const typeSpec of typeSpecs) {
139
+ if (!typeSpec || !typeSpec.importPath)
140
+ continue;
141
+ for (const inlinedFile of typeSpec.typeDefinition.inlinedFiles) {
142
+ if (inlinedFilePaths.has(inlinedFile.filePath)) {
143
+ continue;
144
+ }
145
+ inlinedFilePaths.add(inlinedFile.filePath);
146
+ apiTypesFile += "\n\n";
147
+ apiTypesFile += inlinedFile.text;
148
+ }
149
+ }
150
+ }
151
+ }
152
+ return {
153
+ apiClientClassFile: template({
154
+ className,
155
+ imports,
156
+ methods,
157
+ }),
158
+ apiTypesFile,
159
+ };
160
+ };
161
+ exports.generateApiClientClass = generateApiClientClass;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateApiClients = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const utils_1 = require("./utils");
6
+ const parse_discovered_apis_1 = require("./parse-discovered-apis");
7
+ const fs_1 = tslib_1.__importDefault(require("fs"));
8
+ const path_1 = tslib_1.__importDefault(require("path"));
9
+ const generate_api_client_class_1 = require("./generate-api-client-class");
10
+ const generate_hook_1 = require("./generate-hook");
11
+ const copy_api_client_1 = require("./copy-api-client");
12
+ const generateApiClients = async (options = {}) => {
13
+ const { tsConfigPath = "tsconfig.json", targetTypeSystem = "preserve-backend", typeResolutionMode = "source-imports", verbose = false, allowOverwrite = false, cleanOutputDir = false, outputDirectory = "out/apis", resourcesFilePath = "cdk.itzworking.json", } = options;
14
+ if (targetTypeSystem !== "preserve-backend") {
15
+ console.error(`Unsupported targetTypeSystem: ${targetTypeSystem}. Only 'preserve-backend' is supported.`);
16
+ process.exit(1);
17
+ }
18
+ const resources = (0, utils_1.getITzWorkingResources)({ resourcesFilePath, verbose });
19
+ const apisByBasePath = (0, parse_discovered_apis_1.parseDiscoveredApis)(resources, {
20
+ tsConfigPath,
21
+ typeResolutionMode,
22
+ verbose,
23
+ });
24
+ if (cleanOutputDir) {
25
+ fs_1.default.rmSync(outputDirectory, { recursive: true, force: true });
26
+ }
27
+ (0, copy_api_client_1.copyApiClient)({ outputDirectory, allowOverwrite, verbose });
28
+ for (const [basePath, apis] of Object.entries(apisByBasePath)) {
29
+ (0, utils_1.writeFile)({
30
+ filePath: path_1.default.join(outputDirectory, (0, utils_1.toFileName)(basePath), "index.ts"),
31
+ content: `export * from "./${(0, utils_1.toFileName)(basePath)}-api";`,
32
+ allowOverwrite,
33
+ verbose,
34
+ });
35
+ const { apiClientClassFile, apiTypesFile } = (0, generate_api_client_class_1.generateApiClientClass)({ basePath, apis }, {
36
+ typeResolutionMode,
37
+ verbose,
38
+ });
39
+ (0, utils_1.writeFile)({
40
+ filePath: path_1.default.join(outputDirectory, (0, utils_1.toFileName)(basePath), `${(0, utils_1.toFileName)(basePath)}-api.ts`),
41
+ content: apiClientClassFile.replace(/^[\s\S]*?(?=import)/m, ""),
42
+ allowOverwrite,
43
+ verbose,
44
+ });
45
+ if (apiTypesFile) {
46
+ (0, utils_1.writeFile)({
47
+ filePath: path_1.default.join(outputDirectory, (0, utils_1.toFileName)(basePath), `types.ts`),
48
+ content: apiTypesFile,
49
+ allowOverwrite,
50
+ verbose,
51
+ });
52
+ fs_1.default.appendFileSync(path_1.default.join(outputDirectory, (0, utils_1.toFileName)(basePath), "index.ts"), `\nexport * from "./types";`);
53
+ }
54
+ }
55
+ for (const [basePath, apis] of Object.entries(apisByBasePath)) {
56
+ for (const api of apis) {
57
+ const { hookName, hookFile } = (0, generate_hook_1.generateHook)({ basePath, api }, {
58
+ typeResolutionMode,
59
+ verbose,
60
+ });
61
+ (0, utils_1.writeFile)({
62
+ filePath: path_1.default.join(outputDirectory, (0, utils_1.toFileName)(basePath), "hooks", `${(0, utils_1.toFileName)(hookName)}.ts`),
63
+ content: hookFile.replace(/^[\s\S]*?(?=import)/m, ""),
64
+ allowOverwrite,
65
+ verbose,
66
+ });
67
+ }
68
+ }
69
+ };
70
+ exports.generateApiClients = generateApiClients;
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateHook = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const utils_1 = require("./utils");
6
+ const handlebars_1 = tslib_1.__importDefault(require("handlebars"));
7
+ const template_loader_1 = require("./template-loader");
8
+ const generateHook = ({ basePath, api, }, options) => {
9
+ const apiClassName = (0, utils_1.toClassName)(basePath) + "Api";
10
+ const actionName = (0, utils_1.toPropertyName)(api.actionName);
11
+ const actionClassName = (0, utils_1.toClassName)(api.actionName);
12
+ const hookName = `use${actionClassName}`;
13
+ const pathParams = (api.fullPath.match(/\{[^}]+}/g) || []).map((p) => p.replace(/[{}]/g, ""));
14
+ let mutationParams = "";
15
+ if (api.method !== "GET") {
16
+ if (pathParams.length > 0 || api.inputType) {
17
+ mutationParams += "variables: {\n";
18
+ if (pathParams.length > 0) {
19
+ mutationParams +=
20
+ pathParams.map((p) => ` ${p}: string`).join(",\n") +
21
+ (api.inputType ? ",\n" : "");
22
+ }
23
+ if (api.inputType) {
24
+ mutationParams += ` body: ${api.inputType.typeName}`;
25
+ }
26
+ mutationParams += "\n }";
27
+ }
28
+ }
29
+ let queryKeyParams = "";
30
+ let queryKeyArray = "";
31
+ let queryKeyInput = "";
32
+ if (api.method === "GET") {
33
+ if (pathParams.length > 0) {
34
+ queryKeyParams = pathParams.map((p) => `${p}: string`).join(", ");
35
+ queryKeyInput = pathParams.map((p) => `${p}`).join(", ");
36
+ }
37
+ queryKeyArray = api.fullPath
38
+ .split("/")
39
+ .filter(Boolean)
40
+ .map((p) => {
41
+ if (p.startsWith("{") && p.endsWith("}")) {
42
+ return " `$" + `${p}` + "`";
43
+ }
44
+ return ` "${p}"`;
45
+ })
46
+ .join(",\n");
47
+ }
48
+ const imports = [
49
+ { path: `../${(0, utils_1.toFileName)(apiClassName)}`, names: apiClassName },
50
+ ...(0, utils_1.prepareImports)(api, options).map((i) => i.path === "./types" ? { path: "../types", names: i.names } : i),
51
+ ];
52
+ const data = {
53
+ imports,
54
+ hookName,
55
+ mutationParams,
56
+ apiClassName,
57
+ actionName,
58
+ actionClassName,
59
+ relatedDataName: (0, utils_1.toPropertyName)(api.relatedDataName),
60
+ queryKeyParams,
61
+ queryKeyArray,
62
+ queryKeyInput,
63
+ outputType: api.outputType,
64
+ };
65
+ const templateFile = api.method === "GET" ? "hook-query.hbs" : "hook-mutation.hbs";
66
+ const templateSource = (0, template_loader_1.loadTemplate)(templateFile);
67
+ const template = handlebars_1.default.compile(templateSource);
68
+ const hookFile = template(data);
69
+ return { hookName, hookFile };
70
+ };
71
+ exports.generateHook = generateHook;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handlebarsHelpers = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const handlebars_1 = tslib_1.__importDefault(require("handlebars"));
6
+ const handlebarsHelpers = () => {
7
+ handlebars_1.default.registerHelper("eq", function (a, b) {
8
+ return a === b;
9
+ });
10
+ handlebars_1.default.registerHelper("lowercase", function (str) {
11
+ return typeof str === "string" ? str.toLowerCase() : str;
12
+ });
13
+ };
14
+ exports.handlebarsHelpers = handlebarsHelpers;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./generate-api-clients"), exports);
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseDiscoveredApis = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const ts_morph_1 = require("ts-morph");
6
+ const path = tslib_1.__importStar(require("path"));
7
+ const api_type_spec_utils_1 = require("./api-type-spec-utils");
8
+ const INPUT_DECORATOR_NAME = "LambdaInput";
9
+ const OUTPUT_DECORATOR_NAME = "LambdaOutput";
10
+ const parseDiscoveredApis = (resources, options) => {
11
+ const parsedApis = {};
12
+ const project = new ts_morph_1.Project({
13
+ tsConfigFilePath: options.tsConfigPath,
14
+ skipAddingFilesFromTsConfig: true,
15
+ });
16
+ for (const stackResources of Object.values(resources.discoveredResources)) {
17
+ for (const api of stackResources.apis) {
18
+ const method = api.id.split("--")[0];
19
+ const fullPath = "/" + api.id.split("--").slice(1).join("/");
20
+ const basePath = fullPath.split("/")[1];
21
+ const handlerPath = path.join(api.folderPath, "handler.ts");
22
+ const sourceFile = project.addSourceFileAtPathIfExists(handlerPath);
23
+ if (!sourceFile)
24
+ continue;
25
+ const handlerVar = sourceFile.getVariableDeclaration("handler");
26
+ if (!handlerVar)
27
+ continue;
28
+ const initializer = handlerVar.getInitializer();
29
+ let className = null;
30
+ if (initializer && initializer.getKind() === ts_morph_1.SyntaxKind.CallExpression) {
31
+ const callExpr = initializer.asKind(ts_morph_1.SyntaxKind.CallExpression);
32
+ if (callExpr) {
33
+ const expr = callExpr.getExpression();
34
+ const exprText = expr.getText();
35
+ className = exprText.split(".")[0];
36
+ }
37
+ }
38
+ if (!className)
39
+ continue;
40
+ const actionName = className.replace(/Lambda|lambda|Handler|handler/g, "");
41
+ const relatedDataName = actionName.replace(/^[A-Z][a-z]*/, "");
42
+ const classDecl = sourceFile.getClass(className);
43
+ if (!classDecl)
44
+ continue;
45
+ let inputTypeName = null;
46
+ let outputTypeName = null;
47
+ for (const decorator of classDecl.getDecorators()) {
48
+ const name = decorator.getName();
49
+ const args = decorator.getArguments();
50
+ if (name === INPUT_DECORATOR_NAME && args.length > 0) {
51
+ inputTypeName = args[0].getText().replace(/\W/g, "");
52
+ }
53
+ if (name === OUTPUT_DECORATOR_NAME && args.length > 0) {
54
+ outputTypeName = args[0].getText().replace(/\W/g, "");
55
+ }
56
+ }
57
+ const imports = {};
58
+ let inputImportPath = null;
59
+ let outputImportPath = null;
60
+ for (const importDecl of sourceFile.getImportDeclarations()) {
61
+ const namedImports = importDecl.getNamedImports();
62
+ for (const namedImport of namedImports) {
63
+ const importName = namedImport.getName();
64
+ const importPath = importDecl.getModuleSpecifierValue();
65
+ if (inputTypeName && importName === inputTypeName) {
66
+ inputImportPath = importPath;
67
+ if (!imports[importPath])
68
+ imports[importPath] = [];
69
+ imports[importPath].push(importName);
70
+ }
71
+ if (outputTypeName && importName === outputTypeName) {
72
+ outputImportPath = importPath;
73
+ if (!imports[importPath])
74
+ imports[importPath] = [];
75
+ imports[importPath].push(importName);
76
+ }
77
+ }
78
+ }
79
+ const inputType = inputTypeName
80
+ ? {
81
+ typeName: inputTypeName,
82
+ importPath: inputImportPath,
83
+ typeDefinition: {
84
+ allowedImports: [],
85
+ inlinedFiles: [],
86
+ },
87
+ structure: null,
88
+ kind: "unknown",
89
+ }
90
+ : null;
91
+ const outputType = outputTypeName
92
+ ? {
93
+ typeName: outputTypeName,
94
+ importPath: outputImportPath,
95
+ typeDefinition: {
96
+ allowedImports: [],
97
+ inlinedFiles: [],
98
+ },
99
+ structure: null,
100
+ kind: "unknown",
101
+ }
102
+ : null;
103
+ (0, api_type_spec_utils_1.setTypeKind)(inputType, sourceFile);
104
+ (0, api_type_spec_utils_1.setTypeKind)(outputType, sourceFile);
105
+ if (options.typeResolutionMode === "inline-types") {
106
+ (0, api_type_spec_utils_1.extractTypeDefinition)(inputType, sourceFile);
107
+ (0, api_type_spec_utils_1.extractTypeDefinition)(outputType, sourceFile);
108
+ }
109
+ if (!parsedApis[basePath]) {
110
+ parsedApis[basePath] = [];
111
+ }
112
+ parsedApis[basePath].push({
113
+ fullPath,
114
+ method,
115
+ actionName,
116
+ relatedDataName,
117
+ inputType,
118
+ outputType,
119
+ imports,
120
+ });
121
+ }
122
+ }
123
+ return parsedApis;
124
+ };
125
+ exports.parseDiscoveredApis = parseDiscoveredApis;