@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/cli/main.js
CHANGED
|
@@ -255,7 +255,20 @@ function insertIntoTree(tree, segments, leaf, fullName) {
|
|
|
255
255
|
}
|
|
256
256
|
}
|
|
257
257
|
__name(insertIntoTree, "insertIntoTree");
|
|
258
|
-
function
|
|
258
|
+
function buildResponseType(c, outDir) {
|
|
259
|
+
if (c.controllerRef) {
|
|
260
|
+
let relPath = relative3(outDir, c.controllerRef.filePath).replace(/\.ts$/, "");
|
|
261
|
+
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
262
|
+
return `Awaited<ReturnType<import('${relPath}').${c.controllerRef.className}['${c.controllerRef.methodName}']>>`;
|
|
263
|
+
}
|
|
264
|
+
const respRef = c.contractSource.responseRef;
|
|
265
|
+
if (respRef) {
|
|
266
|
+
return respRef.isArray ? `Array<${respRef.name}>` : respRef.name;
|
|
267
|
+
}
|
|
268
|
+
return c.contractSource.response;
|
|
269
|
+
}
|
|
270
|
+
__name(buildResponseType, "buildResponseType");
|
|
271
|
+
function emitRouterTypeBlock(tree, indent, outDir) {
|
|
259
272
|
const pad = " ".repeat(indent);
|
|
260
273
|
const lines = [];
|
|
261
274
|
for (const [key, node] of tree) {
|
|
@@ -267,14 +280,13 @@ function emitRouterTypeBlock(tree, indent) {
|
|
|
267
280
|
const query = queryRef ? queryRef.isArray ? `Array<${queryRef.name}>` : queryRef.name : c.contractSource.query ?? "never";
|
|
268
281
|
const bodyRef = c.contractSource.bodyRef;
|
|
269
282
|
const body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
|
|
270
|
-
const
|
|
271
|
-
const response = respRef ? respRef.isArray ? `Array<${respRef.name}>` : respRef.name : c.contractSource.response;
|
|
283
|
+
const response = buildResponseType(c, outDir);
|
|
272
284
|
const safeMethod = JSON.stringify(method);
|
|
273
285
|
const safeUrl = JSON.stringify(c.path);
|
|
274
286
|
lines.push(`${pad}${objKey}: { method: ${safeMethod}; url: ${safeUrl}; query: ${query}; body: ${body}; response: ${response} };`);
|
|
275
287
|
} else {
|
|
276
288
|
lines.push(`${pad}${objKey}: {`);
|
|
277
|
-
lines.push(...emitRouterTypeBlock(node.children, indent + 2));
|
|
289
|
+
lines.push(...emitRouterTypeBlock(node.children, indent + 2, outDir));
|
|
278
290
|
lines.push(`${pad}};`);
|
|
279
291
|
}
|
|
280
292
|
}
|
|
@@ -296,18 +308,20 @@ function emitApiObjectBlock(tree, indent) {
|
|
|
296
308
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
297
309
|
lines.push(`${pad}${objKey}: {`);
|
|
298
310
|
lines.push(`${pad} queryKey: (query?: ${typeAccess}['query']) => query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
299
|
-
lines.push(`${pad} queryOptions: (query?: ${typeAccess}['query'])
|
|
300
|
-
lines.push(`${pad}
|
|
301
|
-
lines.push(`${pad}
|
|
302
|
-
lines.push(`${pad}
|
|
311
|
+
lines.push(`${pad} queryOptions: (query?: ${typeAccess}['query']) =>`);
|
|
312
|
+
lines.push(`${pad} _queryOptions({`);
|
|
313
|
+
lines.push(`${pad} queryKey: query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
314
|
+
lines.push(`${pad} queryFn: () => fetcher.get<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { query }),`);
|
|
315
|
+
lines.push(`${pad} }),`);
|
|
303
316
|
lines.push(`${pad}},`);
|
|
304
317
|
} else {
|
|
305
318
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
306
319
|
lines.push(`${pad}${objKey}: {`);
|
|
307
320
|
lines.push(`${pad} queryKey: () => [${flatName}] as const,`);
|
|
308
|
-
lines.push(`${pad} mutationOptions: ()
|
|
309
|
-
lines.push(`${pad}
|
|
310
|
-
lines.push(`${pad}
|
|
321
|
+
lines.push(`${pad} mutationOptions: () =>`);
|
|
322
|
+
lines.push(`${pad} _mutationOptions({`);
|
|
323
|
+
lines.push(`${pad} mutationFn: (body: ${typeAccess}['body']) => fetcher.${fetcherMethod}<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { body }),`);
|
|
324
|
+
lines.push(`${pad} }),`);
|
|
311
325
|
lines.push(`${pad}},`);
|
|
312
326
|
}
|
|
313
327
|
} else {
|
|
@@ -330,11 +344,15 @@ function buildApiFile(routes, outDir) {
|
|
|
330
344
|
for (const r of contracted) {
|
|
331
345
|
const cs = r.contract?.contractSource;
|
|
332
346
|
if (!cs) continue;
|
|
333
|
-
|
|
347
|
+
const refs = r.controllerRef ? [
|
|
348
|
+
cs.queryRef,
|
|
349
|
+
cs.bodyRef
|
|
350
|
+
] : [
|
|
334
351
|
cs.queryRef,
|
|
335
352
|
cs.bodyRef,
|
|
336
353
|
cs.responseRef
|
|
337
|
-
]
|
|
354
|
+
];
|
|
355
|
+
for (const ref of refs) {
|
|
338
356
|
if (!ref) continue;
|
|
339
357
|
let names = importsByFile.get(ref.filePath);
|
|
340
358
|
if (!names) {
|
|
@@ -344,10 +362,18 @@ function buildApiFile(routes, outDir) {
|
|
|
344
362
|
names.add(ref.name);
|
|
345
363
|
}
|
|
346
364
|
}
|
|
365
|
+
const hasGetRoutes = contracted.some((r) => r.method === "GET");
|
|
366
|
+
const hasMutationRoutes = contracted.some((r) => r.method !== "GET");
|
|
347
367
|
const lines = [
|
|
348
368
|
"// Generated by @dudousxd/nestjs-inertia-codegen. Do not edit.",
|
|
349
369
|
""
|
|
350
370
|
];
|
|
371
|
+
const tqImports = [];
|
|
372
|
+
if (hasGetRoutes) tqImports.push("queryOptions as _queryOptions");
|
|
373
|
+
if (hasMutationRoutes) tqImports.push("mutationOptions as _mutationOptions");
|
|
374
|
+
if (tqImports.length > 0) {
|
|
375
|
+
lines.push(`import { ${tqImports.join(", ")} } from '@tanstack/react-query';`);
|
|
376
|
+
}
|
|
351
377
|
lines.push("import { route } from './routes.js';");
|
|
352
378
|
lines.push("import { createFetcher } from '@dudousxd/nestjs-inertia-client';");
|
|
353
379
|
if (importsByFile.size > 0 && outDir) {
|
|
@@ -401,13 +427,14 @@ function buildApiFile(routes, outDir) {
|
|
|
401
427
|
method: r.method,
|
|
402
428
|
name,
|
|
403
429
|
path: r.path,
|
|
430
|
+
controllerRef: r.controllerRef,
|
|
404
431
|
contractSource: c.contractSource
|
|
405
432
|
};
|
|
406
433
|
insertIntoTree(tree, segments, leaf, name);
|
|
407
434
|
}
|
|
408
435
|
void detectCollisions;
|
|
409
436
|
lines.push("export type ApiRouter = {");
|
|
410
|
-
lines.push(...emitRouterTypeBlock(tree, 2));
|
|
437
|
+
lines.push(...emitRouterTypeBlock(tree, 2, outDir ?? ""));
|
|
411
438
|
lines.push("};");
|
|
412
439
|
lines.push("");
|
|
413
440
|
lines.push("export const api = {");
|
|
@@ -677,9 +704,28 @@ import { join as join9 } from "path";
|
|
|
677
704
|
import chokidar from "chokidar";
|
|
678
705
|
|
|
679
706
|
// src/discovery/contracts-fast.ts
|
|
707
|
+
import { readFileSync } from "fs";
|
|
680
708
|
import { dirname, join as join7, resolve as resolve2 } from "path";
|
|
681
709
|
import fg2 from "fast-glob";
|
|
682
710
|
import { Node, Project, SyntaxKind } from "ts-morph";
|
|
711
|
+
var _projectRoot = "";
|
|
712
|
+
var _tsconfigPaths = null;
|
|
713
|
+
var _debug = process.env.NESTJS_INERTIA_DEBUG === "1";
|
|
714
|
+
function dbg(...args) {
|
|
715
|
+
if (_debug) console.log("[codegen:debug]", ...args);
|
|
716
|
+
}
|
|
717
|
+
__name(dbg, "dbg");
|
|
718
|
+
function loadTsconfigPaths(tsconfigPath) {
|
|
719
|
+
try {
|
|
720
|
+
const raw = readFileSync(tsconfigPath, "utf8");
|
|
721
|
+
const stripped = raw.replace(/\/\/.*$/gm, "");
|
|
722
|
+
const parsed = JSON.parse(stripped);
|
|
723
|
+
return parsed.compilerOptions?.paths ?? null;
|
|
724
|
+
} catch {
|
|
725
|
+
return null;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
__name(loadTsconfigPaths, "loadTsconfigPaths");
|
|
683
729
|
async function discoverContractsFast(opts) {
|
|
684
730
|
const { cwd, glob, tsconfig } = opts;
|
|
685
731
|
const tsconfigPath = tsconfig ? resolve2(tsconfig) : join7(cwd, "tsconfig.json");
|
|
@@ -712,6 +758,8 @@ async function discoverContractsFast(opts) {
|
|
|
712
758
|
project.addSourceFileAtPath(f);
|
|
713
759
|
}
|
|
714
760
|
const routes = [];
|
|
761
|
+
_projectRoot = cwd;
|
|
762
|
+
_tsconfigPaths = loadTsconfigPaths(tsconfigPath);
|
|
715
763
|
for (const sourceFile of project.getSourceFiles()) {
|
|
716
764
|
routes.push(...extractFromSourceFile(sourceFile, project));
|
|
717
765
|
}
|
|
@@ -913,17 +961,41 @@ function findTypeInFile(name, file) {
|
|
|
913
961
|
return null;
|
|
914
962
|
}
|
|
915
963
|
__name(findTypeInFile, "findTypeInFile");
|
|
964
|
+
function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
|
|
965
|
+
if (moduleSpecifier.startsWith(".")) {
|
|
966
|
+
const dir = dirname(sourceFile.getFilePath());
|
|
967
|
+
return [
|
|
968
|
+
resolve2(dir, `${moduleSpecifier}.ts`),
|
|
969
|
+
resolve2(dir, moduleSpecifier, "index.ts")
|
|
970
|
+
];
|
|
971
|
+
}
|
|
972
|
+
const baseUrl = _projectRoot;
|
|
973
|
+
dbg("resolveModuleSpecifier", moduleSpecifier, "paths:", JSON.stringify(_tsconfigPaths), "baseUrl:", baseUrl);
|
|
974
|
+
if (_tsconfigPaths) {
|
|
975
|
+
for (const [pattern, mappings] of Object.entries(_tsconfigPaths)) {
|
|
976
|
+
const prefix = pattern.replace("*", "");
|
|
977
|
+
if (moduleSpecifier.startsWith(prefix)) {
|
|
978
|
+
const rest = moduleSpecifier.slice(prefix.length);
|
|
979
|
+
const candidates = [];
|
|
980
|
+
for (const mapping of mappings) {
|
|
981
|
+
const resolved = resolve2(baseUrl, mapping.replace("*", rest));
|
|
982
|
+
candidates.push(`${resolved}.ts`, resolve2(resolved, "index.ts"));
|
|
983
|
+
}
|
|
984
|
+
dbg(" resolved candidates:", candidates);
|
|
985
|
+
return candidates;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
return [];
|
|
990
|
+
}
|
|
991
|
+
__name(resolveModuleSpecifier, "resolveModuleSpecifier");
|
|
916
992
|
function resolveImportedType(name, sourceFile, project) {
|
|
917
993
|
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
918
994
|
const namedImport = importDecl.getNamedImports().find((n) => n.getName() === name);
|
|
919
995
|
if (!namedImport) continue;
|
|
920
996
|
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
const candidates = [
|
|
924
|
-
resolve2(dir, `${moduleSpecifier}.ts`),
|
|
925
|
-
resolve2(dir, moduleSpecifier, "index.ts")
|
|
926
|
-
];
|
|
997
|
+
const candidates = resolveModuleSpecifier(moduleSpecifier, sourceFile, project);
|
|
998
|
+
if (candidates.length === 0) continue;
|
|
927
999
|
for (const candidate of candidates) {
|
|
928
1000
|
let importedFile = project.getSourceFile(candidate);
|
|
929
1001
|
if (!importedFile) {
|
|
@@ -967,6 +1039,18 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
967
1039
|
}
|
|
968
1040
|
return "Array<unknown>";
|
|
969
1041
|
}
|
|
1042
|
+
if ([
|
|
1043
|
+
"Record",
|
|
1044
|
+
"Omit",
|
|
1045
|
+
"Pick",
|
|
1046
|
+
"Partial",
|
|
1047
|
+
"Required",
|
|
1048
|
+
"Readonly",
|
|
1049
|
+
"Map",
|
|
1050
|
+
"Set"
|
|
1051
|
+
].includes(name)) {
|
|
1052
|
+
return typeNode.getText();
|
|
1053
|
+
}
|
|
970
1054
|
if (name === "Promise") {
|
|
971
1055
|
const typeArgs = typeNode.getTypeArguments();
|
|
972
1056
|
const firstTypeArg = typeArgs[0];
|
|
@@ -979,7 +1063,8 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
979
1063
|
if (resolved) {
|
|
980
1064
|
return expandTypeDecl(resolved, project, depth - 1);
|
|
981
1065
|
}
|
|
982
|
-
|
|
1066
|
+
dbg("unresolvable type:", name, "in", sourceFile.getFilePath());
|
|
1067
|
+
return "unknown";
|
|
983
1068
|
}
|
|
984
1069
|
const kind = typeNode.getKind();
|
|
985
1070
|
if (kind === SyntaxKind.StringKeyword) return "string";
|
|
@@ -1141,7 +1226,7 @@ function tryResolveTypeRef(typeNode, sourceFile, project) {
|
|
|
1141
1226
|
return null;
|
|
1142
1227
|
}
|
|
1143
1228
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1144
|
-
if (localDecl
|
|
1229
|
+
if (localDecl?.isExported()) {
|
|
1145
1230
|
return {
|
|
1146
1231
|
name,
|
|
1147
1232
|
filePath: sourceFile.getFilePath()
|
|
@@ -1203,7 +1288,7 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
1203
1288
|
if (val && Node.isIdentifier(val)) {
|
|
1204
1289
|
const name = val.getText();
|
|
1205
1290
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1206
|
-
if (localDecl
|
|
1291
|
+
if (localDecl?.isExported()) {
|
|
1207
1292
|
responseRef = {
|
|
1208
1293
|
name,
|
|
1209
1294
|
filePath: sourceFile.getFilePath()
|
|
@@ -1329,6 +1414,11 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1329
1414
|
path: combined,
|
|
1330
1415
|
name: routeName,
|
|
1331
1416
|
params,
|
|
1417
|
+
controllerRef: {
|
|
1418
|
+
className,
|
|
1419
|
+
methodName,
|
|
1420
|
+
filePath: sourceFile.getFilePath()
|
|
1421
|
+
},
|
|
1332
1422
|
contract: {
|
|
1333
1423
|
contractSource: {
|
|
1334
1424
|
query: contractDef.query,
|
|
@@ -1363,7 +1453,11 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1363
1453
|
path: combined,
|
|
1364
1454
|
name: routeName,
|
|
1365
1455
|
params,
|
|
1366
|
-
|
|
1456
|
+
controllerRef: {
|
|
1457
|
+
className,
|
|
1458
|
+
methodName,
|
|
1459
|
+
filePath: sourceFile.getFilePath()
|
|
1460
|
+
},
|
|
1367
1461
|
...dtoContract ? {
|
|
1368
1462
|
contract: {
|
|
1369
1463
|
contractSource: {
|
|
@@ -1545,7 +1639,7 @@ async function watch(config, onChange) {
|
|
|
1545
1639
|
__name(watch, "watch");
|
|
1546
1640
|
|
|
1547
1641
|
// src/index.ts
|
|
1548
|
-
var VERSION = "1.
|
|
1642
|
+
var VERSION = "1.3.0";
|
|
1549
1643
|
|
|
1550
1644
|
// src/cli/codegen.ts
|
|
1551
1645
|
async function runCodegen(opts = {}) {
|
|
@@ -1577,7 +1671,7 @@ __name(runCodegen, "runCodegen");
|
|
|
1577
1671
|
|
|
1578
1672
|
// src/cli/init.ts
|
|
1579
1673
|
import { execSync } from "child_process";
|
|
1580
|
-
import { readFileSync, writeFileSync } from "fs";
|
|
1674
|
+
import { readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
1581
1675
|
import { access as access2, mkdir as mkdir7, readFile as readFile4, writeFile as writeFile7 } from "fs/promises";
|
|
1582
1676
|
import { join as join10 } from "path";
|
|
1583
1677
|
import { createInterface } from "readline";
|
|
@@ -1801,7 +1895,7 @@ __name(findAfterLastImport, "findAfterLastImport");
|
|
|
1801
1895
|
function patchAppModule(filePath, rootView) {
|
|
1802
1896
|
let content;
|
|
1803
1897
|
try {
|
|
1804
|
-
content =
|
|
1898
|
+
content = readFileSync2(filePath, "utf8");
|
|
1805
1899
|
} catch {
|
|
1806
1900
|
return "skipped";
|
|
1807
1901
|
}
|
|
@@ -1846,7 +1940,7 @@ __name(patchAppModule, "patchAppModule");
|
|
|
1846
1940
|
function patchMainTs(filePath) {
|
|
1847
1941
|
let content;
|
|
1848
1942
|
try {
|
|
1849
|
-
content =
|
|
1943
|
+
content = readFileSync2(filePath, "utf8");
|
|
1850
1944
|
} catch {
|
|
1851
1945
|
return "skipped";
|
|
1852
1946
|
}
|
|
@@ -1919,11 +2013,19 @@ function htmlShellTemplate(framework, _engine) {
|
|
|
1919
2013
|
__name(htmlShellTemplate, "htmlShellTemplate");
|
|
1920
2014
|
function viteConfigTemplate(framework) {
|
|
1921
2015
|
const pluginOption = `{ ${framework}: true }`;
|
|
1922
|
-
return `import {
|
|
2016
|
+
return `import { resolve } from 'node:path';
|
|
2017
|
+
import { defineConfig } from 'vite';
|
|
1923
2018
|
import nestInertia from '@dudousxd/nestjs-inertia-vite/plugin';
|
|
1924
2019
|
|
|
1925
2020
|
export default defineConfig({
|
|
1926
2021
|
plugins: [nestInertia(${pluginOption})],
|
|
2022
|
+
resolve: {
|
|
2023
|
+
alias: {
|
|
2024
|
+
'@': resolve(__dirname, 'src'),
|
|
2025
|
+
'~': resolve(__dirname, 'inertia'),
|
|
2026
|
+
'~codegen': resolve(__dirname, '.nestjs-inertia'),
|
|
2027
|
+
},
|
|
2028
|
+
},
|
|
1927
2029
|
});
|
|
1928
2030
|
`;
|
|
1929
2031
|
}
|