@acrool/rtk-query-codegen-openapi 0.0.6 → 0.0.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
@@ -23,6 +23,7 @@ import type {
23
23
  ParameterDefinition,
24
24
  ParameterMatcher,
25
25
  TextMatcher,
26
+ GenerateApiResult,
26
27
  } from './types';
27
28
  import { capitalize, getOperationDefinitions, getV3Doc, removeUndefined, isQuery as testIsQuery } from './utils';
28
29
  import { factory } from './utils/factory';
@@ -38,7 +39,7 @@ function defaultIsDataResponse(code: string, includeDefault: boolean) {
38
39
  return !Number.isNaN(parsedCode) && parsedCode >= 200 && parsedCode < 300;
39
40
  }
40
41
 
41
- function getOperationName({ verb, path }: Pick<OperationDefinition, 'verb' | 'path' >) {
42
+ function getOperationName({ verb, path }: Pick<OperationDefinition, 'verb' | 'path'>) {
42
43
  return _getOperationName(verb, path, undefined);
43
44
  }
44
45
 
@@ -118,8 +119,10 @@ export async function generateApi(
118
119
  mergeReadWriteOnly = false,
119
120
  httpResolverOptions,
120
121
  sharedTypesFile,
122
+ queryMatch,
123
+ endpointsQueryReturnTypeFile = './endpointsQueryReturnType',
121
124
  }: GenerationOptions
122
- ) {
125
+ ): Promise<GenerateApiResult> {
123
126
  const v3Doc = (v3DocCache[spec] ??= await getV3Doc(spec, httpResolverOptions));
124
127
 
125
128
  const apiGen = new ApiGenerator(v3Doc, {
@@ -151,34 +154,36 @@ export async function generateApi(
151
154
 
152
155
  const components = v3Doc.components;
153
156
  if (components) {
154
- const componentDefinitions = Object.entries(components).map(([componentType, componentDefs]) => {
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
- });
168
- return factory.createModuleDeclaration(
169
- [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
170
- factory.createIdentifier('Scheme'),
171
- factory.createModuleBlock(typeEntries),
172
- ts.NodeFlags.Namespace
157
+ // 只處理 schemas,其他 component 類型暫時不處理
158
+ if (components.schemas) {
159
+ const typeEntries = Object.entries(components.schemas).map(([name, def]) => {
160
+ addSchemeTypeName(name);
161
+ const typeName = capitalize(camelCase(name));
162
+ definedTypeNames.add(typeName);
163
+ const typeNode = wrapWithSchemeIfComponent(apiGen.getTypeFromSchema(def as OpenAPIV3.SchemaObject));
164
+ return factory.createTypeAliasDeclaration(
165
+ [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
166
+ factory.createIdentifier(typeName),
167
+ undefined,
168
+ typeNode
169
+ );
170
+ });
171
+
172
+ allTypeDefinitions.push(
173
+ factory.createModuleDeclaration(
174
+ [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
175
+ factory.createIdentifier('Scheme'),
176
+ factory.createModuleBlock(typeEntries),
177
+ ts.NodeFlags.Namespace
178
+ )
173
179
  );
174
- });
175
- allTypeDefinitions.push(...componentDefinitions);
180
+ }
176
181
  }
177
182
 
178
183
  const enumEntries = [
179
- ...apiGen.enumAliases.filter(e => ts.isEnumDeclaration(e)),
180
- ...apiGen.enumAliases.filter(e => ts.isTypeAliasDeclaration(e)),
181
- ].map(enumDecl => {
184
+ ...apiGen.enumAliases.filter((e) => ts.isEnumDeclaration(e)),
185
+ ...apiGen.enumAliases.filter((e) => ts.isTypeAliasDeclaration(e)),
186
+ ].map((enumDecl) => {
182
187
  if (ts.isEnumDeclaration(enumDecl)) {
183
188
  return factory.createEnumDeclaration(
184
189
  [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
@@ -195,15 +200,15 @@ export async function generateApi(
195
200
  }
196
201
  return enumDecl;
197
202
  });
198
-
203
+
199
204
  const unionTypeEnums = apiGen.aliases
200
- .filter(alias => {
205
+ .filter((alias) => {
201
206
  if (ts.isTypeAliasDeclaration(alias) && alias.type) {
202
207
  return ts.isUnionTypeNode(alias.type);
203
208
  }
204
209
  return false;
205
210
  })
206
- .map(alias => {
211
+ .map((alias) => {
207
212
  if (ts.isTypeAliasDeclaration(alias)) {
208
213
  return factory.createTypeAliasDeclaration(
209
214
  [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
@@ -214,9 +219,9 @@ export async function generateApi(
214
219
  }
215
220
  return alias;
216
221
  });
217
-
222
+
218
223
  const allEnumEntries = [...enumEntries, ...unionTypeEnums];
219
-
224
+
220
225
  if (allEnumEntries.length > 0) {
221
226
  allTypeDefinitions.push(
222
227
  factory.createModuleDeclaration(
@@ -230,7 +235,7 @@ export async function generateApi(
230
235
 
231
236
  if (apiGen.aliases.length > 0) {
232
237
  const aliasEntries = apiGen.aliases
233
- .filter(alias => {
238
+ .filter((alias) => {
234
239
  if (ts.isTypeAliasDeclaration(alias)) {
235
240
  const isDefinedInComponents = definedTypeNames.has(alias.name.text);
236
241
  const isUnionTypeEnum = ts.isUnionTypeNode(alias.type);
@@ -238,7 +243,7 @@ export async function generateApi(
238
243
  }
239
244
  return false;
240
245
  })
241
- .map(alias => {
246
+ .map((alias) => {
242
247
  if (ts.isTypeAliasDeclaration(alias)) {
243
248
  return factory.createTypeAliasDeclaration(
244
249
  [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
@@ -251,10 +256,8 @@ export async function generateApi(
251
256
  });
252
257
 
253
258
  if (aliasEntries.length > 0) {
254
- const existingSchemeIndex = allTypeDefinitions.findIndex(def =>
255
- ts.isModuleDeclaration(def) &&
256
- ts.isIdentifier(def.name) &&
257
- def.name.text === 'Scheme'
259
+ const existingSchemeIndex = allTypeDefinitions.findIndex(
260
+ (def) => ts.isModuleDeclaration(def) && ts.isIdentifier(def.name) && def.name.text === 'Scheme'
258
261
  );
259
262
 
260
263
  if (existingSchemeIndex >= 0) {
@@ -281,10 +284,10 @@ export async function generateApi(
281
284
 
282
285
  const fs = await import('node:fs/promises');
283
286
  const path = await import('node:path');
284
-
287
+
285
288
  const sharedTypesDir = path.dirname(sharedTypesFile);
286
289
  await fs.mkdir(sharedTypesDir, { recursive: true });
287
-
290
+
288
291
  const output = printer.printNode(
289
292
  ts.EmitHint.Unspecified,
290
293
  factory.createSourceFile(
@@ -314,6 +317,7 @@ export async function generateApi(
314
317
  const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
315
318
 
316
319
  const interfaces: Record<string, ts.InterfaceDeclaration | ts.TypeAliasDeclaration> = {};
320
+
317
321
  function registerInterface(declaration: ts.InterfaceDeclaration | ts.TypeAliasDeclaration) {
318
322
  const name = declaration.name.escapedText.toString();
319
323
  if (name in interfaces) {
@@ -330,50 +334,64 @@ export async function generateApi(
330
334
  apiFile = apiFile.replace(/\\/g, '/');
331
335
  if (!apiFile.startsWith('.')) apiFile = `./${apiFile}`;
332
336
  }
337
+ if (endpointsQueryReturnTypeFile.startsWith('.')) {
338
+ endpointsQueryReturnTypeFile = path.relative(path.dirname(outputFile), endpointsQueryReturnTypeFile);
339
+ endpointsQueryReturnTypeFile = endpointsQueryReturnTypeFile.replace(/\\/g, '/');
340
+ if (!endpointsQueryReturnTypeFile.startsWith('.')) endpointsQueryReturnTypeFile = `./${endpointsQueryReturnTypeFile}`;
341
+ }
333
342
  }
334
343
  apiFile = apiFile.replace(/\.[jt]sx?$/, '');
335
-
336
- const sharedTypesImportPath = sharedTypesFile && outputFile
337
- ? (() => {
338
- let rel = path.relative(path.dirname(outputFile), sharedTypesFile)
339
- .replace(/\\/g, '/')
340
- .replace(/\.[jt]sx?$/, '');
341
- if (!rel.startsWith('.')) rel = './' + rel;
342
- return rel;
343
- })()
344
- : './shared-types';
345
-
346
- return printer.printNode(
344
+ endpointsQueryReturnTypeFile = endpointsQueryReturnTypeFile.replace(/\.[jt]sx?$/, '');
345
+
346
+ const sharedTypesImportPath =
347
+ sharedTypesFile && outputFile
348
+ ? (() => {
349
+ let rel = path
350
+ .relative(path.dirname(outputFile), sharedTypesFile)
351
+ .replace(/\\/g, '/')
352
+ .replace(/\.[jt]sx?$/, '');
353
+ if (!rel.startsWith('.')) rel = './' + rel;
354
+ return rel;
355
+ })()
356
+ : './shared-types';
357
+
358
+ // 收集操作名稱
359
+ const operationNames: string[] = [];
360
+
361
+ const sourceCode = printer.printNode(
347
362
  ts.EmitHint.Unspecified,
348
363
  factory.createSourceFile(
349
364
  [
350
365
  generateImportNode(apiFile, { [apiImport]: 'api' }),
351
- generateImportNode('@acrool/react-fetcher', { IRestFulEndpointsQueryReturn: 'IRestFulEndpointsQueryReturn' }),
352
- ...(sharedTypesFile ? [
353
- generateImportNode(sharedTypesImportPath, {
354
- Scheme: 'Scheme',
355
- ...(useEnumType ? { Enum: 'Enum' } : {})
356
- })
357
- ] : []),
366
+ generateImportNode(endpointsQueryReturnTypeFile, { IRestFulEndpointsQueryReturn: 'IRestFulEndpointsQueryReturn' }),
367
+ ...(sharedTypesFile
368
+ ? [
369
+ generateImportNode(sharedTypesImportPath, {
370
+ Scheme: 'Scheme',
371
+ ...(useEnumType ? { Enum: 'Enum' } : {}),
372
+ }),
373
+ ]
374
+ : []),
358
375
  ...(tag ? [generateTagTypes({ addTagTypes: extractAllTagTypes({ operationDefinitions }) })] : []),
359
376
  generateCreateApiCall({
360
377
  tag,
361
378
  endpointDefinitions: factory.createObjectLiteralExpression(
362
- operationDefinitions.map((operationDefinition) =>
363
- generateEndpoint({
379
+ operationDefinitions.map((operationDefinition) => {
380
+ const operationName = getOperationName({ verb: operationDefinition.verb, path: operationDefinition.path });
381
+ const finalOperationName = operationNameSuffix ? capitalize(operationName + operationNameSuffix) : operationName;
382
+ operationNames.push(finalOperationName);
383
+
384
+ return generateEndpoint({
364
385
  operationDefinition,
365
386
  overrides: getOverrides(operationDefinition, endpointOverrides),
366
387
  sharedTypesFile: !!sharedTypesFile,
367
- })
368
- ),
388
+ queryMatch,
389
+ });
390
+ }),
369
391
  true
370
392
  ),
371
393
  }),
372
- factory.createExportAssignment(
373
- undefined,
374
- undefined,
375
- factory.createIdentifier(generatedApiName)
376
- ),
394
+ factory.createExportAssignment(undefined, undefined, factory.createIdentifier(generatedApiName)),
377
395
  ...Object.values(interfaces),
378
396
  ...(sharedTypesFile ? [] : [...apiGen.aliases, ...apiGen.enumAliases]),
379
397
  ...(hooks
@@ -383,6 +401,7 @@ export async function generateApi(
383
401
  operationDefinitions,
384
402
  endpointOverrides,
385
403
  config: hooks,
404
+ queryMatch,
386
405
  }),
387
406
  ]
388
407
  : []),
@@ -393,6 +412,11 @@ export async function generateApi(
393
412
  resultFile
394
413
  );
395
414
 
415
+ return {
416
+ sourceCode,
417
+ operationNames,
418
+ };
419
+
396
420
  function extractAllTagTypes({ operationDefinitions }: { operationDefinitions: OperationDefinition[] }) {
397
421
  const allTagTypes = new Set<string>();
398
422
 
@@ -409,10 +433,12 @@ export async function generateApi(
409
433
  operationDefinition,
410
434
  overrides,
411
435
  sharedTypesFile,
436
+ queryMatch,
412
437
  }: {
413
438
  operationDefinition: OperationDefinition;
414
439
  overrides?: EndpointOverrides;
415
440
  sharedTypesFile: boolean;
441
+ queryMatch?: (method: string, path: string) => boolean;
416
442
  }) {
417
443
  const {
418
444
  verb,
@@ -423,7 +449,7 @@ export async function generateApi(
423
449
  } = operationDefinition;
424
450
  const operationName = getOperationName({ verb, path });
425
451
  const tags = tag ? getTags({ verb, pathItem }) : [];
426
- const isQuery = testIsQuery(verb, overrides);
452
+ const isQuery = testIsQuery(verb, path, overrides, queryMatch);
427
453
 
428
454
  const returnsJson = apiGen.getResponseType(responses) === 'json';
429
455
  let ResponseType: ts.TypeNode = factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
@@ -475,10 +501,11 @@ export async function generateApi(
475
501
 
476
502
  const parameters = supportDeepObjects([...pathItemParameters, ...operationParameters])
477
503
  .filter(argumentMatches(overrides?.parameterFilter))
478
- .filter(param => param.in !== 'header');
504
+ .filter((param) => param.in !== 'header');
479
505
 
480
506
  const allNames = parameters.map((p) => p.name);
481
507
  const queryArg: QueryArgDefinitions = {};
508
+
482
509
  function generateName(name: string, potentialPrefix: string) {
483
510
  const isPureSnakeCase = /^[a-zA-Z][a-zA-Z0-9_]*$/.test(name);
484
511
  const hasNamingConflict = allNames.filter((n) => n === name).length > 1;
@@ -501,7 +528,9 @@ export async function generateApi(
501
528
  origin: 'param',
502
529
  name,
503
530
  originalName: param.name,
504
- type: wrapWithSchemeIfComponent(apiGen.getTypeFromSchema(isReference(param) ? param : param.schema, undefined, 'writeOnly')),
531
+ type: wrapWithSchemeIfComponent(
532
+ apiGen.getTypeFromSchema(isReference(param) ? param : param.schema, undefined, 'writeOnly')
533
+ ),
505
534
  required: param.required,
506
535
  param,
507
536
  };
@@ -580,10 +609,7 @@ export async function generateApi(
580
609
  operationName: operationNameSuffix ? capitalize(operationName + operationNameSuffix) : operationName,
581
610
  type: isQuery ? 'query' : 'mutation',
582
611
  Response: ResponseTypeName,
583
- QueryArg: factory.createTypeReferenceNode(
584
- factory.createIdentifier('IRestFulEndpointsQueryReturn'),
585
- [QueryArg]
586
- ),
612
+ QueryArg: factory.createTypeReferenceNode(factory.createIdentifier('IRestFulEndpointsQueryReturn'), [QueryArg]),
587
613
  queryFn: generateQueryFn({
588
614
  operationDefinition,
589
615
  queryArg,
@@ -614,13 +640,24 @@ export async function generateApi(
614
640
  encodePathParams: boolean;
615
641
  encodeQueryParams: boolean;
616
642
  }) {
617
- const { path, verb } = operationDefinition;
643
+ const { path, verb, operation } = operationDefinition;
618
644
 
619
645
  const bodyParameter = Object.values(queryArg).find((def) => def.origin === 'body');
620
646
 
621
647
  const rootObject = factory.createIdentifier('queryArg');
622
648
  const variablesObject = factory.createPropertyAccessExpression(rootObject, factory.createIdentifier('variables'));
623
649
 
650
+ // 提取 content type 的輔助函數
651
+ function getContentType(): string | undefined {
652
+ if (operation.requestBody) {
653
+ const requestBody = apiGen.resolve(operation.requestBody);
654
+ const contentTypes = Object.keys(requestBody.content || {});
655
+ // 直接返回第一個可用的 content type
656
+ return contentTypes[0];
657
+ }
658
+ return undefined;
659
+ }
660
+
624
661
  function pickParams(paramIn: string) {
625
662
  return Object.values(queryArg).filter((def) => def.origin === 'param' && def.param.in === paramIn);
626
663
  }
@@ -629,8 +666,8 @@ export async function generateApi(
629
666
  if (parameters.length === 0) return undefined;
630
667
 
631
668
  const properties = parameters.map((param) => {
632
- const value = isFlatArg
633
- ? variablesObject
669
+ const value = isFlatArg
670
+ ? variablesObject
634
671
  : factory.createPropertyAccessExpression(variablesObject, factory.createIdentifier(param.name));
635
672
 
636
673
  const encodedValue =
@@ -655,6 +692,8 @@ export async function generateApi(
655
692
  );
656
693
  }
657
694
 
695
+ const contentType = getContentType();
696
+
658
697
  return factory.createArrowFunction(
659
698
  undefined,
660
699
  undefined,
@@ -674,13 +713,22 @@ export async function generateApi(
674
713
  factory.createIdentifier('method'),
675
714
  factory.createStringLiteral(verb.toUpperCase())
676
715
  ),
716
+ contentType
717
+ ? factory.createPropertyAssignment(
718
+ factory.createIdentifier('contentType'),
719
+ factory.createStringLiteral(contentType)
720
+ )
721
+ : undefined,
677
722
  bodyParameter === undefined
678
723
  ? undefined
679
724
  : factory.createPropertyAssignment(
680
725
  factory.createIdentifier('body'),
681
726
  isFlatArg
682
727
  ? variablesObject
683
- : factory.createPropertyAccessExpression(variablesObject, factory.createIdentifier(bodyParameter.name))
728
+ : factory.createPropertyAccessExpression(
729
+ variablesObject,
730
+ factory.createIdentifier(bodyParameter.name)
731
+ )
684
732
  ),
685
733
  createObjectLiteralProperty(pickParams('cookie'), 'cookies'),
686
734
  createObjectLiteralProperty(pickParams('query'), 'params'),
@@ -710,42 +758,36 @@ export async function generateApi(
710
758
  function wrapWithSchemeIfComponent(typeNode: ts.TypeNode): ts.TypeNode {
711
759
  if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {
712
760
  const typeName = typeNode.typeName.text;
713
-
761
+
714
762
  // 檢查是否為 enum 類型(包括在 enumAliases 和 aliases 中的)
715
- const isEnumType = useEnumType && (
716
- apiGen.enumAliases.some(enumDecl => {
763
+ const isEnumType =
764
+ useEnumType &&
765
+ (apiGen.enumAliases.some((enumDecl) => {
717
766
  if (ts.isEnumDeclaration(enumDecl) || ts.isTypeAliasDeclaration(enumDecl)) {
718
767
  return enumDecl.name.text === typeName;
719
768
  }
720
769
  return false;
721
770
  }) ||
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;
771
+ apiGen.aliases.some((alias) => {
772
+ if (ts.isTypeAliasDeclaration(alias) && alias.type) {
773
+ // 檢查是否為 union type 的 enum
774
+ if (ts.isUnionTypeNode(alias.type)) {
775
+ return alias.name.text === typeName;
776
+ }
727
777
  }
728
- }
729
- return false;
730
- })
731
- );
732
-
778
+ return false;
779
+ }));
780
+
733
781
  if (isEnumType) {
734
782
  return factory.createTypeReferenceNode(
735
- factory.createQualifiedName(
736
- factory.createIdentifier('Enum'),
737
- typeNode.typeName
738
- ),
783
+ factory.createQualifiedName(factory.createIdentifier('Enum'), typeNode.typeName),
739
784
  typeNode.typeArguments?.map(wrapWithSchemeIfComponent)
740
785
  );
741
786
  }
742
-
787
+
743
788
  if (schemeTypeNames.has(typeName)) {
744
789
  return factory.createTypeReferenceNode(
745
- factory.createQualifiedName(
746
- factory.createIdentifier('Scheme'),
747
- typeNode.typeName
748
- ),
790
+ factory.createQualifiedName(factory.createIdentifier('Scheme'), typeNode.typeName),
749
791
  typeNode.typeArguments?.map(wrapWithSchemeIfComponent)
750
792
  );
751
793
  }
@@ -762,42 +804,48 @@ export async function generateApi(
762
804
  if (ts.isUnionTypeNode(typeNode)) {
763
805
  // 檢查是否為 enum 的 union type
764
806
  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
- )) {
807
+ if (
808
+ unionTypes.length > 0 &&
809
+ unionTypes.every(
810
+ (type) =>
811
+ ts.isLiteralTypeNode(type) && (ts.isStringLiteral(type.literal) || ts.isNumericLiteral(type.literal))
812
+ )
813
+ ) {
769
814
  // 這是一個 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;
815
+ const enumValues = unionTypes
816
+ .map((type) => {
817
+ if (ts.isLiteralTypeNode(type)) {
818
+ if (ts.isStringLiteral(type.literal)) {
819
+ return type.literal.text;
820
+ } else if (ts.isNumericLiteral(type.literal)) {
821
+ return type.literal.text;
822
+ }
776
823
  }
777
- }
778
- return null;
779
- }).filter(Boolean);
780
-
824
+ return null;
825
+ })
826
+ .filter(Boolean);
827
+
781
828
  // 查找對應的 enum 類型
782
- const matchingEnum = apiGen.aliases.find(alias => {
829
+ const matchingEnum = apiGen.aliases.find((alias) => {
783
830
  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;
831
+ const aliasValues = alias.type.types
832
+ .map((type) => {
833
+ if (ts.isLiteralTypeNode(type)) {
834
+ if (ts.isStringLiteral(type.literal)) {
835
+ return type.literal.text;
836
+ } else if (ts.isNumericLiteral(type.literal)) {
837
+ return type.literal.text;
838
+ }
790
839
  }
791
- }
792
- return null;
793
- }).filter(Boolean);
794
-
795
- return aliasValues.length === enumValues.length &&
796
- aliasValues.every(val => enumValues.includes(val));
840
+ return null;
841
+ })
842
+ .filter(Boolean);
843
+
844
+ return aliasValues.length === enumValues.length && aliasValues.every((val) => enumValues.includes(val));
797
845
  }
798
846
  return false;
799
847
  });
800
-
848
+
801
849
  // 對於所有的 enum 類型,直接使用字串型別,不轉換為 Enum
802
850
  // 這樣可以避免自動命名造成的變更問題
803
851
  if (matchingEnum && ts.isTypeAliasDeclaration(matchingEnum)) {
@@ -805,12 +853,12 @@ export async function generateApi(
805
853
  return typeNode;
806
854
  }
807
855
  }
808
-
856
+
809
857
  return factory.createUnionTypeNode(typeNode.types.map(wrapWithSchemeIfComponent));
810
858
  }
811
859
  if (ts.isTypeLiteralNode(typeNode)) {
812
860
  return factory.createTypeLiteralNode(
813
- typeNode.members.map(member => {
861
+ typeNode.members.map((member) => {
814
862
  if (ts.isPropertySignature(member) && member.type) {
815
863
  return factory.updatePropertySignature(
816
864
  member,
@@ -856,8 +904,8 @@ function generatePathExpression(
856
904
  ? factory.createTemplateExpression(
857
905
  factory.createTemplateHead(head),
858
906
  expressions.map(([prop, literal], index) => {
859
- const value = isFlatArg
860
- ? rootObject
907
+ const value = isFlatArg
908
+ ? rootObject
861
909
  : factory.createPropertyAccessExpression(rootObject, factory.createIdentifier(prop));
862
910
  const encodedValue = encodePathParams
863
911
  ? factory.createCallExpression(factory.createIdentifier('encodeURIComponent'), undefined, [
@@ -11,39 +11,43 @@ type GetReactHookNameParams = {
11
11
  operationDefinition: OperationDefinition;
12
12
  endpointOverrides: EndpointOverrides[] | undefined;
13
13
  config: HooksConfigOptions;
14
+ queryMatch?: (method: string, path: string) => boolean;
14
15
  };
15
16
 
16
17
  type CreateBindingParams = {
17
18
  operationDefinition: OperationDefinition;
18
19
  overrides?: EndpointOverrides;
19
20
  isLazy?: boolean;
21
+ queryMatch?: (method: string, path: string) => boolean;
20
22
  };
21
23
 
22
24
  const createBinding = ({
23
25
  operationDefinition: { verb, path },
24
26
  overrides,
25
27
  isLazy = false,
28
+ queryMatch,
26
29
  }: CreateBindingParams) =>
27
30
  factory.createBindingElement(
28
31
  undefined,
29
32
  undefined,
30
33
  factory.createIdentifier(
31
34
  `use${isLazy ? 'Lazy' : ''}${capitalize(getOperationName(verb, path, undefined))}${
32
- isQuery(verb, overrides) ? 'Query' : 'Mutation'
35
+ isQuery(verb, path, overrides, queryMatch) ? 'Query' : 'Mutation'
33
36
  }`
34
37
  ),
35
38
  undefined
36
39
  );
37
40
 
38
- const getReactHookName = ({ operationDefinition, endpointOverrides, config }: GetReactHookNameParams) => {
41
+ const getReactHookName = ({ operationDefinition, endpointOverrides, config, queryMatch }: GetReactHookNameParams) => {
39
42
  const overrides = getOverrides(operationDefinition, endpointOverrides);
40
43
 
41
44
  const baseParams = {
42
45
  operationDefinition,
43
46
  overrides,
47
+ queryMatch,
44
48
  };
45
49
 
46
- const _isQuery = isQuery(operationDefinition.verb, overrides);
50
+ const _isQuery = isQuery(operationDefinition.verb, operationDefinition.path, overrides, queryMatch);
47
51
 
48
52
  // If `config` is true, just generate everything
49
53
  if (typeof config === 'boolean') {
@@ -66,12 +70,14 @@ type GenerateReactHooksParams = {
66
70
  operationDefinitions: OperationDefinition[];
67
71
  endpointOverrides: EndpointOverrides[] | undefined;
68
72
  config: HooksConfigOptions;
73
+ queryMatch?: (method: string, path: string) => boolean;
69
74
  };
70
75
  export const generateReactHooks = ({
71
76
  exportName,
72
77
  operationDefinitions,
73
78
  endpointOverrides,
74
79
  config,
80
+ queryMatch,
75
81
  }: GenerateReactHooksParams) =>
76
82
  factory.createVariableStatement(
77
83
  [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
@@ -80,7 +86,7 @@ export const generateReactHooks = ({
80
86
  factory.createVariableDeclaration(
81
87
  factory.createObjectBindingPattern(
82
88
  operationDefinitions
83
- .map((operationDefinition) => getReactHookName({ operationDefinition, endpointOverrides, config }))
89
+ .map((operationDefinition) => getReactHookName({ operationDefinition, endpointOverrides, config, queryMatch }))
84
90
  .flat()
85
91
  ),
86
92
  undefined,