@bitstack/ng-query-codegen-openapi 0.0.30 → 0.0.31-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/README.md +8 -1
- package/lib/bin/cli.mjs +78 -60
- package/lib/bin/cli.mjs.map +1 -1
- package/lib/index.d.mts +4 -13
- package/lib/index.d.ts +4 -13
- package/lib/index.js +351 -290
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +351 -290
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/bin/utils.ts +74 -3
- package/src/generators/common-types-generator.ts +6 -2
- package/src/generators/component-schema-generator.ts +40 -7
- package/src/generators/do-not-modify-generator.ts +2 -2
- package/src/generators/rtk-enhance-endpoints-generator.ts +113 -0
- package/src/generators/{query-service-generator.ts → rtk-query-generator.ts} +45 -33
- package/src/generators/tag-types-generator.ts +30 -0
- package/src/generators/types-generator.ts +216 -112
- package/src/generators/utils-generator.ts +2 -4
- package/src/index.ts +6 -3
- package/src/services/api-code-generator.ts +62 -74
- package/src/services/endpoint-info-extractor.ts +66 -14
- package/src/services/file-writer-service.ts +27 -23
- package/src/services/openapi-parser-service.ts +10 -2
- package/src/services/unified-code-generator.ts +55 -26
- package/src/types.ts +19 -40
- package/src/generators/api-service-generator.ts +0 -112
- package/src/generators/cache-keys-generator.ts +0 -43
- package/src/generators/index-generator.ts +0 -11
- package/src/services/api-service-generator.ts +0 -24
- package/src/services/query-code-generator.ts +0 -24
package/src/types.ts
CHANGED
|
@@ -3,22 +3,13 @@ import type { OpenAPIV3 } from 'openapi-types';
|
|
|
3
3
|
import ts from 'typescript';
|
|
4
4
|
|
|
5
5
|
// 重新匯出服務相關類型
|
|
6
|
-
export type {
|
|
7
|
-
GroupConfig,
|
|
8
|
-
GroupInfo
|
|
9
|
-
} from './services/group-service';
|
|
6
|
+
export type { GroupConfig, GroupInfo } from './services/group-service';
|
|
10
7
|
|
|
11
|
-
export type {
|
|
12
|
-
FileWriteResult
|
|
13
|
-
} from './services/file-writer-service';
|
|
14
|
-
|
|
15
|
-
export type {
|
|
16
|
-
EndpointCacheKey
|
|
17
|
-
} from './services/unified-code-generator';
|
|
8
|
+
export type { FileWriteResult } from './services/file-writer-service';
|
|
18
9
|
|
|
19
10
|
export type {
|
|
20
11
|
UnifiedGenerationOptions as EndpointGenerationOptions,
|
|
21
|
-
UnifiedGenerationResult as EndpointGenerationResult
|
|
12
|
+
UnifiedGenerationResult as EndpointGenerationResult,
|
|
22
13
|
} from './services/unified-code-generator';
|
|
23
14
|
|
|
24
15
|
export type OperationDefinition = {
|
|
@@ -69,12 +60,12 @@ export interface CommonOptions {
|
|
|
69
60
|
importName: string;
|
|
70
61
|
};
|
|
71
62
|
/**
|
|
72
|
-
*
|
|
63
|
+
* HTTP client configuration for API calls
|
|
73
64
|
* defaults to { file: "@core/httpClient/webapi/webapi-http-client.providers", importName: "WEBAPI_HTTP_CLIENT" }
|
|
74
65
|
*/
|
|
75
66
|
httpClient?: {
|
|
76
67
|
file: string;
|
|
77
|
-
|
|
68
|
+
importReturnTypeName: string; // 用於指定別名導入,例如 IRestFulEndpointsQueryReturn
|
|
78
69
|
};
|
|
79
70
|
/**
|
|
80
71
|
* defaults to "enhancedApi"
|
|
@@ -94,9 +85,9 @@ export interface CommonOptions {
|
|
|
94
85
|
operationNameSuffix?: string;
|
|
95
86
|
/**
|
|
96
87
|
* defaults to `false`
|
|
97
|
-
* `true` will generate
|
|
88
|
+
* `true` will generate lazy query hooks (useLazy prefix) for query endpoints
|
|
98
89
|
*/
|
|
99
|
-
|
|
90
|
+
useLazyQueries?: boolean;
|
|
100
91
|
/**
|
|
101
92
|
* defaults to false
|
|
102
93
|
* `true` will generate a union type for `undefined` properties like: `{ id?: string | undefined }` instead of `{ id?: string }`
|
|
@@ -173,11 +164,6 @@ export interface OutputFileOptions extends Partial<CommonOptions> {
|
|
|
173
164
|
filterEndpoints?: EndpointMatcher;
|
|
174
165
|
endpointOverrides?: EndpointOverrides[];
|
|
175
166
|
queryMatch?: (method: string, path: string) => boolean;
|
|
176
|
-
/**
|
|
177
|
-
* defaults to false
|
|
178
|
-
* If passed as true it will generate TS enums instead of union of strings
|
|
179
|
-
*/
|
|
180
|
-
useEnumType?: boolean;
|
|
181
167
|
sharedTypesFile?: string;
|
|
182
168
|
/**
|
|
183
169
|
* groupKey for service class naming, e.g., "room" -> "RoomService"
|
|
@@ -204,30 +190,23 @@ export type ConfigFile =
|
|
|
204
190
|
| Id<
|
|
205
191
|
Omit<CommonOptions, 'outputFile'> & {
|
|
206
192
|
// outputFiles: { [outputFile: string]: Omit<OutputFileOptions, 'outputFile'> };
|
|
207
|
-
outputFiles: OutputFilesConfig
|
|
193
|
+
outputFiles: OutputFilesConfig;
|
|
208
194
|
}
|
|
209
195
|
>;
|
|
210
196
|
|
|
211
197
|
export type GenerateApiResult = {
|
|
212
198
|
operationNames: string[];
|
|
199
|
+
tags: string[]; // 收集到的所有 tags
|
|
213
200
|
files: {
|
|
214
201
|
types: string;
|
|
215
|
-
|
|
216
|
-
queryService: string;
|
|
202
|
+
queryService: string; // RTK Query generated file
|
|
217
203
|
index: string;
|
|
204
|
+
enhanceEndpoints?: string; // RTK Query enhance endpoints file
|
|
218
205
|
commonTypes?: string;
|
|
219
|
-
cacheKeys?: string;
|
|
220
206
|
componentSchema?: string;
|
|
221
|
-
allEndpointCacheKeys?: Array<{
|
|
222
|
-
operationName: string
|
|
223
|
-
queryKeyName: string
|
|
224
|
-
groupKey: string
|
|
225
|
-
}>;
|
|
226
207
|
};
|
|
227
208
|
};
|
|
228
209
|
|
|
229
|
-
|
|
230
|
-
|
|
231
210
|
export type QueryArgDefinition = {
|
|
232
211
|
name: string;
|
|
233
212
|
originalName: string;
|
|
@@ -236,13 +215,13 @@ export type QueryArgDefinition = {
|
|
|
236
215
|
param?: OpenAPIV3.ParameterObject;
|
|
237
216
|
} & (
|
|
238
217
|
| {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
218
|
+
origin: 'param';
|
|
219
|
+
param: OpenAPIV3.ParameterObject;
|
|
220
|
+
}
|
|
242
221
|
| {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
222
|
+
origin: 'body';
|
|
223
|
+
body: OpenAPIV3.RequestBodyObject;
|
|
224
|
+
}
|
|
225
|
+
);
|
|
247
226
|
|
|
248
|
-
export type QueryArgDefinitions = Record<string, QueryArgDefinition>;
|
|
227
|
+
export type QueryArgDefinitions = Record<string, QueryArgDefinition>;
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import type { GenerationOptions } from '../types';
|
|
2
|
-
|
|
3
|
-
export function generateApiServiceFile(endpointInfos: Array<{
|
|
4
|
-
operationName: string;
|
|
5
|
-
argTypeName: string;
|
|
6
|
-
responseTypeName: string;
|
|
7
|
-
isQuery: boolean;
|
|
8
|
-
verb: string;
|
|
9
|
-
path: string;
|
|
10
|
-
queryKeyName: string;
|
|
11
|
-
queryParams: any[];
|
|
12
|
-
pathParams: any[];
|
|
13
|
-
isVoidArg: boolean;
|
|
14
|
-
summary: string;
|
|
15
|
-
}>, options: GenerationOptions) {
|
|
16
|
-
|
|
17
|
-
const { apiConfiguration, httpClient, groupKey } = options;
|
|
18
|
-
|
|
19
|
-
// 確定服務名稱,使用業務名稱
|
|
20
|
-
const apiServiceClassName = groupKey ?
|
|
21
|
-
`${groupKey.charAt(0).toUpperCase() + groupKey.slice(1)}ApiService` :
|
|
22
|
-
'ApiService';
|
|
23
|
-
|
|
24
|
-
return `/* eslint-disable */
|
|
25
|
-
// [Warning] Generated automatically - do not edit manually
|
|
26
|
-
|
|
27
|
-
import { ${httpClient!.importName} } from '${httpClient!.file}';
|
|
28
|
-
import { Injectable, inject } from '@angular/core';
|
|
29
|
-
import { Observable } from 'rxjs';
|
|
30
|
-
import { ${apiConfiguration!.importName} } from '${apiConfiguration!.file}';
|
|
31
|
-
import { RequestOptions, IRestFulEndpointsQueryReturn } from '../common-types';
|
|
32
|
-
import { withoutUndefined } from '../utils';
|
|
33
|
-
import {${endpointInfos.map(info => ` ${info.argTypeName}, ${info.responseTypeName}`).join(',')} } from './types';
|
|
34
|
-
|
|
35
|
-
@Injectable({
|
|
36
|
-
providedIn: 'root',
|
|
37
|
-
})
|
|
38
|
-
export class ${apiServiceClassName} {
|
|
39
|
-
private config = inject(${apiConfiguration!.importName});
|
|
40
|
-
private http = inject(${httpClient!.importName});
|
|
41
|
-
|
|
42
|
-
${endpointInfos.map(info => {
|
|
43
|
-
const isGet = info.verb.toUpperCase() === 'GET';
|
|
44
|
-
const hasArgs = info.argTypeName !== 'VoidApiArg';
|
|
45
|
-
|
|
46
|
-
// 生成 URL 模板字面量,直接替換路徑參數
|
|
47
|
-
const generateUrlTemplate = (path: string) => {
|
|
48
|
-
const hasPathParams = path.includes('{');
|
|
49
|
-
if (hasPathParams && hasArgs) {
|
|
50
|
-
// 將 {paramName} 替換為 ${args.variables.paramName}
|
|
51
|
-
const urlTemplate = path.replace(/\{([^}]+)\}/g, '${args.variables.$1}');
|
|
52
|
-
return `\`\${this.config.rootUrl}${urlTemplate}\``;
|
|
53
|
-
} else {
|
|
54
|
-
return `\`\${this.config.rootUrl}${path}\``;
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const hasQueryParams = info.queryParams.length > 0;
|
|
59
|
-
const hasBody = !isGet && hasArgs && !info.isVoidArg;
|
|
60
|
-
|
|
61
|
-
// 生成 HTTP 請求選項
|
|
62
|
-
const generateRequestOptions = () => {
|
|
63
|
-
const options = [
|
|
64
|
-
'...args.fetchOptions',
|
|
65
|
-
];
|
|
66
|
-
|
|
67
|
-
// 添加 Content-Type header,特別是對於有 body 的請求
|
|
68
|
-
if (hasBody || !isGet) {
|
|
69
|
-
options.push(`headers: { 'Content-Type': 'application/json', ...args.fetchOptions?.headers }`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (hasQueryParams && hasArgs) {
|
|
73
|
-
options.push(generateQueryParams(info.queryParams));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (hasBody) {
|
|
77
|
-
options.push('body: args.variables.body');
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return options.length > 0 ? `{ ${options.join(', ')} }` : '{}';
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
return `
|
|
84
|
-
/**
|
|
85
|
-
* ${info.summary}
|
|
86
|
-
*/
|
|
87
|
-
${info.operationName}(
|
|
88
|
-
${hasArgs ? `args: IRestFulEndpointsQueryReturn<${info.argTypeName}>, ` : ''}
|
|
89
|
-
): Observable<${info.responseTypeName}> {
|
|
90
|
-
const url = ${generateUrlTemplate(info.path)};
|
|
91
|
-
return this.http.request('${info.verb.toLowerCase()}', url, ${generateRequestOptions()});
|
|
92
|
-
}`;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}).join('')}
|
|
97
|
-
}
|
|
98
|
-
`;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* 產生 QueryParams 參數
|
|
104
|
-
* @param queryParams
|
|
105
|
-
*/
|
|
106
|
-
function generateQueryParams(queryParams: Record<string, string>[]) {
|
|
107
|
-
const paramEntries = queryParams.map(param =>
|
|
108
|
-
`${param.name}: args.variables.${param.name}`
|
|
109
|
-
).join(', ');
|
|
110
|
-
|
|
111
|
-
return `params: withoutUndefined({ ${paramEntries} })`;
|
|
112
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { toCamelCase } from './utils-generator';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export function generateCacheKeysFile(allEndpointInfos: Array<{
|
|
5
|
-
operationName: string;
|
|
6
|
-
queryKeyName: string;
|
|
7
|
-
groupKey: string;
|
|
8
|
-
}>) {
|
|
9
|
-
|
|
10
|
-
// 按 group 分組
|
|
11
|
-
const groupedKeys = allEndpointInfos.reduce((acc, info) => {
|
|
12
|
-
if (!acc[info.groupKey]) {
|
|
13
|
-
acc[info.groupKey] = [];
|
|
14
|
-
}
|
|
15
|
-
acc[info.groupKey].push({
|
|
16
|
-
operationName: info.operationName,
|
|
17
|
-
queryKeyName: info.queryKeyName
|
|
18
|
-
});
|
|
19
|
-
return acc;
|
|
20
|
-
}, {} as Record<string, Array<{operationName: string, queryKeyName: string}>>);
|
|
21
|
-
|
|
22
|
-
// 生成 enum 內容
|
|
23
|
-
const enumEntries = Object.entries(groupedKeys).map(([groupKey, keys]) => {
|
|
24
|
-
const groupComment = ` // ${groupKey.charAt(0).toUpperCase() + groupKey.slice(1)} related cache keys`;
|
|
25
|
-
const keyEntries = keys.map(key =>
|
|
26
|
-
` ${key.queryKeyName} = '${toCamelCase(key.queryKeyName)}',`
|
|
27
|
-
).join('\n');
|
|
28
|
-
|
|
29
|
-
return `${groupComment}\n${keyEntries}`;
|
|
30
|
-
}).join('\n\n');
|
|
31
|
-
|
|
32
|
-
return `// [Warning] Generated automatically - do not edit manually
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Cache keys enum for all API queries
|
|
36
|
-
*/
|
|
37
|
-
export enum ECacheKeys {
|
|
38
|
-
${enumEntries}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export default ECacheKeys;
|
|
42
|
-
`;
|
|
43
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { GenerationOptions } from '../types';
|
|
2
|
-
|
|
3
|
-
export function generateIndexFile(groupKey: string, options: GenerationOptions) {
|
|
4
|
-
const { groupKey: optionsGroupKey } = options;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
return `export * from './types';
|
|
8
|
-
export * from './api.service';
|
|
9
|
-
export * from './query.service';
|
|
10
|
-
`;
|
|
11
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { GenerationOptions } from '../types';
|
|
2
|
-
import { generateApiServiceFile } from '../generators/api-service-generator';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* API 服務程式碼生成器 - 專門負責生成 API Service 相關程式碼
|
|
6
|
-
*/
|
|
7
|
-
export class ApiServiceGenerator {
|
|
8
|
-
constructor(private options: GenerationOptions) {}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* 生成 API Service 檔案內容
|
|
12
|
-
*/
|
|
13
|
-
generateApiService(endpointInfos: Array<any>): string {
|
|
14
|
-
const generatorOptions = {
|
|
15
|
-
...this.options,
|
|
16
|
-
apiConfiguration: this.options.apiConfiguration || {
|
|
17
|
-
file: '@/store/webapi',
|
|
18
|
-
importName: 'WebApiConfiguration'
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
return generateApiServiceFile(endpointInfos, generatorOptions);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { GenerationOptions } from '../types';
|
|
2
|
-
import { generateQueryServiceFile } from '../generators/query-service-generator';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Query 服務程式碼生成器 - 專門負責生成 Query Service 相關程式碼
|
|
6
|
-
*/
|
|
7
|
-
export class QueryCodeGenerator {
|
|
8
|
-
constructor(private options: GenerationOptions) {}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* 生成 Query Service 檔案內容
|
|
12
|
-
*/
|
|
13
|
-
generateQueryService(endpointInfos: Array<any>): string {
|
|
14
|
-
const generatorOptions = {
|
|
15
|
-
...this.options,
|
|
16
|
-
apiConfiguration: this.options.apiConfiguration || {
|
|
17
|
-
file: '@/store/webapi',
|
|
18
|
-
importName: 'WebApiConfiguration'
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
return generateQueryServiceFile(endpointInfos, generatorOptions);
|
|
23
|
-
}
|
|
24
|
-
}
|