@dudousxd/nestjs-inertia-codegen 1.3.0 → 1.4.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 +354 -93
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +345 -84
- package/dist/cli/main.js.map +1 -1
- package/dist/index.cjs +155 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +153 -56
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/cli/main.js
CHANGED
|
@@ -216,16 +216,6 @@ function validateNameSegment(seg, fullName) {
|
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
218
|
__name(validateNameSegment, "validateNameSegment");
|
|
219
|
-
function detectCollisions(tree, name) {
|
|
220
|
-
for (const [key, node] of tree) {
|
|
221
|
-
if (node.kind === "leaf") {
|
|
222
|
-
} else {
|
|
223
|
-
void key;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
void name;
|
|
227
|
-
}
|
|
228
|
-
__name(detectCollisions, "detectCollisions");
|
|
229
219
|
function insertIntoTree(tree, segments, leaf, fullName) {
|
|
230
220
|
const head = segments[0];
|
|
231
221
|
const rest = segments.slice(1);
|
|
@@ -255,6 +245,16 @@ function insertIntoTree(tree, segments, leaf, fullName) {
|
|
|
255
245
|
}
|
|
256
246
|
}
|
|
257
247
|
__name(insertIntoTree, "insertIntoTree");
|
|
248
|
+
function buildParamsType(params) {
|
|
249
|
+
const pathParams = params.filter((p) => p.source === "path");
|
|
250
|
+
if (pathParams.length === 0) return "never";
|
|
251
|
+
return `{ ${pathParams.map((p) => `${p.name}: string`).join("; ")} }`;
|
|
252
|
+
}
|
|
253
|
+
__name(buildParamsType, "buildParamsType");
|
|
254
|
+
function hasPathParams(params) {
|
|
255
|
+
return params.some((p) => p.source === "path");
|
|
256
|
+
}
|
|
257
|
+
__name(hasPathParams, "hasPathParams");
|
|
258
258
|
function buildResponseType(c, outDir) {
|
|
259
259
|
if (c.controllerRef) {
|
|
260
260
|
let relPath = relative3(outDir, c.controllerRef.filePath).replace(/\.ts$/, "");
|
|
@@ -281,9 +281,10 @@ function emitRouterTypeBlock(tree, indent, outDir) {
|
|
|
281
281
|
const bodyRef = c.contractSource.bodyRef;
|
|
282
282
|
const body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
|
|
283
283
|
const response = buildResponseType(c, outDir);
|
|
284
|
+
const params = buildParamsType(c.params);
|
|
284
285
|
const safeMethod = JSON.stringify(method);
|
|
285
286
|
const safeUrl = JSON.stringify(c.path);
|
|
286
|
-
lines.push(`${pad}${objKey}: { method: ${safeMethod}; url: ${safeUrl}; query: ${query}; body: ${body}; response: ${response} };`);
|
|
287
|
+
lines.push(`${pad}${objKey}: { method: ${safeMethod}; url: ${safeUrl}; params: ${params}; query: ${query}; body: ${body}; response: ${response} };`);
|
|
287
288
|
} else {
|
|
288
289
|
lines.push(`${pad}${objKey}: {`);
|
|
289
290
|
lines.push(...emitRouterTypeBlock(node.children, indent + 2, outDir));
|
|
@@ -306,21 +307,60 @@ function emitApiObjectBlock(tree, indent) {
|
|
|
306
307
|
const fetcherMethod = method.toLowerCase();
|
|
307
308
|
if (method === "GET") {
|
|
308
309
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
310
|
+
const withParams = hasPathParams(c.params);
|
|
309
311
|
lines.push(`${pad}${objKey}: {`);
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
312
|
+
if (withParams) {
|
|
313
|
+
lines.push(`${pad} queryKey: (params: ${typeAccess}['params'], query?: ${typeAccess}['query']) => query !== undefined ? [${flatName}, params, query] as const : [${flatName}, params] as const,`);
|
|
314
|
+
lines.push(`${pad} queryOptions: (params: ${typeAccess}['params'], query?: ${typeAccess}['query']) =>`);
|
|
315
|
+
lines.push(`${pad} _queryOptions({`);
|
|
316
|
+
lines.push(`${pad} queryKey: query !== undefined ? [${flatName}, params, query] as const : [${flatName}, params] as const,`);
|
|
317
|
+
lines.push(`${pad} queryFn: () => fetcher.get<${typeAccess}['response']>(route(${flatName} as never, params as never) || ${safePath}, { query }),`);
|
|
318
|
+
lines.push(`${pad} }),`);
|
|
319
|
+
lines.push(`${pad} infiniteQueryOptions: (params: ${typeAccess}['params'], query?: ${typeAccess}['query']) => ({`);
|
|
320
|
+
lines.push(`${pad} queryKey: query !== undefined ? [${flatName}, params, query] as const : [${flatName}, params] as const,`);
|
|
321
|
+
lines.push(`${pad} queryFn: ({ pageParam }: { pageParam: number }) => fetcher.get<${typeAccess}['response']>(route(${flatName} as never, params as never) || ${safePath}, { query: { ...query, page: pageParam } }),`);
|
|
322
|
+
lines.push(`${pad} initialPageParam: 1,`);
|
|
323
|
+
lines.push(`${pad} getNextPageParam: (lastPage: ${typeAccess}['response']) => {`);
|
|
324
|
+
lines.push(`${pad} const meta = (lastPage as any)?.meta;`);
|
|
325
|
+
lines.push(`${pad} if (meta?.page != null && meta?.lastPage != null) {`);
|
|
326
|
+
lines.push(`${pad} return meta.page < meta.lastPage ? meta.page + 1 : undefined;`);
|
|
327
|
+
lines.push(`${pad} }`);
|
|
328
|
+
lines.push(`${pad} return undefined;`);
|
|
329
|
+
lines.push(`${pad} },`);
|
|
330
|
+
lines.push(`${pad} }),`);
|
|
331
|
+
} else {
|
|
332
|
+
lines.push(`${pad} queryKey: (query?: ${typeAccess}['query']) => query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
333
|
+
lines.push(`${pad} queryOptions: (query?: ${typeAccess}['query']) =>`);
|
|
334
|
+
lines.push(`${pad} _queryOptions({`);
|
|
335
|
+
lines.push(`${pad} queryKey: query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
336
|
+
lines.push(`${pad} queryFn: () => fetcher.get<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { query }),`);
|
|
337
|
+
lines.push(`${pad} }),`);
|
|
338
|
+
lines.push(`${pad} infiniteQueryOptions: (query?: ${typeAccess}['query']) => ({`);
|
|
339
|
+
lines.push(`${pad} queryKey: query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
340
|
+
lines.push(`${pad} queryFn: ({ pageParam }: { pageParam: number }) => fetcher.get<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { query: { ...query, page: pageParam } }),`);
|
|
341
|
+
lines.push(`${pad} initialPageParam: 1,`);
|
|
342
|
+
lines.push(`${pad} getNextPageParam: (lastPage: ${typeAccess}['response']) => {`);
|
|
343
|
+
lines.push(`${pad} const meta = (lastPage as any)?.meta;`);
|
|
344
|
+
lines.push(`${pad} if (meta?.page != null && meta?.lastPage != null) {`);
|
|
345
|
+
lines.push(`${pad} return meta.page < meta.lastPage ? meta.page + 1 : undefined;`);
|
|
346
|
+
lines.push(`${pad} }`);
|
|
347
|
+
lines.push(`${pad} return undefined;`);
|
|
348
|
+
lines.push(`${pad} },`);
|
|
349
|
+
lines.push(`${pad} }),`);
|
|
350
|
+
}
|
|
316
351
|
lines.push(`${pad}},`);
|
|
317
352
|
} else {
|
|
318
353
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
354
|
+
const withParams = hasPathParams(c.params);
|
|
319
355
|
lines.push(`${pad}${objKey}: {`);
|
|
320
356
|
lines.push(`${pad} queryKey: () => [${flatName}] as const,`);
|
|
321
357
|
lines.push(`${pad} mutationOptions: () =>`);
|
|
322
358
|
lines.push(`${pad} _mutationOptions({`);
|
|
323
|
-
|
|
359
|
+
if (withParams) {
|
|
360
|
+
lines.push(`${pad} mutationFn: (input: { params: ${typeAccess}['params']; body: ${typeAccess}['body'] }) => fetcher.${fetcherMethod}<${typeAccess}['response']>(route(${flatName} as never, input.params as never) || ${safePath}, { body: input.body }),`);
|
|
361
|
+
} else {
|
|
362
|
+
lines.push(`${pad} mutationFn: (body: ${typeAccess}['body']) => fetcher.${fetcherMethod}<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { body }),`);
|
|
363
|
+
}
|
|
324
364
|
lines.push(`${pad} }),`);
|
|
325
365
|
lines.push(`${pad}},`);
|
|
326
366
|
}
|
|
@@ -427,12 +467,12 @@ function buildApiFile(routes, outDir) {
|
|
|
427
467
|
method: r.method,
|
|
428
468
|
name,
|
|
429
469
|
path: r.path,
|
|
470
|
+
params: r.params,
|
|
430
471
|
controllerRef: r.controllerRef,
|
|
431
472
|
contractSource: c.contractSource
|
|
432
473
|
};
|
|
433
474
|
insertIntoTree(tree, segments, leaf, name);
|
|
434
475
|
}
|
|
435
|
-
void detectCollisions;
|
|
436
476
|
lines.push("export type ApiRouter = {");
|
|
437
477
|
lines.push(...emitRouterTypeBlock(tree, 2, outDir ?? ""));
|
|
438
478
|
lines.push("};");
|
|
@@ -530,8 +570,9 @@ __name(emitIndex, "emitIndex");
|
|
|
530
570
|
|
|
531
571
|
// src/emit/emit-pages.ts
|
|
532
572
|
import { mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
|
|
533
|
-
import { join as join5 } from "path";
|
|
534
|
-
async function emitPages(pages, outDir) {
|
|
573
|
+
import { join as join5, relative as relative4 } from "path";
|
|
574
|
+
async function emitPages(pages, outDir, options = {}) {
|
|
575
|
+
const propsExport = options.propsExport ?? "ComponentProps";
|
|
535
576
|
await mkdir4(outDir, {
|
|
536
577
|
recursive: true
|
|
537
578
|
});
|
|
@@ -540,14 +581,40 @@ async function emitPages(pages, outDir) {
|
|
|
540
581
|
const key = needsQuotes(p.name) ? JSON.stringify(p.name) : p.name;
|
|
541
582
|
return ` ${key}: ${propType};`;
|
|
542
583
|
}).join("\n");
|
|
584
|
+
const pageNameUnion = pages.length > 0 ? pages.map((p) => JSON.stringify(p.name)).join(" | ") : "never";
|
|
585
|
+
const augBody = pages.map((p) => {
|
|
586
|
+
const key = needsQuotes(p.name) ? JSON.stringify(p.name) : p.name;
|
|
587
|
+
const valueType = buildAugmentationType(p, outDir, propsExport);
|
|
588
|
+
return ` ${key}: ${valueType};`;
|
|
589
|
+
}).join("\n");
|
|
590
|
+
const propsHelper = "\nexport type InertiaProps<K extends InertiaPageName> = import('@dudousxd/nestjs-inertia').InertiaPages[K];\n";
|
|
543
591
|
const content = `// Generated by @dudousxd/nestjs-inertia-codegen. Do not edit.
|
|
544
592
|
export interface InertiaPages {
|
|
545
593
|
${body}
|
|
546
594
|
}
|
|
595
|
+
|
|
596
|
+
export type InertiaPageName = ${pageNameUnion};
|
|
597
|
+
` + propsHelper + `
|
|
598
|
+
declare module '@dudousxd/nestjs-inertia' {
|
|
599
|
+
interface InertiaPages {
|
|
600
|
+
${augBody}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
547
603
|
`;
|
|
548
604
|
await writeFile4(join5(outDir, "pages.d.ts"), content, "utf8");
|
|
549
605
|
}
|
|
550
606
|
__name(emitPages, "emitPages");
|
|
607
|
+
function buildAugmentationType(page, outDir, propsExport) {
|
|
608
|
+
if (!page.propsSource) {
|
|
609
|
+
return "Record<string, unknown>";
|
|
610
|
+
}
|
|
611
|
+
let importPath = relative4(outDir, page.absolutePath).replace(/\.(tsx?|vue|svelte)$/, "");
|
|
612
|
+
if (!importPath.startsWith(".")) {
|
|
613
|
+
importPath = `./${importPath}`;
|
|
614
|
+
}
|
|
615
|
+
return `import('${importPath}').${propsExport}`;
|
|
616
|
+
}
|
|
617
|
+
__name(buildAugmentationType, "buildAugmentationType");
|
|
551
618
|
function needsQuotes(name) {
|
|
552
619
|
return !/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
|
|
553
620
|
}
|
|
@@ -684,7 +751,9 @@ async function generate(config, routes = []) {
|
|
|
684
751
|
propsExport: config.pages.propsExport,
|
|
685
752
|
componentNameStrategy: config.pages.componentNameStrategy
|
|
686
753
|
});
|
|
687
|
-
await emitPages(pages, config.codegen.outDir
|
|
754
|
+
await emitPages(pages, config.codegen.outDir, {
|
|
755
|
+
propsExport: config.pages.propsExport
|
|
756
|
+
});
|
|
688
757
|
await emitCache(pages, config.codegen.outDir);
|
|
689
758
|
const hasRoutes = routes.length > 0;
|
|
690
759
|
const hasContracts = routes.some((r) => r.contract);
|
|
@@ -708,8 +777,18 @@ import { readFileSync } from "fs";
|
|
|
708
777
|
import { dirname, join as join7, resolve as resolve2 } from "path";
|
|
709
778
|
import fg2 from "fast-glob";
|
|
710
779
|
import { Node, Project, SyntaxKind } from "ts-morph";
|
|
711
|
-
var
|
|
712
|
-
|
|
780
|
+
var _ctx = {
|
|
781
|
+
projectRoot: "",
|
|
782
|
+
tsconfigPaths: null
|
|
783
|
+
};
|
|
784
|
+
function _projectRoot() {
|
|
785
|
+
return _ctx.projectRoot;
|
|
786
|
+
}
|
|
787
|
+
__name(_projectRoot, "_projectRoot");
|
|
788
|
+
function _tsconfigPaths() {
|
|
789
|
+
return _ctx.tsconfigPaths;
|
|
790
|
+
}
|
|
791
|
+
__name(_tsconfigPaths, "_tsconfigPaths");
|
|
713
792
|
var _debug = process.env.NESTJS_INERTIA_DEBUG === "1";
|
|
714
793
|
function dbg(...args) {
|
|
715
794
|
if (_debug) console.log("[codegen:debug]", ...args);
|
|
@@ -758,10 +837,17 @@ async function discoverContractsFast(opts) {
|
|
|
758
837
|
project.addSourceFileAtPath(f);
|
|
759
838
|
}
|
|
760
839
|
const routes = [];
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
840
|
+
const prevCtx = _ctx;
|
|
841
|
+
_ctx = {
|
|
842
|
+
projectRoot: cwd,
|
|
843
|
+
tsconfigPaths: loadTsconfigPaths(tsconfigPath)
|
|
844
|
+
};
|
|
845
|
+
try {
|
|
846
|
+
for (const sourceFile of project.getSourceFiles()) {
|
|
847
|
+
routes.push(...extractFromSourceFile(sourceFile, project));
|
|
848
|
+
}
|
|
849
|
+
} finally {
|
|
850
|
+
_ctx = prevCtx;
|
|
765
851
|
}
|
|
766
852
|
return routes;
|
|
767
853
|
}
|
|
@@ -969,10 +1055,11 @@ function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
|
|
|
969
1055
|
resolve2(dir, moduleSpecifier, "index.ts")
|
|
970
1056
|
];
|
|
971
1057
|
}
|
|
972
|
-
const baseUrl = _projectRoot;
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
1058
|
+
const baseUrl = _projectRoot();
|
|
1059
|
+
const tsconfigPaths = _tsconfigPaths();
|
|
1060
|
+
dbg("resolveModuleSpecifier", moduleSpecifier, "paths:", JSON.stringify(tsconfigPaths), "baseUrl:", baseUrl);
|
|
1061
|
+
if (tsconfigPaths) {
|
|
1062
|
+
for (const [pattern, mappings] of Object.entries(tsconfigPaths)) {
|
|
976
1063
|
const prefix = pattern.replace("*", "");
|
|
977
1064
|
if (moduleSpecifier.startsWith(prefix)) {
|
|
978
1065
|
const rest = moduleSpecifier.slice(prefix.length);
|
|
@@ -1458,18 +1545,16 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1458
1545
|
methodName,
|
|
1459
1546
|
filePath: sourceFile.getFilePath()
|
|
1460
1547
|
},
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
responseRef: dtoContract.responseRef
|
|
1470
|
-
}
|
|
1548
|
+
contract: {
|
|
1549
|
+
contractSource: {
|
|
1550
|
+
query: dtoContract?.query ?? null,
|
|
1551
|
+
body: dtoContract?.body ?? null,
|
|
1552
|
+
response: dtoContract?.response ?? "unknown",
|
|
1553
|
+
queryRef: dtoContract?.queryRef,
|
|
1554
|
+
bodyRef: dtoContract?.bodyRef,
|
|
1555
|
+
responseRef: dtoContract?.responseRef
|
|
1471
1556
|
}
|
|
1472
|
-
}
|
|
1557
|
+
}
|
|
1473
1558
|
});
|
|
1474
1559
|
}
|
|
1475
1560
|
}
|
|
@@ -1479,7 +1564,8 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1479
1564
|
__name(extractFromSourceFile, "extractFromSourceFile");
|
|
1480
1565
|
|
|
1481
1566
|
// src/watch/lock-file.ts
|
|
1482
|
-
import {
|
|
1567
|
+
import { open } from "fs/promises";
|
|
1568
|
+
import { mkdir as mkdir6, readFile as readFile2, unlink } from "fs/promises";
|
|
1483
1569
|
import { join as join8 } from "path";
|
|
1484
1570
|
var LOCK_FILE = ".watcher.lock";
|
|
1485
1571
|
function isProcessAlive(pid) {
|
|
@@ -1496,20 +1582,29 @@ async function acquireLock(outDir) {
|
|
|
1496
1582
|
recursive: true
|
|
1497
1583
|
});
|
|
1498
1584
|
const lockPath = join8(outDir, LOCK_FILE);
|
|
1499
|
-
try {
|
|
1500
|
-
const raw = await readFile2(lockPath, "utf8");
|
|
1501
|
-
const existing = JSON.parse(raw);
|
|
1502
|
-
if (isProcessAlive(existing.pid)) {
|
|
1503
|
-
return null;
|
|
1504
|
-
}
|
|
1505
|
-
} catch {
|
|
1506
|
-
}
|
|
1507
1585
|
const lockData = {
|
|
1508
1586
|
pid: process.pid,
|
|
1509
1587
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1510
1588
|
};
|
|
1511
|
-
|
|
1589
|
+
try {
|
|
1590
|
+
const fd = await open(lockPath, "wx");
|
|
1591
|
+
await fd.writeFile(`${JSON.stringify(lockData, null, 2)}
|
|
1512
1592
|
`, "utf8");
|
|
1593
|
+
await fd.close();
|
|
1594
|
+
} catch (err) {
|
|
1595
|
+
if (err.code === "EEXIST") {
|
|
1596
|
+
try {
|
|
1597
|
+
const raw = await readFile2(lockPath, "utf8");
|
|
1598
|
+
const existing = JSON.parse(raw);
|
|
1599
|
+
if (isProcessAlive(existing.pid)) return null;
|
|
1600
|
+
await unlink(lockPath);
|
|
1601
|
+
return acquireLock(outDir);
|
|
1602
|
+
} catch {
|
|
1603
|
+
return null;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
return null;
|
|
1607
|
+
}
|
|
1513
1608
|
return {
|
|
1514
1609
|
release: /* @__PURE__ */ __name(async () => {
|
|
1515
1610
|
try {
|
|
@@ -1573,7 +1668,8 @@ async function watch(config, onChange) {
|
|
|
1573
1668
|
pagesDebounceTimer = void 0;
|
|
1574
1669
|
try {
|
|
1575
1670
|
await generate(config);
|
|
1576
|
-
} catch {
|
|
1671
|
+
} catch (err) {
|
|
1672
|
+
console.error("[nestjs-inertia-codegen] Pages generation failed:", err instanceof Error ? err.message : err);
|
|
1577
1673
|
}
|
|
1578
1674
|
onChange?.();
|
|
1579
1675
|
}, PAGES_DEBOUNCE_MS);
|
|
@@ -1611,7 +1707,8 @@ async function watch(config, onChange) {
|
|
|
1611
1707
|
if (hasContracts) {
|
|
1612
1708
|
await emitApi(routes, config.codegen.outDir);
|
|
1613
1709
|
}
|
|
1614
|
-
} catch {
|
|
1710
|
+
} catch (err) {
|
|
1711
|
+
console.error("[nestjs-inertia-codegen] Contracts generation failed:", err instanceof Error ? err.message : err);
|
|
1615
1712
|
}
|
|
1616
1713
|
onChange?.();
|
|
1617
1714
|
}, config.contracts.debounceMs);
|
|
@@ -1639,7 +1736,7 @@ async function watch(config, onChange) {
|
|
|
1639
1736
|
__name(watch, "watch");
|
|
1640
1737
|
|
|
1641
1738
|
// src/index.ts
|
|
1642
|
-
var VERSION = "1.
|
|
1739
|
+
var VERSION = "1.4.0";
|
|
1643
1740
|
|
|
1644
1741
|
// src/cli/codegen.ts
|
|
1645
1742
|
async function runCodegen(opts = {}) {
|
|
@@ -1669,11 +1766,163 @@ async function runCodegen(opts = {}) {
|
|
|
1669
1766
|
}
|
|
1670
1767
|
__name(runCodegen, "runCodegen");
|
|
1671
1768
|
|
|
1672
|
-
// src/cli/
|
|
1673
|
-
import {
|
|
1674
|
-
import { readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
1675
|
-
import { access as access2, mkdir as mkdir7, readFile as readFile4, writeFile as writeFile7 } from "fs/promises";
|
|
1769
|
+
// src/cli/doctor.ts
|
|
1770
|
+
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
1676
1771
|
import { join as join10 } from "path";
|
|
1772
|
+
function checkFileExists(cwd, file) {
|
|
1773
|
+
return existsSync(join10(cwd, file));
|
|
1774
|
+
}
|
|
1775
|
+
__name(checkFileExists, "checkFileExists");
|
|
1776
|
+
function readJson(path) {
|
|
1777
|
+
try {
|
|
1778
|
+
const raw = readFileSync2(path, "utf8").replace(/\/\/.*$/gm, "");
|
|
1779
|
+
return JSON.parse(raw);
|
|
1780
|
+
} catch {
|
|
1781
|
+
return null;
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
__name(readJson, "readJson");
|
|
1785
|
+
function getPackageVersion(cwd, pkg) {
|
|
1786
|
+
try {
|
|
1787
|
+
const pkgJson = readJson(join10(cwd, "node_modules", pkg, "package.json"));
|
|
1788
|
+
return pkgJson?.version ?? null;
|
|
1789
|
+
} catch {
|
|
1790
|
+
return null;
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
__name(getPackageVersion, "getPackageVersion");
|
|
1794
|
+
async function runDoctor(opts) {
|
|
1795
|
+
const { cwd } = opts;
|
|
1796
|
+
const checks = [];
|
|
1797
|
+
checks.push({
|
|
1798
|
+
name: "nestjs-inertia.config.ts exists",
|
|
1799
|
+
pass: checkFileExists(cwd, "nestjs-inertia.config.ts"),
|
|
1800
|
+
fix: "Run: pnpm exec nestjs-inertia init"
|
|
1801
|
+
});
|
|
1802
|
+
const hasApi = checkFileExists(cwd, ".nestjs-inertia/api.ts");
|
|
1803
|
+
const hasRoutes = checkFileExists(cwd, ".nestjs-inertia/routes.ts");
|
|
1804
|
+
const hasPages = checkFileExists(cwd, ".nestjs-inertia/pages.d.ts");
|
|
1805
|
+
checks.push({
|
|
1806
|
+
name: ".nestjs-inertia/ codegen output exists",
|
|
1807
|
+
pass: hasApi && hasRoutes && hasPages,
|
|
1808
|
+
fix: "Run: pnpm exec nestjs-inertia codegen"
|
|
1809
|
+
});
|
|
1810
|
+
const tsconfig = readJson(join10(cwd, "tsconfig.json"));
|
|
1811
|
+
const paths = tsconfig?.compilerOptions?.paths;
|
|
1812
|
+
checks.push({
|
|
1813
|
+
name: "tsconfig.json has @/* path alias",
|
|
1814
|
+
pass: !!paths?.["@/*"],
|
|
1815
|
+
fix: 'Add to tsconfig.json compilerOptions.paths: { "@/*": ["./src/*"] }'
|
|
1816
|
+
});
|
|
1817
|
+
const inertiaTsconfig = readJson(join10(cwd, "tsconfig.inertia.json"));
|
|
1818
|
+
if (inertiaTsconfig) {
|
|
1819
|
+
const inertiaPaths = inertiaTsconfig.compilerOptions?.paths;
|
|
1820
|
+
checks.push({
|
|
1821
|
+
name: "tsconfig.inertia.json has ~/* and ~codegen/* aliases",
|
|
1822
|
+
pass: !!inertiaPaths?.["~/*"] && !!inertiaPaths?.["~codegen/*"],
|
|
1823
|
+
fix: 'Add paths: { "~/*": ["inertia/*"], "~codegen/*": [".nestjs-inertia/*"] }'
|
|
1824
|
+
});
|
|
1825
|
+
}
|
|
1826
|
+
if (checkFileExists(cwd, "vite.config.ts")) {
|
|
1827
|
+
const viteContent = readFileSync2(join10(cwd, "vite.config.ts"), "utf8");
|
|
1828
|
+
checks.push({
|
|
1829
|
+
name: "vite.config.ts has resolve.alias",
|
|
1830
|
+
pass: viteContent.includes("resolve") && viteContent.includes("alias"),
|
|
1831
|
+
fix: "Add resolve.alias with @\u2192src, ~\u2192inertia, ~codegen\u2192.nestjs-inertia"
|
|
1832
|
+
});
|
|
1833
|
+
checks.push({
|
|
1834
|
+
name: "vite.config.ts references nestjs-inertia",
|
|
1835
|
+
pass: viteContent.includes("nestInertia") || viteContent.includes("nestjs-inertia") || viteContent.includes("setupInertiaVite"),
|
|
1836
|
+
fix: "Add: import nestInertia from '@dudousxd/nestjs-inertia-vite/plugin'"
|
|
1837
|
+
});
|
|
1838
|
+
}
|
|
1839
|
+
const libPackages = [
|
|
1840
|
+
"@dudousxd/nestjs-inertia",
|
|
1841
|
+
"@dudousxd/nestjs-inertia-codegen",
|
|
1842
|
+
"@dudousxd/nestjs-inertia-client",
|
|
1843
|
+
"@dudousxd/nestjs-inertia-vite",
|
|
1844
|
+
"@dudousxd/nestjs-inertia-testing"
|
|
1845
|
+
];
|
|
1846
|
+
const versions = libPackages.map((pkg) => ({
|
|
1847
|
+
pkg,
|
|
1848
|
+
version: getPackageVersion(cwd, pkg)
|
|
1849
|
+
}));
|
|
1850
|
+
const installed = versions.filter((v) => v.version !== null);
|
|
1851
|
+
const uniqueVersions = new Set(installed.map((v) => v.version));
|
|
1852
|
+
const requiredPkgs = [
|
|
1853
|
+
"@dudousxd/nestjs-inertia",
|
|
1854
|
+
"@dudousxd/nestjs-inertia-codegen",
|
|
1855
|
+
"@dudousxd/nestjs-inertia-client"
|
|
1856
|
+
];
|
|
1857
|
+
const missingRequired = requiredPkgs.filter((pkg) => !getPackageVersion(cwd, pkg));
|
|
1858
|
+
checks.push({
|
|
1859
|
+
name: "Core packages installed (core + codegen + client)",
|
|
1860
|
+
pass: missingRequired.length === 0,
|
|
1861
|
+
fix: missingRequired.length > 0 ? `Missing: ${missingRequired.join(", ")}` : void 0
|
|
1862
|
+
});
|
|
1863
|
+
if (installed.length > 1) {
|
|
1864
|
+
checks.push({
|
|
1865
|
+
name: "All packages on same version",
|
|
1866
|
+
pass: uniqueVersions.size === 1,
|
|
1867
|
+
fix: `Versions: ${installed.map((v) => `${v.pkg.replace("@dudousxd/", "")}@${v.version}`).join(", ")}`
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
const inertiaReact = getPackageVersion(cwd, "@inertiajs/react");
|
|
1871
|
+
const inertiaVue = getPackageVersion(cwd, "@inertiajs/vue3");
|
|
1872
|
+
const inertiaSvelte = getPackageVersion(cwd, "@inertiajs/svelte");
|
|
1873
|
+
const inertiaVersion = inertiaReact ?? inertiaVue ?? inertiaSvelte;
|
|
1874
|
+
const inertiaFramework = inertiaReact ? "react" : inertiaVue ? "vue" : inertiaSvelte ? "svelte" : null;
|
|
1875
|
+
if (inertiaVersion) {
|
|
1876
|
+
const majorVersion = Number.parseInt(inertiaVersion.split(".")[0] ?? "0", 10);
|
|
1877
|
+
checks.push({
|
|
1878
|
+
name: `@inertiajs/${inertiaFramework} is v3+`,
|
|
1879
|
+
pass: majorVersion >= 3,
|
|
1880
|
+
fix: `Current: v${inertiaVersion}. Run: pnpm add @inertiajs/${inertiaFramework}@^3.0.0`
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1883
|
+
if (checkFileExists(cwd, ".gitignore")) {
|
|
1884
|
+
const gitignore = readFileSync2(join10(cwd, ".gitignore"), "utf8");
|
|
1885
|
+
checks.push({
|
|
1886
|
+
name: ".gitignore includes .nestjs-inertia/",
|
|
1887
|
+
pass: gitignore.includes(".nestjs-inertia"),
|
|
1888
|
+
fix: "Add .nestjs-inertia/ to .gitignore"
|
|
1889
|
+
});
|
|
1890
|
+
}
|
|
1891
|
+
const pkgJson = readJson(join10(cwd, "package.json"));
|
|
1892
|
+
const scripts = pkgJson?.scripts ?? {};
|
|
1893
|
+
checks.push({
|
|
1894
|
+
name: "package.json has build:client script",
|
|
1895
|
+
pass: !!scripts["build:client"],
|
|
1896
|
+
fix: 'Add: "build:client": "vite build"'
|
|
1897
|
+
});
|
|
1898
|
+
console.log("");
|
|
1899
|
+
console.log("\x1B[1mnestjs-inertia doctor\x1B[0m");
|
|
1900
|
+
console.log("");
|
|
1901
|
+
let hasFailures = false;
|
|
1902
|
+
for (const check of checks) {
|
|
1903
|
+
const icon = check.pass ? "\x1B[32m\u2713\x1B[0m" : "\x1B[31m\u2717\x1B[0m";
|
|
1904
|
+
console.log(` ${icon} ${check.name}`);
|
|
1905
|
+
if (!check.pass && check.fix) {
|
|
1906
|
+
console.log(` \x1B[2m${check.fix}\x1B[0m`);
|
|
1907
|
+
hasFailures = true;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
console.log("");
|
|
1911
|
+
if (hasFailures) {
|
|
1912
|
+
console.log(`\x1B[33m${checks.filter((c) => !c.pass).length} issue(s) found\x1B[0m`);
|
|
1913
|
+
} else {
|
|
1914
|
+
console.log("\x1B[32mAll checks passed!\x1B[0m");
|
|
1915
|
+
}
|
|
1916
|
+
console.log("");
|
|
1917
|
+
return hasFailures ? 1 : 0;
|
|
1918
|
+
}
|
|
1919
|
+
__name(runDoctor, "runDoctor");
|
|
1920
|
+
|
|
1921
|
+
// src/cli/init.ts
|
|
1922
|
+
import { execFileSync } from "child_process";
|
|
1923
|
+
import { readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
1924
|
+
import { access as access2, mkdir as mkdir7, readFile as readFile4, writeFile as writeFile6 } from "fs/promises";
|
|
1925
|
+
import { join as join11 } from "path";
|
|
1677
1926
|
import { createInterface } from "readline";
|
|
1678
1927
|
var GITIGNORE_ENTRY = ".nestjs-inertia/";
|
|
1679
1928
|
var green = /* @__PURE__ */ __name((s) => `\x1B[32m${s}\x1B[0m`, "green");
|
|
@@ -1704,7 +1953,7 @@ ${bold(title)}`);
|
|
|
1704
1953
|
__name(logSection, "logSection");
|
|
1705
1954
|
async function readPackageJson(cwd) {
|
|
1706
1955
|
try {
|
|
1707
|
-
const raw = await readFile4(
|
|
1956
|
+
const raw = await readFile4(join11(cwd, "package.json"), "utf8");
|
|
1708
1957
|
return JSON.parse(raw);
|
|
1709
1958
|
} catch {
|
|
1710
1959
|
return {};
|
|
@@ -1742,7 +1991,7 @@ __name(detectTemplateEngine, "detectTemplateEngine");
|
|
|
1742
1991
|
async function detectPackageManager(cwd) {
|
|
1743
1992
|
async function exists(file) {
|
|
1744
1993
|
try {
|
|
1745
|
-
await access2(
|
|
1994
|
+
await access2(join11(cwd, file));
|
|
1746
1995
|
return true;
|
|
1747
1996
|
} catch {
|
|
1748
1997
|
return false;
|
|
@@ -1791,12 +2040,12 @@ async function writeIfNotExists(filePath, content, label) {
|
|
|
1791
2040
|
recursive: true
|
|
1792
2041
|
});
|
|
1793
2042
|
}
|
|
1794
|
-
await
|
|
2043
|
+
await writeFile6(filePath, content, "utf8");
|
|
1795
2044
|
logCreated(label);
|
|
1796
2045
|
}
|
|
1797
2046
|
__name(writeIfNotExists, "writeIfNotExists");
|
|
1798
2047
|
async function handleViteConfig(cwd, framework) {
|
|
1799
|
-
const filePath =
|
|
2048
|
+
const filePath = join11(cwd, "vite.config.ts");
|
|
1800
2049
|
if (await fileExists2(filePath)) {
|
|
1801
2050
|
const existing = await readFile4(filePath, "utf8");
|
|
1802
2051
|
const hasPlugin = existing.includes("nestInertia") || existing.includes("nestjs-inertia-vite/plugin");
|
|
@@ -1816,7 +2065,7 @@ async function handleViteConfig(cwd, framework) {
|
|
|
1816
2065
|
recursive: true
|
|
1817
2066
|
});
|
|
1818
2067
|
}
|
|
1819
|
-
await
|
|
2068
|
+
await writeFile6(filePath, viteConfigTemplate(framework), "utf8");
|
|
1820
2069
|
logCreated("vite.config.ts");
|
|
1821
2070
|
}
|
|
1822
2071
|
__name(handleViteConfig, "handleViteConfig");
|
|
@@ -1833,27 +2082,33 @@ async function patchGitignore(gitignorePath) {
|
|
|
1833
2082
|
` : `${existing}
|
|
1834
2083
|
${GITIGNORE_ENTRY}
|
|
1835
2084
|
`;
|
|
1836
|
-
await
|
|
2085
|
+
await writeFile6(gitignorePath, newContent, "utf8");
|
|
1837
2086
|
logPatched(".gitignore", "added .nestjs-inertia/");
|
|
1838
2087
|
}
|
|
1839
2088
|
__name(patchGitignore, "patchGitignore");
|
|
1840
2089
|
function installDeps(pkgManager, deps, dev) {
|
|
1841
2090
|
if (deps.length === 0) return;
|
|
1842
|
-
const
|
|
1843
|
-
|
|
2091
|
+
const args = [];
|
|
2092
|
+
if (pkgManager === "npm") {
|
|
2093
|
+
args.push("install");
|
|
2094
|
+
if (dev) args.push("--save-dev");
|
|
2095
|
+
} else {
|
|
2096
|
+
args.push("add");
|
|
2097
|
+
if (dev) args.push("-D");
|
|
2098
|
+
}
|
|
2099
|
+
args.push(...deps);
|
|
1844
2100
|
logPatched(deps.join(", "), "installed");
|
|
1845
2101
|
try {
|
|
1846
|
-
|
|
2102
|
+
execFileSync(pkgManager, args, {
|
|
1847
2103
|
stdio: "inherit"
|
|
1848
2104
|
});
|
|
1849
2105
|
} catch {
|
|
1850
|
-
logWarning(`Failed to install deps.
|
|
1851
|
-
${cmd}`);
|
|
2106
|
+
logWarning(`Failed to install: ${deps.join(", ")}`);
|
|
1852
2107
|
}
|
|
1853
2108
|
}
|
|
1854
2109
|
__name(installDeps, "installDeps");
|
|
1855
2110
|
async function patchPackageJsonScripts(cwd, scripts) {
|
|
1856
|
-
const pkgPath =
|
|
2111
|
+
const pkgPath = join11(cwd, "package.json");
|
|
1857
2112
|
let pkg = {};
|
|
1858
2113
|
try {
|
|
1859
2114
|
pkg = JSON.parse(await readFile4(pkgPath, "utf8"));
|
|
@@ -1875,7 +2130,7 @@ async function patchPackageJsonScripts(cwd, scripts) {
|
|
|
1875
2130
|
return;
|
|
1876
2131
|
}
|
|
1877
2132
|
pkg.scripts = existing;
|
|
1878
|
-
await
|
|
2133
|
+
await writeFile6(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
1879
2134
|
`, "utf8");
|
|
1880
2135
|
}
|
|
1881
2136
|
__name(patchPackageJsonScripts, "patchPackageJsonScripts");
|
|
@@ -1895,7 +2150,7 @@ __name(findAfterLastImport, "findAfterLastImport");
|
|
|
1895
2150
|
function patchAppModule(filePath, rootView) {
|
|
1896
2151
|
let content;
|
|
1897
2152
|
try {
|
|
1898
|
-
content =
|
|
2153
|
+
content = readFileSync3(filePath, "utf8");
|
|
1899
2154
|
} catch {
|
|
1900
2155
|
return "skipped";
|
|
1901
2156
|
}
|
|
@@ -1940,7 +2195,7 @@ __name(patchAppModule, "patchAppModule");
|
|
|
1940
2195
|
function patchMainTs(filePath) {
|
|
1941
2196
|
let content;
|
|
1942
2197
|
try {
|
|
1943
|
-
content =
|
|
2198
|
+
content = readFileSync3(filePath, "utf8");
|
|
1944
2199
|
} catch {
|
|
1945
2200
|
return "skipped";
|
|
1946
2201
|
}
|
|
@@ -2145,16 +2400,16 @@ ${bold("nestjs-inertia init")}`);
|
|
|
2145
2400
|
const entryExt = framework === "react" ? "tsx" : "ts";
|
|
2146
2401
|
const pageExt = framework === "react" ? "tsx" : framework === "vue" ? "vue" : "svelte";
|
|
2147
2402
|
logSection("Scaffold files");
|
|
2148
|
-
await writeIfNotExists(
|
|
2149
|
-
await writeIfNotExists(
|
|
2150
|
-
await writeIfNotExists(
|
|
2403
|
+
await writeIfNotExists(join11(cwd, "nestjs-inertia.config.ts"), configTemplate(framework), "nestjs-inertia.config.ts");
|
|
2404
|
+
await writeIfNotExists(join11(cwd, "nestjs-inertia.d.ts"), DTS_TEMPLATE, "nestjs-inertia.d.ts");
|
|
2405
|
+
await writeIfNotExists(join11(cwd, "inertia", shellFileName), htmlShellTemplate(framework, engine), `inertia/${shellFileName}`);
|
|
2151
2406
|
await handleViteConfig(cwd, framework);
|
|
2152
|
-
await writeIfNotExists(
|
|
2153
|
-
await writeIfNotExists(
|
|
2154
|
-
await writeIfNotExists(
|
|
2407
|
+
await writeIfNotExists(join11(cwd, "inertia", `app.${entryExt}`), entryPointTemplate(framework), `inertia/app.${entryExt}`);
|
|
2408
|
+
await writeIfNotExists(join11(cwd, "inertia", "pages", `Home.${pageExt}`), samplePageTemplate(framework), `inertia/pages/Home.${pageExt}`);
|
|
2409
|
+
await writeIfNotExists(join11(cwd, "src", "home.controller.ts"), SAMPLE_CONTROLLER, "src/home.controller.ts");
|
|
2155
2410
|
logSection("Patch existing files");
|
|
2156
2411
|
const rootView = engine === "html" ? "inertia/index.html" : `inertia/index.${engine === "handlebars" ? "hbs" : engine}`;
|
|
2157
|
-
const appModulePath =
|
|
2412
|
+
const appModulePath = join11(cwd, "src", "app.module.ts");
|
|
2158
2413
|
const appModuleResult = patchAppModule(appModulePath, rootView);
|
|
2159
2414
|
if (appModuleResult === "patched") {
|
|
2160
2415
|
logPatched("src/app.module.ts", "added InertiaModule.forRoot");
|
|
@@ -2164,7 +2419,7 @@ ${bold("nestjs-inertia init")}`);
|
|
|
2164
2419
|
} else {
|
|
2165
2420
|
logWarning("src/app.module.ts not found \u2014 add InertiaModule.forRoot() manually");
|
|
2166
2421
|
}
|
|
2167
|
-
const mainTsPath =
|
|
2422
|
+
const mainTsPath = join11(cwd, "src", "main.ts");
|
|
2168
2423
|
const mainTsResult = patchMainTs(mainTsPath);
|
|
2169
2424
|
if (mainTsResult === "patched") {
|
|
2170
2425
|
logPatched("src/main.ts", "added setupInertiaVite after NestFactory.create");
|
|
@@ -2173,7 +2428,7 @@ ${bold("nestjs-inertia init")}`);
|
|
|
2173
2428
|
} else {
|
|
2174
2429
|
logWarning("src/main.ts not found \u2014 add setupInertiaVite() manually");
|
|
2175
2430
|
}
|
|
2176
|
-
await patchGitignore(
|
|
2431
|
+
await patchGitignore(join11(cwd, ".gitignore"));
|
|
2177
2432
|
await patchPackageJsonScripts(cwd, {
|
|
2178
2433
|
"build:client": "vite build",
|
|
2179
2434
|
"build:ssr": "VITE_SSR=1 vite build --ssr"
|
|
@@ -2250,6 +2505,12 @@ async function run(argv) {
|
|
|
2250
2505
|
cwd: process.cwd()
|
|
2251
2506
|
});
|
|
2252
2507
|
});
|
|
2508
|
+
cli.command("doctor", "Diagnose your nestjs-inertia setup").action(async () => {
|
|
2509
|
+
const code = await runDoctor({
|
|
2510
|
+
cwd: process.cwd()
|
|
2511
|
+
});
|
|
2512
|
+
process.exitCode = code;
|
|
2513
|
+
});
|
|
2253
2514
|
cli.help();
|
|
2254
2515
|
cli.version(VERSION);
|
|
2255
2516
|
try {
|