@dudousxd/nestjs-inertia-codegen 1.0.6 → 1.2.0
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/CHANGELOG.md +40 -0
- package/dist/cli/main.cjs +163 -18
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +164 -19
- package/dist/cli/main.js.map +1 -1
- package/dist/index.cjs +163 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +164 -19
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.cts
CHANGED
|
@@ -62,10 +62,18 @@ declare class CodegenError extends Error {
|
|
|
62
62
|
constructor(message: string, options?: ErrorOptions);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
interface TypeRef {
|
|
66
|
+
name: string;
|
|
67
|
+
filePath: string;
|
|
68
|
+
isArray?: boolean;
|
|
69
|
+
}
|
|
65
70
|
interface ContractSource {
|
|
66
71
|
query: string | null;
|
|
67
72
|
body: string | null;
|
|
68
73
|
response: string;
|
|
74
|
+
queryRef?: TypeRef | null;
|
|
75
|
+
bodyRef?: TypeRef | null;
|
|
76
|
+
responseRef?: TypeRef | null;
|
|
69
77
|
}
|
|
70
78
|
interface ContractDescriptor {
|
|
71
79
|
contractSource: ContractSource;
|
|
@@ -120,6 +128,6 @@ declare function acquireLock(outDir: string): Promise<{
|
|
|
120
128
|
release: () => Promise<void>;
|
|
121
129
|
} | null>;
|
|
122
130
|
|
|
123
|
-
declare const VERSION = "1.0
|
|
131
|
+
declare const VERSION = "1.2.0";
|
|
124
132
|
|
|
125
133
|
export { CodegenError, ConfigError, type ResolvedConfig, type ScopeConfig, type UserConfig, VERSION, type Watcher, acquireLock, defineConfig, generate, loadConfig, watch };
|
package/dist/index.d.ts
CHANGED
|
@@ -62,10 +62,18 @@ declare class CodegenError extends Error {
|
|
|
62
62
|
constructor(message: string, options?: ErrorOptions);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
interface TypeRef {
|
|
66
|
+
name: string;
|
|
67
|
+
filePath: string;
|
|
68
|
+
isArray?: boolean;
|
|
69
|
+
}
|
|
65
70
|
interface ContractSource {
|
|
66
71
|
query: string | null;
|
|
67
72
|
body: string | null;
|
|
68
73
|
response: string;
|
|
74
|
+
queryRef?: TypeRef | null;
|
|
75
|
+
bodyRef?: TypeRef | null;
|
|
76
|
+
responseRef?: TypeRef | null;
|
|
69
77
|
}
|
|
70
78
|
interface ContractDescriptor {
|
|
71
79
|
contractSource: ContractSource;
|
|
@@ -120,6 +128,6 @@ declare function acquireLock(outDir: string): Promise<{
|
|
|
120
128
|
release: () => Promise<void>;
|
|
121
129
|
} | null>;
|
|
122
130
|
|
|
123
|
-
declare const VERSION = "1.0
|
|
131
|
+
declare const VERSION = "1.2.0";
|
|
124
132
|
|
|
125
133
|
export { CodegenError, ConfigError, type ResolvedConfig, type ScopeConfig, type UserConfig, VERSION, type Watcher, acquireLock, defineConfig, generate, loadConfig, watch };
|
package/dist/index.js
CHANGED
|
@@ -197,12 +197,12 @@ __name(extractPropsSource, "extractPropsSource");
|
|
|
197
197
|
|
|
198
198
|
// src/emit/emit-api.ts
|
|
199
199
|
import { mkdir, writeFile } from "fs/promises";
|
|
200
|
-
import { join as join2 } from "path";
|
|
200
|
+
import { join as join2, relative as relative3 } from "path";
|
|
201
201
|
async function emitApi(routes, outDir) {
|
|
202
202
|
await mkdir(outDir, {
|
|
203
203
|
recursive: true
|
|
204
204
|
});
|
|
205
|
-
const content = buildApiFile(routes);
|
|
205
|
+
const content = buildApiFile(routes, outDir);
|
|
206
206
|
await writeFile(join2(outDir, "api.ts"), content, "utf8");
|
|
207
207
|
}
|
|
208
208
|
__name(emitApi, "emitApi");
|
|
@@ -275,9 +275,12 @@ function emitRouterTypeBlock(tree, indent) {
|
|
|
275
275
|
if (node.kind === "leaf") {
|
|
276
276
|
const c = node;
|
|
277
277
|
const method = c.method.toUpperCase();
|
|
278
|
-
const
|
|
279
|
-
const
|
|
280
|
-
const
|
|
278
|
+
const queryRef = c.contractSource.queryRef;
|
|
279
|
+
const query = queryRef ? queryRef.isArray ? `Array<${queryRef.name}>` : queryRef.name : c.contractSource.query ?? "never";
|
|
280
|
+
const bodyRef = c.contractSource.bodyRef;
|
|
281
|
+
const body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
|
|
282
|
+
const respRef = c.contractSource.responseRef;
|
|
283
|
+
const response = respRef ? respRef.isArray ? `Array<${respRef.name}>` : respRef.name : c.contractSource.response;
|
|
281
284
|
const safeMethod = JSON.stringify(method);
|
|
282
285
|
const safeUrl = JSON.stringify(c.path);
|
|
283
286
|
lines.push(`${pad}${objKey}: { method: ${safeMethod}; url: ${safeUrl}; query: ${query}; body: ${body}; response: ${response} };`);
|
|
@@ -304,15 +307,16 @@ function emitApiObjectBlock(tree, indent) {
|
|
|
304
307
|
if (method === "GET") {
|
|
305
308
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
306
309
|
lines.push(`${pad}${objKey}: {`);
|
|
307
|
-
lines.push(`${pad}
|
|
308
|
-
lines.push(`${pad}
|
|
309
|
-
lines.push(`${pad}
|
|
310
|
-
lines.push(`${pad}
|
|
311
|
-
lines.push(`${pad}
|
|
310
|
+
lines.push(`${pad} queryKey: (query?: ${typeAccess}['query']) => query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
311
|
+
lines.push(`${pad} queryOptions: (query?: ${typeAccess}['query']) => ({`);
|
|
312
|
+
lines.push(`${pad} queryKey: query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
313
|
+
lines.push(`${pad} queryFn: () => fetcher.get<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { query }),`);
|
|
314
|
+
lines.push(`${pad} }),`);
|
|
312
315
|
lines.push(`${pad}},`);
|
|
313
316
|
} else {
|
|
314
317
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
315
318
|
lines.push(`${pad}${objKey}: {`);
|
|
319
|
+
lines.push(`${pad} queryKey: () => [${flatName}] as const,`);
|
|
316
320
|
lines.push(`${pad} mutationOptions: () => ({`);
|
|
317
321
|
lines.push(`${pad} mutationFn: (body: ${typeAccess}['body']) => fetcher.${fetcherMethod}<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { body }),`);
|
|
318
322
|
lines.push(`${pad} }),`);
|
|
@@ -332,18 +336,43 @@ function buildRouterTypeAccess(name) {
|
|
|
332
336
|
return `ApiRouter${segments.map((s) => `[${JSON.stringify(s)}]`).join("")}`;
|
|
333
337
|
}
|
|
334
338
|
__name(buildRouterTypeAccess, "buildRouterTypeAccess");
|
|
335
|
-
function buildApiFile(routes) {
|
|
339
|
+
function buildApiFile(routes, outDir) {
|
|
336
340
|
const contracted = routes.filter((r) => r.contract);
|
|
337
|
-
const
|
|
341
|
+
const importsByFile = /* @__PURE__ */ new Map();
|
|
342
|
+
for (const r of contracted) {
|
|
343
|
+
const cs = r.contract?.contractSource;
|
|
344
|
+
if (!cs) continue;
|
|
345
|
+
for (const ref of [
|
|
346
|
+
cs.queryRef,
|
|
347
|
+
cs.bodyRef,
|
|
348
|
+
cs.responseRef
|
|
349
|
+
]) {
|
|
350
|
+
if (!ref) continue;
|
|
351
|
+
let names = importsByFile.get(ref.filePath);
|
|
352
|
+
if (!names) {
|
|
353
|
+
names = /* @__PURE__ */ new Set();
|
|
354
|
+
importsByFile.set(ref.filePath, names);
|
|
355
|
+
}
|
|
356
|
+
names.add(ref.name);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
338
359
|
const lines = [
|
|
339
360
|
"// Generated by @dudousxd/nestjs-inertia-codegen. Do not edit.",
|
|
340
361
|
""
|
|
341
362
|
];
|
|
342
|
-
if (hasGetRoutes) {
|
|
343
|
-
lines.push("import { queryOptions } from '@tanstack/query-core';");
|
|
344
|
-
}
|
|
345
363
|
lines.push("import { route } from './routes.js';");
|
|
346
364
|
lines.push("import { createFetcher } from '@dudousxd/nestjs-inertia-client';");
|
|
365
|
+
if (importsByFile.size > 0 && outDir) {
|
|
366
|
+
lines.push("");
|
|
367
|
+
for (const [filePath, names] of importsByFile) {
|
|
368
|
+
let relPath = relative3(outDir, filePath).replace(/\.ts$/, "");
|
|
369
|
+
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
370
|
+
const sortedNames = [
|
|
371
|
+
...names
|
|
372
|
+
].sort();
|
|
373
|
+
lines.push(`import type { ${sortedNames.join(", ")} } from '${relPath}';`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
347
376
|
lines.push("");
|
|
348
377
|
lines.push("export const fetcher = createFetcher();");
|
|
349
378
|
lines.push("");
|
|
@@ -940,7 +969,8 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
940
969
|
const name = Node.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
|
|
941
970
|
if (name === "string" || name === "number" || name === "boolean") return name;
|
|
942
971
|
if (name === "Date") return "string";
|
|
943
|
-
if (name === "unknown" || name === "any") return "unknown";
|
|
972
|
+
if (name === "unknown" || name === "any" || name === "void") return "unknown";
|
|
973
|
+
if (name === "StreamableFile" || name === "Observable" || name === "ReadableStream") return "unknown";
|
|
944
974
|
if (name === "Array") {
|
|
945
975
|
const typeArgs = typeNode.getTypeArguments();
|
|
946
976
|
const firstTypeArg = typeArgs[0];
|
|
@@ -1088,6 +1118,68 @@ function resolveIdentifierToClassType(node, sourceFile, project, depth) {
|
|
|
1088
1118
|
return name;
|
|
1089
1119
|
}
|
|
1090
1120
|
__name(resolveIdentifierToClassType, "resolveIdentifierToClassType");
|
|
1121
|
+
function tryResolveTypeRef(typeNode, sourceFile, project) {
|
|
1122
|
+
if (Node.isTypeReference(typeNode)) {
|
|
1123
|
+
const typeName = typeNode.getTypeName();
|
|
1124
|
+
const name = Node.isIdentifier(typeName) ? typeName.getText() : null;
|
|
1125
|
+
if (!name) return null;
|
|
1126
|
+
if (name === "Promise") {
|
|
1127
|
+
const typeArgs = typeNode.getTypeArguments();
|
|
1128
|
+
const first = typeArgs[0];
|
|
1129
|
+
if (first) return tryResolveTypeRef(first, sourceFile, project);
|
|
1130
|
+
return null;
|
|
1131
|
+
}
|
|
1132
|
+
if (name === "Array") {
|
|
1133
|
+
const typeArgs = typeNode.getTypeArguments();
|
|
1134
|
+
const first = typeArgs[0];
|
|
1135
|
+
if (first) {
|
|
1136
|
+
const inner = tryResolveTypeRef(first, sourceFile, project);
|
|
1137
|
+
if (inner) return {
|
|
1138
|
+
...inner,
|
|
1139
|
+
isArray: true
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
return null;
|
|
1143
|
+
}
|
|
1144
|
+
if ([
|
|
1145
|
+
"string",
|
|
1146
|
+
"number",
|
|
1147
|
+
"boolean",
|
|
1148
|
+
"void",
|
|
1149
|
+
"unknown",
|
|
1150
|
+
"any",
|
|
1151
|
+
"Date"
|
|
1152
|
+
].includes(name)) {
|
|
1153
|
+
return null;
|
|
1154
|
+
}
|
|
1155
|
+
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1156
|
+
if (localDecl && localDecl.isExported()) {
|
|
1157
|
+
return {
|
|
1158
|
+
name,
|
|
1159
|
+
filePath: sourceFile.getFilePath()
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
const resolved = resolveImportedType(name, sourceFile, project);
|
|
1163
|
+
if (resolved && (resolved.kind === "class" || resolved.kind === "interface")) {
|
|
1164
|
+
const decl = resolved.decl;
|
|
1165
|
+
if (decl.isExported()) {
|
|
1166
|
+
return {
|
|
1167
|
+
name,
|
|
1168
|
+
filePath: resolved.file.getFilePath()
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
if (Node.isArrayTypeNode(typeNode)) {
|
|
1174
|
+
const inner = tryResolveTypeRef(typeNode.getElementTypeNode(), sourceFile, project);
|
|
1175
|
+
if (inner) return {
|
|
1176
|
+
...inner,
|
|
1177
|
+
isArray: true
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
return null;
|
|
1181
|
+
}
|
|
1182
|
+
__name(tryResolveTypeRef, "tryResolveTypeRef");
|
|
1091
1183
|
function extractDtoContract(method, sourceFile, project) {
|
|
1092
1184
|
const body = extractBodyType(method, sourceFile, project);
|
|
1093
1185
|
const query = extractQueryType(method, sourceFile, project);
|
|
@@ -1096,11 +1188,61 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
1096
1188
|
if (body === null && query === null && paramsType === null && response === "unknown") {
|
|
1097
1189
|
return null;
|
|
1098
1190
|
}
|
|
1191
|
+
let bodyRef = null;
|
|
1192
|
+
let queryRef = null;
|
|
1193
|
+
let responseRef = null;
|
|
1194
|
+
for (const param of method.getParameters()) {
|
|
1195
|
+
if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode()) {
|
|
1196
|
+
bodyRef = tryResolveTypeRef(param.getTypeNode(), sourceFile, project);
|
|
1197
|
+
}
|
|
1198
|
+
if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode()) {
|
|
1199
|
+
queryRef = tryResolveTypeRef(param.getTypeNode(), sourceFile, project);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
const returnTypeNode = method.getReturnTypeNode();
|
|
1203
|
+
if (returnTypeNode) {
|
|
1204
|
+
responseRef = tryResolveTypeRef(returnTypeNode, sourceFile, project);
|
|
1205
|
+
}
|
|
1206
|
+
if (!responseRef) {
|
|
1207
|
+
const apiResp = method.getDecorator("ApiResponse");
|
|
1208
|
+
if (apiResp) {
|
|
1209
|
+
const args = apiResp.getArguments();
|
|
1210
|
+
const optsArg = args[0];
|
|
1211
|
+
if (optsArg && Node.isObjectLiteralExpression(optsArg)) {
|
|
1212
|
+
for (const prop of optsArg.getProperties()) {
|
|
1213
|
+
if (Node.isPropertyAssignment(prop) && prop.getName() === "type") {
|
|
1214
|
+
const val = prop.getInitializer();
|
|
1215
|
+
if (val && Node.isIdentifier(val)) {
|
|
1216
|
+
const name = val.getText();
|
|
1217
|
+
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1218
|
+
if (localDecl && localDecl.isExported()) {
|
|
1219
|
+
responseRef = {
|
|
1220
|
+
name,
|
|
1221
|
+
filePath: sourceFile.getFilePath()
|
|
1222
|
+
};
|
|
1223
|
+
} else {
|
|
1224
|
+
const resolved = resolveImportedType(name, sourceFile, project);
|
|
1225
|
+
if (resolved && (resolved.kind === "class" || resolved.kind === "interface") && resolved.decl.isExported()) {
|
|
1226
|
+
responseRef = {
|
|
1227
|
+
name,
|
|
1228
|
+
filePath: resolved.file.getFilePath()
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1099
1238
|
return {
|
|
1100
1239
|
query,
|
|
1101
1240
|
body,
|
|
1102
1241
|
response,
|
|
1103
|
-
params: paramsType
|
|
1242
|
+
params: paramsType,
|
|
1243
|
+
queryRef,
|
|
1244
|
+
bodyRef,
|
|
1245
|
+
responseRef
|
|
1104
1246
|
};
|
|
1105
1247
|
}
|
|
1106
1248
|
__name(extractDtoContract, "extractDtoContract");
|
|
@@ -1239,7 +1381,10 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1239
1381
|
contractSource: {
|
|
1240
1382
|
query: dtoContract.query,
|
|
1241
1383
|
body: dtoContract.body,
|
|
1242
|
-
response: dtoContract.response
|
|
1384
|
+
response: dtoContract.response,
|
|
1385
|
+
queryRef: dtoContract.queryRef,
|
|
1386
|
+
bodyRef: dtoContract.bodyRef,
|
|
1387
|
+
responseRef: dtoContract.responseRef
|
|
1243
1388
|
}
|
|
1244
1389
|
}
|
|
1245
1390
|
} : {}
|
|
@@ -1412,7 +1557,7 @@ async function watch(config, onChange) {
|
|
|
1412
1557
|
__name(watch, "watch");
|
|
1413
1558
|
|
|
1414
1559
|
// src/index.ts
|
|
1415
|
-
var VERSION = "1.0
|
|
1560
|
+
var VERSION = "1.2.0";
|
|
1416
1561
|
export {
|
|
1417
1562
|
CodegenError,
|
|
1418
1563
|
ConfigError,
|