@acrool/rtk-query-codegen-openapi 0.0.2-test.6 → 0.0.2-test.7

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
@@ -38,8 +38,8 @@ function defaultIsDataResponse(code: string, includeDefault: boolean) {
38
38
  return !Number.isNaN(parsedCode) && parsedCode >= 200 && parsedCode < 300;
39
39
  }
40
40
 
41
- function getOperationName({ verb, path, operation }: Pick<OperationDefinition, 'verb' | 'path' | 'operation'>) {
42
- return _getOperationName(verb, path, operation.operationId);
41
+ function getOperationName({ verb, path }: Pick<OperationDefinition, 'verb' | 'path' >) {
42
+ return _getOperationName(verb, path, undefined);
43
43
  }
44
44
 
45
45
  function getTags({ verb, pathItem }: Pick<OperationDefinition, 'verb' | 'pathItem'>): string[] {
@@ -128,49 +128,72 @@ export async function generateApi(
128
128
  mergeReadWriteOnly,
129
129
  });
130
130
 
131
- // 如果提供了 sharedTypesFile,則將 components 輸出到該文件
131
+ const schemeTypeNames = new Set<string>();
132
+
133
+ function addSchemeTypeName(name: string) {
134
+ schemeTypeNames.add(name);
135
+ schemeTypeNames.add(camelCase(name));
136
+ schemeTypeNames.add(capitalize(camelCase(name)));
137
+ }
138
+
132
139
  if (sharedTypesFile) {
140
+ const resultFile = ts.createSourceFile(
141
+ 'sharedTypes.ts',
142
+ '',
143
+ ts.ScriptTarget.Latest,
144
+ /*setParentNodes*/ false,
145
+ ts.ScriptKind.TS
146
+ );
147
+ const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
148
+
149
+ const allTypeDefinitions: ts.Statement[] = [];
150
+
133
151
  const components = v3Doc.components;
134
152
  if (components) {
135
- const resultFile = ts.createSourceFile(
136
- 'sharedTypes.ts',
137
- '',
138
- ts.ScriptTarget.Latest,
139
- /*setParentNodes*/ false,
140
- ts.ScriptKind.TS
141
- );
142
- const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
143
-
144
- // 將 components 轉換為 TypeScript 類型定義
145
- const typeDefinitions = Object.entries(components).flatMap(([_, componentDefs]) => {
146
- return Object.entries(componentDefs as Record<string, unknown>).map(([name, def]) => {
147
- const typeNode = apiGen.getTypeFromSchema(def as OpenAPIV3.SchemaObject);
153
+ 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));
148
159
  return factory.createTypeAliasDeclaration(
149
160
  [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
150
- factory.createIdentifier(name),
161
+ factory.createIdentifier(typeName),
151
162
  undefined,
152
163
  typeNode
153
164
  );
154
165
  });
155
- });
156
166
 
157
- const output = printer.printNode(
158
- ts.EmitHint.Unspecified,
159
- factory.createSourceFile(
160
- typeDefinitions,
161
- factory.createToken(ts.SyntaxKind.EndOfFileToken),
162
- ts.NodeFlags.None
163
- ),
164
- resultFile
165
- );
167
+ return factory.createModuleDeclaration(
168
+ [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
169
+ factory.createIdentifier('Scheme'),
170
+ factory.createModuleBlock(typeEntries),
171
+ ts.NodeFlags.Namespace
172
+ );
173
+ });
174
+ allTypeDefinitions.push(...componentDefinitions);
175
+ }
166
176
 
167
- // 寫入文件
168
- const fs = await import('node:fs/promises');
169
- await fs.writeFile(sharedTypesFile, output, 'utf-8');
177
+ if (useEnumType) {
178
+ allTypeDefinitions.push(...apiGen.enumAliases);
170
179
  }
180
+
181
+ allTypeDefinitions.push(...apiGen.aliases);
182
+
183
+ const output = printer.printNode(
184
+ ts.EmitHint.Unspecified,
185
+ factory.createSourceFile(
186
+ allTypeDefinitions,
187
+ factory.createToken(ts.SyntaxKind.EndOfFileToken),
188
+ ts.NodeFlags.None
189
+ ),
190
+ resultFile
191
+ );
192
+
193
+ const fs = await import('node:fs/promises');
194
+ await fs.writeFile(sharedTypesFile, output, 'utf-8');
171
195
  }
172
196
 
173
- // temporary workaround for https://github.com/oazapfts/oazapfts/issues/491
174
197
  if (apiGen.spec.components?.schemas) {
175
198
  apiGen.preprocessComponents(apiGen.spec.components.schemas);
176
199
  }
@@ -203,17 +226,18 @@ export async function generateApi(
203
226
  apiFile = apiFile.replace(/\\/g, '/');
204
227
  if (!apiFile.startsWith('.')) apiFile = `./${apiFile}`;
205
228
  }
206
- // 處理 sharedTypesFile 的路徑
207
- if (sharedTypesFile && sharedTypesFile.startsWith('.')) {
208
- sharedTypesFile = path.relative(path.dirname(outputFile), sharedTypesFile);
209
- sharedTypesFile = sharedTypesFile.replace(/\\/g, '/');
210
- if (!sharedTypesFile.startsWith('.')) sharedTypesFile = `./${sharedTypesFile}`;
211
- }
212
229
  }
213
230
  apiFile = apiFile.replace(/\.[jt]sx?$/, '');
214
- if (sharedTypesFile) {
215
- sharedTypesFile = sharedTypesFile.replace(/\.[jt]sx?$/, '');
216
- }
231
+
232
+ const sharedTypesImportPath = sharedTypesFile && outputFile
233
+ ? (() => {
234
+ let rel = path.relative(path.dirname(outputFile), sharedTypesFile)
235
+ .replace(/\\/g, '/')
236
+ .replace(/\.[jt]sx?$/, '');
237
+ if (!rel.startsWith('.')) rel = './' + rel;
238
+ return rel;
239
+ })()
240
+ : './shared-types';
217
241
 
218
242
  return printer.printNode(
219
243
  ts.EmitHint.Unspecified,
@@ -221,18 +245,7 @@ export async function generateApi(
221
245
  [
222
246
  generateImportNode(apiFile, { [apiImport]: 'api' }),
223
247
  generateImportNode('@acrool/react-fetcher', { IRestFulEndpointsQueryReturn: 'IRestFulEndpointsQueryReturn' }),
224
- ...(sharedTypesFile ? [
225
- factory.createImportDeclaration(
226
- undefined,
227
- factory.createImportClause(
228
- false,
229
- undefined,
230
- factory.createNamespaceImport(factory.createIdentifier('SharedTypes'))
231
- ),
232
- factory.createStringLiteral(sharedTypesFile),
233
- undefined
234
- )
235
- ] : []),
248
+ ...(sharedTypesFile ? [generateImportNode(sharedTypesImportPath, { Scheme: 'Scheme' })] : []),
236
249
  ...(tag ? [generateTagTypes({ addTagTypes: extractAllTagTypes({ operationDefinitions }) })] : []),
237
250
  generateCreateApiCall({
238
251
  tag,
@@ -241,6 +254,7 @@ export async function generateApi(
241
254
  generateEndpoint({
242
255
  operationDefinition,
243
256
  overrides: getOverrides(operationDefinition, endpointOverrides),
257
+ sharedTypesFile: !!sharedTypesFile,
244
258
  })
245
259
  ),
246
260
  true
@@ -285,9 +299,11 @@ export async function generateApi(
285
299
  function generateEndpoint({
286
300
  operationDefinition,
287
301
  overrides,
302
+ sharedTypesFile,
288
303
  }: {
289
304
  operationDefinition: OperationDefinition;
290
305
  overrides?: EndpointOverrides;
306
+ sharedTypesFile: boolean;
291
307
  }) {
292
308
  const {
293
309
  verb,
@@ -296,73 +312,56 @@ export async function generateApi(
296
312
  operation,
297
313
  operation: { responses, requestBody },
298
314
  } = operationDefinition;
299
- const operationName = getOperationName({ verb, path, operation });
315
+ const operationName = getOperationName({ verb, path });
300
316
  const tags = tag ? getTags({ verb, pathItem }) : [];
301
317
  const isQuery = testIsQuery(verb, overrides);
302
318
 
303
319
  const returnsJson = apiGen.getResponseType(responses) === 'json';
304
320
  let ResponseType: ts.TypeNode = factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
305
-
306
- function replaceReferences(schema: any): ts.TypeNode {
307
- if (!schema) return factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
308
-
309
- const refName = getReferenceName(schema);
310
- if (refName && sharedTypesFile) {
311
- return factory.createTypeReferenceNode(
312
- factory.createQualifiedName(
313
- factory.createIdentifier('SharedTypes'),
314
- factory.createIdentifier(refName)
315
- ),
316
- undefined
317
- );
318
- }
319
-
320
- if (schema.type === 'object' && schema.properties) {
321
- const members = Object.entries(schema.properties).map(([key, value]: [string, any]) => {
322
- return factory.createPropertySignature(
323
- undefined,
324
- factory.createIdentifier(key),
325
- schema.required?.includes(key) ? undefined : factory.createToken(ts.SyntaxKind.QuestionToken),
326
- replaceReferences(value)
327
- );
328
- });
329
- return factory.createTypeLiteralNode(members);
330
- }
331
-
332
- if (schema.type === 'array' && schema.items) {
333
- return factory.createArrayTypeNode(replaceReferences(schema.items));
334
- }
335
-
336
- return apiGen.getTypeFromSchema(schema);
337
- }
338
-
339
321
  if (returnsJson) {
340
322
  const returnTypes = Object.entries(responses || {})
341
323
  .map(
342
- ([code, response]) => {
343
- const resolvedResponse = apiGen.resolve(response);
344
- if (!resolvedResponse.content?.['application/json']?.schema) {
345
- return [code, resolvedResponse, factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword)] as const;
346
- }
347
-
348
- const schema = resolvedResponse.content['application/json'].schema;
349
- const type = replaceReferences(schema);
350
-
351
- return [code, resolvedResponse, type] as const;
352
- }
324
+ ([code, response]) =>
325
+ [
326
+ code,
327
+ apiGen.resolve(response),
328
+ wrapWithSchemeIfComponent(
329
+ apiGen.getTypeFromResponse(response, 'readOnly') ||
330
+ factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword)
331
+ ),
332
+ ] as const
353
333
  )
354
334
  .filter(([status, response]) =>
355
335
  isDataResponse(status, includeDefault, apiGen.resolve(response), responses || {})
356
336
  )
357
337
  .filter(([_1, _2, type]) => type !== keywordType.void)
358
- .map(([code, response, type]) =>
359
- ts.addSyntheticLeadingComment(
360
- { ...type },
338
+ .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
+ return ts.addSyntheticLeadingComment(
359
+ type,
361
360
  ts.SyntaxKind.MultiLineCommentTrivia,
362
361
  `* status ${code} ${response.description} `,
363
362
  false
364
- )
365
- );
363
+ );
364
+ });
366
365
  if (returnTypes.length > 0) {
367
366
  ResponseType = factory.createUnionTypeNode(returnTypes);
368
367
  }
@@ -392,28 +391,36 @@ export async function generateApi(
392
391
  const queryArg: QueryArgDefinitions = {};
393
392
  function generateName(name: string, potentialPrefix: string) {
394
393
  const isPureSnakeCase = /^[a-zA-Z][a-zA-Z0-9_]*$/.test(name);
395
- // prefix with `query`, `path` or `body` if there are multiple paramters with the same name
396
394
  const hasNamingConflict = allNames.filter((n) => n === name).length > 1;
397
395
  if (hasNamingConflict) {
398
396
  name = `${potentialPrefix}_${name}`;
399
397
  }
400
- // convert to camelCase if the name is pure snake_case and there are no naming conflicts
401
398
  const camelCaseName = camelCase(name);
402
399
  if (isPureSnakeCase && !allNames.includes(camelCaseName)) {
403
400
  name = camelCaseName;
404
401
  }
405
- // if there are still any naming conflicts, prepend with underscore
406
402
  while (name in queryArg) {
407
403
  name = `_${name}`;
408
404
  }
409
405
  return name;
410
406
  }
411
407
 
408
+ for (const param of parameters) {
409
+ const name = generateName(param.name, param.in);
410
+ queryArg[name] = {
411
+ origin: 'param',
412
+ name,
413
+ originalName: param.name,
414
+ type: wrapWithSchemeIfComponent(apiGen.getTypeFromSchema(isReference(param) ? param : param.schema, undefined, 'writeOnly')),
415
+ required: param.required,
416
+ param,
417
+ };
418
+ }
419
+
412
420
  if (requestBody) {
413
421
  const body = apiGen.resolve(requestBody);
414
422
  const schema = apiGen.getSchemaFromContent(body.content);
415
- const type = replaceReferences(schema);
416
-
423
+ const type = wrapWithSchemeIfComponent(apiGen.getTypeFromSchema(schema));
417
424
  const schemaName = camelCase(
418
425
  (type as any).name ||
419
426
  getReferenceName(schema) ||
@@ -426,27 +433,12 @@ export async function generateApi(
426
433
  origin: 'body',
427
434
  name,
428
435
  originalName: schemaName,
429
- type,
436
+ type: wrapWithSchemeIfComponent(apiGen.getTypeFromSchema(schema, undefined, 'writeOnly')),
430
437
  required: true,
431
438
  body,
432
439
  };
433
440
  }
434
441
 
435
- for (const param of parameters) {
436
- const name = generateName(param.name, param.in);
437
- const paramSchema = isReference(param) ? param : param.schema;
438
- const type = replaceReferences(paramSchema);
439
-
440
- queryArg[name] = {
441
- origin: 'param',
442
- name,
443
- originalName: param.name,
444
- type,
445
- required: param.required,
446
- param,
447
- };
448
- }
449
-
450
442
  const propertyName = (name: string | ts.PropertyName): ts.PropertyName => {
451
443
  if (typeof name === 'string') {
452
444
  return isValidIdentifier(name) ? factory.createIdentifier(name) : factory.createStringLiteral(name);
@@ -617,14 +609,56 @@ export async function generateApi(
617
609
  );
618
610
  }
619
611
 
620
- // eslint-disable-next-line no-empty-pattern
621
612
  function generateQueryEndpointProps({}: { operationDefinition: OperationDefinition }): ObjectPropertyDefinitions {
622
- return {}; /* TODO needs implementation - skip for now */
613
+ return {};
623
614
  }
624
615
 
625
- // eslint-disable-next-line no-empty-pattern
626
616
  function generateMutationEndpointProps({}: { operationDefinition: OperationDefinition }): ObjectPropertyDefinitions {
627
- return {}; /* TODO needs implementation - skip for now */
617
+ return {};
618
+ }
619
+
620
+ function wrapWithSchemeIfComponent(typeNode: ts.TypeNode): ts.TypeNode {
621
+ if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {
622
+ const typeName = typeNode.typeName.text;
623
+ if (schemeTypeNames.has(typeName)) {
624
+ return factory.createTypeReferenceNode(
625
+ factory.createQualifiedName(
626
+ factory.createIdentifier('Scheme'),
627
+ typeNode.typeName
628
+ ),
629
+ typeNode.typeArguments?.map(wrapWithSchemeIfComponent)
630
+ );
631
+ }
632
+ if (typeNode.typeArguments) {
633
+ return factory.createTypeReferenceNode(
634
+ typeNode.typeName,
635
+ typeNode.typeArguments.map(wrapWithSchemeIfComponent)
636
+ );
637
+ }
638
+ }
639
+ if (ts.isArrayTypeNode(typeNode)) {
640
+ return factory.createArrayTypeNode(wrapWithSchemeIfComponent(typeNode.elementType));
641
+ }
642
+ if (ts.isUnionTypeNode(typeNode)) {
643
+ return factory.createUnionTypeNode(typeNode.types.map(wrapWithSchemeIfComponent));
644
+ }
645
+ if (ts.isTypeLiteralNode(typeNode)) {
646
+ return factory.createTypeLiteralNode(
647
+ typeNode.members.map(member => {
648
+ if (ts.isPropertySignature(member) && member.type) {
649
+ return factory.updatePropertySignature(
650
+ member,
651
+ member.modifiers,
652
+ member.name,
653
+ member.questionToken,
654
+ wrapWithSchemeIfComponent(member.type)
655
+ );
656
+ }
657
+ return member;
658
+ })
659
+ );
660
+ }
661
+ return typeNode;
628
662
  }
629
663
  }
630
664
 
@@ -20,7 +20,7 @@ type CreateBindingParams = {
20
20
  };
21
21
 
22
22
  const createBinding = ({
23
- operationDefinition: { verb, path, operation },
23
+ operationDefinition: { verb, path },
24
24
  overrides,
25
25
  isLazy = false,
26
26
  }: CreateBindingParams) =>
@@ -28,7 +28,7 @@ const createBinding = ({
28
28
  undefined,
29
29
  undefined,
30
30
  factory.createIdentifier(
31
- `use${isLazy ? 'Lazy' : ''}${capitalize(getOperationName(verb, path, operation.operationId))}${
31
+ `use${isLazy ? 'Lazy' : ''}${capitalize(getOperationName(verb, path, undefined))}${
32
32
  isQuery(verb, overrides) ? 'Query' : 'Mutation'
33
33
  }`
34
34
  ),
package/src/index.ts CHANGED
@@ -4,10 +4,84 @@ import path from 'node:path';
4
4
  import { generateApi } from './generate';
5
5
  import type { CommonOptions, ConfigFile, GenerationOptions, OutputFileOptions } from './types';
6
6
  import { isValidUrl, prettify } from './utils';
7
- export type { ConfigFile } from './types';
7
+ import camelCase from 'lodash.camelcase';
8
+ export type { OutputFilesConfig, ConfigFile } from './types';
8
9
 
9
10
  const require = createRequire(__filename);
10
11
 
12
+
13
+
14
+ // 確保目錄存在的函數
15
+ async function ensureDirectoryExists(filePath: string) {
16
+ const dirname = path.dirname(filePath);
17
+ if (!fs.existsSync(dirname)) {
18
+ await fs.promises.mkdir(dirname, { recursive: true });
19
+ }
20
+ }
21
+
22
+
23
+ // 檢查檔案是否存在的函數
24
+ function fileExists(filePath: string): boolean {
25
+ try {
26
+ return fs.statSync(filePath).isFile();
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+
32
+ // 獲取資料夾名稱並轉換為 API 名稱
33
+ function getApiNameFromDir(dirPath: string): string {
34
+ const dirName = path.basename(dirPath);
35
+ return `${dirName}Api`;
36
+ }
37
+
38
+ // 確保基礎文件存在的函數
39
+ async function ensureBaseFilesExist(outputDir: string) {
40
+ const enhanceEndpointsPath = path.join(outputDir, 'enhanceEndpoints.ts');
41
+ const indexPath = path.join(outputDir, 'index.ts');
42
+ const apiName = getApiNameFromDir(outputDir);
43
+
44
+ // 如果 enhanceEndpoints.ts 不存在,創建它
45
+ if (!fileExists(enhanceEndpointsPath)) {
46
+ const enhanceEndpointsContent = `import api from './query.generated';
47
+
48
+ const enhancedApi = api.enhanceEndpoints({
49
+ endpoints: {
50
+ },
51
+ });
52
+
53
+ export default enhancedApi;
54
+ `;
55
+ await fs.promises.writeFile(enhanceEndpointsPath, enhanceEndpointsContent, 'utf-8');
56
+ }
57
+
58
+ // 如果 index.ts 不存在,創建它
59
+ if (!fileExists(indexPath)) {
60
+ const indexContent = `export * from './query.generated';
61
+ export {default as ${apiName}} from './enhanceEndpoints';
62
+ `;
63
+ await fs.promises.writeFile(indexPath, indexContent, 'utf-8');
64
+ }
65
+ }
66
+
67
+
68
+ // 從路徑中提取分類名稱
69
+ function getGroupNameFromPath(path: string, pattern: RegExp): string {
70
+ // console.log('pattern', pattern);
71
+
72
+ const match = path.match(pattern);
73
+ // console.log('match', path, match);
74
+
75
+ if (match && match[1]) {
76
+ return camelCase(match[1]);
77
+ }
78
+ return 'common';
79
+ }
80
+
81
+
82
+
83
+
84
+
11
85
  export async function generateEndpoints(options: GenerationOptions): Promise<string | void> {
12
86
  const schemaLocation = options.schemaFile;
13
87
 
@@ -20,8 +94,15 @@ export async function generateEndpoints(options: GenerationOptions): Promise<str
20
94
  });
21
95
  const { outputFile, prettierConfigFile } = options;
22
96
  if (outputFile) {
97
+ const outputPath = path.resolve(process.cwd(), outputFile);
98
+ await ensureDirectoryExists(outputPath);
99
+
100
+ // 確保基礎文件存在
101
+ const outputDir = path.dirname(outputPath);
102
+ await ensureBaseFilesExist(outputDir);
103
+
23
104
  fs.writeFileSync(
24
- path.resolve(process.cwd(), outputFile),
105
+ outputPath,
25
106
  await prettify(outputFile, sourceCode, prettierConfigFile)
26
107
  );
27
108
  } else {
@@ -29,18 +110,49 @@ export async function generateEndpoints(options: GenerationOptions): Promise<str
29
110
  }
30
111
  }
31
112
 
113
+
32
114
  export function parseConfig(fullConfig: ConfigFile) {
33
115
  const outFiles: (CommonOptions & OutputFileOptions)[] = [];
34
116
 
35
117
  if ('outputFiles' in fullConfig) {
36
118
  const { outputFiles, ...commonConfig } = fullConfig;
37
- for (const [outputFile, specificConfig] of Object.entries(outputFiles)) {
38
- outFiles.push({
39
- ...commonConfig,
40
- ...specificConfig,
41
- outputFile,
119
+
120
+ // 讀取 OpenAPI 文檔
121
+ const openApiDoc = JSON.parse(fs.readFileSync(fullConfig.schemaFile, 'utf-8'));
122
+ const paths = Object.keys(openApiDoc.paths);
123
+
124
+
125
+ // 從配置中獲取分類規則
126
+ const [outputPath, config] = Object.entries(outputFiles)[0];
127
+ const patterns = config.groupMatch;
128
+
129
+ const filterEndpoint = config.filterEndpoint;
130
+
131
+
132
+ const pattern = patterns;
133
+ // 根據路徑自動分類
134
+ const groupedPaths = paths.reduce((acc, path) => {
135
+
136
+ const groupName = getGroupNameFromPath(path, pattern);
137
+ if (!acc[groupName]) {
138
+ acc[groupName] = [];
139
+ }
140
+ acc[groupName].push(path);
141
+ return acc;
142
+ }, {} as Record<string, string[]>);
143
+
144
+ // 為每個分類生成配置
145
+ Object.entries(groupedPaths).forEach(([groupName, paths]) => {
146
+ const finalOutputPath = outputPath.replace('$1', groupName);
147
+
148
+ const filterEndpoints = filterEndpoint(groupName);
149
+ outFiles.push({
150
+ ...commonConfig,
151
+ outputFile: finalOutputPath,
152
+ filterEndpoints: [filterEndpoints],
153
+ });
42
154
  });
43
- }
155
+
44
156
  } else {
45
157
  outFiles.push(fullConfig);
46
158
  }
package/src/types.ts CHANGED
@@ -142,10 +142,18 @@ export type EndpointOverrides = {
142
142
  parameterFilter: ParameterMatcher;
143
143
  }>;
144
144
 
145
+ export type OutputFilesConfig = {
146
+ [outputFile: string]: {
147
+ groupMatch: RegExp,
148
+ filterEndpoint: (groupName: string) => RegExp
149
+ }
150
+ };
151
+
145
152
  export type ConfigFile =
146
153
  | Id<Require<CommonOptions & OutputFileOptions, 'outputFile'>>
147
154
  | Id<
148
155
  Omit<CommonOptions, 'outputFile'> & {
149
- outputFiles: { [outputFile: string]: Omit<OutputFileOptions, 'outputFile'> };
156
+ // outputFiles: { [outputFile: string]: Omit<OutputFileOptions, 'outputFile'> };
157
+ outputFiles: OutputFilesConfig
150
158
  }
151
159
  >;