@bitstack/ng-query-codegen-openapi 0.0.30 → 0.0.31-alpha.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/README.md +8 -1
- package/lib/bin/cli.mjs +78 -60
- package/lib/bin/cli.mjs.map +1 -1
- package/lib/index.d.mts +4 -13
- package/lib/index.d.ts +4 -13
- package/lib/index.js +351 -290
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +351 -290
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/bin/utils.ts +74 -3
- package/src/generators/common-types-generator.ts +6 -2
- package/src/generators/component-schema-generator.ts +40 -7
- package/src/generators/do-not-modify-generator.ts +2 -2
- package/src/generators/rtk-enhance-endpoints-generator.ts +113 -0
- package/src/generators/{query-service-generator.ts → rtk-query-generator.ts} +45 -33
- package/src/generators/tag-types-generator.ts +30 -0
- package/src/generators/types-generator.ts +216 -112
- package/src/generators/utils-generator.ts +2 -4
- package/src/index.ts +6 -3
- package/src/services/api-code-generator.ts +62 -74
- package/src/services/endpoint-info-extractor.ts +66 -14
- package/src/services/file-writer-service.ts +27 -23
- package/src/services/openapi-parser-service.ts +10 -2
- package/src/services/unified-code-generator.ts +55 -26
- package/src/types.ts +19 -40
- package/src/generators/api-service-generator.ts +0 -112
- package/src/generators/cache-keys-generator.ts +0 -43
- package/src/generators/index-generator.ts +0 -11
- package/src/services/api-service-generator.ts +0 -24
- package/src/services/query-code-generator.ts +0 -24
package/lib/index.mjs
CHANGED
|
@@ -311,6 +311,13 @@ var FileWriterService = class {
|
|
|
311
311
|
async writeFile(filePath, content) {
|
|
312
312
|
try {
|
|
313
313
|
const resolvedPath = path3.resolve(process.cwd(), filePath);
|
|
314
|
+
const fileName = path3.basename(resolvedPath);
|
|
315
|
+
if (fileName === "enhanceEndpoints.ts" && fs3.existsSync(resolvedPath)) {
|
|
316
|
+
return {
|
|
317
|
+
path: resolvedPath,
|
|
318
|
+
success: true
|
|
319
|
+
};
|
|
320
|
+
}
|
|
314
321
|
await ensureDirectoryExists(resolvedPath);
|
|
315
322
|
fs3.writeFileSync(resolvedPath, content);
|
|
316
323
|
return {
|
|
@@ -338,7 +345,7 @@ var FileWriterService = class {
|
|
|
338
345
|
return results;
|
|
339
346
|
}
|
|
340
347
|
/**
|
|
341
|
-
*
|
|
348
|
+
* 為群組寫入 RTK Query 檔案結構
|
|
342
349
|
* @param groupOutputDir - 群組輸出目錄
|
|
343
350
|
* @param files - 檔案內容
|
|
344
351
|
*/
|
|
@@ -347,11 +354,11 @@ var FileWriterService = class {
|
|
|
347
354
|
if (files.types) {
|
|
348
355
|
filesToWrite[path3.join(groupOutputDir, "types.ts")] = files.types;
|
|
349
356
|
}
|
|
350
|
-
if (files.apiService) {
|
|
351
|
-
filesToWrite[path3.join(groupOutputDir, "api.service.ts")] = files.apiService;
|
|
352
|
-
}
|
|
353
357
|
if (files.queryService) {
|
|
354
|
-
filesToWrite[path3.join(groupOutputDir, "query.
|
|
358
|
+
filesToWrite[path3.join(groupOutputDir, "query.generated.ts")] = files.queryService;
|
|
359
|
+
}
|
|
360
|
+
if (files.enhanceEndpoints) {
|
|
361
|
+
filesToWrite[path3.join(groupOutputDir, "enhanceEndpoints.ts")] = files.enhanceEndpoints;
|
|
355
362
|
}
|
|
356
363
|
if (files.index) {
|
|
357
364
|
filesToWrite[path3.join(groupOutputDir, "index.ts")] = files.index;
|
|
@@ -368,9 +375,6 @@ var FileWriterService = class {
|
|
|
368
375
|
if (sharedFiles.commonTypes) {
|
|
369
376
|
filesToWrite[path3.join(outputDir, "common-types.ts")] = sharedFiles.commonTypes;
|
|
370
377
|
}
|
|
371
|
-
if (sharedFiles.cacheKeys) {
|
|
372
|
-
filesToWrite[path3.join(outputDir, "cache-keys.ts")] = sharedFiles.cacheKeys;
|
|
373
|
-
}
|
|
374
378
|
if (sharedFiles.doNotModify) {
|
|
375
379
|
filesToWrite[path3.join(outputDir, "DO_NOT_MODIFY.md")] = sharedFiles.doNotModify;
|
|
376
380
|
}
|
|
@@ -399,7 +403,6 @@ var OpenApiParserService = class {
|
|
|
399
403
|
this.v3Doc = v3Doc;
|
|
400
404
|
this.apiGen = new ApiGenerator(v3Doc, {
|
|
401
405
|
unionUndefined: options.unionUndefined,
|
|
402
|
-
useEnumType: options.useEnumType,
|
|
403
406
|
mergeReadWriteOnly: options.mergeReadWriteOnly
|
|
404
407
|
});
|
|
405
408
|
}
|
|
@@ -409,6 +412,12 @@ var OpenApiParserService = class {
|
|
|
409
412
|
*/
|
|
410
413
|
initialize() {
|
|
411
414
|
if (this.apiGen.spec.components?.schemas) {
|
|
415
|
+
Object.keys(this.apiGen.spec.components.schemas).forEach((schemaName) => {
|
|
416
|
+
const schema = this.apiGen.spec.components.schemas[schemaName];
|
|
417
|
+
if (schema && typeof schema === "object" && "title" in schema) {
|
|
418
|
+
delete schema.title;
|
|
419
|
+
}
|
|
420
|
+
});
|
|
412
421
|
this.apiGen.preprocessComponents(this.apiGen.spec.components.schemas);
|
|
413
422
|
Object.keys(this.apiGen.spec.components.schemas).forEach((schemaName) => {
|
|
414
423
|
try {
|
|
@@ -447,64 +456,6 @@ var OpenApiParserService = class {
|
|
|
447
456
|
}
|
|
448
457
|
};
|
|
449
458
|
|
|
450
|
-
// src/generators/cache-keys-generator.ts
|
|
451
|
-
init_esm_shims();
|
|
452
|
-
|
|
453
|
-
// src/generators/utils-generator.ts
|
|
454
|
-
init_esm_shims();
|
|
455
|
-
function generateUtilsFile() {
|
|
456
|
-
return `/* eslint-disable */
|
|
457
|
-
// [Warning] Generated automatically - do not edit manually
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Clear undefined in object
|
|
461
|
-
*/
|
|
462
|
-
export function withoutUndefined(obj?: Record<string, any>) {
|
|
463
|
-
if(typeof obj === 'undefined') return;
|
|
464
|
-
return Object.fromEntries(
|
|
465
|
-
Object.entries(obj).filter(([_, v]) => v !== undefined && v !== null)
|
|
466
|
-
);
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
`;
|
|
470
|
-
}
|
|
471
|
-
function toCamelCase(str) {
|
|
472
|
-
return str.toLowerCase().replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// src/generators/cache-keys-generator.ts
|
|
476
|
-
function generateCacheKeysFile(allEndpointInfos) {
|
|
477
|
-
const groupedKeys = allEndpointInfos.reduce((acc, info) => {
|
|
478
|
-
if (!acc[info.groupKey]) {
|
|
479
|
-
acc[info.groupKey] = [];
|
|
480
|
-
}
|
|
481
|
-
acc[info.groupKey].push({
|
|
482
|
-
operationName: info.operationName,
|
|
483
|
-
queryKeyName: info.queryKeyName
|
|
484
|
-
});
|
|
485
|
-
return acc;
|
|
486
|
-
}, {});
|
|
487
|
-
const enumEntries = Object.entries(groupedKeys).map(([groupKey, keys]) => {
|
|
488
|
-
const groupComment = ` // ${groupKey.charAt(0).toUpperCase() + groupKey.slice(1)} related cache keys`;
|
|
489
|
-
const keyEntries = keys.map(
|
|
490
|
-
(key) => ` ${key.queryKeyName} = '${toCamelCase(key.queryKeyName)}',`
|
|
491
|
-
).join("\n");
|
|
492
|
-
return `${groupComment}
|
|
493
|
-
${keyEntries}`;
|
|
494
|
-
}).join("\n\n");
|
|
495
|
-
return `// [Warning] Generated automatically - do not edit manually
|
|
496
|
-
|
|
497
|
-
/**
|
|
498
|
-
* Cache keys enum for all API queries
|
|
499
|
-
*/
|
|
500
|
-
export enum ECacheKeys {
|
|
501
|
-
${enumEntries}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
export default ECacheKeys;
|
|
505
|
-
`;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
459
|
// src/generators/common-types-generator.ts
|
|
509
460
|
init_esm_shims();
|
|
510
461
|
function generateCommonTypesFile() {
|
|
@@ -525,11 +476,15 @@ export interface QueryConfig {
|
|
|
525
476
|
keepUnusedDataFor?: number,
|
|
526
477
|
}
|
|
527
478
|
|
|
479
|
+
export interface IRequestConfig extends RequestOptions {
|
|
480
|
+
timeout?: number;
|
|
481
|
+
}
|
|
482
|
+
|
|
528
483
|
export type IRestFulEndpointsQueryReturn<TVariables> = TVariables extends void ?
|
|
529
|
-
void | {fetchOptions?:
|
|
484
|
+
(void | {fetchOptions?: IRequestConfig;}):
|
|
530
485
|
{
|
|
531
486
|
variables: TVariables;
|
|
532
|
-
fetchOptions?:
|
|
487
|
+
fetchOptions?: IRequestConfig;
|
|
533
488
|
config?: QueryConfig;
|
|
534
489
|
};
|
|
535
490
|
`;
|
|
@@ -538,6 +493,19 @@ export type IRestFulEndpointsQueryReturn<TVariables> = TVariables extends void ?
|
|
|
538
493
|
// src/generators/component-schema-generator.ts
|
|
539
494
|
init_esm_shims();
|
|
540
495
|
import ts from "typescript";
|
|
496
|
+
function toPascalCase(name) {
|
|
497
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
498
|
+
}
|
|
499
|
+
function renameIdentifier(node, oldName, newName) {
|
|
500
|
+
return ts.transform(node, [
|
|
501
|
+
(context) => (rootNode) => ts.visitNode(rootNode, function visit(node2) {
|
|
502
|
+
if (ts.isIdentifier(node2) && node2.text === oldName) {
|
|
503
|
+
return ts.factory.createIdentifier(newName);
|
|
504
|
+
}
|
|
505
|
+
return ts.visitEachChild(node2, visit, context);
|
|
506
|
+
})
|
|
507
|
+
]).transformed[0];
|
|
508
|
+
}
|
|
541
509
|
function generateComponentSchemaFile(interfaces) {
|
|
542
510
|
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
543
511
|
const resultFile = ts.createSourceFile(
|
|
@@ -547,10 +515,19 @@ function generateComponentSchemaFile(interfaces) {
|
|
|
547
515
|
false,
|
|
548
516
|
ts.ScriptKind.TS
|
|
549
517
|
);
|
|
518
|
+
const renamedInterfaces = [];
|
|
519
|
+
const typeNameMapping = {};
|
|
520
|
+
Object.entries(interfaces).forEach(([originalName, node]) => {
|
|
521
|
+
const pascalCaseName = toPascalCase(originalName);
|
|
522
|
+
typeNameMapping[originalName] = pascalCaseName;
|
|
523
|
+
const renamedNode = renameIdentifier(node, originalName, pascalCaseName);
|
|
524
|
+
const printed = printer.printNode(ts.EmitHint.Unspecified, renamedNode, resultFile);
|
|
525
|
+
renamedInterfaces.push(printed);
|
|
526
|
+
});
|
|
550
527
|
return `/* eslint-disable */
|
|
551
|
-
// [Warning] Generated automatically - do not edit manually
|
|
552
|
-
|
|
553
|
-
${
|
|
528
|
+
// [Warning] Generated automatically - do not edit manually
|
|
529
|
+
|
|
530
|
+
${renamedInterfaces.join("\n")}
|
|
554
531
|
`;
|
|
555
532
|
}
|
|
556
533
|
|
|
@@ -606,7 +583,13 @@ var EndpointInfoExtractor = class {
|
|
|
606
583
|
*/
|
|
607
584
|
extractSingleEndpointInfo(operationDefinition) {
|
|
608
585
|
const { verb, path: path5, operation } = operationDefinition;
|
|
609
|
-
const {
|
|
586
|
+
const {
|
|
587
|
+
operationNameSuffix = "",
|
|
588
|
+
argSuffix = "Req",
|
|
589
|
+
responseSuffix = "Res",
|
|
590
|
+
queryMatch,
|
|
591
|
+
endpointOverrides
|
|
592
|
+
} = this.options;
|
|
610
593
|
const operationName = getOperationName({ verb, path: path5 });
|
|
611
594
|
const finalOperationName = operationNameSuffix ? capitalize(operationName + operationNameSuffix) : operationName;
|
|
612
595
|
const argTypeName = capitalize(operationName + operationNameSuffix + argSuffix);
|
|
@@ -614,7 +597,9 @@ var EndpointInfoExtractor = class {
|
|
|
614
597
|
const isQuery2 = isQuery(verb, path5, getOverrides(operationDefinition, endpointOverrides), queryMatch);
|
|
615
598
|
const queryKeyName = `${operationName.replace(/([A-Z])/g, "_$1").toUpperCase()}`;
|
|
616
599
|
const summary = operation.summary || `${verb.toUpperCase()} ${path5}`;
|
|
617
|
-
const { queryParams, pathParams, isVoidArg } = this.extractParameters(operationDefinition);
|
|
600
|
+
const { queryParams, pathParams, isVoidArg, hasRequestBody } = this.extractParameters(operationDefinition);
|
|
601
|
+
const contentType = this.extractContentType(operation);
|
|
602
|
+
const tags = Array.isArray(operation.tags) ? operation.tags : [];
|
|
618
603
|
return {
|
|
619
604
|
operationName: finalOperationName,
|
|
620
605
|
argTypeName,
|
|
@@ -626,7 +611,10 @@ var EndpointInfoExtractor = class {
|
|
|
626
611
|
queryParams,
|
|
627
612
|
pathParams,
|
|
628
613
|
isVoidArg,
|
|
629
|
-
summary
|
|
614
|
+
summary,
|
|
615
|
+
contentType,
|
|
616
|
+
hasRequestBody,
|
|
617
|
+
tags
|
|
630
618
|
};
|
|
631
619
|
}
|
|
632
620
|
/**
|
|
@@ -636,15 +624,21 @@ var EndpointInfoExtractor = class {
|
|
|
636
624
|
extractParameters(operationDefinition) {
|
|
637
625
|
const { operation, pathItem } = operationDefinition;
|
|
638
626
|
const operationParameters = this.resolveArray(operation.parameters);
|
|
639
|
-
const pathItemParameters = this.resolveArray(pathItem.parameters).filter(
|
|
640
|
-
|
|
627
|
+
const pathItemParameters = this.resolveArray(pathItem.parameters).filter(
|
|
628
|
+
(pp) => !operationParameters.some((op) => op.name === pp.name && op.in === pp.in)
|
|
629
|
+
);
|
|
630
|
+
const allParameters = supportDeepObjects([...pathItemParameters, ...operationParameters]).filter(
|
|
631
|
+
(param) => param.in !== "header"
|
|
632
|
+
);
|
|
641
633
|
const queryParams = allParameters.filter((param) => param.in === "query");
|
|
642
634
|
const pathParams = allParameters.filter((param) => param.in === "path");
|
|
635
|
+
const hasRequestBody = !!operation.requestBody;
|
|
643
636
|
const isVoidArg = queryParams.length === 0 && pathParams.length === 0 && !operation.requestBody;
|
|
644
637
|
return {
|
|
645
638
|
queryParams,
|
|
646
639
|
pathParams,
|
|
647
|
-
isVoidArg
|
|
640
|
+
isVoidArg,
|
|
641
|
+
hasRequestBody
|
|
648
642
|
};
|
|
649
643
|
}
|
|
650
644
|
/**
|
|
@@ -654,11 +648,46 @@ var EndpointInfoExtractor = class {
|
|
|
654
648
|
if (!parameters) return [];
|
|
655
649
|
return Array.isArray(parameters) ? parameters : [parameters];
|
|
656
650
|
}
|
|
651
|
+
/**
|
|
652
|
+
* 提取操作的 content type
|
|
653
|
+
* @param operation - 操作對象
|
|
654
|
+
*/
|
|
655
|
+
extractContentType(operation) {
|
|
656
|
+
if (!operation.requestBody) {
|
|
657
|
+
return "application/json";
|
|
658
|
+
}
|
|
659
|
+
const content = operation.requestBody.content;
|
|
660
|
+
if (!content || typeof content !== "object") {
|
|
661
|
+
return "application/json";
|
|
662
|
+
}
|
|
663
|
+
const contentTypes = Object.keys(content);
|
|
664
|
+
if (contentTypes.length === 0) {
|
|
665
|
+
return "application/json";
|
|
666
|
+
}
|
|
667
|
+
return contentTypes[0];
|
|
668
|
+
}
|
|
657
669
|
};
|
|
658
670
|
|
|
659
671
|
// src/generators/types-generator.ts
|
|
660
672
|
init_esm_shims();
|
|
673
|
+
var toPascalCase2 = (name) => {
|
|
674
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
675
|
+
};
|
|
661
676
|
function generateTypesFile(endpointInfos, _options, schemaInterfaces, operationDefinitions) {
|
|
677
|
+
const schemaTypeMap = {};
|
|
678
|
+
if (schemaInterfaces) {
|
|
679
|
+
Object.keys(schemaInterfaces).forEach((actualTypeName) => {
|
|
680
|
+
schemaTypeMap[actualTypeName] = actualTypeName;
|
|
681
|
+
if (actualTypeName.endsWith("Vo")) {
|
|
682
|
+
const openApiName = actualTypeName.slice(0, -2) + "VO";
|
|
683
|
+
schemaTypeMap[openApiName] = actualTypeName;
|
|
684
|
+
}
|
|
685
|
+
if (actualTypeName.endsWith("Dto")) {
|
|
686
|
+
const openApiName = actualTypeName.slice(0, -3) + "DTO";
|
|
687
|
+
schemaTypeMap[openApiName] = actualTypeName;
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
}
|
|
662
691
|
let importStatement = `/* eslint-disable */
|
|
663
692
|
// [Warning] Generated automatically - do not edit manually
|
|
664
693
|
|
|
@@ -675,35 +704,19 @@ function generateTypesFile(endpointInfos, _options, schemaInterfaces, operationD
|
|
|
675
704
|
const reqTypeName = endpoint.argTypeName;
|
|
676
705
|
const resTypeName = endpoint.responseTypeName;
|
|
677
706
|
if (reqTypeName) {
|
|
678
|
-
const requestTypeContent = generateRequestTypeContent(endpoint, operationDefinitions);
|
|
679
|
-
if (requestTypeContent.trim() === ""
|
|
680
|
-
endpointTypes.push(
|
|
681
|
-
`export type ${reqTypeName} = void;`,
|
|
682
|
-
``
|
|
683
|
-
);
|
|
707
|
+
const requestTypeContent = generateRequestTypeContent(endpoint, operationDefinitions, schemaTypeMap);
|
|
708
|
+
if (requestTypeContent.trim() === "") {
|
|
709
|
+
endpointTypes.push(`export type ${reqTypeName} = void;`, ``);
|
|
684
710
|
} else {
|
|
685
|
-
endpointTypes.push(
|
|
686
|
-
`export type ${reqTypeName} = {`,
|
|
687
|
-
requestTypeContent,
|
|
688
|
-
`};`,
|
|
689
|
-
``
|
|
690
|
-
);
|
|
711
|
+
endpointTypes.push(`export type ${reqTypeName} = {`, requestTypeContent, `};`, ``);
|
|
691
712
|
}
|
|
692
713
|
}
|
|
693
714
|
if (resTypeName) {
|
|
694
|
-
const responseTypeContent = generateResponseTypeContent(endpoint, operationDefinitions);
|
|
695
|
-
if (responseTypeContent.trim() === ""
|
|
696
|
-
endpointTypes.push(
|
|
697
|
-
`export type ${resTypeName} = void;`,
|
|
698
|
-
``
|
|
699
|
-
);
|
|
715
|
+
const responseTypeContent = generateResponseTypeContent(endpoint, operationDefinitions, schemaTypeMap);
|
|
716
|
+
if (responseTypeContent.trim() === "") {
|
|
717
|
+
endpointTypes.push(`export type ${resTypeName} = void;`, ``);
|
|
700
718
|
} else {
|
|
701
|
-
endpointTypes.push(
|
|
702
|
-
`export type ${resTypeName} = {`,
|
|
703
|
-
responseTypeContent,
|
|
704
|
-
`};`,
|
|
705
|
-
``
|
|
706
|
-
);
|
|
719
|
+
endpointTypes.push(`export type ${resTypeName} = {`, responseTypeContent, `};`, ``);
|
|
707
720
|
}
|
|
708
721
|
}
|
|
709
722
|
});
|
|
@@ -711,27 +724,23 @@ function generateTypesFile(endpointInfos, _options, schemaInterfaces, operationD
|
|
|
711
724
|
typeDefinitions.push(endpointTypes.join("\n"));
|
|
712
725
|
}
|
|
713
726
|
if (typeDefinitions.length === 0) {
|
|
714
|
-
typeDefinitions.push(
|
|
715
|
-
`// \u6B64\u6A94\u6848\u7528\u65BC\u5B9A\u7FA9 API \u76F8\u95DC\u7684\u985E\u578B`,
|
|
716
|
-
`// \u985E\u578B\u5B9A\u7FA9\u6703\u6839\u64DA OpenAPI Schema \u81EA\u52D5\u751F\u6210`,
|
|
717
|
-
``
|
|
718
|
-
);
|
|
727
|
+
typeDefinitions.push(`// \u6B64\u6A94\u6848\u7528\u65BC\u5B9A\u7FA9 API \u76F8\u95DC\u7684\u985E\u578B`, `// \u985E\u578B\u5B9A\u7FA9\u6703\u6839\u64DA OpenAPI Schema \u81EA\u52D5\u751F\u6210`, ``);
|
|
719
728
|
}
|
|
720
729
|
return importStatement + typeDefinitions.join("\n\n");
|
|
721
730
|
}
|
|
722
|
-
function generateRequestTypeContent(endpoint, operationDefinitions) {
|
|
731
|
+
function generateRequestTypeContent(endpoint, operationDefinitions, schemaTypeMap = {}) {
|
|
723
732
|
const properties = [];
|
|
724
733
|
if (endpoint.queryParams && endpoint.queryParams.length > 0) {
|
|
725
734
|
endpoint.queryParams.forEach((param) => {
|
|
726
735
|
const optional = param.required ? "" : "?";
|
|
727
|
-
const paramType = getTypeFromParameter(param);
|
|
736
|
+
const paramType = getTypeFromParameter(param, schemaTypeMap);
|
|
728
737
|
properties.push(` ${param.name}${optional}: ${paramType};`);
|
|
729
738
|
});
|
|
730
739
|
}
|
|
731
740
|
if (endpoint.pathParams && endpoint.pathParams.length > 0) {
|
|
732
741
|
endpoint.pathParams.forEach((param) => {
|
|
733
742
|
const optional = param.required ? "" : "?";
|
|
734
|
-
const paramType = getTypeFromParameter(param);
|
|
743
|
+
const paramType = getTypeFromParameter(param, schemaTypeMap);
|
|
735
744
|
properties.push(` ${param.name}${optional}: ${paramType};`);
|
|
736
745
|
});
|
|
737
746
|
}
|
|
@@ -742,16 +751,22 @@ function generateRequestTypeContent(endpoint, operationDefinitions) {
|
|
|
742
751
|
if (operationDef?.operation?.requestBody) {
|
|
743
752
|
const requestBody = operationDef.operation.requestBody;
|
|
744
753
|
const content = requestBody.content;
|
|
745
|
-
const jsonContent = content["application/json"];
|
|
754
|
+
const jsonContent = content["application/json"] || content["*/*"];
|
|
746
755
|
const formContent = content["multipart/form-data"] || content["application/x-www-form-urlencoded"];
|
|
747
756
|
if (jsonContent?.schema) {
|
|
748
|
-
const bodyType = getTypeFromSchema(jsonContent.schema);
|
|
757
|
+
const bodyType = getTypeFromSchema(jsonContent.schema, schemaTypeMap, 1);
|
|
749
758
|
properties.push(` body: ${bodyType};`);
|
|
750
759
|
} else if (formContent?.schema) {
|
|
751
|
-
const bodyType = getTypeFromSchema(formContent.schema);
|
|
760
|
+
const bodyType = getTypeFromSchema(formContent.schema, schemaTypeMap, 1);
|
|
752
761
|
properties.push(` body: ${bodyType};`);
|
|
753
762
|
} else {
|
|
754
|
-
|
|
763
|
+
const firstContent = Object.values(content)[0];
|
|
764
|
+
if (firstContent?.schema) {
|
|
765
|
+
const bodyType = getTypeFromSchema(firstContent.schema, schemaTypeMap, 1);
|
|
766
|
+
properties.push(` body: ${bodyType};`);
|
|
767
|
+
} else {
|
|
768
|
+
properties.push(` body?: any; // Request body from OpenAPI`);
|
|
769
|
+
}
|
|
755
770
|
}
|
|
756
771
|
}
|
|
757
772
|
if (properties.length === 0) {
|
|
@@ -759,7 +774,7 @@ function generateRequestTypeContent(endpoint, operationDefinitions) {
|
|
|
759
774
|
}
|
|
760
775
|
return properties.join("\n");
|
|
761
776
|
}
|
|
762
|
-
function generateResponseTypeContent(endpoint, operationDefinitions) {
|
|
777
|
+
function generateResponseTypeContent(endpoint, operationDefinitions, schemaTypeMap = {}) {
|
|
763
778
|
const properties = [];
|
|
764
779
|
const operationDef = operationDefinitions?.find((op) => {
|
|
765
780
|
return op.operation?.operationId === endpoint.operationName || op.operation?.operationId === endpoint.operationName.toLowerCase() || // 也嘗試匹配 verb + path 組合
|
|
@@ -768,9 +783,9 @@ function generateResponseTypeContent(endpoint, operationDefinitions) {
|
|
|
768
783
|
if (operationDef?.operation?.responses) {
|
|
769
784
|
const successResponse = operationDef.operation.responses["200"] || operationDef.operation.responses["201"];
|
|
770
785
|
if (successResponse?.content) {
|
|
771
|
-
const jsonContent = successResponse.content["application/json"];
|
|
786
|
+
const jsonContent = successResponse.content["application/json"] || successResponse.content["*/*"] || Object.values(successResponse.content)[0];
|
|
772
787
|
if (jsonContent?.schema) {
|
|
773
|
-
const responseProps = parseSchemaProperties(jsonContent.schema);
|
|
788
|
+
const responseProps = parseSchemaProperties(jsonContent.schema, schemaTypeMap);
|
|
774
789
|
properties.push(...responseProps);
|
|
775
790
|
} else {
|
|
776
791
|
properties.push(` // Success response from OpenAPI`);
|
|
@@ -783,83 +798,114 @@ function generateResponseTypeContent(endpoint, operationDefinitions) {
|
|
|
783
798
|
}
|
|
784
799
|
return properties.join("\n");
|
|
785
800
|
}
|
|
786
|
-
function parseSchemaProperties(schema) {
|
|
801
|
+
function parseSchemaProperties(schema, schemaTypeMap = {}) {
|
|
787
802
|
const properties = [];
|
|
788
803
|
if (schema.type === "object" && schema.properties) {
|
|
789
804
|
const required = schema.required || [];
|
|
790
805
|
Object.entries(schema.properties).forEach(([propName, propSchema]) => {
|
|
791
806
|
const isRequired = required.includes(propName);
|
|
792
807
|
const optional = isRequired ? "" : "?";
|
|
793
|
-
const propType = getTypeFromSchema(propSchema);
|
|
794
|
-
const
|
|
795
|
-
|
|
808
|
+
const propType = getTypeFromSchema(propSchema, schemaTypeMap, 1);
|
|
809
|
+
const needsQuotes = /[^a-zA-Z0-9_$]/.test(propName);
|
|
810
|
+
const quotedPropName = needsQuotes ? `"${propName}"` : propName;
|
|
811
|
+
if (propSchema.description) {
|
|
812
|
+
properties.push(` /** ${propSchema.description} */`);
|
|
813
|
+
}
|
|
814
|
+
properties.push(` ${quotedPropName}${optional}: ${propType};`);
|
|
796
815
|
});
|
|
797
816
|
}
|
|
798
817
|
return properties;
|
|
799
818
|
}
|
|
800
|
-
function getTypeFromSchema(schema) {
|
|
819
|
+
function getTypeFromSchema(schema, schemaTypeMap = {}, indentLevel = 0) {
|
|
801
820
|
if (!schema) return "any";
|
|
802
821
|
if (schema.$ref) {
|
|
803
822
|
const refPath = schema.$ref;
|
|
804
823
|
if (refPath.startsWith("#/components/schemas/")) {
|
|
805
|
-
const
|
|
806
|
-
|
|
824
|
+
const originalTypeName = refPath.replace("#/components/schemas/", "");
|
|
825
|
+
const actualTypeName = schemaTypeMap[originalTypeName] || originalTypeName;
|
|
826
|
+
const pascalCaseTypeName = toPascalCase2(actualTypeName);
|
|
827
|
+
const baseType2 = `Schema.${pascalCaseTypeName}`;
|
|
828
|
+
return schema.nullable ? `${baseType2} | null` : baseType2;
|
|
807
829
|
}
|
|
808
830
|
}
|
|
831
|
+
let baseType;
|
|
809
832
|
switch (schema.type) {
|
|
810
833
|
case "string":
|
|
811
834
|
if (schema.enum) {
|
|
812
|
-
|
|
835
|
+
baseType = schema.enum.map((val) => `"${val}"`).join(" | ");
|
|
836
|
+
} else if (schema.format === "binary") {
|
|
837
|
+
baseType = "Blob";
|
|
838
|
+
} else {
|
|
839
|
+
baseType = "string";
|
|
813
840
|
}
|
|
814
|
-
|
|
841
|
+
break;
|
|
815
842
|
case "number":
|
|
816
843
|
case "integer":
|
|
817
|
-
|
|
844
|
+
baseType = "number";
|
|
845
|
+
break;
|
|
818
846
|
case "boolean":
|
|
819
|
-
|
|
847
|
+
baseType = "boolean";
|
|
848
|
+
break;
|
|
820
849
|
case "array":
|
|
821
|
-
const itemType = schema.items ? getTypeFromSchema(schema.items) : "any";
|
|
822
|
-
|
|
850
|
+
const itemType = schema.items ? getTypeFromSchema(schema.items, schemaTypeMap, indentLevel) : "any";
|
|
851
|
+
const needsParentheses = itemType.includes("|");
|
|
852
|
+
baseType = needsParentheses ? `(${itemType})[]` : `${itemType}[]`;
|
|
853
|
+
break;
|
|
823
854
|
case "object":
|
|
824
855
|
if (schema.properties) {
|
|
825
|
-
const
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
856
|
+
const entries = Object.entries(schema.properties);
|
|
857
|
+
if (entries.length === 0) {
|
|
858
|
+
if (schema.additionalProperties) {
|
|
859
|
+
const valueType = schema.additionalProperties === true ? "any" : getTypeFromSchema(schema.additionalProperties, schemaTypeMap, indentLevel);
|
|
860
|
+
baseType = `Record<string, ${valueType}>`;
|
|
861
|
+
} else {
|
|
862
|
+
baseType = "{}";
|
|
863
|
+
}
|
|
864
|
+
} else {
|
|
865
|
+
const nextIndent = " ".repeat(indentLevel + 1);
|
|
866
|
+
const currentIndent = " ".repeat(indentLevel);
|
|
867
|
+
const props = [];
|
|
868
|
+
entries.forEach(([key, propSchema]) => {
|
|
869
|
+
const required = schema.required || [];
|
|
870
|
+
const optional = required.includes(key) ? "" : "?";
|
|
871
|
+
const type = getTypeFromSchema(propSchema, schemaTypeMap, indentLevel + 1);
|
|
872
|
+
const needsQuotes = /[^a-zA-Z0-9_$]/.test(key);
|
|
873
|
+
const quotedKey = needsQuotes ? `"${key}"` : key;
|
|
874
|
+
if (propSchema.description) {
|
|
875
|
+
props.push(`${nextIndent}/** ${propSchema.description} */`);
|
|
876
|
+
}
|
|
877
|
+
props.push(`${nextIndent}${quotedKey}${optional}: ${type};`);
|
|
878
|
+
});
|
|
879
|
+
baseType = `{
|
|
880
|
+
${props.join("\n")}
|
|
881
|
+
${currentIndent}}`;
|
|
882
|
+
}
|
|
883
|
+
} else if (schema.additionalProperties) {
|
|
884
|
+
const valueType = schema.additionalProperties === true ? "any" : getTypeFromSchema(schema.additionalProperties, schemaTypeMap, indentLevel);
|
|
885
|
+
baseType = `Record<string, ${valueType}>`;
|
|
886
|
+
} else {
|
|
887
|
+
baseType = "any";
|
|
832
888
|
}
|
|
833
|
-
|
|
889
|
+
break;
|
|
834
890
|
default:
|
|
835
|
-
|
|
891
|
+
baseType = "any";
|
|
892
|
+
break;
|
|
836
893
|
}
|
|
894
|
+
return schema.nullable ? `${baseType} | null` : baseType;
|
|
837
895
|
}
|
|
838
|
-
function getTypeFromParameter(param) {
|
|
896
|
+
function getTypeFromParameter(param, schemaTypeMap = {}) {
|
|
839
897
|
if (!param.schema) return "any";
|
|
840
|
-
return getTypeFromSchema(param.schema);
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
// src/generators/index-generator.ts
|
|
844
|
-
init_esm_shims();
|
|
845
|
-
function generateIndexFile(groupKey, options) {
|
|
846
|
-
const { groupKey: optionsGroupKey } = options;
|
|
847
|
-
return `export * from './types';
|
|
848
|
-
export * from './api.service';
|
|
849
|
-
export * from './query.service';
|
|
850
|
-
`;
|
|
898
|
+
return getTypeFromSchema(param.schema, schemaTypeMap);
|
|
851
899
|
}
|
|
852
900
|
|
|
853
|
-
// src/
|
|
901
|
+
// src/generators/rtk-query-generator.ts
|
|
854
902
|
init_esm_shims();
|
|
855
|
-
|
|
856
|
-
// src/generators/query-service-generator.ts
|
|
857
|
-
init_esm_shims();
|
|
858
|
-
function generateQueryServiceFile(endpointInfos, options) {
|
|
903
|
+
function generateRtkQueryFile(endpointInfos, options) {
|
|
859
904
|
const { groupKey, refetchOnMountOrArgChange = 60 } = options;
|
|
860
905
|
const queryServiceName = groupKey ? `${groupKey.charAt(0).toUpperCase() + groupKey.slice(1)}QueryService` : "QueryService";
|
|
861
906
|
const apiServiceName = groupKey ? `${groupKey.charAt(0).toUpperCase() + groupKey.slice(1)}ApiService` : "ApiService";
|
|
862
|
-
const queryMethods = endpointInfos.filter((info) => info.isQuery).map(
|
|
907
|
+
const queryMethods = endpointInfos.filter((info) => info.isQuery).map(
|
|
908
|
+
(info) => `
|
|
863
909
|
/**
|
|
864
910
|
* ${info.summary}
|
|
865
911
|
*/
|
|
@@ -872,8 +918,10 @@ function generateQueryServiceFile(endpointInfos, options) {
|
|
|
872
918
|
fetchOptions: args?.fetchOptions,
|
|
873
919
|
config: args?.config,
|
|
874
920
|
})${info.argTypeName !== "VoidApiArg" ? "(args)" : "()"};
|
|
875
|
-
}`
|
|
876
|
-
|
|
921
|
+
}`
|
|
922
|
+
).join("");
|
|
923
|
+
const mutationMethods = endpointInfos.filter((info) => !info.isQuery).map(
|
|
924
|
+
(info) => `
|
|
877
925
|
/**
|
|
878
926
|
* ${info.summary}
|
|
879
927
|
*/
|
|
@@ -887,8 +935,10 @@ function generateQueryServiceFile(endpointInfos, options) {
|
|
|
887
935
|
return this.apiService.${info.operationName}(${info.argTypeName !== "VoidApiArg" ? "args" : ""});
|
|
888
936
|
},
|
|
889
937
|
});
|
|
890
|
-
}`
|
|
891
|
-
|
|
938
|
+
}`
|
|
939
|
+
).join("");
|
|
940
|
+
const lazyQueryMethods = endpointInfos.filter((info) => info.isQuery).map(
|
|
941
|
+
(info) => `
|
|
892
942
|
/**
|
|
893
943
|
* ${info.summary}
|
|
894
944
|
*/
|
|
@@ -907,7 +957,8 @@ function generateQueryServiceFile(endpointInfos, options) {
|
|
|
907
957
|
return this.apiService.${info.operationName}(${info.argTypeName !== "VoidApiArg" ? "args" : ""});
|
|
908
958
|
}
|
|
909
959
|
};
|
|
910
|
-
}`
|
|
960
|
+
}`
|
|
961
|
+
).join("");
|
|
911
962
|
return `/* eslint-disable */
|
|
912
963
|
// [Warning] Generated automatically - do not edit manually
|
|
913
964
|
|
|
@@ -931,38 +982,15 @@ ${queryMethods}${mutationMethods}${lazyQueryMethods}
|
|
|
931
982
|
`;
|
|
932
983
|
}
|
|
933
984
|
|
|
934
|
-
// src/
|
|
935
|
-
var QueryCodeGenerator = class {
|
|
936
|
-
constructor(options) {
|
|
937
|
-
this.options = options;
|
|
938
|
-
}
|
|
939
|
-
/**
|
|
940
|
-
* 生成 Query Service 檔案內容
|
|
941
|
-
*/
|
|
942
|
-
generateQueryService(endpointInfos) {
|
|
943
|
-
const generatorOptions = {
|
|
944
|
-
...this.options,
|
|
945
|
-
apiConfiguration: this.options.apiConfiguration || {
|
|
946
|
-
file: "@/store/webapi",
|
|
947
|
-
importName: "WebApiConfiguration"
|
|
948
|
-
}
|
|
949
|
-
};
|
|
950
|
-
return generateQueryServiceFile(endpointInfos, generatorOptions);
|
|
951
|
-
}
|
|
952
|
-
};
|
|
953
|
-
|
|
954
|
-
// src/services/api-service-generator.ts
|
|
955
|
-
init_esm_shims();
|
|
956
|
-
|
|
957
|
-
// src/generators/api-service-generator.ts
|
|
985
|
+
// src/generators/rtk-enhance-endpoints-generator.ts
|
|
958
986
|
init_esm_shims();
|
|
959
|
-
function
|
|
987
|
+
function generateRtkEnhanceEndpointsFile(endpointInfos, options) {
|
|
960
988
|
const { apiConfiguration, httpClient, groupKey } = options;
|
|
961
989
|
const apiServiceClassName = groupKey ? `${groupKey.charAt(0).toUpperCase() + groupKey.slice(1)}ApiService` : "ApiService";
|
|
962
990
|
return `/* eslint-disable */
|
|
963
991
|
// [Warning] Generated automatically - do not edit manually
|
|
964
992
|
|
|
965
|
-
import { ${httpClient.
|
|
993
|
+
import { ${httpClient.importReturnTypeName} } from '${httpClient.file}';
|
|
966
994
|
import { Injectable, inject } from '@angular/core';
|
|
967
995
|
import { Observable } from 'rxjs';
|
|
968
996
|
import { ${apiConfiguration.importName} } from '${apiConfiguration.file}';
|
|
@@ -975,11 +1003,12 @@ import {${endpointInfos.map((info) => ` ${info.argTypeName}, ${info.responseType
|
|
|
975
1003
|
})
|
|
976
1004
|
export class ${apiServiceClassName} {
|
|
977
1005
|
private config = inject(${apiConfiguration.importName});
|
|
978
|
-
private http = inject(${httpClient.
|
|
1006
|
+
private http = inject(${httpClient.importReturnTypeName});
|
|
979
1007
|
|
|
980
1008
|
${endpointInfos.map((info) => {
|
|
981
1009
|
const isGet = info.verb.toUpperCase() === "GET";
|
|
982
1010
|
const hasArgs = info.argTypeName !== "VoidApiArg";
|
|
1011
|
+
const hasRequestBody = info.hasRequestBody;
|
|
983
1012
|
const generateUrlTemplate = (path5) => {
|
|
984
1013
|
const hasPathParams = path5.includes("{");
|
|
985
1014
|
if (hasPathParams && hasArgs) {
|
|
@@ -990,21 +1019,18 @@ ${endpointInfos.map((info) => {
|
|
|
990
1019
|
}
|
|
991
1020
|
};
|
|
992
1021
|
const hasQueryParams = info.queryParams.length > 0;
|
|
993
|
-
const hasBody = !isGet && hasArgs
|
|
1022
|
+
const hasBody = !isGet && hasArgs;
|
|
994
1023
|
const generateRequestOptions = () => {
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
options2.push(`headers: { 'Content-Type': 'application/json', ...args.fetchOptions?.headers }`);
|
|
1000
|
-
}
|
|
1001
|
-
if (hasQueryParams && hasArgs) {
|
|
1002
|
-
options2.push(generateQueryParams(info.queryParams));
|
|
1024
|
+
let paramsSection = "";
|
|
1025
|
+
if (info.queryParams && info.queryParams.length > 0) {
|
|
1026
|
+
const paramsLines = info.queryParams.map((param) => `${param.name}: args.variables.${param.name},`).join("\n");
|
|
1027
|
+
paramsSection = `params: {${paramsLines}},`;
|
|
1003
1028
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1029
|
+
return `{
|
|
1030
|
+
...args.fetchOptions,
|
|
1031
|
+
headers: { 'Content-Type': '${info.contentType}', ...args.fetchOptions?.headers },
|
|
1032
|
+
${paramsSection}${info.hasRequestBody ? `body: args.variables.body,` : ""}
|
|
1033
|
+
}`;
|
|
1008
1034
|
};
|
|
1009
1035
|
return `
|
|
1010
1036
|
/**
|
|
@@ -1020,32 +1046,6 @@ ${endpointInfos.map((info) => {
|
|
|
1020
1046
|
}
|
|
1021
1047
|
`;
|
|
1022
1048
|
}
|
|
1023
|
-
function generateQueryParams(queryParams) {
|
|
1024
|
-
const paramEntries = queryParams.map(
|
|
1025
|
-
(param) => `${param.name}: args.variables.${param.name}`
|
|
1026
|
-
).join(", ");
|
|
1027
|
-
return `params: withoutUndefined({ ${paramEntries} })`;
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
// src/services/api-service-generator.ts
|
|
1031
|
-
var ApiServiceGenerator = class {
|
|
1032
|
-
constructor(options) {
|
|
1033
|
-
this.options = options;
|
|
1034
|
-
}
|
|
1035
|
-
/**
|
|
1036
|
-
* 生成 API Service 檔案內容
|
|
1037
|
-
*/
|
|
1038
|
-
generateApiService(endpointInfos) {
|
|
1039
|
-
const generatorOptions = {
|
|
1040
|
-
...this.options,
|
|
1041
|
-
apiConfiguration: this.options.apiConfiguration || {
|
|
1042
|
-
file: "@/store/webapi",
|
|
1043
|
-
importName: "WebApiConfiguration"
|
|
1044
|
-
}
|
|
1045
|
-
};
|
|
1046
|
-
return generateApiServiceFile(endpointInfos, generatorOptions);
|
|
1047
|
-
}
|
|
1048
|
-
};
|
|
1049
1049
|
|
|
1050
1050
|
// src/services/api-code-generator.ts
|
|
1051
1051
|
var ApiCodeGenerator = class {
|
|
@@ -1053,36 +1053,41 @@ var ApiCodeGenerator = class {
|
|
|
1053
1053
|
this.parserService = parserService;
|
|
1054
1054
|
this.options = options;
|
|
1055
1055
|
this.infoExtractor = new EndpointInfoExtractor(options);
|
|
1056
|
-
this.queryGenerator = new QueryCodeGenerator(options);
|
|
1057
|
-
this.apiServiceGenerator = new ApiServiceGenerator(options);
|
|
1058
1056
|
}
|
|
1059
1057
|
infoExtractor;
|
|
1060
|
-
queryGenerator;
|
|
1061
|
-
apiServiceGenerator;
|
|
1062
1058
|
/**
|
|
1063
|
-
* 生成完整的
|
|
1059
|
+
* 生成完整的 RTK Query 程式碼
|
|
1064
1060
|
*/
|
|
1065
1061
|
async generate() {
|
|
1066
1062
|
const operationDefinitions = this.parserService.getOperationDefinitions(this.options.filterEndpoints);
|
|
1067
1063
|
const endpointInfos = this.infoExtractor.extractEndpointInfos(operationDefinitions);
|
|
1064
|
+
return this.generateRtkQueryCode(endpointInfos);
|
|
1065
|
+
}
|
|
1066
|
+
/**
|
|
1067
|
+
* 生成 RTK Query 代碼
|
|
1068
|
+
*/
|
|
1069
|
+
generateRtkQueryCode(endpointInfos) {
|
|
1068
1070
|
const typesContent = this.generateTypes(endpointInfos);
|
|
1069
|
-
const
|
|
1070
|
-
const
|
|
1071
|
+
const rtkQueryContent = generateRtkQueryFile(endpointInfos, this.options);
|
|
1072
|
+
const enhanceEndpointsContent = generateRtkEnhanceEndpointsFile(endpointInfos, this.options);
|
|
1071
1073
|
const indexContent = this.generateIndex();
|
|
1072
|
-
const allEndpointCacheKeys = endpointInfos.filter((info) => info.isQuery).map((info) => ({
|
|
1073
|
-
operationName: info.operationName,
|
|
1074
|
-
queryKeyName: info.queryKeyName,
|
|
1075
|
-
groupKey: this.options.groupKey || "_common"
|
|
1076
|
-
}));
|
|
1077
1074
|
const operationNames = endpointInfos.map((info) => info.operationName);
|
|
1075
|
+
const allTags = /* @__PURE__ */ new Set();
|
|
1076
|
+
endpointInfos.forEach((info) => {
|
|
1077
|
+
if (info.tags && Array.isArray(info.tags)) {
|
|
1078
|
+
info.tags.forEach((tag) => allTags.add(tag));
|
|
1079
|
+
}
|
|
1080
|
+
});
|
|
1078
1081
|
return {
|
|
1079
1082
|
operationNames,
|
|
1083
|
+
tags: Array.from(allTags),
|
|
1080
1084
|
files: {
|
|
1081
1085
|
types: typesContent,
|
|
1082
|
-
|
|
1083
|
-
|
|
1086
|
+
queryService: rtkQueryContent,
|
|
1087
|
+
// RTK Query 檔案
|
|
1084
1088
|
index: indexContent,
|
|
1085
|
-
|
|
1089
|
+
enhanceEndpoints: enhanceEndpointsContent
|
|
1090
|
+
// 新增的 enhance endpoints 檔案
|
|
1086
1091
|
}
|
|
1087
1092
|
};
|
|
1088
1093
|
}
|
|
@@ -1098,43 +1103,35 @@ var ApiCodeGenerator = class {
|
|
|
1098
1103
|
}
|
|
1099
1104
|
};
|
|
1100
1105
|
const apiGen = this.parserService.getApiGenerator();
|
|
1101
|
-
const schemaInterfaces = apiGen.aliases.reduce(
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1106
|
+
const schemaInterfaces = apiGen.aliases.reduce(
|
|
1107
|
+
(curr, alias) => {
|
|
1108
|
+
if (ts2.isInterfaceDeclaration(alias) || ts2.isTypeAliasDeclaration(alias)) {
|
|
1109
|
+
const name = alias.name.text;
|
|
1110
|
+
return {
|
|
1111
|
+
...curr,
|
|
1112
|
+
[name]: alias
|
|
1113
|
+
};
|
|
1114
|
+
}
|
|
1115
|
+
return curr;
|
|
1116
|
+
},
|
|
1117
|
+
{}
|
|
1118
|
+
);
|
|
1111
1119
|
const operationDefinitions = this.parserService.getOperationDefinitions(this.options.filterEndpoints);
|
|
1112
1120
|
return generateTypesFile(endpointInfos, generatorOptions, schemaInterfaces, operationDefinitions);
|
|
1113
1121
|
}
|
|
1114
|
-
/**
|
|
1115
|
-
* 生成 API Service 檔案內容
|
|
1116
|
-
*/
|
|
1117
|
-
generateApiService(endpointInfos) {
|
|
1118
|
-
return this.apiServiceGenerator.generateApiService(endpointInfos);
|
|
1119
|
-
}
|
|
1120
|
-
/**
|
|
1121
|
-
* 生成 Query Service 檔案內容
|
|
1122
|
-
*/
|
|
1123
|
-
generateQueryService(endpointInfos) {
|
|
1124
|
-
return this.queryGenerator.generateQueryService(endpointInfos);
|
|
1125
|
-
}
|
|
1126
1122
|
/**
|
|
1127
1123
|
* 生成 Index 檔案內容
|
|
1128
1124
|
*/
|
|
1129
1125
|
generateIndex() {
|
|
1130
|
-
const
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1126
|
+
const groupKey = this.options.groupKey || "";
|
|
1127
|
+
const exportName = groupKey ? `${groupKey.charAt(0).toLowerCase() + groupKey.slice(1)}Api` : "api";
|
|
1128
|
+
return `/* eslint-disable */
|
|
1129
|
+
// [Warning] Generated automatically - do not edit manually
|
|
1130
|
+
|
|
1131
|
+
export { default as ${exportName} } from "./enhanceEndpoints";
|
|
1132
|
+
export * from "./query.generated";
|
|
1133
|
+
export * from "./types";
|
|
1134
|
+
`;
|
|
1138
1135
|
}
|
|
1139
1136
|
// /**
|
|
1140
1137
|
// * 獲取已解析的 parser service(供外部使用)
|
|
@@ -1151,6 +1148,46 @@ var ApiCodeGenerator = class {
|
|
|
1151
1148
|
// }
|
|
1152
1149
|
};
|
|
1153
1150
|
|
|
1151
|
+
// src/generators/utils-generator.ts
|
|
1152
|
+
init_esm_shims();
|
|
1153
|
+
function generateUtilsFile() {
|
|
1154
|
+
return `/* eslint-disable */
|
|
1155
|
+
// [Warning] Generated automatically - do not edit manually
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
* Clear undefined in object
|
|
1159
|
+
*/
|
|
1160
|
+
export function withoutUndefined(obj?: Record<string, any>) {
|
|
1161
|
+
if(typeof obj === 'undefined') return;
|
|
1162
|
+
return Object.fromEntries(
|
|
1163
|
+
Object.entries(obj).filter(([_, v]) => v !== undefined && v !== null)
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
`;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
// src/generators/tag-types-generator.ts
|
|
1171
|
+
init_esm_shims();
|
|
1172
|
+
function generateTagTypesFile(tags) {
|
|
1173
|
+
if (tags.length === 0) {
|
|
1174
|
+
return `/* eslint-disable */
|
|
1175
|
+
// [Warning] Generated automatically - do not edit manually
|
|
1176
|
+
|
|
1177
|
+
export enum ECacheTagTypes {
|
|
1178
|
+
}
|
|
1179
|
+
`;
|
|
1180
|
+
}
|
|
1181
|
+
const enumEntries = tags.sort().map((tag) => ` ${tag} = '${tag}',`).join("\n");
|
|
1182
|
+
return `/* eslint-disable */
|
|
1183
|
+
// [Warning] Generated automatically - do not edit manually
|
|
1184
|
+
|
|
1185
|
+
export enum ECacheTagTypes {
|
|
1186
|
+
${enumEntries}
|
|
1187
|
+
}
|
|
1188
|
+
`;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1154
1191
|
// src/services/unified-code-generator.ts
|
|
1155
1192
|
var UnifiedCodeGenerator = class {
|
|
1156
1193
|
_options;
|
|
@@ -1161,17 +1198,18 @@ var UnifiedCodeGenerator = class {
|
|
|
1161
1198
|
openApiDoc = null;
|
|
1162
1199
|
parserService = null;
|
|
1163
1200
|
schemaInterfaces = {};
|
|
1164
|
-
allEndpointCacheKeys = [];
|
|
1165
1201
|
actualSchemaFile = "";
|
|
1166
1202
|
// 生成內容存儲
|
|
1167
1203
|
generatedContent = {
|
|
1168
1204
|
groups: [],
|
|
1169
|
-
cacheKeys: null,
|
|
1170
1205
|
commonTypes: "",
|
|
1171
1206
|
componentSchema: "",
|
|
1172
1207
|
doNotModify: "",
|
|
1173
|
-
utils: ""
|
|
1208
|
+
utils: "",
|
|
1209
|
+
tagTypes: ""
|
|
1174
1210
|
};
|
|
1211
|
+
// 收集所有 tags
|
|
1212
|
+
allTags = /* @__PURE__ */ new Set();
|
|
1175
1213
|
constructor(options) {
|
|
1176
1214
|
this._options = options;
|
|
1177
1215
|
}
|
|
@@ -1181,11 +1219,11 @@ var UnifiedCodeGenerator = class {
|
|
|
1181
1219
|
async generateAll() {
|
|
1182
1220
|
await this.prepare();
|
|
1183
1221
|
await this.generateApi();
|
|
1184
|
-
this.generateCacheKeysContent();
|
|
1185
1222
|
this.generateCommonTypesContent();
|
|
1186
1223
|
this.generateSchemaContent();
|
|
1187
1224
|
this.generateUtilsContent();
|
|
1188
1225
|
this.generateDoNotModifyContent();
|
|
1226
|
+
this.generateTagTypesContent();
|
|
1189
1227
|
return await this.release();
|
|
1190
1228
|
}
|
|
1191
1229
|
/**
|
|
@@ -1238,18 +1276,15 @@ var UnifiedCodeGenerator = class {
|
|
|
1238
1276
|
outputPath: groupInfo.outputPath,
|
|
1239
1277
|
content: groupContent
|
|
1240
1278
|
});
|
|
1279
|
+
if (groupContent.tags && Array.isArray(groupContent.tags)) {
|
|
1280
|
+
groupContent.tags.forEach((tag) => this.allTags.add(tag));
|
|
1281
|
+
}
|
|
1241
1282
|
}
|
|
1242
1283
|
} catch (error) {
|
|
1243
1284
|
throw new Error(`\u7FA4\u7D44 ${groupInfo.groupKey} \u751F\u6210\u5931\u6557: ${error}`);
|
|
1244
1285
|
}
|
|
1245
1286
|
}
|
|
1246
1287
|
}
|
|
1247
|
-
/**
|
|
1248
|
-
* 生成 cache keys
|
|
1249
|
-
*/
|
|
1250
|
-
async generateCacheKeysContent() {
|
|
1251
|
-
this.generatedContent.cacheKeys = generateCacheKeysFile(this.allEndpointCacheKeys);
|
|
1252
|
-
}
|
|
1253
1288
|
/**
|
|
1254
1289
|
* 生成 common types
|
|
1255
1290
|
*/
|
|
@@ -1274,6 +1309,13 @@ var UnifiedCodeGenerator = class {
|
|
|
1274
1309
|
async generateUtilsContent() {
|
|
1275
1310
|
this.generatedContent.utils = generateUtilsFile();
|
|
1276
1311
|
}
|
|
1312
|
+
/**
|
|
1313
|
+
* 生成 Tag Types
|
|
1314
|
+
*/
|
|
1315
|
+
async generateTagTypesContent() {
|
|
1316
|
+
const tagsArray = Array.from(this.allTags);
|
|
1317
|
+
this.generatedContent.tagTypes = generateTagTypesFile(tagsArray);
|
|
1318
|
+
}
|
|
1277
1319
|
/**
|
|
1278
1320
|
* 發佈階段:統一寫入所有檔案
|
|
1279
1321
|
*/
|
|
@@ -1290,8 +1332,8 @@ var UnifiedCodeGenerator = class {
|
|
|
1290
1332
|
groupOutputDir,
|
|
1291
1333
|
{
|
|
1292
1334
|
types: group.content.files.types,
|
|
1293
|
-
apiService: group.content.files.apiService,
|
|
1294
1335
|
queryService: group.content.files.queryService,
|
|
1336
|
+
enhanceEndpoints: group.content.files.enhanceEndpoints,
|
|
1295
1337
|
index: group.content.files.index
|
|
1296
1338
|
}
|
|
1297
1339
|
);
|
|
@@ -1303,11 +1345,10 @@ var UnifiedCodeGenerator = class {
|
|
|
1303
1345
|
}
|
|
1304
1346
|
}
|
|
1305
1347
|
const outputDir = this.generatedContent.groups[0] ? path4.dirname(path4.dirname(this.generatedContent.groups[0].outputPath)) : "./generated";
|
|
1306
|
-
if (this.generatedContent.
|
|
1348
|
+
if (this.generatedContent.commonTypes || this.generatedContent.doNotModify || this.generatedContent.utils) {
|
|
1307
1349
|
const sharedResults = await this.fileWriterService.writeSharedFiles(
|
|
1308
1350
|
outputDir,
|
|
1309
1351
|
{
|
|
1310
|
-
cacheKeys: this.generatedContent.cacheKeys || void 0,
|
|
1311
1352
|
commonTypes: this.generatedContent.commonTypes || void 0,
|
|
1312
1353
|
doNotModify: this.generatedContent.doNotModify || void 0,
|
|
1313
1354
|
utils: this.generatedContent.utils || void 0
|
|
@@ -1315,6 +1356,13 @@ var UnifiedCodeGenerator = class {
|
|
|
1315
1356
|
);
|
|
1316
1357
|
results.push(...sharedResults);
|
|
1317
1358
|
}
|
|
1359
|
+
if (this.generatedContent.tagTypes) {
|
|
1360
|
+
const tagTypesResult = await this.fileWriterService.writeFile(
|
|
1361
|
+
path4.join(outputDir, "tagTypes.ts"),
|
|
1362
|
+
this.generatedContent.tagTypes
|
|
1363
|
+
);
|
|
1364
|
+
results.push(tagTypesResult);
|
|
1365
|
+
}
|
|
1318
1366
|
if (this.generatedContent.componentSchema) {
|
|
1319
1367
|
const schemaResults = await this.fileWriterService.writeSchemaFile(
|
|
1320
1368
|
outputDir,
|
|
@@ -1322,6 +1370,12 @@ var UnifiedCodeGenerator = class {
|
|
|
1322
1370
|
);
|
|
1323
1371
|
results.push(...schemaResults);
|
|
1324
1372
|
}
|
|
1373
|
+
const mainIndexContent = this.generateMainIndex(generatedGroups);
|
|
1374
|
+
const mainIndexResult = await this.fileWriterService.writeFile(
|
|
1375
|
+
path4.join(outputDir, "index.ts"),
|
|
1376
|
+
mainIndexContent
|
|
1377
|
+
);
|
|
1378
|
+
results.push(mainIndexResult);
|
|
1325
1379
|
} catch (error) {
|
|
1326
1380
|
errors.push(error);
|
|
1327
1381
|
}
|
|
@@ -1351,12 +1405,19 @@ var UnifiedCodeGenerator = class {
|
|
|
1351
1405
|
}
|
|
1352
1406
|
const apiGenerator = new ApiCodeGenerator(this.parserService, groupOptions);
|
|
1353
1407
|
const result = await apiGenerator.generate();
|
|
1354
|
-
if (result.files && "allEndpointCacheKeys" in result.files) {
|
|
1355
|
-
const cacheKeys = result.files.allEndpointCacheKeys;
|
|
1356
|
-
this.allEndpointCacheKeys.push(...cacheKeys);
|
|
1357
|
-
}
|
|
1358
1408
|
return result;
|
|
1359
1409
|
}
|
|
1410
|
+
/**
|
|
1411
|
+
* 生成主 index.ts 檔案
|
|
1412
|
+
*/
|
|
1413
|
+
generateMainIndex(generatedGroups) {
|
|
1414
|
+
const exports = generatedGroups.map((groupKey) => `export * from "./${groupKey}";`).join("\n");
|
|
1415
|
+
return `/* eslint-disable */
|
|
1416
|
+
// [Warning] Generated automatically - do not edit manually
|
|
1417
|
+
|
|
1418
|
+
${exports}
|
|
1419
|
+
`;
|
|
1420
|
+
}
|
|
1360
1421
|
};
|
|
1361
1422
|
|
|
1362
1423
|
// src/index.ts
|