@dudousxd/nestjs-inertia-codegen 1.2.0 → 1.3.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 +11 -0
- package/dist/cli/main.cjs +133 -31
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +131 -29
- package/dist/cli/main.js.map +1 -1
- package/dist/index.cjs +119 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +119 -25
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -308,7 +308,20 @@ function insertIntoTree(tree, segments, leaf, fullName) {
|
|
|
308
308
|
}
|
|
309
309
|
}
|
|
310
310
|
__name(insertIntoTree, "insertIntoTree");
|
|
311
|
-
function
|
|
311
|
+
function buildResponseType(c, outDir) {
|
|
312
|
+
if (c.controllerRef) {
|
|
313
|
+
let relPath = (0, import_node_path3.relative)(outDir, c.controllerRef.filePath).replace(/\.ts$/, "");
|
|
314
|
+
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
315
|
+
return `Awaited<ReturnType<import('${relPath}').${c.controllerRef.className}['${c.controllerRef.methodName}']>>`;
|
|
316
|
+
}
|
|
317
|
+
const respRef = c.contractSource.responseRef;
|
|
318
|
+
if (respRef) {
|
|
319
|
+
return respRef.isArray ? `Array<${respRef.name}>` : respRef.name;
|
|
320
|
+
}
|
|
321
|
+
return c.contractSource.response;
|
|
322
|
+
}
|
|
323
|
+
__name(buildResponseType, "buildResponseType");
|
|
324
|
+
function emitRouterTypeBlock(tree, indent, outDir) {
|
|
312
325
|
const pad = " ".repeat(indent);
|
|
313
326
|
const lines = [];
|
|
314
327
|
for (const [key, node] of tree) {
|
|
@@ -320,14 +333,13 @@ function emitRouterTypeBlock(tree, indent) {
|
|
|
320
333
|
const query = queryRef ? queryRef.isArray ? `Array<${queryRef.name}>` : queryRef.name : c.contractSource.query ?? "never";
|
|
321
334
|
const bodyRef = c.contractSource.bodyRef;
|
|
322
335
|
const body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
|
|
323
|
-
const
|
|
324
|
-
const response = respRef ? respRef.isArray ? `Array<${respRef.name}>` : respRef.name : c.contractSource.response;
|
|
336
|
+
const response = buildResponseType(c, outDir);
|
|
325
337
|
const safeMethod = JSON.stringify(method);
|
|
326
338
|
const safeUrl = JSON.stringify(c.path);
|
|
327
339
|
lines.push(`${pad}${objKey}: { method: ${safeMethod}; url: ${safeUrl}; query: ${query}; body: ${body}; response: ${response} };`);
|
|
328
340
|
} else {
|
|
329
341
|
lines.push(`${pad}${objKey}: {`);
|
|
330
|
-
lines.push(...emitRouterTypeBlock(node.children, indent + 2));
|
|
342
|
+
lines.push(...emitRouterTypeBlock(node.children, indent + 2, outDir));
|
|
331
343
|
lines.push(`${pad}};`);
|
|
332
344
|
}
|
|
333
345
|
}
|
|
@@ -349,18 +361,20 @@ function emitApiObjectBlock(tree, indent) {
|
|
|
349
361
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
350
362
|
lines.push(`${pad}${objKey}: {`);
|
|
351
363
|
lines.push(`${pad} queryKey: (query?: ${typeAccess}['query']) => query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
352
|
-
lines.push(`${pad} queryOptions: (query?: ${typeAccess}['query'])
|
|
353
|
-
lines.push(`${pad}
|
|
354
|
-
lines.push(`${pad}
|
|
355
|
-
lines.push(`${pad}
|
|
364
|
+
lines.push(`${pad} queryOptions: (query?: ${typeAccess}['query']) =>`);
|
|
365
|
+
lines.push(`${pad} _queryOptions({`);
|
|
366
|
+
lines.push(`${pad} queryKey: query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
367
|
+
lines.push(`${pad} queryFn: () => fetcher.get<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { query }),`);
|
|
368
|
+
lines.push(`${pad} }),`);
|
|
356
369
|
lines.push(`${pad}},`);
|
|
357
370
|
} else {
|
|
358
371
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
359
372
|
lines.push(`${pad}${objKey}: {`);
|
|
360
373
|
lines.push(`${pad} queryKey: () => [${flatName}] as const,`);
|
|
361
|
-
lines.push(`${pad} mutationOptions: ()
|
|
362
|
-
lines.push(`${pad}
|
|
363
|
-
lines.push(`${pad}
|
|
374
|
+
lines.push(`${pad} mutationOptions: () =>`);
|
|
375
|
+
lines.push(`${pad} _mutationOptions({`);
|
|
376
|
+
lines.push(`${pad} mutationFn: (body: ${typeAccess}['body']) => fetcher.${fetcherMethod}<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { body }),`);
|
|
377
|
+
lines.push(`${pad} }),`);
|
|
364
378
|
lines.push(`${pad}},`);
|
|
365
379
|
}
|
|
366
380
|
} else {
|
|
@@ -383,11 +397,15 @@ function buildApiFile(routes, outDir) {
|
|
|
383
397
|
for (const r of contracted) {
|
|
384
398
|
const cs = r.contract?.contractSource;
|
|
385
399
|
if (!cs) continue;
|
|
386
|
-
|
|
400
|
+
const refs = r.controllerRef ? [
|
|
401
|
+
cs.queryRef,
|
|
402
|
+
cs.bodyRef
|
|
403
|
+
] : [
|
|
387
404
|
cs.queryRef,
|
|
388
405
|
cs.bodyRef,
|
|
389
406
|
cs.responseRef
|
|
390
|
-
]
|
|
407
|
+
];
|
|
408
|
+
for (const ref of refs) {
|
|
391
409
|
if (!ref) continue;
|
|
392
410
|
let names = importsByFile.get(ref.filePath);
|
|
393
411
|
if (!names) {
|
|
@@ -397,10 +415,18 @@ function buildApiFile(routes, outDir) {
|
|
|
397
415
|
names.add(ref.name);
|
|
398
416
|
}
|
|
399
417
|
}
|
|
418
|
+
const hasGetRoutes = contracted.some((r) => r.method === "GET");
|
|
419
|
+
const hasMutationRoutes = contracted.some((r) => r.method !== "GET");
|
|
400
420
|
const lines = [
|
|
401
421
|
"// Generated by @dudousxd/nestjs-inertia-codegen. Do not edit.",
|
|
402
422
|
""
|
|
403
423
|
];
|
|
424
|
+
const tqImports = [];
|
|
425
|
+
if (hasGetRoutes) tqImports.push("queryOptions as _queryOptions");
|
|
426
|
+
if (hasMutationRoutes) tqImports.push("mutationOptions as _mutationOptions");
|
|
427
|
+
if (tqImports.length > 0) {
|
|
428
|
+
lines.push(`import { ${tqImports.join(", ")} } from '@tanstack/react-query';`);
|
|
429
|
+
}
|
|
404
430
|
lines.push("import { route } from './routes.js';");
|
|
405
431
|
lines.push("import { createFetcher } from '@dudousxd/nestjs-inertia-client';");
|
|
406
432
|
if (importsByFile.size > 0 && outDir) {
|
|
@@ -454,13 +480,14 @@ function buildApiFile(routes, outDir) {
|
|
|
454
480
|
method: r.method,
|
|
455
481
|
name,
|
|
456
482
|
path: r.path,
|
|
483
|
+
controllerRef: r.controllerRef,
|
|
457
484
|
contractSource: c.contractSource
|
|
458
485
|
};
|
|
459
486
|
insertIntoTree(tree, segments, leaf, name);
|
|
460
487
|
}
|
|
461
488
|
void detectCollisions;
|
|
462
489
|
lines.push("export type ApiRouter = {");
|
|
463
|
-
lines.push(...emitRouterTypeBlock(tree, 2));
|
|
490
|
+
lines.push(...emitRouterTypeBlock(tree, 2, outDir ?? ""));
|
|
464
491
|
lines.push("};");
|
|
465
492
|
lines.push("");
|
|
466
493
|
lines.push("export const api = {");
|
|
@@ -730,9 +757,28 @@ var import_node_path10 = require("path");
|
|
|
730
757
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
731
758
|
|
|
732
759
|
// src/discovery/contracts-fast.ts
|
|
760
|
+
var import_node_fs = require("fs");
|
|
733
761
|
var import_node_path8 = require("path");
|
|
734
762
|
var import_fast_glob2 = __toESM(require("fast-glob"), 1);
|
|
735
763
|
var import_ts_morph = require("ts-morph");
|
|
764
|
+
var _projectRoot = "";
|
|
765
|
+
var _tsconfigPaths = null;
|
|
766
|
+
var _debug = process.env.NESTJS_INERTIA_DEBUG === "1";
|
|
767
|
+
function dbg(...args) {
|
|
768
|
+
if (_debug) console.log("[codegen:debug]", ...args);
|
|
769
|
+
}
|
|
770
|
+
__name(dbg, "dbg");
|
|
771
|
+
function loadTsconfigPaths(tsconfigPath) {
|
|
772
|
+
try {
|
|
773
|
+
const raw = (0, import_node_fs.readFileSync)(tsconfigPath, "utf8");
|
|
774
|
+
const stripped = raw.replace(/\/\/.*$/gm, "");
|
|
775
|
+
const parsed = JSON.parse(stripped);
|
|
776
|
+
return parsed.compilerOptions?.paths ?? null;
|
|
777
|
+
} catch {
|
|
778
|
+
return null;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
__name(loadTsconfigPaths, "loadTsconfigPaths");
|
|
736
782
|
async function discoverContractsFast(opts) {
|
|
737
783
|
const { cwd, glob, tsconfig } = opts;
|
|
738
784
|
const tsconfigPath = tsconfig ? (0, import_node_path8.resolve)(tsconfig) : (0, import_node_path8.join)(cwd, "tsconfig.json");
|
|
@@ -765,6 +811,8 @@ async function discoverContractsFast(opts) {
|
|
|
765
811
|
project.addSourceFileAtPath(f);
|
|
766
812
|
}
|
|
767
813
|
const routes = [];
|
|
814
|
+
_projectRoot = cwd;
|
|
815
|
+
_tsconfigPaths = loadTsconfigPaths(tsconfigPath);
|
|
768
816
|
for (const sourceFile of project.getSourceFiles()) {
|
|
769
817
|
routes.push(...extractFromSourceFile(sourceFile, project));
|
|
770
818
|
}
|
|
@@ -966,17 +1014,41 @@ function findTypeInFile(name, file) {
|
|
|
966
1014
|
return null;
|
|
967
1015
|
}
|
|
968
1016
|
__name(findTypeInFile, "findTypeInFile");
|
|
1017
|
+
function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
|
|
1018
|
+
if (moduleSpecifier.startsWith(".")) {
|
|
1019
|
+
const dir = (0, import_node_path8.dirname)(sourceFile.getFilePath());
|
|
1020
|
+
return [
|
|
1021
|
+
(0, import_node_path8.resolve)(dir, `${moduleSpecifier}.ts`),
|
|
1022
|
+
(0, import_node_path8.resolve)(dir, moduleSpecifier, "index.ts")
|
|
1023
|
+
];
|
|
1024
|
+
}
|
|
1025
|
+
const baseUrl = _projectRoot;
|
|
1026
|
+
dbg("resolveModuleSpecifier", moduleSpecifier, "paths:", JSON.stringify(_tsconfigPaths), "baseUrl:", baseUrl);
|
|
1027
|
+
if (_tsconfigPaths) {
|
|
1028
|
+
for (const [pattern, mappings] of Object.entries(_tsconfigPaths)) {
|
|
1029
|
+
const prefix = pattern.replace("*", "");
|
|
1030
|
+
if (moduleSpecifier.startsWith(prefix)) {
|
|
1031
|
+
const rest = moduleSpecifier.slice(prefix.length);
|
|
1032
|
+
const candidates = [];
|
|
1033
|
+
for (const mapping of mappings) {
|
|
1034
|
+
const resolved = (0, import_node_path8.resolve)(baseUrl, mapping.replace("*", rest));
|
|
1035
|
+
candidates.push(`${resolved}.ts`, (0, import_node_path8.resolve)(resolved, "index.ts"));
|
|
1036
|
+
}
|
|
1037
|
+
dbg(" resolved candidates:", candidates);
|
|
1038
|
+
return candidates;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
return [];
|
|
1043
|
+
}
|
|
1044
|
+
__name(resolveModuleSpecifier, "resolveModuleSpecifier");
|
|
969
1045
|
function resolveImportedType(name, sourceFile, project) {
|
|
970
1046
|
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
971
1047
|
const namedImport = importDecl.getNamedImports().find((n) => n.getName() === name);
|
|
972
1048
|
if (!namedImport) continue;
|
|
973
1049
|
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
const candidates = [
|
|
977
|
-
(0, import_node_path8.resolve)(dir, `${moduleSpecifier}.ts`),
|
|
978
|
-
(0, import_node_path8.resolve)(dir, moduleSpecifier, "index.ts")
|
|
979
|
-
];
|
|
1050
|
+
const candidates = resolveModuleSpecifier(moduleSpecifier, sourceFile, project);
|
|
1051
|
+
if (candidates.length === 0) continue;
|
|
980
1052
|
for (const candidate of candidates) {
|
|
981
1053
|
let importedFile = project.getSourceFile(candidate);
|
|
982
1054
|
if (!importedFile) {
|
|
@@ -1020,6 +1092,18 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
1020
1092
|
}
|
|
1021
1093
|
return "Array<unknown>";
|
|
1022
1094
|
}
|
|
1095
|
+
if ([
|
|
1096
|
+
"Record",
|
|
1097
|
+
"Omit",
|
|
1098
|
+
"Pick",
|
|
1099
|
+
"Partial",
|
|
1100
|
+
"Required",
|
|
1101
|
+
"Readonly",
|
|
1102
|
+
"Map",
|
|
1103
|
+
"Set"
|
|
1104
|
+
].includes(name)) {
|
|
1105
|
+
return typeNode.getText();
|
|
1106
|
+
}
|
|
1023
1107
|
if (name === "Promise") {
|
|
1024
1108
|
const typeArgs = typeNode.getTypeArguments();
|
|
1025
1109
|
const firstTypeArg = typeArgs[0];
|
|
@@ -1032,7 +1116,8 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
1032
1116
|
if (resolved) {
|
|
1033
1117
|
return expandTypeDecl(resolved, project, depth - 1);
|
|
1034
1118
|
}
|
|
1035
|
-
|
|
1119
|
+
dbg("unresolvable type:", name, "in", sourceFile.getFilePath());
|
|
1120
|
+
return "unknown";
|
|
1036
1121
|
}
|
|
1037
1122
|
const kind = typeNode.getKind();
|
|
1038
1123
|
if (kind === import_ts_morph.SyntaxKind.StringKeyword) return "string";
|
|
@@ -1194,7 +1279,7 @@ function tryResolveTypeRef(typeNode, sourceFile, project) {
|
|
|
1194
1279
|
return null;
|
|
1195
1280
|
}
|
|
1196
1281
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1197
|
-
if (localDecl
|
|
1282
|
+
if (localDecl?.isExported()) {
|
|
1198
1283
|
return {
|
|
1199
1284
|
name,
|
|
1200
1285
|
filePath: sourceFile.getFilePath()
|
|
@@ -1256,7 +1341,7 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
1256
1341
|
if (val && import_ts_morph.Node.isIdentifier(val)) {
|
|
1257
1342
|
const name = val.getText();
|
|
1258
1343
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1259
|
-
if (localDecl
|
|
1344
|
+
if (localDecl?.isExported()) {
|
|
1260
1345
|
responseRef = {
|
|
1261
1346
|
name,
|
|
1262
1347
|
filePath: sourceFile.getFilePath()
|
|
@@ -1382,6 +1467,11 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1382
1467
|
path: combined,
|
|
1383
1468
|
name: routeName,
|
|
1384
1469
|
params,
|
|
1470
|
+
controllerRef: {
|
|
1471
|
+
className,
|
|
1472
|
+
methodName,
|
|
1473
|
+
filePath: sourceFile.getFilePath()
|
|
1474
|
+
},
|
|
1385
1475
|
contract: {
|
|
1386
1476
|
contractSource: {
|
|
1387
1477
|
query: contractDef.query,
|
|
@@ -1416,7 +1506,11 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1416
1506
|
path: combined,
|
|
1417
1507
|
name: routeName,
|
|
1418
1508
|
params,
|
|
1419
|
-
|
|
1509
|
+
controllerRef: {
|
|
1510
|
+
className,
|
|
1511
|
+
methodName,
|
|
1512
|
+
filePath: sourceFile.getFilePath()
|
|
1513
|
+
},
|
|
1420
1514
|
...dtoContract ? {
|
|
1421
1515
|
contract: {
|
|
1422
1516
|
contractSource: {
|
|
@@ -1598,7 +1692,7 @@ async function watch(config, onChange) {
|
|
|
1598
1692
|
__name(watch, "watch");
|
|
1599
1693
|
|
|
1600
1694
|
// src/index.ts
|
|
1601
|
-
var VERSION = "1.
|
|
1695
|
+
var VERSION = "1.3.0";
|
|
1602
1696
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1603
1697
|
0 && (module.exports = {
|
|
1604
1698
|
CodegenError,
|