@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/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 ts3 from "typescript";
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 generateComponentSchemaFile(interfaces) {
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 ts2 from "typescript";
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 generateTypesFile(endpointInfos, _options, schemaInterfaces, operationDefinitions) {
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
- const hasSchemaTypes = schemaInterfaces && Object.keys(schemaInterfaces).length > 0;
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 (ts2.isInterfaceDeclaration(alias) || ts2.isTypeAliasDeclaration(alias)) {
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 (ts3.isInterfaceDeclaration(alias) || ts3.isTypeAliasDeclaration(alias)) {
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(this.schemaInterfaces);
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 exports = generatedGroups.map((groupKey) => `export * from "./${groupKey}";`).join("\n");
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
- ${exports}
1733
+ export * from "./schema";
1734
+ ${groupExports}
1417
1735
  `;
1418
1736
  }
1419
1737
  };