@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.d.cts
CHANGED
|
@@ -78,6 +78,11 @@ interface ContractSource {
|
|
|
78
78
|
interface ContractDescriptor {
|
|
79
79
|
contractSource: ContractSource;
|
|
80
80
|
}
|
|
81
|
+
interface ControllerRef {
|
|
82
|
+
className: string;
|
|
83
|
+
methodName: string;
|
|
84
|
+
filePath: string;
|
|
85
|
+
}
|
|
81
86
|
interface RouteDescriptor {
|
|
82
87
|
method: string;
|
|
83
88
|
path: string;
|
|
@@ -87,6 +92,7 @@ interface RouteDescriptor {
|
|
|
87
92
|
source: 'path' | 'query' | 'body' | 'header';
|
|
88
93
|
}>;
|
|
89
94
|
contract?: ContractDescriptor;
|
|
95
|
+
controllerRef?: ControllerRef;
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
/**
|
|
@@ -128,6 +134,6 @@ declare function acquireLock(outDir: string): Promise<{
|
|
|
128
134
|
release: () => Promise<void>;
|
|
129
135
|
} | null>;
|
|
130
136
|
|
|
131
|
-
declare const VERSION = "1.
|
|
137
|
+
declare const VERSION = "1.3.0";
|
|
132
138
|
|
|
133
139
|
export { CodegenError, ConfigError, type ResolvedConfig, type ScopeConfig, type UserConfig, VERSION, type Watcher, acquireLock, defineConfig, generate, loadConfig, watch };
|
package/dist/index.d.ts
CHANGED
|
@@ -78,6 +78,11 @@ interface ContractSource {
|
|
|
78
78
|
interface ContractDescriptor {
|
|
79
79
|
contractSource: ContractSource;
|
|
80
80
|
}
|
|
81
|
+
interface ControllerRef {
|
|
82
|
+
className: string;
|
|
83
|
+
methodName: string;
|
|
84
|
+
filePath: string;
|
|
85
|
+
}
|
|
81
86
|
interface RouteDescriptor {
|
|
82
87
|
method: string;
|
|
83
88
|
path: string;
|
|
@@ -87,6 +92,7 @@ interface RouteDescriptor {
|
|
|
87
92
|
source: 'path' | 'query' | 'body' | 'header';
|
|
88
93
|
}>;
|
|
89
94
|
contract?: ContractDescriptor;
|
|
95
|
+
controllerRef?: ControllerRef;
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
/**
|
|
@@ -128,6 +134,6 @@ declare function acquireLock(outDir: string): Promise<{
|
|
|
128
134
|
release: () => Promise<void>;
|
|
129
135
|
} | null>;
|
|
130
136
|
|
|
131
|
-
declare const VERSION = "1.
|
|
137
|
+
declare const VERSION = "1.3.0";
|
|
132
138
|
|
|
133
139
|
export { CodegenError, ConfigError, type ResolvedConfig, type ScopeConfig, type UserConfig, VERSION, type Watcher, acquireLock, defineConfig, generate, loadConfig, watch };
|
package/dist/index.js
CHANGED
|
@@ -267,7 +267,20 @@ function insertIntoTree(tree, segments, leaf, fullName) {
|
|
|
267
267
|
}
|
|
268
268
|
}
|
|
269
269
|
__name(insertIntoTree, "insertIntoTree");
|
|
270
|
-
function
|
|
270
|
+
function buildResponseType(c, outDir) {
|
|
271
|
+
if (c.controllerRef) {
|
|
272
|
+
let relPath = relative3(outDir, c.controllerRef.filePath).replace(/\.ts$/, "");
|
|
273
|
+
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
274
|
+
return `Awaited<ReturnType<import('${relPath}').${c.controllerRef.className}['${c.controllerRef.methodName}']>>`;
|
|
275
|
+
}
|
|
276
|
+
const respRef = c.contractSource.responseRef;
|
|
277
|
+
if (respRef) {
|
|
278
|
+
return respRef.isArray ? `Array<${respRef.name}>` : respRef.name;
|
|
279
|
+
}
|
|
280
|
+
return c.contractSource.response;
|
|
281
|
+
}
|
|
282
|
+
__name(buildResponseType, "buildResponseType");
|
|
283
|
+
function emitRouterTypeBlock(tree, indent, outDir) {
|
|
271
284
|
const pad = " ".repeat(indent);
|
|
272
285
|
const lines = [];
|
|
273
286
|
for (const [key, node] of tree) {
|
|
@@ -279,14 +292,13 @@ function emitRouterTypeBlock(tree, indent) {
|
|
|
279
292
|
const query = queryRef ? queryRef.isArray ? `Array<${queryRef.name}>` : queryRef.name : c.contractSource.query ?? "never";
|
|
280
293
|
const bodyRef = c.contractSource.bodyRef;
|
|
281
294
|
const body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
|
|
282
|
-
const
|
|
283
|
-
const response = respRef ? respRef.isArray ? `Array<${respRef.name}>` : respRef.name : c.contractSource.response;
|
|
295
|
+
const response = buildResponseType(c, outDir);
|
|
284
296
|
const safeMethod = JSON.stringify(method);
|
|
285
297
|
const safeUrl = JSON.stringify(c.path);
|
|
286
298
|
lines.push(`${pad}${objKey}: { method: ${safeMethod}; url: ${safeUrl}; query: ${query}; body: ${body}; response: ${response} };`);
|
|
287
299
|
} else {
|
|
288
300
|
lines.push(`${pad}${objKey}: {`);
|
|
289
|
-
lines.push(...emitRouterTypeBlock(node.children, indent + 2));
|
|
301
|
+
lines.push(...emitRouterTypeBlock(node.children, indent + 2, outDir));
|
|
290
302
|
lines.push(`${pad}};`);
|
|
291
303
|
}
|
|
292
304
|
}
|
|
@@ -308,18 +320,20 @@ function emitApiObjectBlock(tree, indent) {
|
|
|
308
320
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
309
321
|
lines.push(`${pad}${objKey}: {`);
|
|
310
322
|
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}
|
|
313
|
-
lines.push(`${pad}
|
|
314
|
-
lines.push(`${pad}
|
|
323
|
+
lines.push(`${pad} queryOptions: (query?: ${typeAccess}['query']) =>`);
|
|
324
|
+
lines.push(`${pad} _queryOptions({`);
|
|
325
|
+
lines.push(`${pad} queryKey: query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
326
|
+
lines.push(`${pad} queryFn: () => fetcher.get<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { query }),`);
|
|
327
|
+
lines.push(`${pad} }),`);
|
|
315
328
|
lines.push(`${pad}},`);
|
|
316
329
|
} else {
|
|
317
330
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
318
331
|
lines.push(`${pad}${objKey}: {`);
|
|
319
332
|
lines.push(`${pad} queryKey: () => [${flatName}] as const,`);
|
|
320
|
-
lines.push(`${pad} mutationOptions: ()
|
|
321
|
-
lines.push(`${pad}
|
|
322
|
-
lines.push(`${pad}
|
|
333
|
+
lines.push(`${pad} mutationOptions: () =>`);
|
|
334
|
+
lines.push(`${pad} _mutationOptions({`);
|
|
335
|
+
lines.push(`${pad} mutationFn: (body: ${typeAccess}['body']) => fetcher.${fetcherMethod}<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { body }),`);
|
|
336
|
+
lines.push(`${pad} }),`);
|
|
323
337
|
lines.push(`${pad}},`);
|
|
324
338
|
}
|
|
325
339
|
} else {
|
|
@@ -342,11 +356,15 @@ function buildApiFile(routes, outDir) {
|
|
|
342
356
|
for (const r of contracted) {
|
|
343
357
|
const cs = r.contract?.contractSource;
|
|
344
358
|
if (!cs) continue;
|
|
345
|
-
|
|
359
|
+
const refs = r.controllerRef ? [
|
|
360
|
+
cs.queryRef,
|
|
361
|
+
cs.bodyRef
|
|
362
|
+
] : [
|
|
346
363
|
cs.queryRef,
|
|
347
364
|
cs.bodyRef,
|
|
348
365
|
cs.responseRef
|
|
349
|
-
]
|
|
366
|
+
];
|
|
367
|
+
for (const ref of refs) {
|
|
350
368
|
if (!ref) continue;
|
|
351
369
|
let names = importsByFile.get(ref.filePath);
|
|
352
370
|
if (!names) {
|
|
@@ -356,10 +374,18 @@ function buildApiFile(routes, outDir) {
|
|
|
356
374
|
names.add(ref.name);
|
|
357
375
|
}
|
|
358
376
|
}
|
|
377
|
+
const hasGetRoutes = contracted.some((r) => r.method === "GET");
|
|
378
|
+
const hasMutationRoutes = contracted.some((r) => r.method !== "GET");
|
|
359
379
|
const lines = [
|
|
360
380
|
"// Generated by @dudousxd/nestjs-inertia-codegen. Do not edit.",
|
|
361
381
|
""
|
|
362
382
|
];
|
|
383
|
+
const tqImports = [];
|
|
384
|
+
if (hasGetRoutes) tqImports.push("queryOptions as _queryOptions");
|
|
385
|
+
if (hasMutationRoutes) tqImports.push("mutationOptions as _mutationOptions");
|
|
386
|
+
if (tqImports.length > 0) {
|
|
387
|
+
lines.push(`import { ${tqImports.join(", ")} } from '@tanstack/react-query';`);
|
|
388
|
+
}
|
|
363
389
|
lines.push("import { route } from './routes.js';");
|
|
364
390
|
lines.push("import { createFetcher } from '@dudousxd/nestjs-inertia-client';");
|
|
365
391
|
if (importsByFile.size > 0 && outDir) {
|
|
@@ -413,13 +439,14 @@ function buildApiFile(routes, outDir) {
|
|
|
413
439
|
method: r.method,
|
|
414
440
|
name,
|
|
415
441
|
path: r.path,
|
|
442
|
+
controllerRef: r.controllerRef,
|
|
416
443
|
contractSource: c.contractSource
|
|
417
444
|
};
|
|
418
445
|
insertIntoTree(tree, segments, leaf, name);
|
|
419
446
|
}
|
|
420
447
|
void detectCollisions;
|
|
421
448
|
lines.push("export type ApiRouter = {");
|
|
422
|
-
lines.push(...emitRouterTypeBlock(tree, 2));
|
|
449
|
+
lines.push(...emitRouterTypeBlock(tree, 2, outDir ?? ""));
|
|
423
450
|
lines.push("};");
|
|
424
451
|
lines.push("");
|
|
425
452
|
lines.push("export const api = {");
|
|
@@ -689,9 +716,28 @@ import { join as join9 } from "path";
|
|
|
689
716
|
import chokidar from "chokidar";
|
|
690
717
|
|
|
691
718
|
// src/discovery/contracts-fast.ts
|
|
719
|
+
import { readFileSync } from "fs";
|
|
692
720
|
import { dirname, join as join7, resolve as resolve2 } from "path";
|
|
693
721
|
import fg2 from "fast-glob";
|
|
694
722
|
import { Node, Project, SyntaxKind } from "ts-morph";
|
|
723
|
+
var _projectRoot = "";
|
|
724
|
+
var _tsconfigPaths = null;
|
|
725
|
+
var _debug = process.env.NESTJS_INERTIA_DEBUG === "1";
|
|
726
|
+
function dbg(...args) {
|
|
727
|
+
if (_debug) console.log("[codegen:debug]", ...args);
|
|
728
|
+
}
|
|
729
|
+
__name(dbg, "dbg");
|
|
730
|
+
function loadTsconfigPaths(tsconfigPath) {
|
|
731
|
+
try {
|
|
732
|
+
const raw = readFileSync(tsconfigPath, "utf8");
|
|
733
|
+
const stripped = raw.replace(/\/\/.*$/gm, "");
|
|
734
|
+
const parsed = JSON.parse(stripped);
|
|
735
|
+
return parsed.compilerOptions?.paths ?? null;
|
|
736
|
+
} catch {
|
|
737
|
+
return null;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
__name(loadTsconfigPaths, "loadTsconfigPaths");
|
|
695
741
|
async function discoverContractsFast(opts) {
|
|
696
742
|
const { cwd, glob, tsconfig } = opts;
|
|
697
743
|
const tsconfigPath = tsconfig ? resolve2(tsconfig) : join7(cwd, "tsconfig.json");
|
|
@@ -724,6 +770,8 @@ async function discoverContractsFast(opts) {
|
|
|
724
770
|
project.addSourceFileAtPath(f);
|
|
725
771
|
}
|
|
726
772
|
const routes = [];
|
|
773
|
+
_projectRoot = cwd;
|
|
774
|
+
_tsconfigPaths = loadTsconfigPaths(tsconfigPath);
|
|
727
775
|
for (const sourceFile of project.getSourceFiles()) {
|
|
728
776
|
routes.push(...extractFromSourceFile(sourceFile, project));
|
|
729
777
|
}
|
|
@@ -925,17 +973,41 @@ function findTypeInFile(name, file) {
|
|
|
925
973
|
return null;
|
|
926
974
|
}
|
|
927
975
|
__name(findTypeInFile, "findTypeInFile");
|
|
976
|
+
function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
|
|
977
|
+
if (moduleSpecifier.startsWith(".")) {
|
|
978
|
+
const dir = dirname(sourceFile.getFilePath());
|
|
979
|
+
return [
|
|
980
|
+
resolve2(dir, `${moduleSpecifier}.ts`),
|
|
981
|
+
resolve2(dir, moduleSpecifier, "index.ts")
|
|
982
|
+
];
|
|
983
|
+
}
|
|
984
|
+
const baseUrl = _projectRoot;
|
|
985
|
+
dbg("resolveModuleSpecifier", moduleSpecifier, "paths:", JSON.stringify(_tsconfigPaths), "baseUrl:", baseUrl);
|
|
986
|
+
if (_tsconfigPaths) {
|
|
987
|
+
for (const [pattern, mappings] of Object.entries(_tsconfigPaths)) {
|
|
988
|
+
const prefix = pattern.replace("*", "");
|
|
989
|
+
if (moduleSpecifier.startsWith(prefix)) {
|
|
990
|
+
const rest = moduleSpecifier.slice(prefix.length);
|
|
991
|
+
const candidates = [];
|
|
992
|
+
for (const mapping of mappings) {
|
|
993
|
+
const resolved = resolve2(baseUrl, mapping.replace("*", rest));
|
|
994
|
+
candidates.push(`${resolved}.ts`, resolve2(resolved, "index.ts"));
|
|
995
|
+
}
|
|
996
|
+
dbg(" resolved candidates:", candidates);
|
|
997
|
+
return candidates;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return [];
|
|
1002
|
+
}
|
|
1003
|
+
__name(resolveModuleSpecifier, "resolveModuleSpecifier");
|
|
928
1004
|
function resolveImportedType(name, sourceFile, project) {
|
|
929
1005
|
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
930
1006
|
const namedImport = importDecl.getNamedImports().find((n) => n.getName() === name);
|
|
931
1007
|
if (!namedImport) continue;
|
|
932
1008
|
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
const candidates = [
|
|
936
|
-
resolve2(dir, `${moduleSpecifier}.ts`),
|
|
937
|
-
resolve2(dir, moduleSpecifier, "index.ts")
|
|
938
|
-
];
|
|
1009
|
+
const candidates = resolveModuleSpecifier(moduleSpecifier, sourceFile, project);
|
|
1010
|
+
if (candidates.length === 0) continue;
|
|
939
1011
|
for (const candidate of candidates) {
|
|
940
1012
|
let importedFile = project.getSourceFile(candidate);
|
|
941
1013
|
if (!importedFile) {
|
|
@@ -979,6 +1051,18 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
979
1051
|
}
|
|
980
1052
|
return "Array<unknown>";
|
|
981
1053
|
}
|
|
1054
|
+
if ([
|
|
1055
|
+
"Record",
|
|
1056
|
+
"Omit",
|
|
1057
|
+
"Pick",
|
|
1058
|
+
"Partial",
|
|
1059
|
+
"Required",
|
|
1060
|
+
"Readonly",
|
|
1061
|
+
"Map",
|
|
1062
|
+
"Set"
|
|
1063
|
+
].includes(name)) {
|
|
1064
|
+
return typeNode.getText();
|
|
1065
|
+
}
|
|
982
1066
|
if (name === "Promise") {
|
|
983
1067
|
const typeArgs = typeNode.getTypeArguments();
|
|
984
1068
|
const firstTypeArg = typeArgs[0];
|
|
@@ -991,7 +1075,8 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
991
1075
|
if (resolved) {
|
|
992
1076
|
return expandTypeDecl(resolved, project, depth - 1);
|
|
993
1077
|
}
|
|
994
|
-
|
|
1078
|
+
dbg("unresolvable type:", name, "in", sourceFile.getFilePath());
|
|
1079
|
+
return "unknown";
|
|
995
1080
|
}
|
|
996
1081
|
const kind = typeNode.getKind();
|
|
997
1082
|
if (kind === SyntaxKind.StringKeyword) return "string";
|
|
@@ -1153,7 +1238,7 @@ function tryResolveTypeRef(typeNode, sourceFile, project) {
|
|
|
1153
1238
|
return null;
|
|
1154
1239
|
}
|
|
1155
1240
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1156
|
-
if (localDecl
|
|
1241
|
+
if (localDecl?.isExported()) {
|
|
1157
1242
|
return {
|
|
1158
1243
|
name,
|
|
1159
1244
|
filePath: sourceFile.getFilePath()
|
|
@@ -1215,7 +1300,7 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
1215
1300
|
if (val && Node.isIdentifier(val)) {
|
|
1216
1301
|
const name = val.getText();
|
|
1217
1302
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1218
|
-
if (localDecl
|
|
1303
|
+
if (localDecl?.isExported()) {
|
|
1219
1304
|
responseRef = {
|
|
1220
1305
|
name,
|
|
1221
1306
|
filePath: sourceFile.getFilePath()
|
|
@@ -1341,6 +1426,11 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1341
1426
|
path: combined,
|
|
1342
1427
|
name: routeName,
|
|
1343
1428
|
params,
|
|
1429
|
+
controllerRef: {
|
|
1430
|
+
className,
|
|
1431
|
+
methodName,
|
|
1432
|
+
filePath: sourceFile.getFilePath()
|
|
1433
|
+
},
|
|
1344
1434
|
contract: {
|
|
1345
1435
|
contractSource: {
|
|
1346
1436
|
query: contractDef.query,
|
|
@@ -1375,7 +1465,11 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1375
1465
|
path: combined,
|
|
1376
1466
|
name: routeName,
|
|
1377
1467
|
params,
|
|
1378
|
-
|
|
1468
|
+
controllerRef: {
|
|
1469
|
+
className,
|
|
1470
|
+
methodName,
|
|
1471
|
+
filePath: sourceFile.getFilePath()
|
|
1472
|
+
},
|
|
1379
1473
|
...dtoContract ? {
|
|
1380
1474
|
contract: {
|
|
1381
1475
|
contractSource: {
|
|
@@ -1557,7 +1651,7 @@ async function watch(config, onChange) {
|
|
|
1557
1651
|
__name(watch, "watch");
|
|
1558
1652
|
|
|
1559
1653
|
// src/index.ts
|
|
1560
|
-
var VERSION = "1.
|
|
1654
|
+
var VERSION = "1.3.0";
|
|
1561
1655
|
export {
|
|
1562
1656
|
CodegenError,
|
|
1563
1657
|
ConfigError,
|