@acrool/rtk-query-codegen-openapi 0.0.2-test.5 → 0.0.2-test.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/lib/index.d.mts +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +111 -21
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +111 -21
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/generate.ts +128 -23
- package/src/types.ts +1 -0
package/src/generate.ts
CHANGED
|
@@ -117,6 +117,7 @@ export async function generateApi(
|
|
|
117
117
|
useEnumType = false,
|
|
118
118
|
mergeReadWriteOnly = false,
|
|
119
119
|
httpResolverOptions,
|
|
120
|
+
sharedTypesFile,
|
|
120
121
|
}: GenerationOptions
|
|
121
122
|
) {
|
|
122
123
|
const v3Doc = (v3DocCache[spec] ??= await getV3Doc(spec, httpResolverOptions));
|
|
@@ -127,6 +128,48 @@ export async function generateApi(
|
|
|
127
128
|
mergeReadWriteOnly,
|
|
128
129
|
});
|
|
129
130
|
|
|
131
|
+
// 如果提供了 sharedTypesFile,則將 components 輸出到該文件
|
|
132
|
+
if (sharedTypesFile) {
|
|
133
|
+
const components = v3Doc.components;
|
|
134
|
+
if (components) {
|
|
135
|
+
const resultFile = ts.createSourceFile(
|
|
136
|
+
'sharedTypes.ts',
|
|
137
|
+
'',
|
|
138
|
+
ts.ScriptTarget.Latest,
|
|
139
|
+
/*setParentNodes*/ false,
|
|
140
|
+
ts.ScriptKind.TS
|
|
141
|
+
);
|
|
142
|
+
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
143
|
+
|
|
144
|
+
// 將 components 轉換為 TypeScript 類型定義
|
|
145
|
+
const typeDefinitions = Object.entries(components).flatMap(([_, componentDefs]) => {
|
|
146
|
+
return Object.entries(componentDefs as Record<string, unknown>).map(([name, def]) => {
|
|
147
|
+
const typeNode = apiGen.getTypeFromSchema(def as OpenAPIV3.SchemaObject);
|
|
148
|
+
return factory.createTypeAliasDeclaration(
|
|
149
|
+
[factory.createModifier(ts.SyntaxKind.ExportKeyword)],
|
|
150
|
+
factory.createIdentifier(name),
|
|
151
|
+
undefined,
|
|
152
|
+
typeNode
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const output = printer.printNode(
|
|
158
|
+
ts.EmitHint.Unspecified,
|
|
159
|
+
factory.createSourceFile(
|
|
160
|
+
typeDefinitions,
|
|
161
|
+
factory.createToken(ts.SyntaxKind.EndOfFileToken),
|
|
162
|
+
ts.NodeFlags.None
|
|
163
|
+
),
|
|
164
|
+
resultFile
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// 寫入文件
|
|
168
|
+
const fs = await import('node:fs/promises');
|
|
169
|
+
await fs.writeFile(sharedTypesFile, output, 'utf-8');
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
130
173
|
// temporary workaround for https://github.com/oazapfts/oazapfts/issues/491
|
|
131
174
|
if (apiGen.spec.components?.schemas) {
|
|
132
175
|
apiGen.preprocessComponents(apiGen.spec.components.schemas);
|
|
@@ -160,8 +203,17 @@ export async function generateApi(
|
|
|
160
203
|
apiFile = apiFile.replace(/\\/g, '/');
|
|
161
204
|
if (!apiFile.startsWith('.')) apiFile = `./${apiFile}`;
|
|
162
205
|
}
|
|
206
|
+
// 處理 sharedTypesFile 的路徑
|
|
207
|
+
if (sharedTypesFile && sharedTypesFile.startsWith('.')) {
|
|
208
|
+
sharedTypesFile = path.relative(path.dirname(outputFile), sharedTypesFile);
|
|
209
|
+
sharedTypesFile = sharedTypesFile.replace(/\\/g, '/');
|
|
210
|
+
if (!sharedTypesFile.startsWith('.')) sharedTypesFile = `./${sharedTypesFile}`;
|
|
211
|
+
}
|
|
163
212
|
}
|
|
164
213
|
apiFile = apiFile.replace(/\.[jt]sx?$/, '');
|
|
214
|
+
if (sharedTypesFile) {
|
|
215
|
+
sharedTypesFile = sharedTypesFile.replace(/\.[jt]sx?$/, '');
|
|
216
|
+
}
|
|
165
217
|
|
|
166
218
|
return printer.printNode(
|
|
167
219
|
ts.EmitHint.Unspecified,
|
|
@@ -169,6 +221,18 @@ export async function generateApi(
|
|
|
169
221
|
[
|
|
170
222
|
generateImportNode(apiFile, { [apiImport]: 'api' }),
|
|
171
223
|
generateImportNode('@acrool/react-fetcher', { IRestFulEndpointsQueryReturn: 'IRestFulEndpointsQueryReturn' }),
|
|
224
|
+
...(sharedTypesFile ? [
|
|
225
|
+
factory.createImportDeclaration(
|
|
226
|
+
undefined,
|
|
227
|
+
factory.createImportClause(
|
|
228
|
+
false,
|
|
229
|
+
undefined,
|
|
230
|
+
factory.createNamespaceImport(factory.createIdentifier('SharedTypes'))
|
|
231
|
+
),
|
|
232
|
+
factory.createStringLiteral(sharedTypesFile),
|
|
233
|
+
undefined
|
|
234
|
+
)
|
|
235
|
+
] : []),
|
|
172
236
|
...(tag ? [generateTagTypes({ addTagTypes: extractAllTagTypes({ operationDefinitions }) })] : []),
|
|
173
237
|
generateCreateApiCall({
|
|
174
238
|
tag,
|
|
@@ -188,8 +252,7 @@ export async function generateApi(
|
|
|
188
252
|
factory.createIdentifier(generatedApiName)
|
|
189
253
|
),
|
|
190
254
|
...Object.values(interfaces),
|
|
191
|
-
...apiGen.aliases,
|
|
192
|
-
...apiGen.enumAliases,
|
|
255
|
+
...(sharedTypesFile ? [] : [...apiGen.aliases, ...apiGen.enumAliases]),
|
|
193
256
|
...(hooks
|
|
194
257
|
? [
|
|
195
258
|
generateReactHooks({
|
|
@@ -239,16 +302,54 @@ export async function generateApi(
|
|
|
239
302
|
|
|
240
303
|
const returnsJson = apiGen.getResponseType(responses) === 'json';
|
|
241
304
|
let ResponseType: ts.TypeNode = factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
305
|
+
|
|
306
|
+
function replaceReferences(schema: any): ts.TypeNode {
|
|
307
|
+
if (!schema) return factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
|
|
308
|
+
|
|
309
|
+
const refName = getReferenceName(schema);
|
|
310
|
+
if (refName && sharedTypesFile) {
|
|
311
|
+
return factory.createTypeReferenceNode(
|
|
312
|
+
factory.createQualifiedName(
|
|
313
|
+
factory.createIdentifier('SharedTypes'),
|
|
314
|
+
factory.createIdentifier(refName)
|
|
315
|
+
),
|
|
316
|
+
undefined
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (schema.type === 'object' && schema.properties) {
|
|
321
|
+
const members = Object.entries(schema.properties).map(([key, value]: [string, any]) => {
|
|
322
|
+
return factory.createPropertySignature(
|
|
323
|
+
undefined,
|
|
324
|
+
factory.createIdentifier(key),
|
|
325
|
+
schema.required?.includes(key) ? undefined : factory.createToken(ts.SyntaxKind.QuestionToken),
|
|
326
|
+
replaceReferences(value)
|
|
327
|
+
);
|
|
328
|
+
});
|
|
329
|
+
return factory.createTypeLiteralNode(members);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (schema.type === 'array' && schema.items) {
|
|
333
|
+
return factory.createArrayTypeNode(replaceReferences(schema.items));
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
return apiGen.getTypeFromSchema(schema);
|
|
337
|
+
}
|
|
338
|
+
|
|
242
339
|
if (returnsJson) {
|
|
243
340
|
const returnTypes = Object.entries(responses || {})
|
|
244
341
|
.map(
|
|
245
|
-
([code, response]) =>
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
342
|
+
([code, response]) => {
|
|
343
|
+
const resolvedResponse = apiGen.resolve(response);
|
|
344
|
+
if (!resolvedResponse.content?.['application/json']?.schema) {
|
|
345
|
+
return [code, resolvedResponse, factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword)] as const;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const schema = resolvedResponse.content['application/json'].schema;
|
|
349
|
+
const type = replaceReferences(schema);
|
|
350
|
+
|
|
351
|
+
return [code, resolvedResponse, type] as const;
|
|
352
|
+
}
|
|
252
353
|
)
|
|
253
354
|
.filter(([status, response]) =>
|
|
254
355
|
isDataResponse(status, includeDefault, apiGen.resolve(response), responses || {})
|
|
@@ -308,22 +409,11 @@ export async function generateApi(
|
|
|
308
409
|
return name;
|
|
309
410
|
}
|
|
310
411
|
|
|
311
|
-
for (const param of parameters) {
|
|
312
|
-
const name = generateName(param.name, param.in);
|
|
313
|
-
queryArg[name] = {
|
|
314
|
-
origin: 'param',
|
|
315
|
-
name,
|
|
316
|
-
originalName: param.name,
|
|
317
|
-
type: apiGen.getTypeFromSchema(isReference(param) ? param : param.schema, undefined, 'writeOnly'),
|
|
318
|
-
required: param.required,
|
|
319
|
-
param,
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
|
|
323
412
|
if (requestBody) {
|
|
324
413
|
const body = apiGen.resolve(requestBody);
|
|
325
414
|
const schema = apiGen.getSchemaFromContent(body.content);
|
|
326
|
-
const type =
|
|
415
|
+
const type = replaceReferences(schema);
|
|
416
|
+
|
|
327
417
|
const schemaName = camelCase(
|
|
328
418
|
(type as any).name ||
|
|
329
419
|
getReferenceName(schema) ||
|
|
@@ -336,12 +426,27 @@ export async function generateApi(
|
|
|
336
426
|
origin: 'body',
|
|
337
427
|
name,
|
|
338
428
|
originalName: schemaName,
|
|
339
|
-
type
|
|
429
|
+
type,
|
|
340
430
|
required: true,
|
|
341
431
|
body,
|
|
342
432
|
};
|
|
343
433
|
}
|
|
344
434
|
|
|
435
|
+
for (const param of parameters) {
|
|
436
|
+
const name = generateName(param.name, param.in);
|
|
437
|
+
const paramSchema = isReference(param) ? param : param.schema;
|
|
438
|
+
const type = replaceReferences(paramSchema);
|
|
439
|
+
|
|
440
|
+
queryArg[name] = {
|
|
441
|
+
origin: 'param',
|
|
442
|
+
name,
|
|
443
|
+
originalName: param.name,
|
|
444
|
+
type,
|
|
445
|
+
required: param.required,
|
|
446
|
+
param,
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
345
450
|
const propertyName = (name: string | ts.PropertyName): ts.PropertyName => {
|
|
346
451
|
if (typeof name === 'string') {
|
|
347
452
|
return isValidIdentifier(name) ? factory.createIdentifier(name) : factory.createStringLiteral(name);
|
package/src/types.ts
CHANGED
|
@@ -132,6 +132,7 @@ export interface OutputFileOptions extends Partial<CommonOptions> {
|
|
|
132
132
|
* If passed as true it will generate TS enums instead of union of strings
|
|
133
133
|
*/
|
|
134
134
|
useEnumType?: boolean;
|
|
135
|
+
sharedTypesFile?: string;
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
export type EndpointOverrides = {
|