typedapi-client-helpers 0.1.16 → 0.2.1
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/LICENSE +21 -0
- package/README.md +220 -490
- package/dist/index.cjs +274 -47
- package/dist/index.d.cts +79 -95
- package/dist/index.d.ts +79 -95
- package/dist/index.js +267 -47
- package/package.json +32 -13
- package/scripts/generate-api-minimal.mjs +158 -785
- package/scripts/openapi-generator/controller-template.mjs +246 -0
- package/scripts/openapi-generator/http-client-template.mjs +34 -0
- package/scripts/openapi-generator/index.mjs +113 -0
- package/scripts/openapi-generator/method-template.mjs +440 -0
- package/scripts/openapi-generator/names.mjs +145 -0
- package/scripts/openapi-generator/openapi-utils.mjs +147 -0
- package/scripts/openapi-generator/schema.mjs +455 -0
- package/bin/typedapi-generate.js +0 -3
- package/dist/api/generated/Import.d.ts +0 -22
- package/dist/api/generated/Import.d.ts.map +0 -1
- package/dist/api/generated/Import.js +0 -48
- package/dist/api/generated/Import.js.map +0 -1
- package/dist/api/generated/ImportRoute.d.ts +0 -30
- package/dist/api/generated/ImportRoute.d.ts.map +0 -1
- package/dist/api/generated/ImportRoute.js +0 -13
- package/dist/api/generated/ImportRoute.js.map +0 -1
- package/dist/api/generated/Order.d.ts +0 -38
- package/dist/api/generated/Order.d.ts.map +0 -1
- package/dist/api/generated/Order.js +0 -75
- package/dist/api/generated/Order.js.map +0 -1
- package/dist/api/generated/OrderRoute.d.ts +0 -83
- package/dist/api/generated/OrderRoute.d.ts.map +0 -1
- package/dist/api/generated/OrderRoute.js +0 -13
- package/dist/api/generated/OrderRoute.js.map +0 -1
- package/dist/api/generated/Product.d.ts +0 -62
- package/dist/api/generated/Product.d.ts.map +0 -1
- package/dist/api/generated/Product.js +0 -114
- package/dist/api/generated/Product.js.map +0 -1
- package/dist/api/generated/ProductRoute.d.ts +0 -140
- package/dist/api/generated/ProductRoute.d.ts.map +0 -1
- package/dist/api/generated/ProductRoute.js +0 -13
- package/dist/api/generated/ProductRoute.js.map +0 -1
- package/dist/api/generated/Report.d.ts +0 -14
- package/dist/api/generated/Report.d.ts.map +0 -1
- package/dist/api/generated/Report.js +0 -32
- package/dist/api/generated/Report.js.map +0 -1
- package/dist/api/generated/ReportRoute.d.ts +0 -23
- package/dist/api/generated/ReportRoute.d.ts.map +0 -1
- package/dist/api/generated/ReportRoute.js +0 -13
- package/dist/api/generated/ReportRoute.js.map +0 -1
- package/dist/api/generated/Supplier.d.ts +0 -46
- package/dist/api/generated/Supplier.d.ts.map +0 -1
- package/dist/api/generated/Supplier.js +0 -87
- package/dist/api/generated/Supplier.js.map +0 -1
- package/dist/api/generated/SupplierRoute.d.ts +0 -101
- package/dist/api/generated/SupplierRoute.d.ts.map +0 -1
- package/dist/api/generated/SupplierRoute.js +0 -13
- package/dist/api/generated/SupplierRoute.js.map +0 -1
- package/dist/api/generated/data-contracts.d.ts +0 -277
- package/dist/api/generated/data-contracts.d.ts.map +0 -1
- package/dist/api/generated/data-contracts.js +0 -26
- package/dist/api/generated/data-contracts.js.map +0 -1
- package/dist/api/generated/http-client.d.ts +0 -61
- package/dist/api/generated/http-client.d.ts.map +0 -1
- package/dist/api/generated/http-client.js +0 -171
- package/dist/api/generated/http-client.js.map +0 -1
- package/dist/api/index.d.ts +0 -8
- package/dist/api/index.d.ts.map +0 -1
- package/dist/api/index.js +0 -8
- package/dist/api/index.js.map +0 -1
- package/dist/api/methods/Import.api.d.ts +0 -6
- package/dist/api/methods/Import.api.d.ts.map +0 -1
- package/dist/api/methods/Import.api.js +0 -27
- package/dist/api/methods/Import.api.js.map +0 -1
- package/dist/api/methods/Order.api.d.ts +0 -29
- package/dist/api/methods/Order.api.d.ts.map +0 -1
- package/dist/api/methods/Order.api.js +0 -52
- package/dist/api/methods/Order.api.js.map +0 -1
- package/dist/api/methods/Product.api.d.ts +0 -39
- package/dist/api/methods/Product.api.d.ts.map +0 -1
- package/dist/api/methods/Product.api.js +0 -69
- package/dist/api/methods/Product.api.js.map +0 -1
- package/dist/api/methods/Report.api.d.ts +0 -6
- package/dist/api/methods/Report.api.d.ts.map +0 -1
- package/dist/api/methods/Report.api.js +0 -19
- package/dist/api/methods/Report.api.js.map +0 -1
- package/dist/api/methods/Supplier.api.d.ts +0 -35
- package/dist/api/methods/Supplier.api.d.ts.map +0 -1
- package/dist/api/methods/Supplier.api.js +0 -63
- package/dist/api/methods/Supplier.api.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/interfaces/ApiResult.d.ts +0 -17
- package/dist/interfaces/ApiResult.d.ts.map +0 -1
- package/dist/interfaces/ApiResult.js +0 -2
- package/dist/interfaces/ApiResult.js.map +0 -1
- package/dist/interfaces/Filter.d.ts +0 -31
- package/dist/interfaces/Filter.d.ts.map +0 -1
- package/dist/interfaces/Filter.js +0 -2
- package/dist/interfaces/Filter.js.map +0 -1
- package/dist/interfaces/SortType.d.ts +0 -7
- package/dist/interfaces/SortType.d.ts.map +0 -1
- package/dist/interfaces/SortType.js +0 -2
- package/dist/interfaces/SortType.js.map +0 -1
- package/dist/types/ApiCallOptions.d.ts +0 -9
- package/dist/types/ApiCallOptions.d.ts.map +0 -1
- package/dist/types/ApiCallOptions.js +0 -2
- package/dist/types/ApiCallOptions.js.map +0 -1
- package/dist/types/ApiMethodTypes.d.ts +0 -24
- package/dist/types/ApiMethodTypes.d.ts.map +0 -1
- package/dist/types/ApiMethodTypes.js +0 -2
- package/dist/types/ApiMethodTypes.js.map +0 -1
- package/dist/types/HttpResponse.d.ts +0 -16
- package/dist/types/HttpResponse.d.ts.map +0 -1
- package/dist/types/HttpResponse.js +0 -2
- package/dist/types/HttpResponse.js.map +0 -1
- package/dist/utils/SortTypeFromSortDirection.d.ts +0 -22
- package/dist/utils/SortTypeFromSortDirection.d.ts.map +0 -1
- package/dist/utils/SortTypeFromSortDirection.js +0 -45
- package/dist/utils/SortTypeFromSortDirection.js.map +0 -1
- package/dist/utils/buildQuery.d.ts +0 -11
- package/dist/utils/buildQuery.d.ts.map +0 -1
- package/dist/utils/buildQuery.js +0 -94
- package/dist/utils/buildQuery.js.map +0 -1
- package/dist/utils/extractArgsCallbacksAndParams.d.ts +0 -22
- package/dist/utils/extractArgsCallbacksAndParams.d.ts.map +0 -1
- package/dist/utils/extractArgsCallbacksAndParams.js +0 -44
- package/dist/utils/extractArgsCallbacksAndParams.js.map +0 -1
- package/dist/utils/extractArgsToastsAndParams.d.ts +0 -18
- package/dist/utils/extractArgsToastsAndParams.d.ts.map +0 -1
- package/dist/utils/extractArgsToastsAndParams.js +0 -40
- package/dist/utils/extractArgsToastsAndParams.js.map +0 -1
- package/dist/utils/handleApiResponse.d.ts +0 -9
- package/dist/utils/handleApiResponse.d.ts.map +0 -1
- package/dist/utils/handleApiResponse.js +0 -49
- package/dist/utils/handleApiResponse.js.map +0 -1
- package/dist/utils/toFormData.d.ts +0 -8
- package/dist/utils/toFormData.d.ts.map +0 -1
- package/dist/utils/toFormData.js +0 -68
- package/dist/utils/toFormData.js.map +0 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
import fs from "node:fs";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { generateApi } from "
|
|
5
|
+
import { generateApi } from "./openapi-generator/index.mjs";
|
|
5
6
|
|
|
6
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
8
|
const __dirname = path.dirname(__filename);
|
|
@@ -17,79 +18,62 @@ const consumerPackageJson = fs.existsSync(consumerPackageJsonPath)
|
|
|
17
18
|
? JSON.parse(fs.readFileSync(consumerPackageJsonPath, "utf8"))
|
|
18
19
|
: {};
|
|
19
20
|
|
|
20
|
-
const isRunningInsidePackage =
|
|
21
|
-
path.resolve(process.cwd()) === path.resolve(__dirname, "..");
|
|
22
|
-
|
|
23
|
-
const runtimePackageName = isRunningInsidePackage
|
|
24
|
-
? "../.."
|
|
25
|
-
: runtimePackageJson.name;
|
|
26
|
-
|
|
27
21
|
const cwd = process.cwd();
|
|
28
|
-
|
|
29
22
|
const config = {
|
|
30
23
|
...(runtimePackageJson.config ?? {}),
|
|
31
24
|
...(consumerPackageJson.config ?? {}),
|
|
32
25
|
};
|
|
33
26
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
process.env.npm_config_swagger_file ??
|
|
37
|
-
config.swaggerFile;
|
|
27
|
+
const runtimePackageName =
|
|
28
|
+
path.resolve(cwd) === packageRoot ? "../.." : runtimePackageJson.name;
|
|
38
29
|
|
|
39
|
-
const
|
|
40
|
-
process.env.
|
|
41
|
-
process.env.
|
|
42
|
-
config.
|
|
30
|
+
const swaggerFile = getStringSetting(
|
|
31
|
+
process.env.SWAGGER_FILE,
|
|
32
|
+
process.env.npm_config_swagger_file,
|
|
33
|
+
config.swaggerFile,
|
|
34
|
+
config.input,
|
|
35
|
+
);
|
|
43
36
|
|
|
44
|
-
const
|
|
37
|
+
const swaggerUrl = getStringSetting(
|
|
38
|
+
process.env.SWAGGER_URL,
|
|
39
|
+
process.env.npm_config_swagger_url,
|
|
40
|
+
config.swaggerUrl,
|
|
41
|
+
config.url,
|
|
42
|
+
);
|
|
45
43
|
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
config.typedApiSwaggerBackupFile,
|
|
50
|
-
"swagger/swagger.backup.json",
|
|
44
|
+
const defaultSwaggerUrl = getStringSetting(
|
|
45
|
+
config.defaultSwaggerUrl,
|
|
46
|
+
"https://localhost:7000/swagger/v1/swagger.json",
|
|
51
47
|
);
|
|
52
48
|
|
|
53
|
-
const swaggerBackupPath = path.resolve(
|
|
49
|
+
const swaggerBackupPath = path.resolve(
|
|
50
|
+
cwd,
|
|
51
|
+
getStringSetting(
|
|
52
|
+
process.env.TYPED_API_SWAGGER_BACKUP_FILE,
|
|
53
|
+
process.env.npm_config_typed_api_swagger_backup_file,
|
|
54
|
+
config.typedApiSwaggerBackupFile,
|
|
55
|
+
"swagger/swagger.backup.json",
|
|
56
|
+
),
|
|
57
|
+
);
|
|
54
58
|
|
|
55
59
|
const apiRoot = path.resolve(
|
|
56
60
|
cwd,
|
|
57
|
-
|
|
58
|
-
process.env.
|
|
59
|
-
|
|
61
|
+
getStringSetting(
|
|
62
|
+
process.env.API_OUTPUT,
|
|
63
|
+
process.env.npm_config_api_output,
|
|
64
|
+
config.apiOutput,
|
|
65
|
+
config.output,
|
|
60
66
|
"src/api",
|
|
67
|
+
),
|
|
61
68
|
);
|
|
62
69
|
|
|
63
|
-
const generatedDir = path.join(apiRoot, "generated");
|
|
64
70
|
const methodsDir = path.join(apiRoot, "methods");
|
|
65
71
|
|
|
66
|
-
const useTypeOnlyImports =
|
|
67
|
-
process.env.TYPED_API_USE_TYPE_ONLY_IMPORTS === "true" ||
|
|
68
|
-
process.env.npm_config_typed_api_use_type_only_imports === "true" ||
|
|
69
|
-
config.typedApiUseTypeOnlyImports === true;
|
|
70
|
-
|
|
71
|
-
const useFilterFormValues =
|
|
72
|
-
process.env.TYPED_API_USE_FILTER_FORM_VALUES === "true" ||
|
|
73
|
-
process.env.npm_config_typed_api_use_filter_form_values === "true" ||
|
|
74
|
-
config.typedApiUseFilterFormValues === true;
|
|
75
|
-
|
|
76
|
-
function getStringSetting(...values) {
|
|
77
|
-
for (const value of values) {
|
|
78
|
-
if (typeof value === "string" && value.trim() !== "") {
|
|
79
|
-
return value.trim();
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return undefined;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const fallbackDefaultFunctionsPath = "defaultApiFunctions";
|
|
87
|
-
|
|
88
72
|
const defaultFunctionsPathSetting = getStringSetting(
|
|
89
73
|
process.env.TYPED_API_DEFAULT_FUNCTIONS_PATH,
|
|
90
74
|
process.env.npm_config_typed_api_default_functions_path,
|
|
91
75
|
config.typedApiDefaultFunctionsPath,
|
|
92
|
-
|
|
76
|
+
"src/defaultApiFunctions",
|
|
93
77
|
);
|
|
94
78
|
|
|
95
79
|
const defaultFunctionsFilePath = path.resolve(cwd, defaultFunctionsPathSetting);
|
|
@@ -108,27 +92,94 @@ const defaultErrorHandlerName = getStringSetting(
|
|
|
108
92
|
"handleErrors",
|
|
109
93
|
);
|
|
110
94
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
.
|
|
114
|
-
.
|
|
115
|
-
.
|
|
116
|
-
.
|
|
117
|
-
|
|
95
|
+
const generatorOptions = {
|
|
96
|
+
cleanOutput: getBooleanSetting(
|
|
97
|
+
process.env.TYPED_API_CLEAN_OUTPUT,
|
|
98
|
+
process.env.npm_config_typed_api_clean_output,
|
|
99
|
+
process.env.npm_config_clean_output,
|
|
100
|
+
config.typedApiCleanOutput,
|
|
101
|
+
config.cleanOutput,
|
|
102
|
+
true,
|
|
103
|
+
),
|
|
104
|
+
moduleNameFirstTag: getBooleanSetting(
|
|
105
|
+
process.env.TYPED_API_MODULE_NAME_FIRST_TAG,
|
|
106
|
+
process.env.npm_config_typed_api_module_name_first_tag,
|
|
107
|
+
process.env.npm_config_module_name_first_tag,
|
|
108
|
+
config.typedApiModuleNameFirstTag,
|
|
109
|
+
config.moduleNameFirstTag,
|
|
110
|
+
true,
|
|
111
|
+
),
|
|
112
|
+
defaultResponseAsSuccess: getBooleanSetting(
|
|
113
|
+
process.env.TYPED_API_DEFAULT_RESPONSE_AS_SUCCESS,
|
|
114
|
+
process.env.npm_config_typed_api_default_response_as_success,
|
|
115
|
+
process.env.npm_config_default_response_as_success,
|
|
116
|
+
config.typedApiDefaultResponseAsSuccess,
|
|
117
|
+
config.defaultResponseAsSuccess,
|
|
118
|
+
false,
|
|
119
|
+
),
|
|
120
|
+
generateUnionEnums: getBooleanSetting(
|
|
121
|
+
process.env.TYPED_API_GENERATE_UNION_ENUMS,
|
|
122
|
+
process.env.npm_config_typed_api_generate_union_enums,
|
|
123
|
+
process.env.npm_config_generate_union_enums,
|
|
124
|
+
config.typedApiGenerateUnionEnums,
|
|
125
|
+
config.generateUnionEnums,
|
|
126
|
+
false,
|
|
127
|
+
),
|
|
128
|
+
enumNamesAsValues: getBooleanSetting(
|
|
129
|
+
process.env.TYPED_API_ENUM_NAMES_AS_VALUES,
|
|
130
|
+
process.env.npm_config_typed_api_enum_names_as_values,
|
|
131
|
+
process.env.npm_config_enum_names_as_values,
|
|
132
|
+
config.typedApiEnumNamesAsValues,
|
|
133
|
+
config.enumNamesAsValues,
|
|
134
|
+
false,
|
|
135
|
+
),
|
|
136
|
+
useTypeOnlyImports: getBooleanSetting(
|
|
137
|
+
process.env.TYPED_API_USE_TYPE_ONLY_IMPORTS,
|
|
138
|
+
process.env.npm_config_typed_api_use_type_only_imports,
|
|
139
|
+
process.env.npm_config_use_type_only_imports,
|
|
140
|
+
config.typedApiUseTypeOnlyImports,
|
|
141
|
+
config.useTypeOnlyImports,
|
|
142
|
+
true,
|
|
143
|
+
),
|
|
144
|
+
useFilterFormValues: getBooleanSetting(
|
|
145
|
+
process.env.TYPED_API_USE_FILTER_FORM_VALUES,
|
|
146
|
+
process.env.npm_config_typed_api_use_filter_form_values,
|
|
147
|
+
config.typedApiUseFilterFormValues,
|
|
148
|
+
true,
|
|
149
|
+
),
|
|
150
|
+
baseUrl: getStringSetting(
|
|
151
|
+
process.env.TYPED_API_BASE_URL,
|
|
152
|
+
process.env.npm_config_typed_api_base_url,
|
|
153
|
+
config.typedApiBaseUrl,
|
|
154
|
+
),
|
|
155
|
+
runtimePackageName,
|
|
156
|
+
};
|
|
118
157
|
|
|
119
|
-
function
|
|
120
|
-
|
|
158
|
+
function getStringSetting(...values) {
|
|
159
|
+
for (const value of values) {
|
|
160
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
161
|
+
return value.trim();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return undefined;
|
|
121
166
|
}
|
|
122
167
|
|
|
123
|
-
function
|
|
124
|
-
|
|
125
|
-
|
|
168
|
+
function getBooleanSetting(...values) {
|
|
169
|
+
const fallback = values[values.length - 1];
|
|
170
|
+
|
|
171
|
+
for (const value of values.slice(0, -1)) {
|
|
172
|
+
if (typeof value === "boolean") return value;
|
|
173
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
174
|
+
const normalized = value.trim().toLowerCase();
|
|
175
|
+
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
176
|
+
if (["0", "false", "no", "off"].includes(normalized)) return false;
|
|
177
|
+
}
|
|
126
178
|
}
|
|
127
179
|
|
|
128
|
-
|
|
180
|
+
return Boolean(fallback);
|
|
129
181
|
}
|
|
130
182
|
|
|
131
|
-
|
|
132
183
|
function fileExists(filePath) {
|
|
133
184
|
try {
|
|
134
185
|
return fs.existsSync(filePath) && fs.statSync(filePath).isFile();
|
|
@@ -137,22 +188,22 @@ function fileExists(filePath) {
|
|
|
137
188
|
}
|
|
138
189
|
}
|
|
139
190
|
|
|
191
|
+
function displayPath(filePath) {
|
|
192
|
+
return path.relative(cwd, filePath) || ".";
|
|
193
|
+
}
|
|
194
|
+
|
|
140
195
|
function writeSwaggerBackup(content, sourceLabel) {
|
|
141
196
|
fs.mkdirSync(path.dirname(swaggerBackupPath), { recursive: true });
|
|
142
197
|
fs.writeFileSync(swaggerBackupPath, content);
|
|
143
198
|
|
|
144
199
|
console.log(
|
|
145
|
-
`Updated Swagger backup from ${sourceLabel}: ${
|
|
200
|
+
`Updated Swagger backup from ${sourceLabel}: ${displayPath(swaggerBackupPath)}`,
|
|
146
201
|
);
|
|
147
202
|
}
|
|
148
203
|
|
|
149
204
|
function copySwaggerBackupFromFile(filePath) {
|
|
150
205
|
const content = fs.readFileSync(filePath, "utf8");
|
|
151
|
-
|
|
152
|
-
// Validate that the backup is at least JSON. This prevents overwriting a good
|
|
153
|
-
// backup with an invalid or empty file by accident.
|
|
154
206
|
JSON.parse(content);
|
|
155
|
-
|
|
156
207
|
writeSwaggerBackup(content, path.relative(cwd, filePath));
|
|
157
208
|
}
|
|
158
209
|
|
|
@@ -166,10 +217,7 @@ async function downloadSwaggerBackupFromUrl(url) {
|
|
|
166
217
|
}
|
|
167
218
|
|
|
168
219
|
const content = await response.text();
|
|
169
|
-
|
|
170
|
-
// Validate before saving, so a server error page is never stored as backup.
|
|
171
220
|
JSON.parse(content);
|
|
172
|
-
|
|
173
221
|
writeSwaggerBackup(content, url);
|
|
174
222
|
}
|
|
175
223
|
|
|
@@ -179,7 +227,7 @@ function useBackupSwaggerInput(reason) {
|
|
|
179
227
|
}
|
|
180
228
|
|
|
181
229
|
console.warn(
|
|
182
|
-
`${reason} Using Swagger backup: ${
|
|
230
|
+
`${reason} Using Swagger backup: ${displayPath(swaggerBackupPath)}`,
|
|
183
231
|
);
|
|
184
232
|
|
|
185
233
|
return {
|
|
@@ -253,103 +301,26 @@ async function resolveSwaggerInput() {
|
|
|
253
301
|
input: swaggerBackupPath,
|
|
254
302
|
label: defaultSwaggerUrl,
|
|
255
303
|
};
|
|
256
|
-
} catch
|
|
304
|
+
} catch {
|
|
257
305
|
const backupInput = useBackupSwaggerInput(
|
|
258
|
-
|
|
306
|
+
"Default Swagger file was not found and default Swagger URL is not available.",
|
|
259
307
|
);
|
|
260
308
|
|
|
261
309
|
if (backupInput) {
|
|
262
310
|
return backupInput;
|
|
263
311
|
}
|
|
264
312
|
|
|
265
|
-
throw
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
async function writeFormattedFile(filePath, content) {
|
|
270
|
-
fs.writeFileSync(filePath, content);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
function removeGeneratedRouteFiles() {
|
|
274
|
-
const files = fs.readdirSync(generatedDir);
|
|
275
|
-
|
|
276
|
-
for (const file of files) {
|
|
277
|
-
if (/route\.ts$/i.test(file)) {
|
|
278
|
-
fs.rmSync(path.join(generatedDir, file), { force: true });
|
|
279
|
-
}
|
|
313
|
+
throw new Error(
|
|
314
|
+
`No Swagger input found. Add swagger/swagger.json, configure swaggerFile, or configure swaggerUrl.`,
|
|
315
|
+
);
|
|
280
316
|
}
|
|
281
317
|
}
|
|
282
318
|
|
|
283
|
-
async function generateOpenApiClient(swaggerInput) {
|
|
284
|
-
cleanDirectory(generatedDir);
|
|
285
|
-
cleanDirectory(methodsDir);
|
|
286
|
-
fs.mkdirSync(methodsDir, { recursive: true });
|
|
287
|
-
|
|
288
|
-
const generateOptions = {
|
|
289
|
-
name: "Api.ts",
|
|
290
|
-
output: generatedDir,
|
|
291
|
-
httpClientType: "fetch",
|
|
292
|
-
modular: true,
|
|
293
|
-
cleanOutput: true,
|
|
294
|
-
generateClient: true,
|
|
295
|
-
generateRouteTypes: true,
|
|
296
|
-
extractRequestParams: true,
|
|
297
|
-
extractRequestBody: true,
|
|
298
|
-
moduleNameFirstTag: true,
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
generateOptions.input = swaggerInput;
|
|
302
|
-
|
|
303
|
-
await generateApi(generateOptions);
|
|
304
|
-
|
|
305
|
-
removeGeneratedRouteFiles();
|
|
306
|
-
}
|
|
307
|
-
|
|
308
319
|
function createImportStatement({ names, from, typeOnly = false }) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
}
|
|
312
|
-
|
|
320
|
+
const cleanNames = [...new Set(names.filter(Boolean))];
|
|
321
|
+
if (cleanNames.length === 0) return "";
|
|
313
322
|
const importKeyword = typeOnly ? "import type" : "import";
|
|
314
|
-
|
|
315
|
-
return `${importKeyword} {
|
|
316
|
-
${names.map((name) => ` ${name},`).join("\n")}
|
|
317
|
-
} from "${from}";`;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
function createAliasedImportStatement({ imports, from }) {
|
|
321
|
-
if (!imports || imports.length === 0) {
|
|
322
|
-
return "";
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
return `import {
|
|
326
|
-
${imports
|
|
327
|
-
.map(({ name, alias }) =>
|
|
328
|
-
alias && alias !== name ? ` ${name} as ${alias},` : ` ${name},`,
|
|
329
|
-
)
|
|
330
|
-
.join("\n")}
|
|
331
|
-
} from "${from}";`;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function resolveImportFilePath(fromDirectory, importPath) {
|
|
335
|
-
if (!importPath.startsWith(".")) {
|
|
336
|
-
return undefined;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
const basePath = path.resolve(fromDirectory, importPath);
|
|
340
|
-
const candidates = [
|
|
341
|
-
basePath,
|
|
342
|
-
`${basePath}.ts`,
|
|
343
|
-
`${basePath}.tsx`,
|
|
344
|
-
`${basePath}.js`,
|
|
345
|
-
`${basePath}.jsx`,
|
|
346
|
-
path.join(basePath, "index.ts"),
|
|
347
|
-
path.join(basePath, "index.tsx"),
|
|
348
|
-
path.join(basePath, "index.js"),
|
|
349
|
-
path.join(basePath, "index.jsx"),
|
|
350
|
-
];
|
|
351
|
-
|
|
352
|
-
return candidates.find((candidate) => fs.existsSync(candidate));
|
|
323
|
+
return `${importKeyword} { ${cleanNames.join(", ")} } from ${JSON.stringify(from)};`;
|
|
353
324
|
}
|
|
354
325
|
|
|
355
326
|
function createDefaultFunctionsFileIfMissing(filePath) {
|
|
@@ -364,12 +335,11 @@ function createDefaultFunctionsFileIfMissing(filePath) {
|
|
|
364
335
|
|
|
365
336
|
fs.writeFileSync(
|
|
366
337
|
resolvedFilePath,
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
})}
|
|
338
|
+
`${createImportStatement({
|
|
339
|
+
names: ["ApiErrorResult", "ApiSuccessResult"],
|
|
340
|
+
from: runtimePackageName,
|
|
341
|
+
typeOnly: generatorOptions.useTypeOnlyImports !== false,
|
|
342
|
+
})}
|
|
373
343
|
|
|
374
344
|
export function ${defaultSuccessHandlerName}<T>(
|
|
375
345
|
_response: ApiSuccessResult<T>,
|
|
@@ -399,13 +369,13 @@ function toRelativeImportPath(fromDirectory, filePath) {
|
|
|
399
369
|
relativePath = `./${relativePath}`;
|
|
400
370
|
}
|
|
401
371
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
372
|
+
return relativePath
|
|
373
|
+
.replaceAll(path.sep, "/")
|
|
374
|
+
.replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
405
375
|
}
|
|
406
376
|
|
|
407
377
|
function escapeRegExp(value) {
|
|
408
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
378
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
409
379
|
}
|
|
410
380
|
|
|
411
381
|
function fileExportsName(source, exportName) {
|
|
@@ -446,11 +416,10 @@ function fileExportsName(source, exportName) {
|
|
|
446
416
|
return false;
|
|
447
417
|
}
|
|
448
418
|
|
|
449
|
-
function resolveDefaultHandlers(
|
|
450
|
-
const defaultFunctionsFile =
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
419
|
+
function resolveDefaultHandlers() {
|
|
420
|
+
const defaultFunctionsFile = createDefaultFunctionsFileIfMissing(
|
|
421
|
+
defaultFunctionsFilePath,
|
|
422
|
+
);
|
|
454
423
|
const source = fs.readFileSync(defaultFunctionsFile, "utf8");
|
|
455
424
|
const hasSuccessHandler = fileExportsName(source, defaultSuccessHandlerName);
|
|
456
425
|
const hasErrorHandler = fileExportsName(source, defaultErrorHandlerName);
|
|
@@ -472,627 +441,31 @@ function resolveDefaultHandlers(importFromDirectory) {
|
|
|
472
441
|
}
|
|
473
442
|
|
|
474
443
|
return {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
),
|
|
479
|
-
defaultSuccessHandlerName,
|
|
480
|
-
defaultErrorHandlerName,
|
|
444
|
+
path: toRelativeImportPath(methodsDir, defaultFunctionsFile),
|
|
445
|
+
success: defaultSuccessHandlerName,
|
|
446
|
+
error: defaultErrorHandlerName,
|
|
481
447
|
};
|
|
482
448
|
}
|
|
483
449
|
|
|
484
|
-
function callbackOptionsExpression(defaultHandlers) {
|
|
485
|
-
if (defaultHandlers) {
|
|
486
|
-
return `{
|
|
487
|
-
onSuccess: onSuccess ?? typedApiDefaultSuccessHandler,
|
|
488
|
-
onError: onError ?? typedApiDefaultErrorHandler
|
|
489
|
-
}`;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
return `{
|
|
493
|
-
onSuccess,
|
|
494
|
-
onError
|
|
495
|
-
}`;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
function getAllTsFiles(dir) {
|
|
499
|
-
let results = [];
|
|
500
|
-
|
|
501
|
-
if (!fs.existsSync(dir)) {
|
|
502
|
-
return results;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
const list = fs.readdirSync(dir);
|
|
506
|
-
|
|
507
|
-
for (const file of list) {
|
|
508
|
-
const filePath = path.join(dir, file);
|
|
509
|
-
const stat = fs.statSync(filePath);
|
|
510
|
-
|
|
511
|
-
if (stat.isDirectory()) {
|
|
512
|
-
results = results.concat(getAllTsFiles(filePath));
|
|
513
|
-
} else if (filePath.endsWith(".ts")) {
|
|
514
|
-
results.push(filePath);
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
return results;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
function getGeneratedMethods(source) {
|
|
522
|
-
const methodRegex = /^\s*(\w+)\s*=\s*\(/gm;
|
|
523
|
-
const matches = [...source.matchAll(methodRegex)];
|
|
524
|
-
|
|
525
|
-
return matches.map((match, index) => {
|
|
526
|
-
const methodName = match[1];
|
|
527
|
-
const startIndex = match.index ?? 0;
|
|
528
|
-
const endIndex =
|
|
529
|
-
index + 1 < matches.length ? matches[index + 1].index : source.length;
|
|
530
|
-
|
|
531
|
-
const methodSource = source.slice(startIndex, endIndex);
|
|
532
|
-
|
|
533
|
-
return {
|
|
534
|
-
methodName,
|
|
535
|
-
methodSource,
|
|
536
|
-
isFormData: methodSource.includes("type: ContentType.FormData"),
|
|
537
|
-
};
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
function splitTopLevelArguments(value) {
|
|
542
|
-
const args = [];
|
|
543
|
-
let current = "";
|
|
544
|
-
let depth = 0;
|
|
545
|
-
let quote = "";
|
|
546
|
-
|
|
547
|
-
for (let index = 0; index < value.length; index++) {
|
|
548
|
-
const char = value[index];
|
|
549
|
-
const previousChar = value[index - 1];
|
|
550
|
-
|
|
551
|
-
if (quote) {
|
|
552
|
-
current += char;
|
|
553
|
-
|
|
554
|
-
if (char === quote && previousChar !== "\\") {
|
|
555
|
-
quote = "";
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
continue;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
if (char === '"' || char === "'" || char === "`") {
|
|
562
|
-
quote = char;
|
|
563
|
-
current += char;
|
|
564
|
-
continue;
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
if (char === "(" || char === "{" || char === "[" || char === "<") {
|
|
568
|
-
depth++;
|
|
569
|
-
current += char;
|
|
570
|
-
continue;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
if (char === ")" || char === "}" || char === "]" || char === ">") {
|
|
574
|
-
depth--;
|
|
575
|
-
current += char;
|
|
576
|
-
continue;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
if (char === "," && depth === 0) {
|
|
580
|
-
if (current.trim()) {
|
|
581
|
-
args.push(current.trim());
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
current = "";
|
|
585
|
-
continue;
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
current += char;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
if (current.trim()) {
|
|
592
|
-
args.push(current.trim());
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
return args;
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
function getGeneratedMethodArgumentCount(signatureParams) {
|
|
599
|
-
const args = splitTopLevelArguments(signatureParams);
|
|
600
|
-
|
|
601
|
-
if (args.length === 0) {
|
|
602
|
-
return 0;
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
const lastArg = args[args.length - 1];
|
|
606
|
-
|
|
607
|
-
if (/\bparams\s*:\s*RequestParams\b/.test(lastArg)) {
|
|
608
|
-
return args.length - 1;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
return args.length;
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
async function createMethodFiles() {
|
|
615
|
-
const defaultHandlers = resolveDefaultHandlers(methodsDir);
|
|
616
|
-
|
|
617
|
-
const generatedFiles = fs
|
|
618
|
-
.readdirSync(generatedDir)
|
|
619
|
-
.filter(
|
|
620
|
-
(file) =>
|
|
621
|
-
file.endsWith(".ts") &&
|
|
622
|
-
file !== "http-client.ts" &&
|
|
623
|
-
file !== "data-contracts.ts" &&
|
|
624
|
-
!/route\.ts$/i.test(file) &&
|
|
625
|
-
!file.includes(".api"),
|
|
626
|
-
);
|
|
627
|
-
|
|
628
|
-
const methodExports = [];
|
|
629
|
-
|
|
630
|
-
for (const file of generatedFiles) {
|
|
631
|
-
const filePath = path.join(generatedDir, file);
|
|
632
|
-
const source = fs.readFileSync(filePath, "utf8");
|
|
633
|
-
|
|
634
|
-
const baseName = file.replace(".ts", "");
|
|
635
|
-
const className = baseName.split("-").map(pascalCase).join("");
|
|
636
|
-
|
|
637
|
-
const instanceName = `${className.charAt(0).toLowerCase()}${className.slice(1)}Api`;
|
|
638
|
-
|
|
639
|
-
const paginatedMethods = [];
|
|
640
|
-
const simpleQueryMethods = [];
|
|
641
|
-
const nonQueryMethods = [];
|
|
642
|
-
|
|
643
|
-
const generatedMethods = getGeneratedMethods(source);
|
|
644
|
-
|
|
645
|
-
for (const generatedMethod of generatedMethods) {
|
|
646
|
-
const { methodName, methodSource, isFormData } = generatedMethod;
|
|
647
|
-
|
|
648
|
-
const signatureRegex = new RegExp(
|
|
649
|
-
`${methodName}\\s*=\\s*\\(([^)]*)\\)\\s*=>`,
|
|
650
|
-
"m",
|
|
651
|
-
);
|
|
652
|
-
|
|
653
|
-
const signatureMatch = methodSource.match(signatureRegex);
|
|
654
|
-
|
|
655
|
-
if (!signatureMatch) {
|
|
656
|
-
continue;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
const params = signatureMatch[1].trim();
|
|
660
|
-
const methodArgumentCount = getGeneratedMethodArgumentCount(params);
|
|
661
|
-
|
|
662
|
-
const returnTypeMatch = methodSource.match(/this\.request<([^,>]+)/m);
|
|
663
|
-
const returnType = returnTypeMatch?.[1] ?? "";
|
|
664
|
-
|
|
665
|
-
const isPaginated =
|
|
666
|
-
returnType.includes("ApiPaginationResponse") ||
|
|
667
|
-
returnType.trim().endsWith("[]");
|
|
668
|
-
|
|
669
|
-
if (params.startsWith("query")) {
|
|
670
|
-
if (isPaginated) {
|
|
671
|
-
paginatedMethods.push(methodName);
|
|
672
|
-
} else {
|
|
673
|
-
simpleQueryMethods.push(methodName);
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
continue;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
nonQueryMethods.push({
|
|
680
|
-
methodName,
|
|
681
|
-
isFormData,
|
|
682
|
-
methodArgumentCount,
|
|
683
|
-
});
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
const queryTypes = [...paginatedMethods, ...simpleQueryMethods]
|
|
687
|
-
.map((methodName) =>
|
|
688
|
-
`
|
|
689
|
-
export type ${toQueryName(methodName)} =
|
|
690
|
-
NonNullable<
|
|
691
|
-
Parameters<${className}["${methodName}"]>[0]
|
|
692
|
-
>;
|
|
693
|
-
`.trim(),
|
|
694
|
-
)
|
|
695
|
-
.join("\n\n");
|
|
696
|
-
|
|
697
|
-
const hasPaginatedMethods = paginatedMethods.length > 0;
|
|
698
|
-
|
|
699
|
-
const hasFormDataMethods = nonQueryMethods.some(
|
|
700
|
-
({ isFormData }) => isFormData,
|
|
701
|
-
);
|
|
702
|
-
|
|
703
|
-
const hasRegularNonQueryMethods = nonQueryMethods.some(
|
|
704
|
-
({ isFormData }) => !isFormData,
|
|
705
|
-
);
|
|
706
|
-
|
|
707
|
-
const paginatedMethodWrappers = paginatedMethods
|
|
708
|
-
.map((methodName) => {
|
|
709
|
-
if (useFilterFormValues) {
|
|
710
|
-
return `
|
|
711
|
-
export async function ${methodName}(
|
|
712
|
-
filters: FilterFormValues<${toQueryName(methodName)}>[] = [],
|
|
713
|
-
page = 1,
|
|
714
|
-
pageSize = 100,
|
|
715
|
-
sortBy: SortableKeys<
|
|
716
|
-
ExtractResponse<ReturnType<${className}["${methodName}"]>>
|
|
717
|
-
> | null = null,
|
|
718
|
-
sortDirection?: SortDirection,
|
|
719
|
-
options: ApiMethodOptions<
|
|
720
|
-
ExtractResponse<ReturnType<${className}["${methodName}"]>>,
|
|
721
|
-
ExtractError<ReturnType<${className}["${methodName}"]>>,
|
|
722
|
-
RequestParams
|
|
723
|
-
> = {}
|
|
724
|
-
): Promise<ApiResult<ExtractResponse<ReturnType<${className}["${methodName}"]>>>> {
|
|
725
|
-
const { onSuccess, onError, params } = options;
|
|
726
|
-
|
|
727
|
-
return handleApiResponse(
|
|
728
|
-
() =>
|
|
729
|
-
${instanceName}.${methodName}(
|
|
730
|
-
buildQuery<
|
|
731
|
-
${toQueryName(methodName)},
|
|
732
|
-
UnwrapArray<
|
|
733
|
-
ExtractDataIfPaginated<
|
|
734
|
-
ExtractResponse<ReturnType<${className}["${methodName}"]>>
|
|
735
|
-
>
|
|
736
|
-
>
|
|
737
|
-
>(filters, page, pageSize, sortBy, sortDirection),
|
|
738
|
-
params ?? {}
|
|
739
|
-
),
|
|
740
|
-
${callbackOptionsExpression(defaultHandlers)}
|
|
741
|
-
);
|
|
742
|
-
}
|
|
743
|
-
`.trim();
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
return `
|
|
747
|
-
export async function ${methodName}(
|
|
748
|
-
query?: ${toQueryName(methodName)},
|
|
749
|
-
options: ApiMethodOptions<
|
|
750
|
-
ExtractResponse<ReturnType<${className}["${methodName}"]>>,
|
|
751
|
-
ExtractError<ReturnType<${className}["${methodName}"]>>,
|
|
752
|
-
RequestParams
|
|
753
|
-
> = {}
|
|
754
|
-
): Promise<ApiResult<ExtractResponse<ReturnType<${className}["${methodName}"]>>>> {
|
|
755
|
-
const { onSuccess, onError, params } = options;
|
|
756
|
-
|
|
757
|
-
return handleApiResponse(
|
|
758
|
-
() =>
|
|
759
|
-
${instanceName}.${methodName}(
|
|
760
|
-
(query ?? {}) as ${toQueryName(methodName)},
|
|
761
|
-
params ?? {}
|
|
762
|
-
),
|
|
763
|
-
${callbackOptionsExpression(defaultHandlers)}
|
|
764
|
-
);
|
|
765
|
-
}
|
|
766
|
-
`.trim();
|
|
767
|
-
})
|
|
768
|
-
.join("\n\n");
|
|
769
|
-
|
|
770
|
-
const simpleQueryMethodWrappers = simpleQueryMethods
|
|
771
|
-
.map((methodName) =>
|
|
772
|
-
`
|
|
773
|
-
export async function ${methodName}(
|
|
774
|
-
query?: ${toQueryName(methodName)},
|
|
775
|
-
options: ApiMethodOptions<
|
|
776
|
-
ExtractResponse<ReturnType<${className}["${methodName}"]>>,
|
|
777
|
-
ExtractError<ReturnType<${className}["${methodName}"]>>,
|
|
778
|
-
RequestParams
|
|
779
|
-
> = {}
|
|
780
|
-
): Promise<
|
|
781
|
-
ApiResult<
|
|
782
|
-
ExtractResponse<
|
|
783
|
-
ReturnType<
|
|
784
|
-
${className}["${methodName}"]
|
|
785
|
-
>
|
|
786
|
-
>
|
|
787
|
-
>
|
|
788
|
-
> {
|
|
789
|
-
const { onSuccess, onError, params } = options;
|
|
790
|
-
|
|
791
|
-
return handleApiResponse(
|
|
792
|
-
() =>
|
|
793
|
-
${instanceName}.${methodName}(
|
|
794
|
-
(query ?? {}) as ${toQueryName(methodName)},
|
|
795
|
-
params ?? {}
|
|
796
|
-
),
|
|
797
|
-
${callbackOptionsExpression(defaultHandlers)}
|
|
798
|
-
);
|
|
799
|
-
}
|
|
800
|
-
`.trim(),
|
|
801
|
-
)
|
|
802
|
-
.join("\n\n");
|
|
803
|
-
|
|
804
|
-
const nonQueryMethodWrappers = nonQueryMethods
|
|
805
|
-
.map(({ methodName, isFormData, methodArgumentCount }) => {
|
|
806
|
-
if (isFormData) {
|
|
807
|
-
return `
|
|
808
|
-
export async function ${methodName}(
|
|
809
|
-
data: Parameters<
|
|
810
|
-
${className}["${methodName}"]
|
|
811
|
-
>[0],
|
|
812
|
-
options: ApiMethodOptions<
|
|
813
|
-
ExtractResponse<ReturnType<${className}["${methodName}"]>>,
|
|
814
|
-
ExtractError<ReturnType<${className}["${methodName}"]>>,
|
|
815
|
-
RequestParams
|
|
816
|
-
> = {}
|
|
817
|
-
): Promise<
|
|
818
|
-
ApiResult<
|
|
819
|
-
ExtractResponse<
|
|
820
|
-
ReturnType<
|
|
821
|
-
${className}["${methodName}"]
|
|
822
|
-
>
|
|
823
|
-
>
|
|
824
|
-
>
|
|
825
|
-
> {
|
|
826
|
-
const { onSuccess, onError, params } = options;
|
|
827
|
-
const formData = toFormData(data);
|
|
828
|
-
|
|
829
|
-
return handleApiResponse(
|
|
830
|
-
() =>
|
|
831
|
-
${instanceName}.${methodName}(
|
|
832
|
-
formData as unknown as Parameters<
|
|
833
|
-
${className}["${methodName}"]
|
|
834
|
-
>[0],
|
|
835
|
-
params ?? {}
|
|
836
|
-
),
|
|
837
|
-
${callbackOptionsExpression(defaultHandlers)}
|
|
838
|
-
);
|
|
839
|
-
}
|
|
840
|
-
`.trim();
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
return `
|
|
844
|
-
export async function ${methodName}(
|
|
845
|
-
...argsWithOptions: [
|
|
846
|
-
...ApiMethodArguments<
|
|
847
|
-
${className}["${methodName}"],
|
|
848
|
-
RequestParams
|
|
849
|
-
>,
|
|
850
|
-
ApiMethodOptions<
|
|
851
|
-
ExtractResponse<ReturnType<${className}["${methodName}"]>>,
|
|
852
|
-
ExtractError<ReturnType<${className}["${methodName}"]>>,
|
|
853
|
-
RequestParams
|
|
854
|
-
>?
|
|
855
|
-
]
|
|
856
|
-
): Promise<
|
|
857
|
-
ApiResult<
|
|
858
|
-
ExtractResponse<
|
|
859
|
-
ReturnType<
|
|
860
|
-
${className}["${methodName}"]
|
|
861
|
-
>
|
|
862
|
-
>
|
|
863
|
-
>
|
|
864
|
-
> {
|
|
865
|
-
const args = [...argsWithOptions] as unknown[];
|
|
866
|
-
|
|
867
|
-
const options =
|
|
868
|
-
args.length > ${methodArgumentCount}
|
|
869
|
-
? (args.pop() as ApiMethodOptions<
|
|
870
|
-
ExtractResponse<ReturnType<${className}["${methodName}"]>>,
|
|
871
|
-
ExtractError<ReturnType<${className}["${methodName}"]>>,
|
|
872
|
-
RequestParams
|
|
873
|
-
>)
|
|
874
|
-
: {};
|
|
875
|
-
|
|
876
|
-
const { onSuccess, onError, params } = options ?? {};
|
|
877
|
-
|
|
878
|
-
const requestArgs = [
|
|
879
|
-
...args,
|
|
880
|
-
params ?? {}
|
|
881
|
-
] as unknown as Parameters<
|
|
882
|
-
${className}["${methodName}"]
|
|
883
|
-
>;
|
|
884
|
-
|
|
885
|
-
return handleApiResponse(
|
|
886
|
-
() =>
|
|
887
|
-
${instanceName}.${methodName}(
|
|
888
|
-
...requestArgs
|
|
889
|
-
),
|
|
890
|
-
${callbackOptionsExpression(defaultHandlers)}
|
|
891
|
-
);
|
|
892
|
-
}
|
|
893
|
-
`.trim();
|
|
894
|
-
})
|
|
895
|
-
.join("\n\n");
|
|
896
|
-
|
|
897
|
-
const runtimeValueImportNames = [
|
|
898
|
-
...(hasPaginatedMethods && useFilterFormValues ? ["buildQuery"] : []),
|
|
899
|
-
...(hasFormDataMethods ? ["toFormData"] : []),
|
|
900
|
-
"handleApiResponse",
|
|
901
|
-
];
|
|
902
|
-
|
|
903
|
-
const runtimeTypeImportNames = [
|
|
904
|
-
"ApiResult",
|
|
905
|
-
"ApiMethodOptions",
|
|
906
|
-
...(hasRegularNonQueryMethods ? ["ApiMethodArguments"] : []),
|
|
907
|
-
"ExtractResponse",
|
|
908
|
-
"ExtractError",
|
|
909
|
-
|
|
910
|
-
...(hasPaginatedMethods && useFilterFormValues
|
|
911
|
-
? [
|
|
912
|
-
"ExtractDataIfPaginated",
|
|
913
|
-
"FilterFormValues",
|
|
914
|
-
"SortableKeys",
|
|
915
|
-
"SortDirection",
|
|
916
|
-
"UnwrapArray",
|
|
917
|
-
]
|
|
918
|
-
: []),
|
|
919
|
-
];
|
|
920
|
-
|
|
921
|
-
const runtimeValueImports = createImportStatement({
|
|
922
|
-
names: runtimeValueImportNames,
|
|
923
|
-
from: runtimePackageName,
|
|
924
|
-
});
|
|
925
|
-
|
|
926
|
-
const defaultFunctionImports = defaultHandlers
|
|
927
|
-
? createAliasedImportStatement({
|
|
928
|
-
imports: [
|
|
929
|
-
{
|
|
930
|
-
name: defaultHandlers.defaultSuccessHandlerName,
|
|
931
|
-
alias: "typedApiDefaultSuccessHandler",
|
|
932
|
-
},
|
|
933
|
-
{
|
|
934
|
-
name: defaultHandlers.defaultErrorHandlerName,
|
|
935
|
-
alias: "typedApiDefaultErrorHandler",
|
|
936
|
-
},
|
|
937
|
-
],
|
|
938
|
-
from: defaultHandlers.defaultFunctionsPath,
|
|
939
|
-
})
|
|
940
|
-
: "";
|
|
941
|
-
|
|
942
|
-
const runtimeTypeImports = createImportStatement({
|
|
943
|
-
names: runtimeTypeImportNames,
|
|
944
|
-
from: runtimePackageName,
|
|
945
|
-
typeOnly: useTypeOnlyImports,
|
|
946
|
-
});
|
|
947
|
-
|
|
948
|
-
const generatedValueImports = createImportStatement({
|
|
949
|
-
names: [className],
|
|
950
|
-
from: `../generated/${baseName}`,
|
|
951
|
-
});
|
|
952
|
-
|
|
953
|
-
const generatedTypeImports =
|
|
954
|
-
generatedMethods.length > 0
|
|
955
|
-
? createImportStatement({
|
|
956
|
-
names: ["RequestParams"],
|
|
957
|
-
from: "../generated/http-client",
|
|
958
|
-
typeOnly: useTypeOnlyImports,
|
|
959
|
-
})
|
|
960
|
-
: "";
|
|
961
|
-
|
|
962
|
-
const content = `
|
|
963
|
-
${runtimeValueImports}
|
|
964
|
-
|
|
965
|
-
${defaultFunctionImports}
|
|
966
|
-
|
|
967
|
-
${runtimeTypeImports}
|
|
968
|
-
|
|
969
|
-
${generatedValueImports}
|
|
970
|
-
|
|
971
|
-
${generatedTypeImports}
|
|
972
|
-
|
|
973
|
-
/* =======================
|
|
974
|
-
Query Types
|
|
975
|
-
======================= */
|
|
976
|
-
${queryTypes}
|
|
977
|
-
|
|
978
|
-
/* =======================
|
|
979
|
-
API Instance
|
|
980
|
-
======================= */
|
|
981
|
-
const ${instanceName} = new ${className}();
|
|
982
|
-
|
|
983
|
-
/* =======================
|
|
984
|
-
Paginated Query Methods
|
|
985
|
-
======================= */
|
|
986
|
-
${paginatedMethodWrappers}
|
|
987
|
-
|
|
988
|
-
/* =======================
|
|
989
|
-
Simple Query Methods
|
|
990
|
-
======================= */
|
|
991
|
-
${simpleQueryMethodWrappers}
|
|
992
|
-
|
|
993
|
-
/* =======================
|
|
994
|
-
Non-Query Methods
|
|
995
|
-
======================= */
|
|
996
|
-
${nonQueryMethodWrappers}
|
|
997
|
-
`.trim();
|
|
998
|
-
|
|
999
|
-
const outputFile = path.join(methodsDir, `${className}.api.ts`);
|
|
1000
|
-
|
|
1001
|
-
await writeFormattedFile(outputFile, content);
|
|
1002
|
-
|
|
1003
|
-
methodExports.push(`export * from "./methods/${className}.api";`);
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
const apiIndexContent = `
|
|
1007
|
-
export * from "./generated/data-contracts";
|
|
1008
|
-
export * from "./generated/http-client";
|
|
1009
|
-
${methodExports.join("\n")}
|
|
1010
|
-
`.trim();
|
|
1011
|
-
|
|
1012
|
-
await writeFormattedFile(path.join(apiRoot, "index.ts"), apiIndexContent);
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
async function fixGeneratedMixedImports(apiDir) {
|
|
1016
|
-
const tsFiles = getAllTsFiles(apiDir);
|
|
1017
|
-
|
|
1018
|
-
for (const filePath of tsFiles) {
|
|
1019
|
-
if (
|
|
1020
|
-
filePath.endsWith("http-client.ts") ||
|
|
1021
|
-
filePath.endsWith("data-contracts.ts")
|
|
1022
|
-
) {
|
|
1023
|
-
continue;
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
const originalContent = fs.readFileSync(filePath, "utf8");
|
|
1027
|
-
let content = originalContent;
|
|
1028
|
-
|
|
1029
|
-
content = content.replace(
|
|
1030
|
-
/import\s*\{([\s\S]*?)\}\s*from\s*["']\.\/http-client["'];/g,
|
|
1031
|
-
(_match, importsText) => {
|
|
1032
|
-
const importedNames = importsText
|
|
1033
|
-
.split(",")
|
|
1034
|
-
.map((name) => name.trim())
|
|
1035
|
-
.filter(Boolean);
|
|
1036
|
-
|
|
1037
|
-
const typeNames = importedNames.filter(
|
|
1038
|
-
(name) => name === "RequestParams",
|
|
1039
|
-
);
|
|
1040
|
-
|
|
1041
|
-
const valueNames = importedNames.filter(
|
|
1042
|
-
(name) => name !== "RequestParams",
|
|
1043
|
-
);
|
|
1044
|
-
|
|
1045
|
-
const statements = [];
|
|
1046
|
-
|
|
1047
|
-
if (valueNames.length > 0) {
|
|
1048
|
-
statements.push(
|
|
1049
|
-
`import { ${valueNames.join(", ")} } from "./http-client";`,
|
|
1050
|
-
);
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
if (typeNames.length > 0 && useTypeOnlyImports) {
|
|
1054
|
-
statements.push(
|
|
1055
|
-
`import type { ${typeNames.join(", ")} } from "./http-client";`,
|
|
1056
|
-
);
|
|
1057
|
-
} else if (typeNames.length > 0) {
|
|
1058
|
-
statements.push(
|
|
1059
|
-
`import { ${typeNames.join(", ")} } from "./http-client";`,
|
|
1060
|
-
);
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
return statements.join("\n");
|
|
1064
|
-
},
|
|
1065
|
-
);
|
|
1066
|
-
|
|
1067
|
-
if (useTypeOnlyImports) {
|
|
1068
|
-
content = content.replace(
|
|
1069
|
-
/import\s*\{([\s\S]*?)\}\s*from\s*["']\.\/data-contracts["'];/g,
|
|
1070
|
-
(_match, importsText) =>
|
|
1071
|
-
`import type {${importsText}} from "./data-contracts";`,
|
|
1072
|
-
);
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
if (content !== originalContent) {
|
|
1076
|
-
await writeFormattedFile(filePath, content);
|
|
1077
|
-
|
|
1078
|
-
console.log(
|
|
1079
|
-
`Fixed mixed imports in ${path.relative(process.cwd(), filePath)}`,
|
|
1080
|
-
);
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
450
|
async function main() {
|
|
1086
451
|
const swaggerInput = await resolveSwaggerInput();
|
|
1087
452
|
|
|
1088
453
|
console.log(`Generating API from: ${swaggerInput.label}`);
|
|
1089
454
|
console.log(`Output folder: ${apiRoot}`);
|
|
455
|
+
console.log(
|
|
456
|
+
"Generator: local typedapi OpenAPI generator, function-only methods",
|
|
457
|
+
);
|
|
1090
458
|
|
|
1091
|
-
await
|
|
1092
|
-
|
|
1093
|
-
|
|
459
|
+
const result = await generateApi({
|
|
460
|
+
input: swaggerInput.input,
|
|
461
|
+
output: apiRoot,
|
|
462
|
+
...generatorOptions,
|
|
463
|
+
defaultHandlers: resolveDefaultHandlers(),
|
|
464
|
+
});
|
|
1094
465
|
|
|
1095
|
-
|
|
466
|
+
for (const file of result.files) {
|
|
467
|
+
console.log(`Generated ${path.relative(cwd, file)}`);
|
|
468
|
+
}
|
|
1096
469
|
|
|
1097
470
|
console.log("API generation completed.");
|
|
1098
471
|
}
|