@bitstack/ng-query-codegen-openapi 0.0.31-alpha.0 → 0.0.31-alpha.3
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/bin/cli.mjs +43 -36
- package/lib/bin/cli.mjs.map +1 -1
- package/lib/index.d.mts +5 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +101 -86
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +101 -86
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/generators/common-types-generator.ts +2 -2
- package/src/generators/rtk-enhance-endpoints-generator.ts +17 -17
- package/src/generators/rtk-query-generator.ts +4 -4
- package/src/generators/tag-types-generator.ts +37 -21
- package/src/services/api-code-generator.ts +10 -0
- package/src/services/file-writer-service.ts +10 -12
- package/src/services/unified-code-generator.ts +73 -80
- package/src/types.ts +10 -0
package/lib/index.d.mts
CHANGED
|
@@ -127,6 +127,11 @@ interface CommonOptions {
|
|
|
127
127
|
* File path for importing IRestFulEndpointsQueryReturn type
|
|
128
128
|
*/
|
|
129
129
|
endpointsQueryReturnTypeFile?: string;
|
|
130
|
+
/**
|
|
131
|
+
* defaults to 60 (seconds)
|
|
132
|
+
* Number of seconds to wait before refetching data when component mounts or args change
|
|
133
|
+
*/
|
|
134
|
+
refetchOnMountOrArgChange?: number;
|
|
130
135
|
}
|
|
131
136
|
type TextMatcher = string | RegExp | (string | RegExp)[];
|
|
132
137
|
type EndpointMatcherFunction = (operationName: string, operationDefinition: OperationDefinition) => boolean;
|
package/lib/index.d.ts
CHANGED
|
@@ -127,6 +127,11 @@ interface CommonOptions {
|
|
|
127
127
|
* File path for importing IRestFulEndpointsQueryReturn type
|
|
128
128
|
*/
|
|
129
129
|
endpointsQueryReturnTypeFile?: string;
|
|
130
|
+
/**
|
|
131
|
+
* defaults to 60 (seconds)
|
|
132
|
+
* Number of seconds to wait before refetching data when component mounts or args change
|
|
133
|
+
*/
|
|
134
|
+
refetchOnMountOrArgChange?: number;
|
|
130
135
|
}
|
|
131
136
|
type TextMatcher = string | RegExp | (string | RegExp)[];
|
|
132
137
|
type EndpointMatcherFunction = (operationName: string, operationDefinition: OperationDefinition) => boolean;
|
package/lib/index.js
CHANGED
|
@@ -390,6 +390,9 @@ var FileWriterService = class {
|
|
|
390
390
|
if (sharedFiles.commonTypes) {
|
|
391
391
|
filesToWrite[import_node_path3.default.join(outputDir, "common-types.ts")] = sharedFiles.commonTypes;
|
|
392
392
|
}
|
|
393
|
+
if (sharedFiles.tagTypes) {
|
|
394
|
+
filesToWrite[import_node_path3.default.join(outputDir, "tagTypes.ts")] = sharedFiles.tagTypes;
|
|
395
|
+
}
|
|
393
396
|
if (sharedFiles.doNotModify) {
|
|
394
397
|
filesToWrite[import_node_path3.default.join(outputDir, "DO_NOT_MODIFY.md")] = sharedFiles.doNotModify;
|
|
395
398
|
}
|
|
@@ -491,12 +494,12 @@ export interface QueryConfig {
|
|
|
491
494
|
keepUnusedDataFor?: number,
|
|
492
495
|
}
|
|
493
496
|
|
|
494
|
-
export interface IRequestConfig extends RequestOptions {
|
|
497
|
+
export interface IRequestConfig extends RequestOptions {
|
|
495
498
|
timeout?: number;
|
|
496
499
|
}
|
|
497
500
|
|
|
498
501
|
export type IRestFulEndpointsQueryReturn<TVariables> = TVariables extends void ?
|
|
499
|
-
void | {fetchOptions?: IRequestConfig;}:
|
|
502
|
+
(void | {fetchOptions?: IRequestConfig;}):
|
|
500
503
|
{
|
|
501
504
|
variables: TVariables;
|
|
502
505
|
fetchOptions?: IRequestConfig;
|
|
@@ -926,7 +929,7 @@ function generateRtkQueryFile(endpointInfos, options) {
|
|
|
926
929
|
*/
|
|
927
930
|
${info.operationName}Query(${info.argTypeName !== "VoidApiArg" ? `args: IRestFulEndpointsQueryReturn<${info.argTypeName}>` : ""}): Observable<QueryState<${info.responseTypeName}>> {
|
|
928
931
|
return this.ntkQuery.query<${info.responseTypeName}, ${info.argTypeName !== "VoidApiArg" ? `IRestFulEndpointsQueryReturn<${info.argTypeName}>` : "void"}>({
|
|
929
|
-
queryKey: [
|
|
932
|
+
queryKey: [ECacheTagTypes.${info.queryKeyName}${info.argTypeName !== "VoidApiArg" ? ", args.variables" : ""}],
|
|
930
933
|
queryFn: () => {
|
|
931
934
|
return this.apiService.${info.operationName}(${info.argTypeName !== "VoidApiArg" ? "args" : ""});
|
|
932
935
|
},
|
|
@@ -964,7 +967,7 @@ function generateRtkQueryFile(endpointInfos, options) {
|
|
|
964
967
|
trigger: (${info.argTypeName !== "VoidApiArg" ? "args, " : ""}options = {}) => {
|
|
965
968
|
const { skipCache = true } = options;
|
|
966
969
|
if (!skipCache) {
|
|
967
|
-
const cachedResult = this.ntkQuery.getQueryCache<${info.responseTypeName}>([
|
|
970
|
+
const cachedResult = this.ntkQuery.getQueryCache<${info.responseTypeName}>([ECacheTagTypes.${info.queryKeyName}${info.argTypeName !== "VoidApiArg" ? ", args.variables" : ""}]);
|
|
968
971
|
if (cachedResult) {
|
|
969
972
|
return cachedResult;
|
|
970
973
|
}
|
|
@@ -981,8 +984,8 @@ import {Injectable, inject} from '@angular/core';
|
|
|
981
984
|
import {Observable} from 'rxjs';
|
|
982
985
|
import {HttpErrorResponse} from '@angular/common/http';
|
|
983
986
|
import {NtkQueryService, MutationState, QueryState, MutationResponse} from '@core/ntk-query';
|
|
984
|
-
import {
|
|
985
|
-
import {${apiServiceName}} from './
|
|
987
|
+
import {ECacheTagTypes} from '../tagTypes';
|
|
988
|
+
import {${apiServiceName}} from './enhanceEndpoints';
|
|
986
989
|
import {IRestFulEndpointsQueryReturn, RequestOptions} from '../common-types';
|
|
987
990
|
import {${endpointInfos.map((info) => ` ${info.argTypeName}, ${info.responseTypeName}`).join(",")}} from './types';
|
|
988
991
|
|
|
@@ -1005,7 +1008,7 @@ function generateRtkEnhanceEndpointsFile(endpointInfos, options) {
|
|
|
1005
1008
|
return `/* eslint-disable */
|
|
1006
1009
|
// [Warning] Generated automatically - do not edit manually
|
|
1007
1010
|
|
|
1008
|
-
import { ${httpClient.
|
|
1011
|
+
import { ${httpClient.importReturnTypeName} } from '${httpClient.file}';
|
|
1009
1012
|
import { Injectable, inject } from '@angular/core';
|
|
1010
1013
|
import { Observable } from 'rxjs';
|
|
1011
1014
|
import { ${apiConfiguration.importName} } from '${apiConfiguration.file}';
|
|
@@ -1018,7 +1021,7 @@ import {${endpointInfos.map((info) => ` ${info.argTypeName}, ${info.responseType
|
|
|
1018
1021
|
})
|
|
1019
1022
|
export class ${apiServiceClassName} {
|
|
1020
1023
|
private config = inject(${apiConfiguration.importName});
|
|
1021
|
-
private http = inject(${httpClient.
|
|
1024
|
+
private http = inject(${httpClient.importReturnTypeName});
|
|
1022
1025
|
|
|
1023
1026
|
${endpointInfos.map((info) => {
|
|
1024
1027
|
const isGet = info.verb.toUpperCase() === "GET";
|
|
@@ -1036,17 +1039,16 @@ ${endpointInfos.map((info) => {
|
|
|
1036
1039
|
const hasQueryParams = info.queryParams.length > 0;
|
|
1037
1040
|
const hasBody = !isGet && hasArgs;
|
|
1038
1041
|
const generateRequestOptions = () => {
|
|
1039
|
-
|
|
1040
|
-
if (
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
if (hasQueryParams && hasArgs) {
|
|
1044
|
-
options2.push(generateQueryParams(info.queryParams));
|
|
1042
|
+
let paramsSection = "";
|
|
1043
|
+
if (info.queryParams && info.queryParams.length > 0) {
|
|
1044
|
+
const paramsLines = info.queryParams.map((param) => `${param.name}: args.variables.${param.name},`).join("\n");
|
|
1045
|
+
paramsSection = `params: {${paramsLines}},`;
|
|
1045
1046
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1047
|
+
return `{
|
|
1048
|
+
...args.fetchOptions,
|
|
1049
|
+
headers: { 'Content-Type': '${info.contentType}', ...args.fetchOptions?.headers },
|
|
1050
|
+
${paramsSection}${info.hasRequestBody ? `body: args.variables.body,` : ""}
|
|
1051
|
+
}`;
|
|
1050
1052
|
};
|
|
1051
1053
|
return `
|
|
1052
1054
|
/**
|
|
@@ -1062,12 +1064,6 @@ ${endpointInfos.map((info) => {
|
|
|
1062
1064
|
}
|
|
1063
1065
|
`;
|
|
1064
1066
|
}
|
|
1065
|
-
function generateQueryParams(queryParams) {
|
|
1066
|
-
const paramEntries = queryParams.map(
|
|
1067
|
-
(param) => `${param.name}: args.variables.${param.name}`
|
|
1068
|
-
).join(", ");
|
|
1069
|
-
return `params: withoutUndefined({ ${paramEntries} })`;
|
|
1070
|
-
}
|
|
1071
1067
|
|
|
1072
1068
|
// src/services/api-code-generator.ts
|
|
1073
1069
|
var ApiCodeGenerator = class {
|
|
@@ -1093,6 +1089,11 @@ var ApiCodeGenerator = class {
|
|
|
1093
1089
|
const rtkQueryContent = generateRtkQueryFile(endpointInfos, this.options);
|
|
1094
1090
|
const enhanceEndpointsContent = generateRtkEnhanceEndpointsFile(endpointInfos, this.options);
|
|
1095
1091
|
const indexContent = this.generateIndex();
|
|
1092
|
+
const allEndpointCacheKeys = endpointInfos.filter((info) => info.isQuery).map((info) => ({
|
|
1093
|
+
operationName: info.operationName,
|
|
1094
|
+
queryKeyName: info.queryKeyName,
|
|
1095
|
+
groupKey: this.options.groupKey || "_common"
|
|
1096
|
+
}));
|
|
1096
1097
|
const operationNames = endpointInfos.map((info) => info.operationName);
|
|
1097
1098
|
const allTags = /* @__PURE__ */ new Set();
|
|
1098
1099
|
endpointInfos.forEach((info) => {
|
|
@@ -1108,8 +1109,9 @@ var ApiCodeGenerator = class {
|
|
|
1108
1109
|
queryService: rtkQueryContent,
|
|
1109
1110
|
// RTK Query 檔案
|
|
1110
1111
|
index: indexContent,
|
|
1111
|
-
enhanceEndpoints: enhanceEndpointsContent
|
|
1112
|
+
enhanceEndpoints: enhanceEndpointsContent,
|
|
1112
1113
|
// 新增的 enhance endpoints 檔案
|
|
1114
|
+
allEndpointCacheKeys
|
|
1113
1115
|
}
|
|
1114
1116
|
};
|
|
1115
1117
|
}
|
|
@@ -1188,25 +1190,42 @@ export function withoutUndefined(obj?: Record<string, any>) {
|
|
|
1188
1190
|
|
|
1189
1191
|
`;
|
|
1190
1192
|
}
|
|
1193
|
+
function toCamelCase(str) {
|
|
1194
|
+
return str.toLowerCase().replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
1195
|
+
}
|
|
1191
1196
|
|
|
1192
1197
|
// src/generators/tag-types-generator.ts
|
|
1193
1198
|
init_cjs_shims();
|
|
1194
|
-
function generateTagTypesFile(
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1199
|
+
function generateTagTypesFile(allEndpointInfos) {
|
|
1200
|
+
const groupedKeys = allEndpointInfos.reduce(
|
|
1201
|
+
(acc, info) => {
|
|
1202
|
+
if (!acc[info.groupKey]) {
|
|
1203
|
+
acc[info.groupKey] = [];
|
|
1204
|
+
}
|
|
1205
|
+
acc[info.groupKey].push({
|
|
1206
|
+
operationName: info.operationName,
|
|
1207
|
+
queryKeyName: info.queryKeyName
|
|
1208
|
+
});
|
|
1209
|
+
return acc;
|
|
1210
|
+
},
|
|
1211
|
+
{}
|
|
1212
|
+
);
|
|
1213
|
+
const enumEntries = Object.entries(groupedKeys).map(([groupKey, keys]) => {
|
|
1214
|
+
const groupComment = ` // ${groupKey.charAt(0).toUpperCase() + groupKey.slice(1)} related cache keys`;
|
|
1215
|
+
const keyEntries = keys.map((key) => ` ${key.queryKeyName} = '${toCamelCase(key.queryKeyName)}',`).join("\n");
|
|
1216
|
+
return `${groupComment}
|
|
1217
|
+
${keyEntries}`;
|
|
1218
|
+
}).join("\n\n");
|
|
1219
|
+
return `// [Warning] Generated automatically - do not edit manually
|
|
1220
|
+
|
|
1221
|
+
/**
|
|
1222
|
+
* Cache keys enum for all API queries
|
|
1223
|
+
*/
|
|
1207
1224
|
export enum ECacheTagTypes {
|
|
1208
1225
|
${enumEntries}
|
|
1209
1226
|
}
|
|
1227
|
+
|
|
1228
|
+
export default ECacheTagTypes;
|
|
1210
1229
|
`;
|
|
1211
1230
|
}
|
|
1212
1231
|
|
|
@@ -1216,6 +1235,7 @@ var UnifiedCodeGenerator = class {
|
|
|
1216
1235
|
openApiService = new OpenApiService();
|
|
1217
1236
|
groupService = new GroupService();
|
|
1218
1237
|
fileWriterService = new FileWriterService();
|
|
1238
|
+
allEndpointCacheKeys = [];
|
|
1219
1239
|
// 內部狀態存儲
|
|
1220
1240
|
openApiDoc = null;
|
|
1221
1241
|
parserService = null;
|
|
@@ -1245,7 +1265,7 @@ var UnifiedCodeGenerator = class {
|
|
|
1245
1265
|
this.generateSchemaContent();
|
|
1246
1266
|
this.generateUtilsContent();
|
|
1247
1267
|
this.generateDoNotModifyContent();
|
|
1248
|
-
this.
|
|
1268
|
+
this.generatECacheTagTypesContent();
|
|
1249
1269
|
return await this.release();
|
|
1250
1270
|
}
|
|
1251
1271
|
/**
|
|
@@ -1259,23 +1279,23 @@ var UnifiedCodeGenerator = class {
|
|
|
1259
1279
|
this._options.schemaFile
|
|
1260
1280
|
);
|
|
1261
1281
|
}
|
|
1262
|
-
this.openApiDoc = await this.openApiService.getDocument(
|
|
1263
|
-
this.actualSchemaFile,
|
|
1264
|
-
this._options.httpResolverOptions
|
|
1265
|
-
);
|
|
1282
|
+
this.openApiDoc = await this.openApiService.getDocument(this.actualSchemaFile, this._options.httpResolverOptions);
|
|
1266
1283
|
this.parserService = new OpenApiParserService(this.openApiDoc, this._options);
|
|
1267
1284
|
this.parserService.initialize();
|
|
1268
1285
|
const apiGen = this.parserService.getApiGenerator();
|
|
1269
|
-
this.schemaInterfaces = apiGen.aliases.reduce(
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1286
|
+
this.schemaInterfaces = apiGen.aliases.reduce(
|
|
1287
|
+
(curr, alias) => {
|
|
1288
|
+
if (import_typescript3.default.isInterfaceDeclaration(alias) || import_typescript3.default.isTypeAliasDeclaration(alias)) {
|
|
1289
|
+
const name = alias.name.text;
|
|
1290
|
+
return {
|
|
1291
|
+
...curr,
|
|
1292
|
+
[name]: alias
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
return curr;
|
|
1296
|
+
},
|
|
1297
|
+
{}
|
|
1298
|
+
);
|
|
1279
1299
|
}
|
|
1280
1300
|
/**
|
|
1281
1301
|
* 生成階段:產生所有內容但不寫檔
|
|
@@ -1288,10 +1308,7 @@ var UnifiedCodeGenerator = class {
|
|
|
1288
1308
|
const groupInfos = this.groupService.groupPaths(paths, this._options.outputFiles);
|
|
1289
1309
|
for (const groupInfo of Object.values(groupInfos)) {
|
|
1290
1310
|
try {
|
|
1291
|
-
const groupContent = await this.generateApiGroupContent(
|
|
1292
|
-
this._options,
|
|
1293
|
-
groupInfo
|
|
1294
|
-
);
|
|
1311
|
+
const groupContent = await this.generateApiGroupContent(this._options, groupInfo);
|
|
1295
1312
|
if (groupContent.operationNames.length > 0) {
|
|
1296
1313
|
this.generatedContent.groups.push({
|
|
1297
1314
|
groupKey: groupInfo.groupKey,
|
|
@@ -1307,6 +1324,12 @@ var UnifiedCodeGenerator = class {
|
|
|
1307
1324
|
}
|
|
1308
1325
|
}
|
|
1309
1326
|
}
|
|
1327
|
+
/**
|
|
1328
|
+
* 生成 cache keys
|
|
1329
|
+
*/
|
|
1330
|
+
async generatECacheTagTypesContent() {
|
|
1331
|
+
this.generatedContent.tagTypes = generateTagTypesFile(this.allEndpointCacheKeys);
|
|
1332
|
+
}
|
|
1310
1333
|
/**
|
|
1311
1334
|
* 生成 common types
|
|
1312
1335
|
*/
|
|
@@ -1334,10 +1357,10 @@ var UnifiedCodeGenerator = class {
|
|
|
1334
1357
|
/**
|
|
1335
1358
|
* 生成 Tag Types
|
|
1336
1359
|
*/
|
|
1337
|
-
async generateTagTypesContent() {
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
}
|
|
1360
|
+
// private async generateTagTypesContent(): Promise<void> {
|
|
1361
|
+
// const tagsArray = Array.from(this.allTags);
|
|
1362
|
+
// this.generatedContent.tagTypes = generateTagTypesFile(tagsArray);
|
|
1363
|
+
// }
|
|
1341
1364
|
/**
|
|
1342
1365
|
* 發佈階段:統一寫入所有檔案
|
|
1343
1366
|
*/
|
|
@@ -1350,15 +1373,12 @@ var UnifiedCodeGenerator = class {
|
|
|
1350
1373
|
try {
|
|
1351
1374
|
if (group.content?.files) {
|
|
1352
1375
|
const groupOutputDir = import_node_path4.default.dirname(group.outputPath);
|
|
1353
|
-
const groupResults = await this.fileWriterService.writeGroupFiles(
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
index: group.content.files.index
|
|
1360
|
-
}
|
|
1361
|
-
);
|
|
1376
|
+
const groupResults = await this.fileWriterService.writeGroupFiles(groupOutputDir, {
|
|
1377
|
+
types: group.content.files.types,
|
|
1378
|
+
queryService: group.content.files.queryService,
|
|
1379
|
+
enhanceEndpoints: group.content.files.enhanceEndpoints,
|
|
1380
|
+
index: group.content.files.index
|
|
1381
|
+
});
|
|
1362
1382
|
results.push(...groupResults);
|
|
1363
1383
|
generatedGroups.push(group.groupKey);
|
|
1364
1384
|
}
|
|
@@ -1367,24 +1387,15 @@ var UnifiedCodeGenerator = class {
|
|
|
1367
1387
|
}
|
|
1368
1388
|
}
|
|
1369
1389
|
const outputDir = this.generatedContent.groups[0] ? import_node_path4.default.dirname(import_node_path4.default.dirname(this.generatedContent.groups[0].outputPath)) : "./generated";
|
|
1370
|
-
if (this.generatedContent.commonTypes || this.generatedContent.doNotModify || this.generatedContent.utils) {
|
|
1371
|
-
const sharedResults = await this.fileWriterService.writeSharedFiles(
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
}
|
|
1378
|
-
);
|
|
1390
|
+
if (this.generatedContent.tagTypes || this.generatedContent.commonTypes || this.generatedContent.doNotModify || this.generatedContent.utils) {
|
|
1391
|
+
const sharedResults = await this.fileWriterService.writeSharedFiles(outputDir, {
|
|
1392
|
+
tagTypes: this.generatedContent.tagTypes || void 0,
|
|
1393
|
+
commonTypes: this.generatedContent.commonTypes || void 0,
|
|
1394
|
+
doNotModify: this.generatedContent.doNotModify || void 0,
|
|
1395
|
+
utils: this.generatedContent.utils || void 0
|
|
1396
|
+
});
|
|
1379
1397
|
results.push(...sharedResults);
|
|
1380
1398
|
}
|
|
1381
|
-
if (this.generatedContent.tagTypes) {
|
|
1382
|
-
const tagTypesResult = await this.fileWriterService.writeFile(
|
|
1383
|
-
import_node_path4.default.join(outputDir, "tagTypes.ts"),
|
|
1384
|
-
this.generatedContent.tagTypes
|
|
1385
|
-
);
|
|
1386
|
-
results.push(tagTypesResult);
|
|
1387
|
-
}
|
|
1388
1399
|
if (this.generatedContent.componentSchema) {
|
|
1389
1400
|
const schemaResults = await this.fileWriterService.writeSchemaFile(
|
|
1390
1401
|
outputDir,
|
|
@@ -1427,6 +1438,10 @@ var UnifiedCodeGenerator = class {
|
|
|
1427
1438
|
}
|
|
1428
1439
|
const apiGenerator = new ApiCodeGenerator(this.parserService, groupOptions);
|
|
1429
1440
|
const result = await apiGenerator.generate();
|
|
1441
|
+
if (result.files && "allEndpointCacheKeys" in result.files) {
|
|
1442
|
+
const cacheKeys = result.files.allEndpointCacheKeys;
|
|
1443
|
+
this.allEndpointCacheKeys.push(...cacheKeys);
|
|
1444
|
+
}
|
|
1430
1445
|
return result;
|
|
1431
1446
|
}
|
|
1432
1447
|
/**
|