@dudousxd/nestjs-inertia-codegen 1.2.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 +22 -0
- package/dist/cli/main.cjs +467 -104
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +458 -95
- package/dist/cli/main.js.map +1 -1
- package/dist/index.cjs +259 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +257 -66
- 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,7 +245,30 @@ function insertIntoTree(tree, segments, leaf, fullName) {
|
|
|
255
245
|
}
|
|
256
246
|
}
|
|
257
247
|
__name(insertIntoTree, "insertIntoTree");
|
|
258
|
-
function
|
|
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
|
+
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,14 @@ 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
|
|
283
|
+
const response = buildResponseType(c, outDir);
|
|
284
|
+
const params = buildParamsType(c.params);
|
|
272
285
|
const safeMethod = JSON.stringify(method);
|
|
273
286
|
const safeUrl = JSON.stringify(c.path);
|
|
274
|
-
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} };`);
|
|
275
288
|
} else {
|
|
276
289
|
lines.push(`${pad}${objKey}: {`);
|
|
277
|
-
lines.push(...emitRouterTypeBlock(node.children, indent + 2));
|
|
290
|
+
lines.push(...emitRouterTypeBlock(node.children, indent + 2, outDir));
|
|
278
291
|
lines.push(`${pad}};`);
|
|
279
292
|
}
|
|
280
293
|
}
|
|
@@ -294,20 +307,61 @@ function emitApiObjectBlock(tree, indent) {
|
|
|
294
307
|
const fetcherMethod = method.toLowerCase();
|
|
295
308
|
if (method === "GET") {
|
|
296
309
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
310
|
+
const withParams = hasPathParams(c.params);
|
|
297
311
|
lines.push(`${pad}${objKey}: {`);
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
+
}
|
|
303
351
|
lines.push(`${pad}},`);
|
|
304
352
|
} else {
|
|
305
353
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
354
|
+
const withParams = hasPathParams(c.params);
|
|
306
355
|
lines.push(`${pad}${objKey}: {`);
|
|
307
356
|
lines.push(`${pad} queryKey: () => [${flatName}] as const,`);
|
|
308
|
-
lines.push(`${pad} mutationOptions: ()
|
|
309
|
-
lines.push(`${pad}
|
|
310
|
-
|
|
357
|
+
lines.push(`${pad} mutationOptions: () =>`);
|
|
358
|
+
lines.push(`${pad} _mutationOptions({`);
|
|
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
|
+
}
|
|
364
|
+
lines.push(`${pad} }),`);
|
|
311
365
|
lines.push(`${pad}},`);
|
|
312
366
|
}
|
|
313
367
|
} else {
|
|
@@ -330,11 +384,15 @@ function buildApiFile(routes, outDir) {
|
|
|
330
384
|
for (const r of contracted) {
|
|
331
385
|
const cs = r.contract?.contractSource;
|
|
332
386
|
if (!cs) continue;
|
|
333
|
-
|
|
387
|
+
const refs = r.controllerRef ? [
|
|
388
|
+
cs.queryRef,
|
|
389
|
+
cs.bodyRef
|
|
390
|
+
] : [
|
|
334
391
|
cs.queryRef,
|
|
335
392
|
cs.bodyRef,
|
|
336
393
|
cs.responseRef
|
|
337
|
-
]
|
|
394
|
+
];
|
|
395
|
+
for (const ref of refs) {
|
|
338
396
|
if (!ref) continue;
|
|
339
397
|
let names = importsByFile.get(ref.filePath);
|
|
340
398
|
if (!names) {
|
|
@@ -344,10 +402,18 @@ function buildApiFile(routes, outDir) {
|
|
|
344
402
|
names.add(ref.name);
|
|
345
403
|
}
|
|
346
404
|
}
|
|
405
|
+
const hasGetRoutes = contracted.some((r) => r.method === "GET");
|
|
406
|
+
const hasMutationRoutes = contracted.some((r) => r.method !== "GET");
|
|
347
407
|
const lines = [
|
|
348
408
|
"// Generated by @dudousxd/nestjs-inertia-codegen. Do not edit.",
|
|
349
409
|
""
|
|
350
410
|
];
|
|
411
|
+
const tqImports = [];
|
|
412
|
+
if (hasGetRoutes) tqImports.push("queryOptions as _queryOptions");
|
|
413
|
+
if (hasMutationRoutes) tqImports.push("mutationOptions as _mutationOptions");
|
|
414
|
+
if (tqImports.length > 0) {
|
|
415
|
+
lines.push(`import { ${tqImports.join(", ")} } from '@tanstack/react-query';`);
|
|
416
|
+
}
|
|
351
417
|
lines.push("import { route } from './routes.js';");
|
|
352
418
|
lines.push("import { createFetcher } from '@dudousxd/nestjs-inertia-client';");
|
|
353
419
|
if (importsByFile.size > 0 && outDir) {
|
|
@@ -401,13 +467,14 @@ function buildApiFile(routes, outDir) {
|
|
|
401
467
|
method: r.method,
|
|
402
468
|
name,
|
|
403
469
|
path: r.path,
|
|
470
|
+
params: r.params,
|
|
471
|
+
controllerRef: r.controllerRef,
|
|
404
472
|
contractSource: c.contractSource
|
|
405
473
|
};
|
|
406
474
|
insertIntoTree(tree, segments, leaf, name);
|
|
407
475
|
}
|
|
408
|
-
void detectCollisions;
|
|
409
476
|
lines.push("export type ApiRouter = {");
|
|
410
|
-
lines.push(...emitRouterTypeBlock(tree, 2));
|
|
477
|
+
lines.push(...emitRouterTypeBlock(tree, 2, outDir ?? ""));
|
|
411
478
|
lines.push("};");
|
|
412
479
|
lines.push("");
|
|
413
480
|
lines.push("export const api = {");
|
|
@@ -503,8 +570,9 @@ __name(emitIndex, "emitIndex");
|
|
|
503
570
|
|
|
504
571
|
// src/emit/emit-pages.ts
|
|
505
572
|
import { mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
|
|
506
|
-
import { join as join5 } from "path";
|
|
507
|
-
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";
|
|
508
576
|
await mkdir4(outDir, {
|
|
509
577
|
recursive: true
|
|
510
578
|
});
|
|
@@ -513,14 +581,40 @@ async function emitPages(pages, outDir) {
|
|
|
513
581
|
const key = needsQuotes(p.name) ? JSON.stringify(p.name) : p.name;
|
|
514
582
|
return ` ${key}: ${propType};`;
|
|
515
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";
|
|
516
591
|
const content = `// Generated by @dudousxd/nestjs-inertia-codegen. Do not edit.
|
|
517
592
|
export interface InertiaPages {
|
|
518
593
|
${body}
|
|
519
594
|
}
|
|
595
|
+
|
|
596
|
+
export type InertiaPageName = ${pageNameUnion};
|
|
597
|
+
` + propsHelper + `
|
|
598
|
+
declare module '@dudousxd/nestjs-inertia' {
|
|
599
|
+
interface InertiaPages {
|
|
600
|
+
${augBody}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
520
603
|
`;
|
|
521
604
|
await writeFile4(join5(outDir, "pages.d.ts"), content, "utf8");
|
|
522
605
|
}
|
|
523
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");
|
|
524
618
|
function needsQuotes(name) {
|
|
525
619
|
return !/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
|
|
526
620
|
}
|
|
@@ -657,7 +751,9 @@ async function generate(config, routes = []) {
|
|
|
657
751
|
propsExport: config.pages.propsExport,
|
|
658
752
|
componentNameStrategy: config.pages.componentNameStrategy
|
|
659
753
|
});
|
|
660
|
-
await emitPages(pages, config.codegen.outDir
|
|
754
|
+
await emitPages(pages, config.codegen.outDir, {
|
|
755
|
+
propsExport: config.pages.propsExport
|
|
756
|
+
});
|
|
661
757
|
await emitCache(pages, config.codegen.outDir);
|
|
662
758
|
const hasRoutes = routes.length > 0;
|
|
663
759
|
const hasContracts = routes.some((r) => r.contract);
|
|
@@ -677,9 +773,38 @@ import { join as join9 } from "path";
|
|
|
677
773
|
import chokidar from "chokidar";
|
|
678
774
|
|
|
679
775
|
// src/discovery/contracts-fast.ts
|
|
776
|
+
import { readFileSync } from "fs";
|
|
680
777
|
import { dirname, join as join7, resolve as resolve2 } from "path";
|
|
681
778
|
import fg2 from "fast-glob";
|
|
682
779
|
import { Node, Project, SyntaxKind } from "ts-morph";
|
|
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");
|
|
792
|
+
var _debug = process.env.NESTJS_INERTIA_DEBUG === "1";
|
|
793
|
+
function dbg(...args) {
|
|
794
|
+
if (_debug) console.log("[codegen:debug]", ...args);
|
|
795
|
+
}
|
|
796
|
+
__name(dbg, "dbg");
|
|
797
|
+
function loadTsconfigPaths(tsconfigPath) {
|
|
798
|
+
try {
|
|
799
|
+
const raw = readFileSync(tsconfigPath, "utf8");
|
|
800
|
+
const stripped = raw.replace(/\/\/.*$/gm, "");
|
|
801
|
+
const parsed = JSON.parse(stripped);
|
|
802
|
+
return parsed.compilerOptions?.paths ?? null;
|
|
803
|
+
} catch {
|
|
804
|
+
return null;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
__name(loadTsconfigPaths, "loadTsconfigPaths");
|
|
683
808
|
async function discoverContractsFast(opts) {
|
|
684
809
|
const { cwd, glob, tsconfig } = opts;
|
|
685
810
|
const tsconfigPath = tsconfig ? resolve2(tsconfig) : join7(cwd, "tsconfig.json");
|
|
@@ -712,8 +837,17 @@ async function discoverContractsFast(opts) {
|
|
|
712
837
|
project.addSourceFileAtPath(f);
|
|
713
838
|
}
|
|
714
839
|
const routes = [];
|
|
715
|
-
|
|
716
|
-
|
|
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;
|
|
717
851
|
}
|
|
718
852
|
return routes;
|
|
719
853
|
}
|
|
@@ -913,17 +1047,42 @@ function findTypeInFile(name, file) {
|
|
|
913
1047
|
return null;
|
|
914
1048
|
}
|
|
915
1049
|
__name(findTypeInFile, "findTypeInFile");
|
|
1050
|
+
function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
|
|
1051
|
+
if (moduleSpecifier.startsWith(".")) {
|
|
1052
|
+
const dir = dirname(sourceFile.getFilePath());
|
|
1053
|
+
return [
|
|
1054
|
+
resolve2(dir, `${moduleSpecifier}.ts`),
|
|
1055
|
+
resolve2(dir, moduleSpecifier, "index.ts")
|
|
1056
|
+
];
|
|
1057
|
+
}
|
|
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)) {
|
|
1063
|
+
const prefix = pattern.replace("*", "");
|
|
1064
|
+
if (moduleSpecifier.startsWith(prefix)) {
|
|
1065
|
+
const rest = moduleSpecifier.slice(prefix.length);
|
|
1066
|
+
const candidates = [];
|
|
1067
|
+
for (const mapping of mappings) {
|
|
1068
|
+
const resolved = resolve2(baseUrl, mapping.replace("*", rest));
|
|
1069
|
+
candidates.push(`${resolved}.ts`, resolve2(resolved, "index.ts"));
|
|
1070
|
+
}
|
|
1071
|
+
dbg(" resolved candidates:", candidates);
|
|
1072
|
+
return candidates;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
return [];
|
|
1077
|
+
}
|
|
1078
|
+
__name(resolveModuleSpecifier, "resolveModuleSpecifier");
|
|
916
1079
|
function resolveImportedType(name, sourceFile, project) {
|
|
917
1080
|
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
918
1081
|
const namedImport = importDecl.getNamedImports().find((n) => n.getName() === name);
|
|
919
1082
|
if (!namedImport) continue;
|
|
920
1083
|
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
const candidates = [
|
|
924
|
-
resolve2(dir, `${moduleSpecifier}.ts`),
|
|
925
|
-
resolve2(dir, moduleSpecifier, "index.ts")
|
|
926
|
-
];
|
|
1084
|
+
const candidates = resolveModuleSpecifier(moduleSpecifier, sourceFile, project);
|
|
1085
|
+
if (candidates.length === 0) continue;
|
|
927
1086
|
for (const candidate of candidates) {
|
|
928
1087
|
let importedFile = project.getSourceFile(candidate);
|
|
929
1088
|
if (!importedFile) {
|
|
@@ -967,6 +1126,18 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
967
1126
|
}
|
|
968
1127
|
return "Array<unknown>";
|
|
969
1128
|
}
|
|
1129
|
+
if ([
|
|
1130
|
+
"Record",
|
|
1131
|
+
"Omit",
|
|
1132
|
+
"Pick",
|
|
1133
|
+
"Partial",
|
|
1134
|
+
"Required",
|
|
1135
|
+
"Readonly",
|
|
1136
|
+
"Map",
|
|
1137
|
+
"Set"
|
|
1138
|
+
].includes(name)) {
|
|
1139
|
+
return typeNode.getText();
|
|
1140
|
+
}
|
|
970
1141
|
if (name === "Promise") {
|
|
971
1142
|
const typeArgs = typeNode.getTypeArguments();
|
|
972
1143
|
const firstTypeArg = typeArgs[0];
|
|
@@ -979,7 +1150,8 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
979
1150
|
if (resolved) {
|
|
980
1151
|
return expandTypeDecl(resolved, project, depth - 1);
|
|
981
1152
|
}
|
|
982
|
-
|
|
1153
|
+
dbg("unresolvable type:", name, "in", sourceFile.getFilePath());
|
|
1154
|
+
return "unknown";
|
|
983
1155
|
}
|
|
984
1156
|
const kind = typeNode.getKind();
|
|
985
1157
|
if (kind === SyntaxKind.StringKeyword) return "string";
|
|
@@ -1141,7 +1313,7 @@ function tryResolveTypeRef(typeNode, sourceFile, project) {
|
|
|
1141
1313
|
return null;
|
|
1142
1314
|
}
|
|
1143
1315
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1144
|
-
if (localDecl
|
|
1316
|
+
if (localDecl?.isExported()) {
|
|
1145
1317
|
return {
|
|
1146
1318
|
name,
|
|
1147
1319
|
filePath: sourceFile.getFilePath()
|
|
@@ -1203,7 +1375,7 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
1203
1375
|
if (val && Node.isIdentifier(val)) {
|
|
1204
1376
|
const name = val.getText();
|
|
1205
1377
|
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1206
|
-
if (localDecl
|
|
1378
|
+
if (localDecl?.isExported()) {
|
|
1207
1379
|
responseRef = {
|
|
1208
1380
|
name,
|
|
1209
1381
|
filePath: sourceFile.getFilePath()
|
|
@@ -1329,6 +1501,11 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1329
1501
|
path: combined,
|
|
1330
1502
|
name: routeName,
|
|
1331
1503
|
params,
|
|
1504
|
+
controllerRef: {
|
|
1505
|
+
className,
|
|
1506
|
+
methodName,
|
|
1507
|
+
filePath: sourceFile.getFilePath()
|
|
1508
|
+
},
|
|
1332
1509
|
contract: {
|
|
1333
1510
|
contractSource: {
|
|
1334
1511
|
query: contractDef.query,
|
|
@@ -1363,19 +1540,21 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1363
1540
|
path: combined,
|
|
1364
1541
|
name: routeName,
|
|
1365
1542
|
params,
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1543
|
+
controllerRef: {
|
|
1544
|
+
className,
|
|
1545
|
+
methodName,
|
|
1546
|
+
filePath: sourceFile.getFilePath()
|
|
1547
|
+
},
|
|
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
|
|
1377
1556
|
}
|
|
1378
|
-
}
|
|
1557
|
+
}
|
|
1379
1558
|
});
|
|
1380
1559
|
}
|
|
1381
1560
|
}
|
|
@@ -1385,7 +1564,8 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1385
1564
|
__name(extractFromSourceFile, "extractFromSourceFile");
|
|
1386
1565
|
|
|
1387
1566
|
// src/watch/lock-file.ts
|
|
1388
|
-
import {
|
|
1567
|
+
import { open } from "fs/promises";
|
|
1568
|
+
import { mkdir as mkdir6, readFile as readFile2, unlink } from "fs/promises";
|
|
1389
1569
|
import { join as join8 } from "path";
|
|
1390
1570
|
var LOCK_FILE = ".watcher.lock";
|
|
1391
1571
|
function isProcessAlive(pid) {
|
|
@@ -1402,20 +1582,29 @@ async function acquireLock(outDir) {
|
|
|
1402
1582
|
recursive: true
|
|
1403
1583
|
});
|
|
1404
1584
|
const lockPath = join8(outDir, LOCK_FILE);
|
|
1405
|
-
try {
|
|
1406
|
-
const raw = await readFile2(lockPath, "utf8");
|
|
1407
|
-
const existing = JSON.parse(raw);
|
|
1408
|
-
if (isProcessAlive(existing.pid)) {
|
|
1409
|
-
return null;
|
|
1410
|
-
}
|
|
1411
|
-
} catch {
|
|
1412
|
-
}
|
|
1413
1585
|
const lockData = {
|
|
1414
1586
|
pid: process.pid,
|
|
1415
1587
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1416
1588
|
};
|
|
1417
|
-
|
|
1589
|
+
try {
|
|
1590
|
+
const fd = await open(lockPath, "wx");
|
|
1591
|
+
await fd.writeFile(`${JSON.stringify(lockData, null, 2)}
|
|
1418
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
|
+
}
|
|
1419
1608
|
return {
|
|
1420
1609
|
release: /* @__PURE__ */ __name(async () => {
|
|
1421
1610
|
try {
|
|
@@ -1479,7 +1668,8 @@ async function watch(config, onChange) {
|
|
|
1479
1668
|
pagesDebounceTimer = void 0;
|
|
1480
1669
|
try {
|
|
1481
1670
|
await generate(config);
|
|
1482
|
-
} catch {
|
|
1671
|
+
} catch (err) {
|
|
1672
|
+
console.error("[nestjs-inertia-codegen] Pages generation failed:", err instanceof Error ? err.message : err);
|
|
1483
1673
|
}
|
|
1484
1674
|
onChange?.();
|
|
1485
1675
|
}, PAGES_DEBOUNCE_MS);
|
|
@@ -1517,7 +1707,8 @@ async function watch(config, onChange) {
|
|
|
1517
1707
|
if (hasContracts) {
|
|
1518
1708
|
await emitApi(routes, config.codegen.outDir);
|
|
1519
1709
|
}
|
|
1520
|
-
} catch {
|
|
1710
|
+
} catch (err) {
|
|
1711
|
+
console.error("[nestjs-inertia-codegen] Contracts generation failed:", err instanceof Error ? err.message : err);
|
|
1521
1712
|
}
|
|
1522
1713
|
onChange?.();
|
|
1523
1714
|
}, config.contracts.debounceMs);
|
|
@@ -1545,7 +1736,7 @@ async function watch(config, onChange) {
|
|
|
1545
1736
|
__name(watch, "watch");
|
|
1546
1737
|
|
|
1547
1738
|
// src/index.ts
|
|
1548
|
-
var VERSION = "1.
|
|
1739
|
+
var VERSION = "1.4.0";
|
|
1549
1740
|
|
|
1550
1741
|
// src/cli/codegen.ts
|
|
1551
1742
|
async function runCodegen(opts = {}) {
|
|
@@ -1575,11 +1766,163 @@ async function runCodegen(opts = {}) {
|
|
|
1575
1766
|
}
|
|
1576
1767
|
__name(runCodegen, "runCodegen");
|
|
1577
1768
|
|
|
1578
|
-
// src/cli/
|
|
1579
|
-
import {
|
|
1580
|
-
import { readFileSync, writeFileSync } from "fs";
|
|
1581
|
-
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";
|
|
1582
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";
|
|
1583
1926
|
import { createInterface } from "readline";
|
|
1584
1927
|
var GITIGNORE_ENTRY = ".nestjs-inertia/";
|
|
1585
1928
|
var green = /* @__PURE__ */ __name((s) => `\x1B[32m${s}\x1B[0m`, "green");
|
|
@@ -1610,7 +1953,7 @@ ${bold(title)}`);
|
|
|
1610
1953
|
__name(logSection, "logSection");
|
|
1611
1954
|
async function readPackageJson(cwd) {
|
|
1612
1955
|
try {
|
|
1613
|
-
const raw = await readFile4(
|
|
1956
|
+
const raw = await readFile4(join11(cwd, "package.json"), "utf8");
|
|
1614
1957
|
return JSON.parse(raw);
|
|
1615
1958
|
} catch {
|
|
1616
1959
|
return {};
|
|
@@ -1648,7 +1991,7 @@ __name(detectTemplateEngine, "detectTemplateEngine");
|
|
|
1648
1991
|
async function detectPackageManager(cwd) {
|
|
1649
1992
|
async function exists(file) {
|
|
1650
1993
|
try {
|
|
1651
|
-
await access2(
|
|
1994
|
+
await access2(join11(cwd, file));
|
|
1652
1995
|
return true;
|
|
1653
1996
|
} catch {
|
|
1654
1997
|
return false;
|
|
@@ -1697,12 +2040,12 @@ async function writeIfNotExists(filePath, content, label) {
|
|
|
1697
2040
|
recursive: true
|
|
1698
2041
|
});
|
|
1699
2042
|
}
|
|
1700
|
-
await
|
|
2043
|
+
await writeFile6(filePath, content, "utf8");
|
|
1701
2044
|
logCreated(label);
|
|
1702
2045
|
}
|
|
1703
2046
|
__name(writeIfNotExists, "writeIfNotExists");
|
|
1704
2047
|
async function handleViteConfig(cwd, framework) {
|
|
1705
|
-
const filePath =
|
|
2048
|
+
const filePath = join11(cwd, "vite.config.ts");
|
|
1706
2049
|
if (await fileExists2(filePath)) {
|
|
1707
2050
|
const existing = await readFile4(filePath, "utf8");
|
|
1708
2051
|
const hasPlugin = existing.includes("nestInertia") || existing.includes("nestjs-inertia-vite/plugin");
|
|
@@ -1722,7 +2065,7 @@ async function handleViteConfig(cwd, framework) {
|
|
|
1722
2065
|
recursive: true
|
|
1723
2066
|
});
|
|
1724
2067
|
}
|
|
1725
|
-
await
|
|
2068
|
+
await writeFile6(filePath, viteConfigTemplate(framework), "utf8");
|
|
1726
2069
|
logCreated("vite.config.ts");
|
|
1727
2070
|
}
|
|
1728
2071
|
__name(handleViteConfig, "handleViteConfig");
|
|
@@ -1739,27 +2082,33 @@ async function patchGitignore(gitignorePath) {
|
|
|
1739
2082
|
` : `${existing}
|
|
1740
2083
|
${GITIGNORE_ENTRY}
|
|
1741
2084
|
`;
|
|
1742
|
-
await
|
|
2085
|
+
await writeFile6(gitignorePath, newContent, "utf8");
|
|
1743
2086
|
logPatched(".gitignore", "added .nestjs-inertia/");
|
|
1744
2087
|
}
|
|
1745
2088
|
__name(patchGitignore, "patchGitignore");
|
|
1746
2089
|
function installDeps(pkgManager, deps, dev) {
|
|
1747
2090
|
if (deps.length === 0) return;
|
|
1748
|
-
const
|
|
1749
|
-
|
|
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);
|
|
1750
2100
|
logPatched(deps.join(", "), "installed");
|
|
1751
2101
|
try {
|
|
1752
|
-
|
|
2102
|
+
execFileSync(pkgManager, args, {
|
|
1753
2103
|
stdio: "inherit"
|
|
1754
2104
|
});
|
|
1755
2105
|
} catch {
|
|
1756
|
-
logWarning(`Failed to install deps.
|
|
1757
|
-
${cmd}`);
|
|
2106
|
+
logWarning(`Failed to install: ${deps.join(", ")}`);
|
|
1758
2107
|
}
|
|
1759
2108
|
}
|
|
1760
2109
|
__name(installDeps, "installDeps");
|
|
1761
2110
|
async function patchPackageJsonScripts(cwd, scripts) {
|
|
1762
|
-
const pkgPath =
|
|
2111
|
+
const pkgPath = join11(cwd, "package.json");
|
|
1763
2112
|
let pkg = {};
|
|
1764
2113
|
try {
|
|
1765
2114
|
pkg = JSON.parse(await readFile4(pkgPath, "utf8"));
|
|
@@ -1781,7 +2130,7 @@ async function patchPackageJsonScripts(cwd, scripts) {
|
|
|
1781
2130
|
return;
|
|
1782
2131
|
}
|
|
1783
2132
|
pkg.scripts = existing;
|
|
1784
|
-
await
|
|
2133
|
+
await writeFile6(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
1785
2134
|
`, "utf8");
|
|
1786
2135
|
}
|
|
1787
2136
|
__name(patchPackageJsonScripts, "patchPackageJsonScripts");
|
|
@@ -1801,7 +2150,7 @@ __name(findAfterLastImport, "findAfterLastImport");
|
|
|
1801
2150
|
function patchAppModule(filePath, rootView) {
|
|
1802
2151
|
let content;
|
|
1803
2152
|
try {
|
|
1804
|
-
content =
|
|
2153
|
+
content = readFileSync3(filePath, "utf8");
|
|
1805
2154
|
} catch {
|
|
1806
2155
|
return "skipped";
|
|
1807
2156
|
}
|
|
@@ -1846,7 +2195,7 @@ __name(patchAppModule, "patchAppModule");
|
|
|
1846
2195
|
function patchMainTs(filePath) {
|
|
1847
2196
|
let content;
|
|
1848
2197
|
try {
|
|
1849
|
-
content =
|
|
2198
|
+
content = readFileSync3(filePath, "utf8");
|
|
1850
2199
|
} catch {
|
|
1851
2200
|
return "skipped";
|
|
1852
2201
|
}
|
|
@@ -1919,11 +2268,19 @@ function htmlShellTemplate(framework, _engine) {
|
|
|
1919
2268
|
__name(htmlShellTemplate, "htmlShellTemplate");
|
|
1920
2269
|
function viteConfigTemplate(framework) {
|
|
1921
2270
|
const pluginOption = `{ ${framework}: true }`;
|
|
1922
|
-
return `import {
|
|
2271
|
+
return `import { resolve } from 'node:path';
|
|
2272
|
+
import { defineConfig } from 'vite';
|
|
1923
2273
|
import nestInertia from '@dudousxd/nestjs-inertia-vite/plugin';
|
|
1924
2274
|
|
|
1925
2275
|
export default defineConfig({
|
|
1926
2276
|
plugins: [nestInertia(${pluginOption})],
|
|
2277
|
+
resolve: {
|
|
2278
|
+
alias: {
|
|
2279
|
+
'@': resolve(__dirname, 'src'),
|
|
2280
|
+
'~': resolve(__dirname, 'inertia'),
|
|
2281
|
+
'~codegen': resolve(__dirname, '.nestjs-inertia'),
|
|
2282
|
+
},
|
|
2283
|
+
},
|
|
1927
2284
|
});
|
|
1928
2285
|
`;
|
|
1929
2286
|
}
|
|
@@ -2043,16 +2400,16 @@ ${bold("nestjs-inertia init")}`);
|
|
|
2043
2400
|
const entryExt = framework === "react" ? "tsx" : "ts";
|
|
2044
2401
|
const pageExt = framework === "react" ? "tsx" : framework === "vue" ? "vue" : "svelte";
|
|
2045
2402
|
logSection("Scaffold files");
|
|
2046
|
-
await writeIfNotExists(
|
|
2047
|
-
await writeIfNotExists(
|
|
2048
|
-
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}`);
|
|
2049
2406
|
await handleViteConfig(cwd, framework);
|
|
2050
|
-
await writeIfNotExists(
|
|
2051
|
-
await writeIfNotExists(
|
|
2052
|
-
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");
|
|
2053
2410
|
logSection("Patch existing files");
|
|
2054
2411
|
const rootView = engine === "html" ? "inertia/index.html" : `inertia/index.${engine === "handlebars" ? "hbs" : engine}`;
|
|
2055
|
-
const appModulePath =
|
|
2412
|
+
const appModulePath = join11(cwd, "src", "app.module.ts");
|
|
2056
2413
|
const appModuleResult = patchAppModule(appModulePath, rootView);
|
|
2057
2414
|
if (appModuleResult === "patched") {
|
|
2058
2415
|
logPatched("src/app.module.ts", "added InertiaModule.forRoot");
|
|
@@ -2062,7 +2419,7 @@ ${bold("nestjs-inertia init")}`);
|
|
|
2062
2419
|
} else {
|
|
2063
2420
|
logWarning("src/app.module.ts not found \u2014 add InertiaModule.forRoot() manually");
|
|
2064
2421
|
}
|
|
2065
|
-
const mainTsPath =
|
|
2422
|
+
const mainTsPath = join11(cwd, "src", "main.ts");
|
|
2066
2423
|
const mainTsResult = patchMainTs(mainTsPath);
|
|
2067
2424
|
if (mainTsResult === "patched") {
|
|
2068
2425
|
logPatched("src/main.ts", "added setupInertiaVite after NestFactory.create");
|
|
@@ -2071,7 +2428,7 @@ ${bold("nestjs-inertia init")}`);
|
|
|
2071
2428
|
} else {
|
|
2072
2429
|
logWarning("src/main.ts not found \u2014 add setupInertiaVite() manually");
|
|
2073
2430
|
}
|
|
2074
|
-
await patchGitignore(
|
|
2431
|
+
await patchGitignore(join11(cwd, ".gitignore"));
|
|
2075
2432
|
await patchPackageJsonScripts(cwd, {
|
|
2076
2433
|
"build:client": "vite build",
|
|
2077
2434
|
"build:ssr": "VITE_SSR=1 vite build --ssr"
|
|
@@ -2148,6 +2505,12 @@ async function run(argv) {
|
|
|
2148
2505
|
cwd: process.cwd()
|
|
2149
2506
|
});
|
|
2150
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
|
+
});
|
|
2151
2514
|
cli.help();
|
|
2152
2515
|
cli.version(VERSION);
|
|
2153
2516
|
try {
|