@acrool/rtk-query-codegen-openapi 0.0.2-test.8 → 0.0.2-test.9

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/src/generate.ts CHANGED
@@ -147,23 +147,24 @@ export async function generateApi(
147
147
  const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
148
148
 
149
149
  const allTypeDefinitions: ts.Statement[] = [];
150
+ const definedTypeNames = new Set<string>();
150
151
 
151
152
  const components = v3Doc.components;
152
153
  if (components) {
153
154
  const componentDefinitions = Object.entries(components).map(([componentType, componentDefs]) => {
154
- const typeEntries = Object.entries(componentDefs as Record<string, unknown>).map(([name, def]) => {
155
- addSchemeTypeName(name);
156
-
157
- const typeName = capitalize(camelCase(name));
158
- const typeNode = wrapWithSchemeIfComponent(apiGen.getTypeFromSchema(def as OpenAPIV3.SchemaObject));
159
- return factory.createTypeAliasDeclaration(
160
- [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
161
- factory.createIdentifier(typeName),
162
- undefined,
163
- typeNode
164
- );
165
- });
166
-
155
+ const typeEntries = Object.entries(componentDefs as Record<string, unknown>)
156
+ .map(([name, def]) => {
157
+ addSchemeTypeName(name);
158
+ const typeName = capitalize(camelCase(name));
159
+ definedTypeNames.add(typeName);
160
+ const typeNode = wrapWithSchemeIfComponent(apiGen.getTypeFromSchema(def as OpenAPIV3.SchemaObject));
161
+ return factory.createTypeAliasDeclaration(
162
+ [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
163
+ factory.createIdentifier(typeName),
164
+ undefined,
165
+ typeNode
166
+ );
167
+ });
167
168
  return factory.createModuleDeclaration(
168
169
  [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
169
170
  factory.createIdentifier('Scheme'),
@@ -174,12 +175,116 @@ export async function generateApi(
174
175
  allTypeDefinitions.push(...componentDefinitions);
175
176
  }
176
177
 
177
- if (useEnumType) {
178
- allTypeDefinitions.push(...apiGen.enumAliases);
178
+ const enumEntries = [
179
+ ...apiGen.enumAliases.filter(e => ts.isEnumDeclaration(e)),
180
+ ...apiGen.enumAliases.filter(e => ts.isTypeAliasDeclaration(e)),
181
+ ].map(enumDecl => {
182
+ if (ts.isEnumDeclaration(enumDecl)) {
183
+ return factory.createEnumDeclaration(
184
+ [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
185
+ enumDecl.name,
186
+ enumDecl.members
187
+ );
188
+ } else if (ts.isTypeAliasDeclaration(enumDecl)) {
189
+ return factory.createTypeAliasDeclaration(
190
+ [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
191
+ enumDecl.name,
192
+ enumDecl.typeParameters,
193
+ enumDecl.type
194
+ );
195
+ }
196
+ return enumDecl;
197
+ });
198
+
199
+ const unionTypeEnums = apiGen.aliases
200
+ .filter(alias => {
201
+ if (ts.isTypeAliasDeclaration(alias) && alias.type) {
202
+ return ts.isUnionTypeNode(alias.type);
203
+ }
204
+ return false;
205
+ })
206
+ .map(alias => {
207
+ if (ts.isTypeAliasDeclaration(alias)) {
208
+ return factory.createTypeAliasDeclaration(
209
+ [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
210
+ alias.name,
211
+ alias.typeParameters,
212
+ alias.type
213
+ );
214
+ }
215
+ return alias;
216
+ });
217
+
218
+ const allEnumEntries = [...enumEntries, ...unionTypeEnums];
219
+
220
+ if (allEnumEntries.length > 0) {
221
+ allTypeDefinitions.push(
222
+ factory.createModuleDeclaration(
223
+ [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
224
+ factory.createIdentifier('Enum'),
225
+ factory.createModuleBlock(allEnumEntries),
226
+ ts.NodeFlags.Namespace
227
+ )
228
+ );
179
229
  }
180
230
 
181
- allTypeDefinitions.push(...apiGen.aliases);
231
+ if (apiGen.aliases.length > 0) {
232
+ const aliasEntries = apiGen.aliases
233
+ .filter(alias => {
234
+ if (ts.isTypeAliasDeclaration(alias)) {
235
+ const isDefinedInComponents = definedTypeNames.has(alias.name.text);
236
+ const isUnionTypeEnum = ts.isUnionTypeNode(alias.type);
237
+ return !isDefinedInComponents && !isUnionTypeEnum;
238
+ }
239
+ return false;
240
+ })
241
+ .map(alias => {
242
+ if (ts.isTypeAliasDeclaration(alias)) {
243
+ return factory.createTypeAliasDeclaration(
244
+ [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
245
+ alias.name,
246
+ alias.typeParameters,
247
+ alias.type
248
+ );
249
+ }
250
+ return alias;
251
+ });
252
+
253
+ if (aliasEntries.length > 0) {
254
+ const existingSchemeIndex = allTypeDefinitions.findIndex(def =>
255
+ ts.isModuleDeclaration(def) &&
256
+ ts.isIdentifier(def.name) &&
257
+ def.name.text === 'Scheme'
258
+ );
259
+
260
+ if (existingSchemeIndex >= 0) {
261
+ const existingScheme = allTypeDefinitions[existingSchemeIndex] as ts.ModuleDeclaration;
262
+ const mergedMembers = [...(existingScheme.body as ts.ModuleBlock).statements, ...aliasEntries];
263
+ allTypeDefinitions[existingSchemeIndex] = factory.createModuleDeclaration(
264
+ [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
265
+ factory.createIdentifier('Scheme'),
266
+ factory.createModuleBlock(mergedMembers),
267
+ ts.NodeFlags.Namespace
268
+ );
269
+ } else {
270
+ allTypeDefinitions.push(
271
+ factory.createModuleDeclaration(
272
+ [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
273
+ factory.createIdentifier('Scheme'),
274
+ factory.createModuleBlock(aliasEntries),
275
+ ts.NodeFlags.Namespace
276
+ )
277
+ );
278
+ }
279
+ }
280
+ }
182
281
 
282
+ const fs = await import('node:fs/promises');
283
+ const path = await import('node:path');
284
+
285
+ const sharedTypesDir = path.dirname(sharedTypesFile);
286
+ await fs.mkdir(sharedTypesDir, { recursive: true });
287
+
183
288
  const output = printer.printNode(
184
289
  ts.EmitHint.Unspecified,
185
290
  factory.createSourceFile(
@@ -190,7 +295,6 @@ export async function generateApi(
190
295
  resultFile
191
296
  );
192
297
 
193
- const fs = await import('node:fs/promises');
194
298
  await fs.writeFile(sharedTypesFile, output, 'utf-8');
195
299
  }
196
300
 
@@ -245,7 +349,12 @@ export async function generateApi(
245
349
  [
246
350
  generateImportNode(apiFile, { [apiImport]: 'api' }),
247
351
  generateImportNode('@acrool/react-fetcher', { IRestFulEndpointsQueryReturn: 'IRestFulEndpointsQueryReturn' }),
248
- ...(sharedTypesFile ? [generateImportNode(sharedTypesImportPath, { Scheme: 'Scheme' })] : []),
352
+ ...(sharedTypesFile ? [
353
+ generateImportNode(sharedTypesImportPath, {
354
+ Scheme: 'Scheme',
355
+ ...(useEnumType ? { Enum: 'Enum' } : {})
356
+ })
357
+ ] : []),
249
358
  ...(tag ? [generateTagTypes({ addTagTypes: extractAllTagTypes({ operationDefinitions }) })] : []),
250
359
  generateCreateApiCall({
251
360
  tag,
@@ -336,25 +445,6 @@ export async function generateApi(
336
445
  )
337
446
  .filter(([_1, _2, type]) => type !== keywordType.void)
338
447
  .map(([code, response, type]) => {
339
- if (sharedTypesFile && ts.isTypeReferenceNode(type) && type.typeName) {
340
- if (ts.isIdentifier(type.typeName)) {
341
- const typeName = type.typeName.text;
342
- if (typeName in apiGen.aliases || typeName in apiGen.enumAliases) {
343
- return ts.addSyntheticLeadingComment(
344
- factory.createTypeReferenceNode(
345
- factory.createQualifiedName(
346
- factory.createIdentifier('sharedTypes'),
347
- factory.createIdentifier(camelCase(typeName))
348
- ),
349
- type.typeArguments
350
- ),
351
- ts.SyntaxKind.MultiLineCommentTrivia,
352
- `* status ${code} ${response.description} `,
353
- false
354
- );
355
- }
356
- }
357
- }
358
448
  return ts.addSyntheticLeadingComment(
359
449
  type,
360
450
  ts.SyntaxKind.MultiLineCommentTrivia,
@@ -620,6 +710,36 @@ export async function generateApi(
620
710
  function wrapWithSchemeIfComponent(typeNode: ts.TypeNode): ts.TypeNode {
621
711
  if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {
622
712
  const typeName = typeNode.typeName.text;
713
+
714
+ // 檢查是否為 enum 類型(包括在 enumAliases 和 aliases 中的)
715
+ const isEnumType = useEnumType && (
716
+ apiGen.enumAliases.some(enumDecl => {
717
+ if (ts.isEnumDeclaration(enumDecl) || ts.isTypeAliasDeclaration(enumDecl)) {
718
+ return enumDecl.name.text === typeName;
719
+ }
720
+ return false;
721
+ }) ||
722
+ apiGen.aliases.some(alias => {
723
+ if (ts.isTypeAliasDeclaration(alias) && alias.type) {
724
+ // 檢查是否為 union type 的 enum
725
+ if (ts.isUnionTypeNode(alias.type)) {
726
+ return alias.name.text === typeName;
727
+ }
728
+ }
729
+ return false;
730
+ })
731
+ );
732
+
733
+ if (isEnumType) {
734
+ return factory.createTypeReferenceNode(
735
+ factory.createQualifiedName(
736
+ factory.createIdentifier('Enum'),
737
+ typeNode.typeName
738
+ ),
739
+ typeNode.typeArguments?.map(wrapWithSchemeIfComponent)
740
+ );
741
+ }
742
+
623
743
  if (schemeTypeNames.has(typeName)) {
624
744
  return factory.createTypeReferenceNode(
625
745
  factory.createQualifiedName(
@@ -640,6 +760,52 @@ export async function generateApi(
640
760
  return factory.createArrayTypeNode(wrapWithSchemeIfComponent(typeNode.elementType));
641
761
  }
642
762
  if (ts.isUnionTypeNode(typeNode)) {
763
+ // 檢查是否為 enum 的 union type
764
+ const unionTypes = typeNode.types;
765
+ if (unionTypes.length > 0 && unionTypes.every(type =>
766
+ ts.isLiteralTypeNode(type) &&
767
+ (ts.isStringLiteral(type.literal) || ts.isNumericLiteral(type.literal))
768
+ )) {
769
+ // 這是一個 enum 的 union type,我們需要找到對應的 enum 類型
770
+ const enumValues = unionTypes.map(type => {
771
+ if (ts.isLiteralTypeNode(type)) {
772
+ if (ts.isStringLiteral(type.literal)) {
773
+ return type.literal.text;
774
+ } else if (ts.isNumericLiteral(type.literal)) {
775
+ return type.literal.text;
776
+ }
777
+ }
778
+ return null;
779
+ }).filter(Boolean);
780
+
781
+ // 查找對應的 enum 類型
782
+ const matchingEnum = apiGen.aliases.find(alias => {
783
+ if (ts.isTypeAliasDeclaration(alias) && ts.isUnionTypeNode(alias.type)) {
784
+ const aliasValues = alias.type.types.map(type => {
785
+ if (ts.isLiteralTypeNode(type)) {
786
+ if (ts.isStringLiteral(type.literal)) {
787
+ return type.literal.text;
788
+ } else if (ts.isNumericLiteral(type.literal)) {
789
+ return type.literal.text;
790
+ }
791
+ }
792
+ return null;
793
+ }).filter(Boolean);
794
+
795
+ return aliasValues.length === enumValues.length &&
796
+ aliasValues.every(val => enumValues.includes(val));
797
+ }
798
+ return false;
799
+ });
800
+
801
+ // 對於所有的 enum 類型,直接使用字串型別,不轉換為 Enum
802
+ // 這樣可以避免自動命名造成的變更問題
803
+ if (matchingEnum && ts.isTypeAliasDeclaration(matchingEnum)) {
804
+ // 直接返回原始的 union type,不轉換為 Enum
805
+ return typeNode;
806
+ }
807
+ }
808
+
643
809
  return factory.createUnionTypeNode(typeNode.types.map(wrapWithSchemeIfComponent));
644
810
  }
645
811
  if (ts.isTypeLiteralNode(typeNode)) {