@acrool/rtk-query-codegen-openapi 1.3.0 → 1.4.0-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/lib/bin/cli.mjs +49 -41
- package/lib/bin/cli.mjs.map +1 -1
- package/lib/index.js +353 -35
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +353 -35
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/generators/component-schema-generator.ts +50 -1
- package/src/generators/types-generator.ts +159 -32
- package/src/services/unified-code-generator.ts +100 -14
- package/src/utils/schema-ref-analyzer.ts +218 -0
package/lib/index.mjs
CHANGED
|
@@ -93,7 +93,7 @@ init_esm_shims();
|
|
|
93
93
|
// src/services/unified-code-generator.ts
|
|
94
94
|
init_esm_shims();
|
|
95
95
|
import path4 from "node:path";
|
|
96
|
-
import
|
|
96
|
+
import ts4 from "typescript";
|
|
97
97
|
|
|
98
98
|
// src/services/openapi-service.ts
|
|
99
99
|
init_esm_shims();
|
|
@@ -499,7 +499,28 @@ function renameIdentifier(node, oldName, newName) {
|
|
|
499
499
|
})
|
|
500
500
|
]).transformed[0];
|
|
501
501
|
}
|
|
502
|
-
function
|
|
502
|
+
function isStringEnumType(node) {
|
|
503
|
+
if (!ts.isUnionTypeNode(node.type)) return null;
|
|
504
|
+
const members = [];
|
|
505
|
+
for (const member of node.type.types) {
|
|
506
|
+
if (ts.isLiteralTypeNode(member) && ts.isStringLiteral(member.literal)) {
|
|
507
|
+
members.push(member.literal.text);
|
|
508
|
+
} else {
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return members.length > 0 ? members : null;
|
|
513
|
+
}
|
|
514
|
+
function generateEnumDeclaration(name, members) {
|
|
515
|
+
const enumMembers = members.map((value) => {
|
|
516
|
+
const key = value.charAt(0).toUpperCase() + value.slice(1);
|
|
517
|
+
return ` ${key} = "${value}"`;
|
|
518
|
+
});
|
|
519
|
+
return `export enum ${name} {
|
|
520
|
+
${enumMembers.join(",\n")}
|
|
521
|
+
}`;
|
|
522
|
+
}
|
|
523
|
+
function generateComponentSchemaFile(interfaces, includeOnly) {
|
|
503
524
|
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
504
525
|
const resultFile = ts.createSourceFile(
|
|
505
526
|
"component-schema.ts",
|
|
@@ -513,6 +534,16 @@ function generateComponentSchemaFile(interfaces) {
|
|
|
513
534
|
Object.entries(interfaces).forEach(([originalName, node]) => {
|
|
514
535
|
const pascalCaseName = toPascalCase(originalName);
|
|
515
536
|
typeNameMapping[originalName] = pascalCaseName;
|
|
537
|
+
if (includeOnly && !includeOnly.has(originalName)) {
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
if (ts.isTypeAliasDeclaration(node)) {
|
|
541
|
+
const enumMembers = isStringEnumType(node);
|
|
542
|
+
if (enumMembers) {
|
|
543
|
+
renamedInterfaces.push(generateEnumDeclaration(pascalCaseName, enumMembers));
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
516
547
|
const renamedNode = renameIdentifier(node, originalName, pascalCaseName);
|
|
517
548
|
const printed = printer.printNode(ts.EmitHint.Unspecified, renamedNode, resultFile);
|
|
518
549
|
renamedInterfaces.push(printed);
|
|
@@ -551,7 +582,7 @@ function generateDoNotModifyFile() {
|
|
|
551
582
|
|
|
552
583
|
// src/services/api-code-generator.ts
|
|
553
584
|
init_esm_shims();
|
|
554
|
-
import
|
|
585
|
+
import ts3 from "typescript";
|
|
555
586
|
|
|
556
587
|
// src/services/endpoint-info-extractor.ts
|
|
557
588
|
init_esm_shims();
|
|
@@ -653,10 +684,67 @@ var EndpointInfoExtractor = class {
|
|
|
653
684
|
|
|
654
685
|
// src/generators/types-generator.ts
|
|
655
686
|
init_esm_shims();
|
|
687
|
+
import ts2 from "typescript";
|
|
656
688
|
var toPascalCase2 = (name) => {
|
|
657
689
|
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
658
690
|
};
|
|
659
|
-
function
|
|
691
|
+
function renameIdentifier2(node, oldName, newName) {
|
|
692
|
+
return ts2.transform(node, [
|
|
693
|
+
(context) => (rootNode) => ts2.visitNode(rootNode, function visit(node2) {
|
|
694
|
+
if (ts2.isIdentifier(node2) && node2.text === oldName) {
|
|
695
|
+
return ts2.factory.createIdentifier(newName);
|
|
696
|
+
}
|
|
697
|
+
return ts2.visitEachChild(node2, visit, context);
|
|
698
|
+
})
|
|
699
|
+
]).transformed[0];
|
|
700
|
+
}
|
|
701
|
+
function prefixSharedSchemaRefs(node, allSchemaNames, localSchemaNames, declarationName) {
|
|
702
|
+
const sharedNames = /* @__PURE__ */ new Set();
|
|
703
|
+
for (const name of allSchemaNames) {
|
|
704
|
+
if (!localSchemaNames.has(name)) {
|
|
705
|
+
sharedNames.add(toPascalCase2(name));
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return ts2.transform(node, [
|
|
709
|
+
(context) => (rootNode) => ts2.visitNode(rootNode, function visit(node2) {
|
|
710
|
+
if (ts2.isTypeReferenceNode(node2) && ts2.isIdentifier(node2.typeName)) {
|
|
711
|
+
const name = node2.typeName.text;
|
|
712
|
+
if (sharedNames.has(name) && name !== declarationName) {
|
|
713
|
+
const qualifiedName = ts2.factory.createQualifiedName(
|
|
714
|
+
ts2.factory.createIdentifier("Schema"),
|
|
715
|
+
ts2.factory.createIdentifier(name)
|
|
716
|
+
);
|
|
717
|
+
return ts2.factory.createTypeReferenceNode(qualifiedName, node2.typeArguments);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return ts2.visitEachChild(node2, visit, context);
|
|
721
|
+
})
|
|
722
|
+
]).transformed[0];
|
|
723
|
+
}
|
|
724
|
+
function isStringEnumType2(node) {
|
|
725
|
+
if (!ts2.isUnionTypeNode(node.type)) return null;
|
|
726
|
+
const members = [];
|
|
727
|
+
for (const member of node.type.types) {
|
|
728
|
+
if (ts2.isLiteralTypeNode(member) && ts2.isStringLiteral(member.literal)) {
|
|
729
|
+
members.push(member.literal.text);
|
|
730
|
+
} else {
|
|
731
|
+
return null;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
return members.length > 0 ? members : null;
|
|
735
|
+
}
|
|
736
|
+
function generateEnumDeclaration2(name, members) {
|
|
737
|
+
const enumMembers = members.map((value) => {
|
|
738
|
+
const key = value.charAt(0).toUpperCase() + value.slice(1);
|
|
739
|
+
return ` ${key} = "${value}"`;
|
|
740
|
+
});
|
|
741
|
+
return `export enum ${name} {
|
|
742
|
+
${enumMembers.join(",\n")}
|
|
743
|
+
}`;
|
|
744
|
+
}
|
|
745
|
+
function generateTypesFile(endpointInfos, _options, schemaInterfaces, operationDefinitions, localSchemaOptions) {
|
|
746
|
+
const localSchemaNames = localSchemaOptions?.localSchemaNames ?? /* @__PURE__ */ new Set();
|
|
747
|
+
const localSchemaInterfaces = localSchemaOptions?.localSchemaInterfaces ?? {};
|
|
660
748
|
const schemaTypeMap = {};
|
|
661
749
|
if (schemaInterfaces) {
|
|
662
750
|
Object.keys(schemaInterfaces).forEach((actualTypeName) => {
|
|
@@ -671,23 +759,48 @@ function generateTypesFile(endpointInfos, _options, schemaInterfaces, operationD
|
|
|
671
759
|
}
|
|
672
760
|
});
|
|
673
761
|
}
|
|
762
|
+
const hasSharedSchemaTypes = schemaInterfaces && Object.keys(schemaInterfaces).some(
|
|
763
|
+
(name) => !localSchemaNames.has(name)
|
|
764
|
+
);
|
|
674
765
|
let importStatement = `/* eslint-disable */
|
|
675
|
-
// [Warning] Generated automatically - do not edit manually
|
|
676
|
-
|
|
766
|
+
// [Warning] Generated automatically - do not edit manually
|
|
767
|
+
|
|
677
768
|
`;
|
|
678
|
-
|
|
679
|
-
if (hasSchemaTypes) {
|
|
769
|
+
if (hasSharedSchemaTypes) {
|
|
680
770
|
importStatement += `import * as Schema from "../schema";
|
|
681
771
|
`;
|
|
682
772
|
}
|
|
683
773
|
importStatement += "\n";
|
|
684
774
|
const typeDefinitions = [];
|
|
775
|
+
if (Object.keys(localSchemaInterfaces).length > 0) {
|
|
776
|
+
const printer = ts2.createPrinter({ newLine: ts2.NewLineKind.LineFeed });
|
|
777
|
+
const resultFile = ts2.createSourceFile("types.ts", "", ts2.ScriptTarget.Latest, false, ts2.ScriptKind.TS);
|
|
778
|
+
const localTypeDefs = [];
|
|
779
|
+
const allSchemaNameSet = new Set(schemaInterfaces ? Object.keys(schemaInterfaces) : []);
|
|
780
|
+
for (const [originalName, node] of Object.entries(localSchemaInterfaces)) {
|
|
781
|
+
const pascalCaseName = toPascalCase2(originalName);
|
|
782
|
+
if (ts2.isTypeAliasDeclaration(node)) {
|
|
783
|
+
const enumMembers = isStringEnumType2(node);
|
|
784
|
+
if (enumMembers) {
|
|
785
|
+
localTypeDefs.push(generateEnumDeclaration2(pascalCaseName, enumMembers));
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
let transformedNode = renameIdentifier2(node, originalName, pascalCaseName);
|
|
790
|
+
transformedNode = prefixSharedSchemaRefs(transformedNode, allSchemaNameSet, localSchemaNames, pascalCaseName);
|
|
791
|
+
const printed = printer.printNode(ts2.EmitHint.Unspecified, transformedNode, resultFile);
|
|
792
|
+
localTypeDefs.push(printed);
|
|
793
|
+
}
|
|
794
|
+
if (localTypeDefs.length > 0) {
|
|
795
|
+
typeDefinitions.push(localTypeDefs.join("\n"));
|
|
796
|
+
}
|
|
797
|
+
}
|
|
685
798
|
const endpointTypes = [];
|
|
686
799
|
endpointInfos.forEach((endpoint) => {
|
|
687
800
|
const reqTypeName = endpoint.argTypeName;
|
|
688
801
|
const resTypeName = endpoint.responseTypeName;
|
|
689
802
|
if (reqTypeName) {
|
|
690
|
-
const requestTypeContent = generateRequestTypeContent(endpoint, operationDefinitions, schemaTypeMap);
|
|
803
|
+
const requestTypeContent = generateRequestTypeContent(endpoint, operationDefinitions, schemaTypeMap, localSchemaNames);
|
|
691
804
|
if (requestTypeContent.trim() === "") {
|
|
692
805
|
endpointTypes.push(
|
|
693
806
|
`export type ${reqTypeName} = void;`,
|
|
@@ -703,7 +816,7 @@ function generateTypesFile(endpointInfos, _options, schemaInterfaces, operationD
|
|
|
703
816
|
}
|
|
704
817
|
}
|
|
705
818
|
if (resTypeName) {
|
|
706
|
-
const responseTypeResult = generateResponseTypeContent(endpoint, operationDefinitions, schemaTypeMap);
|
|
819
|
+
const responseTypeResult = generateResponseTypeContent(endpoint, operationDefinitions, schemaTypeMap, localSchemaNames);
|
|
707
820
|
if (responseTypeResult.content.trim() === "") {
|
|
708
821
|
endpointTypes.push(
|
|
709
822
|
`export type ${resTypeName} = void;`,
|
|
@@ -736,19 +849,19 @@ function generateTypesFile(endpointInfos, _options, schemaInterfaces, operationD
|
|
|
736
849
|
}
|
|
737
850
|
return importStatement + typeDefinitions.join("\n\n");
|
|
738
851
|
}
|
|
739
|
-
function generateRequestTypeContent(endpoint, operationDefinitions, schemaTypeMap = {}) {
|
|
852
|
+
function generateRequestTypeContent(endpoint, operationDefinitions, schemaTypeMap = {}, localSchemaNames = /* @__PURE__ */ new Set()) {
|
|
740
853
|
const properties = [];
|
|
741
854
|
if (endpoint.queryParams && endpoint.queryParams.length > 0) {
|
|
742
855
|
endpoint.queryParams.forEach((param) => {
|
|
743
856
|
const optional = param.required ? "" : "?";
|
|
744
|
-
const paramType = getTypeFromParameter(param, schemaTypeMap);
|
|
857
|
+
const paramType = getTypeFromParameter(param, schemaTypeMap, localSchemaNames);
|
|
745
858
|
properties.push(` ${param.name}${optional}: ${paramType};`);
|
|
746
859
|
});
|
|
747
860
|
}
|
|
748
861
|
if (endpoint.pathParams && endpoint.pathParams.length > 0) {
|
|
749
862
|
endpoint.pathParams.forEach((param) => {
|
|
750
863
|
const optional = param.required ? "" : "?";
|
|
751
|
-
const paramType = getTypeFromParameter(param, schemaTypeMap);
|
|
864
|
+
const paramType = getTypeFromParameter(param, schemaTypeMap, localSchemaNames);
|
|
752
865
|
properties.push(` ${param.name}${optional}: ${paramType};`);
|
|
753
866
|
});
|
|
754
867
|
}
|
|
@@ -762,15 +875,15 @@ function generateRequestTypeContent(endpoint, operationDefinitions, schemaTypeMa
|
|
|
762
875
|
const jsonContent = content["application/json"] || content["*/*"];
|
|
763
876
|
const formContent = content["multipart/form-data"] || content["application/x-www-form-urlencoded"];
|
|
764
877
|
if (jsonContent?.schema) {
|
|
765
|
-
const bodyType = getTypeFromSchema(jsonContent.schema, schemaTypeMap, 1);
|
|
878
|
+
const bodyType = getTypeFromSchema(jsonContent.schema, schemaTypeMap, 1, localSchemaNames);
|
|
766
879
|
properties.push(` body: ${bodyType};`);
|
|
767
880
|
} else if (formContent?.schema) {
|
|
768
|
-
const bodyType = getTypeFromSchema(formContent.schema, schemaTypeMap, 1);
|
|
881
|
+
const bodyType = getTypeFromSchema(formContent.schema, schemaTypeMap, 1, localSchemaNames);
|
|
769
882
|
properties.push(` body: ${bodyType};`);
|
|
770
883
|
} else {
|
|
771
884
|
const firstContent = Object.values(content)[0];
|
|
772
885
|
if (firstContent?.schema) {
|
|
773
|
-
const bodyType = getTypeFromSchema(firstContent.schema, schemaTypeMap, 1);
|
|
886
|
+
const bodyType = getTypeFromSchema(firstContent.schema, schemaTypeMap, 1, localSchemaNames);
|
|
774
887
|
properties.push(` body: ${bodyType};`);
|
|
775
888
|
} else {
|
|
776
889
|
properties.push(` body?: any; // Request body from OpenAPI`);
|
|
@@ -782,7 +895,7 @@ function generateRequestTypeContent(endpoint, operationDefinitions, schemaTypeMa
|
|
|
782
895
|
}
|
|
783
896
|
return properties.join("\n");
|
|
784
897
|
}
|
|
785
|
-
function generateResponseTypeContent(endpoint, operationDefinitions, schemaTypeMap = {}) {
|
|
898
|
+
function generateResponseTypeContent(endpoint, operationDefinitions, schemaTypeMap = {}, localSchemaNames = /* @__PURE__ */ new Set()) {
|
|
786
899
|
const operationDef = operationDefinitions?.find((op) => {
|
|
787
900
|
return op.operation?.operationId === endpoint.operationName || op.operation?.operationId === endpoint.operationName.toLowerCase() || // 也嘗試匹配 verb + path 組合
|
|
788
901
|
op.verb === endpoint.verb.toLowerCase() && op.path === endpoint.path;
|
|
@@ -794,12 +907,12 @@ function generateResponseTypeContent(endpoint, operationDefinitions, schemaTypeM
|
|
|
794
907
|
if (jsonContent?.schema) {
|
|
795
908
|
const schema = jsonContent.schema;
|
|
796
909
|
if (schema.$ref || schema.type !== "object" || !schema.properties) {
|
|
797
|
-
const directType = getTypeFromSchema(schema, schemaTypeMap, 0);
|
|
910
|
+
const directType = getTypeFromSchema(schema, schemaTypeMap, 0, localSchemaNames);
|
|
798
911
|
if (directType && directType !== "any") {
|
|
799
912
|
return { content: directType, isDirectType: true };
|
|
800
913
|
}
|
|
801
914
|
}
|
|
802
|
-
const responseProps = parseSchemaProperties(schema, schemaTypeMap);
|
|
915
|
+
const responseProps = parseSchemaProperties(schema, schemaTypeMap, localSchemaNames);
|
|
803
916
|
if (responseProps.length > 0) {
|
|
804
917
|
return { content: responseProps.join("\n"), isDirectType: false };
|
|
805
918
|
}
|
|
@@ -808,14 +921,14 @@ function generateResponseTypeContent(endpoint, operationDefinitions, schemaTypeM
|
|
|
808
921
|
}
|
|
809
922
|
return { content: "", isDirectType: false };
|
|
810
923
|
}
|
|
811
|
-
function parseSchemaProperties(schema, schemaTypeMap = {}) {
|
|
924
|
+
function parseSchemaProperties(schema, schemaTypeMap = {}, localSchemaNames = /* @__PURE__ */ new Set()) {
|
|
812
925
|
const properties = [];
|
|
813
926
|
if (schema.type === "object" && schema.properties) {
|
|
814
927
|
const required = schema.required || [];
|
|
815
928
|
Object.entries(schema.properties).forEach(([propName, propSchema]) => {
|
|
816
929
|
const isRequired = required.includes(propName);
|
|
817
930
|
const optional = isRequired ? "" : "?";
|
|
818
|
-
const propType = getTypeFromSchema(propSchema, schemaTypeMap, 1);
|
|
931
|
+
const propType = getTypeFromSchema(propSchema, schemaTypeMap, 1, localSchemaNames);
|
|
819
932
|
const needsQuotes = /[^a-zA-Z0-9_$]/.test(propName);
|
|
820
933
|
const quotedPropName = needsQuotes ? `"${propName}"` : propName;
|
|
821
934
|
if (propSchema.description) {
|
|
@@ -826,7 +939,7 @@ function parseSchemaProperties(schema, schemaTypeMap = {}) {
|
|
|
826
939
|
}
|
|
827
940
|
return properties;
|
|
828
941
|
}
|
|
829
|
-
function getTypeFromSchema(schema, schemaTypeMap = {}, indentLevel = 0) {
|
|
942
|
+
function getTypeFromSchema(schema, schemaTypeMap = {}, indentLevel = 0, localSchemaNames = /* @__PURE__ */ new Set()) {
|
|
830
943
|
if (!schema) return "any";
|
|
831
944
|
if (schema.$ref) {
|
|
832
945
|
const refPath = schema.$ref;
|
|
@@ -834,7 +947,7 @@ function getTypeFromSchema(schema, schemaTypeMap = {}, indentLevel = 0) {
|
|
|
834
947
|
const originalTypeName = refPath.replace("#/components/schemas/", "");
|
|
835
948
|
const actualTypeName = schemaTypeMap[originalTypeName] || originalTypeName;
|
|
836
949
|
const pascalCaseTypeName = toPascalCase2(actualTypeName);
|
|
837
|
-
const baseType2 = `Schema.${pascalCaseTypeName}`;
|
|
950
|
+
const baseType2 = localSchemaNames.has(originalTypeName) ? pascalCaseTypeName : `Schema.${pascalCaseTypeName}`;
|
|
838
951
|
return schema.nullable ? `${baseType2} | null` : baseType2;
|
|
839
952
|
}
|
|
840
953
|
}
|
|
@@ -857,7 +970,7 @@ function getTypeFromSchema(schema, schemaTypeMap = {}, indentLevel = 0) {
|
|
|
857
970
|
baseType = "boolean";
|
|
858
971
|
break;
|
|
859
972
|
case "array":
|
|
860
|
-
const itemType = schema.items ? getTypeFromSchema(schema.items, schemaTypeMap, indentLevel) : "any";
|
|
973
|
+
const itemType = schema.items ? getTypeFromSchema(schema.items, schemaTypeMap, indentLevel, localSchemaNames) : "any";
|
|
861
974
|
const needsParentheses = itemType.includes("|");
|
|
862
975
|
baseType = needsParentheses ? `(${itemType})[]` : `${itemType}[]`;
|
|
863
976
|
break;
|
|
@@ -866,7 +979,7 @@ function getTypeFromSchema(schema, schemaTypeMap = {}, indentLevel = 0) {
|
|
|
866
979
|
const entries = Object.entries(schema.properties);
|
|
867
980
|
if (entries.length === 0) {
|
|
868
981
|
if (schema.additionalProperties) {
|
|
869
|
-
const valueType = schema.additionalProperties === true ? "any" : getTypeFromSchema(schema.additionalProperties, schemaTypeMap, indentLevel);
|
|
982
|
+
const valueType = schema.additionalProperties === true ? "any" : getTypeFromSchema(schema.additionalProperties, schemaTypeMap, indentLevel, localSchemaNames);
|
|
870
983
|
baseType = `Record<string, ${valueType}>`;
|
|
871
984
|
} else {
|
|
872
985
|
baseType = "{}";
|
|
@@ -878,7 +991,7 @@ function getTypeFromSchema(schema, schemaTypeMap = {}, indentLevel = 0) {
|
|
|
878
991
|
entries.forEach(([key, propSchema]) => {
|
|
879
992
|
const required = schema.required || [];
|
|
880
993
|
const optional = required.includes(key) ? "" : "?";
|
|
881
|
-
const type = getTypeFromSchema(propSchema, schemaTypeMap, indentLevel + 1);
|
|
994
|
+
const type = getTypeFromSchema(propSchema, schemaTypeMap, indentLevel + 1, localSchemaNames);
|
|
882
995
|
const needsQuotes = /[^a-zA-Z0-9_$]/.test(key);
|
|
883
996
|
const quotedKey = needsQuotes ? `"${key}"` : key;
|
|
884
997
|
if (propSchema.description) {
|
|
@@ -891,7 +1004,7 @@ ${props.join("\n")}
|
|
|
891
1004
|
${currentIndent}}`;
|
|
892
1005
|
}
|
|
893
1006
|
} else if (schema.additionalProperties) {
|
|
894
|
-
const valueType = schema.additionalProperties === true ? "any" : getTypeFromSchema(schema.additionalProperties, schemaTypeMap, indentLevel);
|
|
1007
|
+
const valueType = schema.additionalProperties === true ? "any" : getTypeFromSchema(schema.additionalProperties, schemaTypeMap, indentLevel, localSchemaNames);
|
|
895
1008
|
baseType = `Record<string, ${valueType}>`;
|
|
896
1009
|
} else {
|
|
897
1010
|
baseType = "any";
|
|
@@ -903,9 +1016,9 @@ ${currentIndent}}`;
|
|
|
903
1016
|
}
|
|
904
1017
|
return schema.nullable ? `${baseType} | null` : baseType;
|
|
905
1018
|
}
|
|
906
|
-
function getTypeFromParameter(param, schemaTypeMap = {}) {
|
|
1019
|
+
function getTypeFromParameter(param, schemaTypeMap = {}, localSchemaNames = /* @__PURE__ */ new Set()) {
|
|
907
1020
|
if (!param.schema) return "any";
|
|
908
|
-
return getTypeFromSchema(param.schema, schemaTypeMap);
|
|
1021
|
+
return getTypeFromSchema(param.schema, schemaTypeMap, 0, localSchemaNames);
|
|
909
1022
|
}
|
|
910
1023
|
|
|
911
1024
|
// src/generators/rtk-query-generator.ts
|
|
@@ -1102,7 +1215,7 @@ var ApiCodeGenerator = class {
|
|
|
1102
1215
|
};
|
|
1103
1216
|
const apiGen = this.parserService.getApiGenerator();
|
|
1104
1217
|
const schemaInterfaces = apiGen.aliases.reduce((curr, alias) => {
|
|
1105
|
-
if (
|
|
1218
|
+
if (ts3.isInterfaceDeclaration(alias) || ts3.isTypeAliasDeclaration(alias)) {
|
|
1106
1219
|
const name = alias.name.text;
|
|
1107
1220
|
return {
|
|
1108
1221
|
...curr,
|
|
@@ -1186,6 +1299,150 @@ ${enumEntries}
|
|
|
1186
1299
|
`;
|
|
1187
1300
|
}
|
|
1188
1301
|
|
|
1302
|
+
// src/utils/schema-ref-analyzer.ts
|
|
1303
|
+
init_esm_shims();
|
|
1304
|
+
function collectRefsFromSchema(schema, refs) {
|
|
1305
|
+
if (!schema || typeof schema !== "object") return;
|
|
1306
|
+
if (schema.$ref && typeof schema.$ref === "string") {
|
|
1307
|
+
const match = schema.$ref.match(/^#\/components\/schemas\/(.+)$/);
|
|
1308
|
+
if (match) {
|
|
1309
|
+
refs.add(match[1]);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
if (schema.properties) {
|
|
1313
|
+
for (const prop of Object.values(schema.properties)) {
|
|
1314
|
+
collectRefsFromSchema(prop, refs);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
if (schema.items) {
|
|
1318
|
+
collectRefsFromSchema(schema.items, refs);
|
|
1319
|
+
}
|
|
1320
|
+
if (schema.additionalProperties && typeof schema.additionalProperties === "object") {
|
|
1321
|
+
collectRefsFromSchema(schema.additionalProperties, refs);
|
|
1322
|
+
}
|
|
1323
|
+
for (const key of ["allOf", "oneOf", "anyOf"]) {
|
|
1324
|
+
if (Array.isArray(schema[key])) {
|
|
1325
|
+
for (const item of schema[key]) {
|
|
1326
|
+
collectRefsFromSchema(item, refs);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
function collectDirectRefs(operationDefs) {
|
|
1332
|
+
const refs = /* @__PURE__ */ new Set();
|
|
1333
|
+
for (const opDef of operationDefs) {
|
|
1334
|
+
const op = opDef.operation;
|
|
1335
|
+
if (op.parameters) {
|
|
1336
|
+
for (const param of op.parameters) {
|
|
1337
|
+
const p = param;
|
|
1338
|
+
if (p.schema) {
|
|
1339
|
+
collectRefsFromSchema(p.schema, refs);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
if (op.requestBody) {
|
|
1344
|
+
const rb = op.requestBody;
|
|
1345
|
+
if (rb.content) {
|
|
1346
|
+
for (const ct of Object.values(rb.content)) {
|
|
1347
|
+
if (ct.schema) {
|
|
1348
|
+
collectRefsFromSchema(ct.schema, refs);
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
if (op.responses) {
|
|
1354
|
+
for (const resp of Object.values(op.responses)) {
|
|
1355
|
+
const r = resp;
|
|
1356
|
+
if (r.content) {
|
|
1357
|
+
for (const ct of Object.values(r.content)) {
|
|
1358
|
+
if (ct.schema) {
|
|
1359
|
+
collectRefsFromSchema(ct.schema, refs);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
return refs;
|
|
1367
|
+
}
|
|
1368
|
+
function resolveTransitiveDeps(directRefs, allSchemas) {
|
|
1369
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
1370
|
+
const queue = [...directRefs];
|
|
1371
|
+
while (queue.length > 0) {
|
|
1372
|
+
const name = queue.pop();
|
|
1373
|
+
if (resolved.has(name)) continue;
|
|
1374
|
+
resolved.add(name);
|
|
1375
|
+
const schema = allSchemas[name];
|
|
1376
|
+
if (!schema) continue;
|
|
1377
|
+
const nestedRefs = /* @__PURE__ */ new Set();
|
|
1378
|
+
collectRefsFromSchema(schema, nestedRefs);
|
|
1379
|
+
for (const ref of nestedRefs) {
|
|
1380
|
+
if (!resolved.has(ref)) {
|
|
1381
|
+
queue.push(ref);
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
return resolved;
|
|
1386
|
+
}
|
|
1387
|
+
function analyzeSchemaRefs(groupOperations, allSchemas, allSchemaNames) {
|
|
1388
|
+
const groupRefs = /* @__PURE__ */ new Map();
|
|
1389
|
+
for (const [groupKey, opDefs] of groupOperations) {
|
|
1390
|
+
const directRefs = collectDirectRefs(opDefs);
|
|
1391
|
+
const fullRefs = resolveTransitiveDeps(directRefs, allSchemas);
|
|
1392
|
+
groupRefs.set(groupKey, fullRefs);
|
|
1393
|
+
}
|
|
1394
|
+
const refCountMap = /* @__PURE__ */ new Map();
|
|
1395
|
+
for (const [groupKey, refs] of groupRefs) {
|
|
1396
|
+
for (const schemaName of refs) {
|
|
1397
|
+
if (!refCountMap.has(schemaName)) {
|
|
1398
|
+
refCountMap.set(schemaName, /* @__PURE__ */ new Set());
|
|
1399
|
+
}
|
|
1400
|
+
refCountMap.get(schemaName).add(groupKey);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
const sharedSchemas = /* @__PURE__ */ new Set();
|
|
1404
|
+
const groupLocalSchemas = /* @__PURE__ */ new Map();
|
|
1405
|
+
const unusedSchemas = /* @__PURE__ */ new Set();
|
|
1406
|
+
for (const schemaName of allSchemaNames) {
|
|
1407
|
+
const groups = refCountMap.get(schemaName);
|
|
1408
|
+
if (!groups || groups.size === 0) {
|
|
1409
|
+
unusedSchemas.add(schemaName);
|
|
1410
|
+
} else if (groups.size === 1) {
|
|
1411
|
+
const groupKey = [...groups][0];
|
|
1412
|
+
if (!groupLocalSchemas.has(groupKey)) {
|
|
1413
|
+
groupLocalSchemas.set(groupKey, /* @__PURE__ */ new Set());
|
|
1414
|
+
}
|
|
1415
|
+
groupLocalSchemas.get(groupKey).add(schemaName);
|
|
1416
|
+
} else {
|
|
1417
|
+
sharedSchemas.add(schemaName);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
let changed = true;
|
|
1421
|
+
while (changed) {
|
|
1422
|
+
changed = false;
|
|
1423
|
+
for (const [groupKey, localSchemas] of groupLocalSchemas) {
|
|
1424
|
+
for (const schemaName of [...localSchemas]) {
|
|
1425
|
+
const schema = allSchemas[schemaName];
|
|
1426
|
+
if (!schema) continue;
|
|
1427
|
+
const deps = /* @__PURE__ */ new Set();
|
|
1428
|
+
collectRefsFromSchema(schema, deps);
|
|
1429
|
+
for (const dep of deps) {
|
|
1430
|
+
for (const [otherGroup, otherLocals] of groupLocalSchemas) {
|
|
1431
|
+
if (otherGroup !== groupKey && otherLocals.has(dep)) {
|
|
1432
|
+
sharedSchemas.add(dep);
|
|
1433
|
+
sharedSchemas.add(schemaName);
|
|
1434
|
+
otherLocals.delete(dep);
|
|
1435
|
+
localSchemas.delete(schemaName);
|
|
1436
|
+
changed = true;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
return { sharedSchemas, groupLocalSchemas, unusedSchemas };
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1189
1446
|
// src/services/unified-code-generator.ts
|
|
1190
1447
|
var UnifiedCodeGenerator = class {
|
|
1191
1448
|
_options;
|
|
@@ -1208,6 +1465,10 @@ var UnifiedCodeGenerator = class {
|
|
|
1208
1465
|
};
|
|
1209
1466
|
// 收集所有 tags
|
|
1210
1467
|
allTags = /* @__PURE__ */ new Set();
|
|
1468
|
+
// 收集每個 group 的 operation definitions(用於 schema 引用分析)
|
|
1469
|
+
groupOperationDefs = /* @__PURE__ */ new Map();
|
|
1470
|
+
// 收集每個 group 的生成選項(用於重新生成 types)
|
|
1471
|
+
groupGenerationOptions = /* @__PURE__ */ new Map();
|
|
1211
1472
|
constructor(options) {
|
|
1212
1473
|
this._options = options;
|
|
1213
1474
|
}
|
|
@@ -1217,6 +1478,7 @@ var UnifiedCodeGenerator = class {
|
|
|
1217
1478
|
async generateAll() {
|
|
1218
1479
|
await this.prepare();
|
|
1219
1480
|
await this.generateApi();
|
|
1481
|
+
this.splitSchemaByUsage();
|
|
1220
1482
|
this.generateCommonTypesContent();
|
|
1221
1483
|
this.generateSchemaContent();
|
|
1222
1484
|
this.generateUtilsContent();
|
|
@@ -1243,7 +1505,7 @@ var UnifiedCodeGenerator = class {
|
|
|
1243
1505
|
this.parserService.initialize();
|
|
1244
1506
|
const apiGen = this.parserService.getApiGenerator();
|
|
1245
1507
|
this.schemaInterfaces = apiGen.aliases.reduce((curr, alias) => {
|
|
1246
|
-
if (
|
|
1508
|
+
if (ts4.isInterfaceDeclaration(alias) || ts4.isTypeAliasDeclaration(alias)) {
|
|
1247
1509
|
const name = alias.name.text;
|
|
1248
1510
|
return {
|
|
1249
1511
|
...curr,
|
|
@@ -1283,6 +1545,55 @@ var UnifiedCodeGenerator = class {
|
|
|
1283
1545
|
}
|
|
1284
1546
|
}
|
|
1285
1547
|
}
|
|
1548
|
+
/**
|
|
1549
|
+
* 分析 schema 引用,將只被單一 group 使用的 schema 移到該 group 的 types.ts
|
|
1550
|
+
*/
|
|
1551
|
+
splitSchemaByUsage() {
|
|
1552
|
+
if (!this.openApiDoc || this.groupOperationDefs.size === 0) return;
|
|
1553
|
+
const rawSchemas = this.openApiDoc.components?.schemas ?? {};
|
|
1554
|
+
const allSchemaNames = Object.keys(this.schemaInterfaces);
|
|
1555
|
+
const analysis = analyzeSchemaRefs(
|
|
1556
|
+
this.groupOperationDefs,
|
|
1557
|
+
rawSchemas,
|
|
1558
|
+
allSchemaNames
|
|
1559
|
+
);
|
|
1560
|
+
this.sharedSchemaNames = analysis.sharedSchemas;
|
|
1561
|
+
for (const group of this.generatedContent.groups) {
|
|
1562
|
+
const localNames = analysis.groupLocalSchemas.get(group.groupKey);
|
|
1563
|
+
if (!localNames || localNames.size === 0) continue;
|
|
1564
|
+
const groupOptions = this.groupGenerationOptions.get(group.groupKey);
|
|
1565
|
+
if (!groupOptions || !this.parserService) continue;
|
|
1566
|
+
const localSchemaInterfaces = {};
|
|
1567
|
+
for (const name of localNames) {
|
|
1568
|
+
if (this.schemaInterfaces[name]) {
|
|
1569
|
+
localSchemaInterfaces[name] = this.schemaInterfaces[name];
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
const infoExtractor = new EndpointInfoExtractor(groupOptions);
|
|
1573
|
+
const operationDefs = this.groupOperationDefs.get(group.groupKey) ?? [];
|
|
1574
|
+
const endpointInfos = infoExtractor.extractEndpointInfos(operationDefs);
|
|
1575
|
+
const generatorOptions = {
|
|
1576
|
+
...groupOptions,
|
|
1577
|
+
apiConfiguration: groupOptions.apiConfiguration || {
|
|
1578
|
+
file: "@/store/webapi",
|
|
1579
|
+
importName: "WebApiConfiguration"
|
|
1580
|
+
}
|
|
1581
|
+
};
|
|
1582
|
+
const newTypesContent = generateTypesFile(
|
|
1583
|
+
endpointInfos,
|
|
1584
|
+
generatorOptions,
|
|
1585
|
+
this.schemaInterfaces,
|
|
1586
|
+
operationDefs,
|
|
1587
|
+
{
|
|
1588
|
+
localSchemaInterfaces,
|
|
1589
|
+
localSchemaNames: localNames
|
|
1590
|
+
}
|
|
1591
|
+
);
|
|
1592
|
+
group.content.files.types = newTypesContent;
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
// 儲存 shared schema 名稱(只有這些會寫入 schema.ts)
|
|
1596
|
+
sharedSchemaNames = null;
|
|
1286
1597
|
/**
|
|
1287
1598
|
* 生成 common types
|
|
1288
1599
|
*/
|
|
@@ -1290,10 +1601,13 @@ var UnifiedCodeGenerator = class {
|
|
|
1290
1601
|
this.generatedContent.commonTypes = generateCommonTypesFile();
|
|
1291
1602
|
}
|
|
1292
1603
|
/**
|
|
1293
|
-
* 生成Schema
|
|
1604
|
+
* 生成Schema(只包含 shared types,排除 group-local 和未使用的 types)
|
|
1294
1605
|
*/
|
|
1295
1606
|
async generateSchemaContent() {
|
|
1296
|
-
this.generatedContent.componentSchema = generateComponentSchemaFile(
|
|
1607
|
+
this.generatedContent.componentSchema = generateComponentSchemaFile(
|
|
1608
|
+
this.schemaInterfaces,
|
|
1609
|
+
this.sharedSchemaNames ?? void 0
|
|
1610
|
+
);
|
|
1297
1611
|
}
|
|
1298
1612
|
/**
|
|
1299
1613
|
* 生成 DO_NOT_MODIFY.md
|
|
@@ -1401,6 +1715,9 @@ var UnifiedCodeGenerator = class {
|
|
|
1401
1715
|
if (!this.openApiDoc || !this.parserService) {
|
|
1402
1716
|
throw new Error("OpenAPI \u6587\u6A94\u672A\u521D\u59CB\u5316\uFF0C\u8ACB\u5148\u8ABF\u7528 prepare()");
|
|
1403
1717
|
}
|
|
1718
|
+
const groupOpDefs = this.parserService.getOperationDefinitions(groupOptions.filterEndpoints);
|
|
1719
|
+
this.groupOperationDefs.set(groupInfo.groupKey, groupOpDefs);
|
|
1720
|
+
this.groupGenerationOptions.set(groupInfo.groupKey, groupOptions);
|
|
1404
1721
|
const apiGenerator = new ApiCodeGenerator(this.parserService, groupOptions);
|
|
1405
1722
|
const result = await apiGenerator.generate();
|
|
1406
1723
|
return result;
|
|
@@ -1409,11 +1726,12 @@ var UnifiedCodeGenerator = class {
|
|
|
1409
1726
|
* 生成主 index.ts 檔案
|
|
1410
1727
|
*/
|
|
1411
1728
|
generateMainIndex(generatedGroups) {
|
|
1412
|
-
const
|
|
1729
|
+
const groupExports = generatedGroups.map((groupKey) => `export * from "./${groupKey}";`).join("\n");
|
|
1413
1730
|
return `/* eslint-disable */
|
|
1414
1731
|
// [Warning] Generated automatically - do not edit manually
|
|
1415
1732
|
|
|
1416
|
-
|
|
1733
|
+
export * from "./schema";
|
|
1734
|
+
${groupExports}
|
|
1417
1735
|
`;
|
|
1418
1736
|
}
|
|
1419
1737
|
};
|