@dudousxd/nestjs-inertia-codegen 1.0.7 → 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 +42 -0
- package/dist/cli/main.cjs +280 -33
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +279 -32
- package/dist/cli/main.js.map +1 -1
- package/dist/index.cjs +266 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.js +267 -28
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# Changelog — @dudousxd/nestjs-inertia-codegen
|
|
2
2
|
|
|
3
|
+
## 2.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- feat(codegen): ReturnType<import(...)> for response types, queryKey helper, TanStack helpers, type ref imports, path alias resolution, debug mode
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies []:
|
|
12
|
+
- @dudousxd/nestjs-inertia@2.0.0
|
|
13
|
+
|
|
14
|
+
## 3.0.0
|
|
15
|
+
|
|
16
|
+
### Minor Changes
|
|
17
|
+
|
|
18
|
+
- feat(codegen): import type references from source instead of inline expansion — eliminates unknown fields from depth limits
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Updated dependencies []:
|
|
23
|
+
- @dudousxd/nestjs-inertia@3.0.0
|
|
24
|
+
|
|
25
|
+
## 2.0.1
|
|
26
|
+
|
|
27
|
+
### Patch Changes
|
|
28
|
+
|
|
29
|
+
- fix(codegen): remove @tanstack/query-core dependency — generated api.ts uses plain object literals
|
|
30
|
+
|
|
31
|
+
- Updated dependencies []:
|
|
32
|
+
- @dudousxd/nestjs-inertia@2.0.1
|
|
33
|
+
|
|
34
|
+
## 2.0.0
|
|
35
|
+
|
|
36
|
+
### Minor Changes
|
|
37
|
+
|
|
38
|
+
- feat(codegen): add queryKey() helper for typed cache invalidation — api.crew.getCrew.queryKey()
|
|
39
|
+
|
|
40
|
+
### Patch Changes
|
|
41
|
+
|
|
42
|
+
- Updated dependencies []:
|
|
43
|
+
- @dudousxd/nestjs-inertia@2.0.0
|
|
44
|
+
|
|
3
45
|
## 1.0.7
|
|
4
46
|
|
|
5
47
|
### Patch Changes
|
package/dist/cli/main.cjs
CHANGED
|
@@ -222,7 +222,7 @@ async function emitApi(routes, outDir) {
|
|
|
222
222
|
await (0, import_promises3.mkdir)(outDir, {
|
|
223
223
|
recursive: true
|
|
224
224
|
});
|
|
225
|
-
const content = buildApiFile(routes);
|
|
225
|
+
const content = buildApiFile(routes, outDir);
|
|
226
226
|
await (0, import_promises3.writeFile)((0, import_node_path3.join)(outDir, "api.ts"), content, "utf8");
|
|
227
227
|
}
|
|
228
228
|
__name(emitApi, "emitApi");
|
|
@@ -287,7 +287,20 @@ function insertIntoTree(tree, segments, leaf, fullName) {
|
|
|
287
287
|
}
|
|
288
288
|
}
|
|
289
289
|
__name(insertIntoTree, "insertIntoTree");
|
|
290
|
-
function
|
|
290
|
+
function buildResponseType(c, outDir) {
|
|
291
|
+
if (c.controllerRef) {
|
|
292
|
+
let relPath = (0, import_node_path3.relative)(outDir, c.controllerRef.filePath).replace(/\.ts$/, "");
|
|
293
|
+
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
294
|
+
return `Awaited<ReturnType<import('${relPath}').${c.controllerRef.className}['${c.controllerRef.methodName}']>>`;
|
|
295
|
+
}
|
|
296
|
+
const respRef = c.contractSource.responseRef;
|
|
297
|
+
if (respRef) {
|
|
298
|
+
return respRef.isArray ? `Array<${respRef.name}>` : respRef.name;
|
|
299
|
+
}
|
|
300
|
+
return c.contractSource.response;
|
|
301
|
+
}
|
|
302
|
+
__name(buildResponseType, "buildResponseType");
|
|
303
|
+
function emitRouterTypeBlock(tree, indent, outDir) {
|
|
291
304
|
const pad = " ".repeat(indent);
|
|
292
305
|
const lines = [];
|
|
293
306
|
for (const [key, node] of tree) {
|
|
@@ -295,15 +308,17 @@ function emitRouterTypeBlock(tree, indent) {
|
|
|
295
308
|
if (node.kind === "leaf") {
|
|
296
309
|
const c = node;
|
|
297
310
|
const method = c.method.toUpperCase();
|
|
298
|
-
const
|
|
299
|
-
const
|
|
300
|
-
const
|
|
311
|
+
const queryRef = c.contractSource.queryRef;
|
|
312
|
+
const query = queryRef ? queryRef.isArray ? `Array<${queryRef.name}>` : queryRef.name : c.contractSource.query ?? "never";
|
|
313
|
+
const bodyRef = c.contractSource.bodyRef;
|
|
314
|
+
const body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
|
|
315
|
+
const response = buildResponseType(c, outDir);
|
|
301
316
|
const safeMethod = JSON.stringify(method);
|
|
302
317
|
const safeUrl = JSON.stringify(c.path);
|
|
303
318
|
lines.push(`${pad}${objKey}: { method: ${safeMethod}; url: ${safeUrl}; query: ${query}; body: ${body}; response: ${response} };`);
|
|
304
319
|
} else {
|
|
305
320
|
lines.push(`${pad}${objKey}: {`);
|
|
306
|
-
lines.push(...emitRouterTypeBlock(node.children, indent + 2));
|
|
321
|
+
lines.push(...emitRouterTypeBlock(node.children, indent + 2, outDir));
|
|
307
322
|
lines.push(`${pad}};`);
|
|
308
323
|
}
|
|
309
324
|
}
|
|
@@ -324,18 +339,21 @@ function emitApiObjectBlock(tree, indent) {
|
|
|
324
339
|
if (method === "GET") {
|
|
325
340
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
326
341
|
lines.push(`${pad}${objKey}: {`);
|
|
342
|
+
lines.push(`${pad} queryKey: (query?: ${typeAccess}['query']) => query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
327
343
|
lines.push(`${pad} queryOptions: (query?: ${typeAccess}['query']) =>`);
|
|
328
|
-
lines.push(`${pad}
|
|
329
|
-
lines.push(`${pad} queryKey: [${flatName}, query],`);
|
|
344
|
+
lines.push(`${pad} _queryOptions({`);
|
|
345
|
+
lines.push(`${pad} queryKey: query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
330
346
|
lines.push(`${pad} queryFn: () => fetcher.get<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { query }),`);
|
|
331
347
|
lines.push(`${pad} }),`);
|
|
332
348
|
lines.push(`${pad}},`);
|
|
333
349
|
} else {
|
|
334
350
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
335
351
|
lines.push(`${pad}${objKey}: {`);
|
|
336
|
-
lines.push(`${pad}
|
|
337
|
-
lines.push(`${pad}
|
|
338
|
-
lines.push(`${pad}
|
|
352
|
+
lines.push(`${pad} queryKey: () => [${flatName}] as const,`);
|
|
353
|
+
lines.push(`${pad} mutationOptions: () =>`);
|
|
354
|
+
lines.push(`${pad} _mutationOptions({`);
|
|
355
|
+
lines.push(`${pad} mutationFn: (body: ${typeAccess}['body']) => fetcher.${fetcherMethod}<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { body }),`);
|
|
356
|
+
lines.push(`${pad} }),`);
|
|
339
357
|
lines.push(`${pad}},`);
|
|
340
358
|
}
|
|
341
359
|
} else {
|
|
@@ -352,18 +370,55 @@ function buildRouterTypeAccess(name) {
|
|
|
352
370
|
return `ApiRouter${segments.map((s) => `[${JSON.stringify(s)}]`).join("")}`;
|
|
353
371
|
}
|
|
354
372
|
__name(buildRouterTypeAccess, "buildRouterTypeAccess");
|
|
355
|
-
function buildApiFile(routes) {
|
|
373
|
+
function buildApiFile(routes, outDir) {
|
|
356
374
|
const contracted = routes.filter((r) => r.contract);
|
|
375
|
+
const importsByFile = /* @__PURE__ */ new Map();
|
|
376
|
+
for (const r of contracted) {
|
|
377
|
+
const cs = r.contract?.contractSource;
|
|
378
|
+
if (!cs) continue;
|
|
379
|
+
const refs = r.controllerRef ? [
|
|
380
|
+
cs.queryRef,
|
|
381
|
+
cs.bodyRef
|
|
382
|
+
] : [
|
|
383
|
+
cs.queryRef,
|
|
384
|
+
cs.bodyRef,
|
|
385
|
+
cs.responseRef
|
|
386
|
+
];
|
|
387
|
+
for (const ref of refs) {
|
|
388
|
+
if (!ref) continue;
|
|
389
|
+
let names = importsByFile.get(ref.filePath);
|
|
390
|
+
if (!names) {
|
|
391
|
+
names = /* @__PURE__ */ new Set();
|
|
392
|
+
importsByFile.set(ref.filePath, names);
|
|
393
|
+
}
|
|
394
|
+
names.add(ref.name);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
357
397
|
const hasGetRoutes = contracted.some((r) => r.method === "GET");
|
|
398
|
+
const hasMutationRoutes = contracted.some((r) => r.method !== "GET");
|
|
358
399
|
const lines = [
|
|
359
400
|
"// Generated by @dudousxd/nestjs-inertia-codegen. Do not edit.",
|
|
360
401
|
""
|
|
361
402
|
];
|
|
362
|
-
|
|
363
|
-
|
|
403
|
+
const tqImports = [];
|
|
404
|
+
if (hasGetRoutes) tqImports.push("queryOptions as _queryOptions");
|
|
405
|
+
if (hasMutationRoutes) tqImports.push("mutationOptions as _mutationOptions");
|
|
406
|
+
if (tqImports.length > 0) {
|
|
407
|
+
lines.push(`import { ${tqImports.join(", ")} } from '@tanstack/react-query';`);
|
|
364
408
|
}
|
|
365
409
|
lines.push("import { route } from './routes.js';");
|
|
366
410
|
lines.push("import { createFetcher } from '@dudousxd/nestjs-inertia-client';");
|
|
411
|
+
if (importsByFile.size > 0 && outDir) {
|
|
412
|
+
lines.push("");
|
|
413
|
+
for (const [filePath, names] of importsByFile) {
|
|
414
|
+
let relPath = (0, import_node_path3.relative)(outDir, filePath).replace(/\.ts$/, "");
|
|
415
|
+
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
416
|
+
const sortedNames = [
|
|
417
|
+
...names
|
|
418
|
+
].sort();
|
|
419
|
+
lines.push(`import type { ${sortedNames.join(", ")} } from '${relPath}';`);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
367
422
|
lines.push("");
|
|
368
423
|
lines.push("export const fetcher = createFetcher();");
|
|
369
424
|
lines.push("");
|
|
@@ -404,13 +459,14 @@ function buildApiFile(routes) {
|
|
|
404
459
|
method: r.method,
|
|
405
460
|
name,
|
|
406
461
|
path: r.path,
|
|
462
|
+
controllerRef: r.controllerRef,
|
|
407
463
|
contractSource: c.contractSource
|
|
408
464
|
};
|
|
409
465
|
insertIntoTree(tree, segments, leaf, name);
|
|
410
466
|
}
|
|
411
467
|
void detectCollisions;
|
|
412
468
|
lines.push("export type ApiRouter = {");
|
|
413
|
-
lines.push(...emitRouterTypeBlock(tree, 2));
|
|
469
|
+
lines.push(...emitRouterTypeBlock(tree, 2, outDir ?? ""));
|
|
414
470
|
lines.push("};");
|
|
415
471
|
lines.push("");
|
|
416
472
|
lines.push("export const api = {");
|
|
@@ -680,9 +736,28 @@ var import_node_path10 = require("path");
|
|
|
680
736
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
681
737
|
|
|
682
738
|
// src/discovery/contracts-fast.ts
|
|
739
|
+
var import_node_fs = require("fs");
|
|
683
740
|
var import_node_path8 = require("path");
|
|
684
741
|
var import_fast_glob2 = __toESM(require("fast-glob"), 1);
|
|
685
742
|
var import_ts_morph = require("ts-morph");
|
|
743
|
+
var _projectRoot = "";
|
|
744
|
+
var _tsconfigPaths = null;
|
|
745
|
+
var _debug = process.env.NESTJS_INERTIA_DEBUG === "1";
|
|
746
|
+
function dbg(...args) {
|
|
747
|
+
if (_debug) console.log("[codegen:debug]", ...args);
|
|
748
|
+
}
|
|
749
|
+
__name(dbg, "dbg");
|
|
750
|
+
function loadTsconfigPaths(tsconfigPath) {
|
|
751
|
+
try {
|
|
752
|
+
const raw = (0, import_node_fs.readFileSync)(tsconfigPath, "utf8");
|
|
753
|
+
const stripped = raw.replace(/\/\/.*$/gm, "");
|
|
754
|
+
const parsed = JSON.parse(stripped);
|
|
755
|
+
return parsed.compilerOptions?.paths ?? null;
|
|
756
|
+
} catch {
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
__name(loadTsconfigPaths, "loadTsconfigPaths");
|
|
686
761
|
async function discoverContractsFast(opts) {
|
|
687
762
|
const { cwd, glob, tsconfig } = opts;
|
|
688
763
|
const tsconfigPath = tsconfig ? (0, import_node_path8.resolve)(tsconfig) : (0, import_node_path8.join)(cwd, "tsconfig.json");
|
|
@@ -715,6 +790,8 @@ async function discoverContractsFast(opts) {
|
|
|
715
790
|
project.addSourceFileAtPath(f);
|
|
716
791
|
}
|
|
717
792
|
const routes = [];
|
|
793
|
+
_projectRoot = cwd;
|
|
794
|
+
_tsconfigPaths = loadTsconfigPaths(tsconfigPath);
|
|
718
795
|
for (const sourceFile of project.getSourceFiles()) {
|
|
719
796
|
routes.push(...extractFromSourceFile(sourceFile, project));
|
|
720
797
|
}
|
|
@@ -916,17 +993,41 @@ function findTypeInFile(name, file) {
|
|
|
916
993
|
return null;
|
|
917
994
|
}
|
|
918
995
|
__name(findTypeInFile, "findTypeInFile");
|
|
996
|
+
function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
|
|
997
|
+
if (moduleSpecifier.startsWith(".")) {
|
|
998
|
+
const dir = (0, import_node_path8.dirname)(sourceFile.getFilePath());
|
|
999
|
+
return [
|
|
1000
|
+
(0, import_node_path8.resolve)(dir, `${moduleSpecifier}.ts`),
|
|
1001
|
+
(0, import_node_path8.resolve)(dir, moduleSpecifier, "index.ts")
|
|
1002
|
+
];
|
|
1003
|
+
}
|
|
1004
|
+
const baseUrl = _projectRoot;
|
|
1005
|
+
dbg("resolveModuleSpecifier", moduleSpecifier, "paths:", JSON.stringify(_tsconfigPaths), "baseUrl:", baseUrl);
|
|
1006
|
+
if (_tsconfigPaths) {
|
|
1007
|
+
for (const [pattern, mappings] of Object.entries(_tsconfigPaths)) {
|
|
1008
|
+
const prefix = pattern.replace("*", "");
|
|
1009
|
+
if (moduleSpecifier.startsWith(prefix)) {
|
|
1010
|
+
const rest = moduleSpecifier.slice(prefix.length);
|
|
1011
|
+
const candidates = [];
|
|
1012
|
+
for (const mapping of mappings) {
|
|
1013
|
+
const resolved = (0, import_node_path8.resolve)(baseUrl, mapping.replace("*", rest));
|
|
1014
|
+
candidates.push(`${resolved}.ts`, (0, import_node_path8.resolve)(resolved, "index.ts"));
|
|
1015
|
+
}
|
|
1016
|
+
dbg(" resolved candidates:", candidates);
|
|
1017
|
+
return candidates;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
return [];
|
|
1022
|
+
}
|
|
1023
|
+
__name(resolveModuleSpecifier, "resolveModuleSpecifier");
|
|
919
1024
|
function resolveImportedType(name, sourceFile, project) {
|
|
920
1025
|
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
921
1026
|
const namedImport = importDecl.getNamedImports().find((n) => n.getName() === name);
|
|
922
1027
|
if (!namedImport) continue;
|
|
923
1028
|
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
const candidates = [
|
|
927
|
-
(0, import_node_path8.resolve)(dir, `${moduleSpecifier}.ts`),
|
|
928
|
-
(0, import_node_path8.resolve)(dir, moduleSpecifier, "index.ts")
|
|
929
|
-
];
|
|
1029
|
+
const candidates = resolveModuleSpecifier(moduleSpecifier, sourceFile, project);
|
|
1030
|
+
if (candidates.length === 0) continue;
|
|
930
1031
|
for (const candidate of candidates) {
|
|
931
1032
|
let importedFile = project.getSourceFile(candidate);
|
|
932
1033
|
if (!importedFile) {
|
|
@@ -960,7 +1061,8 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
960
1061
|
const name = import_ts_morph.Node.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
|
|
961
1062
|
if (name === "string" || name === "number" || name === "boolean") return name;
|
|
962
1063
|
if (name === "Date") return "string";
|
|
963
|
-
if (name === "unknown" || name === "any") return "unknown";
|
|
1064
|
+
if (name === "unknown" || name === "any" || name === "void") return "unknown";
|
|
1065
|
+
if (name === "StreamableFile" || name === "Observable" || name === "ReadableStream") return "unknown";
|
|
964
1066
|
if (name === "Array") {
|
|
965
1067
|
const typeArgs = typeNode.getTypeArguments();
|
|
966
1068
|
const firstTypeArg = typeArgs[0];
|
|
@@ -969,6 +1071,18 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
969
1071
|
}
|
|
970
1072
|
return "Array<unknown>";
|
|
971
1073
|
}
|
|
1074
|
+
if ([
|
|
1075
|
+
"Record",
|
|
1076
|
+
"Omit",
|
|
1077
|
+
"Pick",
|
|
1078
|
+
"Partial",
|
|
1079
|
+
"Required",
|
|
1080
|
+
"Readonly",
|
|
1081
|
+
"Map",
|
|
1082
|
+
"Set"
|
|
1083
|
+
].includes(name)) {
|
|
1084
|
+
return typeNode.getText();
|
|
1085
|
+
}
|
|
972
1086
|
if (name === "Promise") {
|
|
973
1087
|
const typeArgs = typeNode.getTypeArguments();
|
|
974
1088
|
const firstTypeArg = typeArgs[0];
|
|
@@ -981,7 +1095,8 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
981
1095
|
if (resolved) {
|
|
982
1096
|
return expandTypeDecl(resolved, project, depth - 1);
|
|
983
1097
|
}
|
|
984
|
-
|
|
1098
|
+
dbg("unresolvable type:", name, "in", sourceFile.getFilePath());
|
|
1099
|
+
return "unknown";
|
|
985
1100
|
}
|
|
986
1101
|
const kind = typeNode.getKind();
|
|
987
1102
|
if (kind === import_ts_morph.SyntaxKind.StringKeyword) return "string";
|
|
@@ -1108,6 +1223,68 @@ function resolveIdentifierToClassType(node, sourceFile, project, depth) {
|
|
|
1108
1223
|
return name;
|
|
1109
1224
|
}
|
|
1110
1225
|
__name(resolveIdentifierToClassType, "resolveIdentifierToClassType");
|
|
1226
|
+
function tryResolveTypeRef(typeNode, sourceFile, project) {
|
|
1227
|
+
if (import_ts_morph.Node.isTypeReference(typeNode)) {
|
|
1228
|
+
const typeName = typeNode.getTypeName();
|
|
1229
|
+
const name = import_ts_morph.Node.isIdentifier(typeName) ? typeName.getText() : null;
|
|
1230
|
+
if (!name) return null;
|
|
1231
|
+
if (name === "Promise") {
|
|
1232
|
+
const typeArgs = typeNode.getTypeArguments();
|
|
1233
|
+
const first = typeArgs[0];
|
|
1234
|
+
if (first) return tryResolveTypeRef(first, sourceFile, project);
|
|
1235
|
+
return null;
|
|
1236
|
+
}
|
|
1237
|
+
if (name === "Array") {
|
|
1238
|
+
const typeArgs = typeNode.getTypeArguments();
|
|
1239
|
+
const first = typeArgs[0];
|
|
1240
|
+
if (first) {
|
|
1241
|
+
const inner = tryResolveTypeRef(first, sourceFile, project);
|
|
1242
|
+
if (inner) return {
|
|
1243
|
+
...inner,
|
|
1244
|
+
isArray: true
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
return null;
|
|
1248
|
+
}
|
|
1249
|
+
if ([
|
|
1250
|
+
"string",
|
|
1251
|
+
"number",
|
|
1252
|
+
"boolean",
|
|
1253
|
+
"void",
|
|
1254
|
+
"unknown",
|
|
1255
|
+
"any",
|
|
1256
|
+
"Date"
|
|
1257
|
+
].includes(name)) {
|
|
1258
|
+
return null;
|
|
1259
|
+
}
|
|
1260
|
+
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1261
|
+
if (localDecl?.isExported()) {
|
|
1262
|
+
return {
|
|
1263
|
+
name,
|
|
1264
|
+
filePath: sourceFile.getFilePath()
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1267
|
+
const resolved = resolveImportedType(name, sourceFile, project);
|
|
1268
|
+
if (resolved && (resolved.kind === "class" || resolved.kind === "interface")) {
|
|
1269
|
+
const decl = resolved.decl;
|
|
1270
|
+
if (decl.isExported()) {
|
|
1271
|
+
return {
|
|
1272
|
+
name,
|
|
1273
|
+
filePath: resolved.file.getFilePath()
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
if (import_ts_morph.Node.isArrayTypeNode(typeNode)) {
|
|
1279
|
+
const inner = tryResolveTypeRef(typeNode.getElementTypeNode(), sourceFile, project);
|
|
1280
|
+
if (inner) return {
|
|
1281
|
+
...inner,
|
|
1282
|
+
isArray: true
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
return null;
|
|
1286
|
+
}
|
|
1287
|
+
__name(tryResolveTypeRef, "tryResolveTypeRef");
|
|
1111
1288
|
function extractDtoContract(method, sourceFile, project) {
|
|
1112
1289
|
const body = extractBodyType(method, sourceFile, project);
|
|
1113
1290
|
const query = extractQueryType(method, sourceFile, project);
|
|
@@ -1116,11 +1293,61 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
1116
1293
|
if (body === null && query === null && paramsType === null && response === "unknown") {
|
|
1117
1294
|
return null;
|
|
1118
1295
|
}
|
|
1296
|
+
let bodyRef = null;
|
|
1297
|
+
let queryRef = null;
|
|
1298
|
+
let responseRef = null;
|
|
1299
|
+
for (const param of method.getParameters()) {
|
|
1300
|
+
if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode()) {
|
|
1301
|
+
bodyRef = tryResolveTypeRef(param.getTypeNode(), sourceFile, project);
|
|
1302
|
+
}
|
|
1303
|
+
if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode()) {
|
|
1304
|
+
queryRef = tryResolveTypeRef(param.getTypeNode(), sourceFile, project);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
const returnTypeNode = method.getReturnTypeNode();
|
|
1308
|
+
if (returnTypeNode) {
|
|
1309
|
+
responseRef = tryResolveTypeRef(returnTypeNode, sourceFile, project);
|
|
1310
|
+
}
|
|
1311
|
+
if (!responseRef) {
|
|
1312
|
+
const apiResp = method.getDecorator("ApiResponse");
|
|
1313
|
+
if (apiResp) {
|
|
1314
|
+
const args = apiResp.getArguments();
|
|
1315
|
+
const optsArg = args[0];
|
|
1316
|
+
if (optsArg && import_ts_morph.Node.isObjectLiteralExpression(optsArg)) {
|
|
1317
|
+
for (const prop of optsArg.getProperties()) {
|
|
1318
|
+
if (import_ts_morph.Node.isPropertyAssignment(prop) && prop.getName() === "type") {
|
|
1319
|
+
const val = prop.getInitializer();
|
|
1320
|
+
if (val && import_ts_morph.Node.isIdentifier(val)) {
|
|
1321
|
+
const name = val.getText();
|
|
1322
|
+
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1323
|
+
if (localDecl?.isExported()) {
|
|
1324
|
+
responseRef = {
|
|
1325
|
+
name,
|
|
1326
|
+
filePath: sourceFile.getFilePath()
|
|
1327
|
+
};
|
|
1328
|
+
} else {
|
|
1329
|
+
const resolved = resolveImportedType(name, sourceFile, project);
|
|
1330
|
+
if (resolved && (resolved.kind === "class" || resolved.kind === "interface") && resolved.decl.isExported()) {
|
|
1331
|
+
responseRef = {
|
|
1332
|
+
name,
|
|
1333
|
+
filePath: resolved.file.getFilePath()
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1119
1343
|
return {
|
|
1120
1344
|
query,
|
|
1121
1345
|
body,
|
|
1122
1346
|
response,
|
|
1123
|
-
params: paramsType
|
|
1347
|
+
params: paramsType,
|
|
1348
|
+
queryRef,
|
|
1349
|
+
bodyRef,
|
|
1350
|
+
responseRef
|
|
1124
1351
|
};
|
|
1125
1352
|
}
|
|
1126
1353
|
__name(extractDtoContract, "extractDtoContract");
|
|
@@ -1219,6 +1446,11 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1219
1446
|
path: combined,
|
|
1220
1447
|
name: routeName,
|
|
1221
1448
|
params,
|
|
1449
|
+
controllerRef: {
|
|
1450
|
+
className,
|
|
1451
|
+
methodName,
|
|
1452
|
+
filePath: sourceFile.getFilePath()
|
|
1453
|
+
},
|
|
1222
1454
|
contract: {
|
|
1223
1455
|
contractSource: {
|
|
1224
1456
|
query: contractDef.query,
|
|
@@ -1253,13 +1485,20 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1253
1485
|
path: combined,
|
|
1254
1486
|
name: routeName,
|
|
1255
1487
|
params,
|
|
1256
|
-
|
|
1488
|
+
controllerRef: {
|
|
1489
|
+
className,
|
|
1490
|
+
methodName,
|
|
1491
|
+
filePath: sourceFile.getFilePath()
|
|
1492
|
+
},
|
|
1257
1493
|
...dtoContract ? {
|
|
1258
1494
|
contract: {
|
|
1259
1495
|
contractSource: {
|
|
1260
1496
|
query: dtoContract.query,
|
|
1261
1497
|
body: dtoContract.body,
|
|
1262
|
-
response: dtoContract.response
|
|
1498
|
+
response: dtoContract.response,
|
|
1499
|
+
queryRef: dtoContract.queryRef,
|
|
1500
|
+
bodyRef: dtoContract.bodyRef,
|
|
1501
|
+
responseRef: dtoContract.responseRef
|
|
1263
1502
|
}
|
|
1264
1503
|
}
|
|
1265
1504
|
} : {}
|
|
@@ -1432,7 +1671,7 @@ async function watch(config, onChange) {
|
|
|
1432
1671
|
__name(watch, "watch");
|
|
1433
1672
|
|
|
1434
1673
|
// src/index.ts
|
|
1435
|
-
var VERSION = "1.0
|
|
1674
|
+
var VERSION = "1.3.0";
|
|
1436
1675
|
|
|
1437
1676
|
// src/cli/codegen.ts
|
|
1438
1677
|
async function runCodegen(opts = {}) {
|
|
@@ -1464,7 +1703,7 @@ __name(runCodegen, "runCodegen");
|
|
|
1464
1703
|
|
|
1465
1704
|
// src/cli/init.ts
|
|
1466
1705
|
var import_node_child_process = require("child_process");
|
|
1467
|
-
var
|
|
1706
|
+
var import_node_fs2 = require("fs");
|
|
1468
1707
|
var import_promises10 = require("fs/promises");
|
|
1469
1708
|
var import_node_path11 = require("path");
|
|
1470
1709
|
var import_node_readline = require("readline");
|
|
@@ -1688,7 +1927,7 @@ __name(findAfterLastImport, "findAfterLastImport");
|
|
|
1688
1927
|
function patchAppModule(filePath, rootView) {
|
|
1689
1928
|
let content;
|
|
1690
1929
|
try {
|
|
1691
|
-
content = (0,
|
|
1930
|
+
content = (0, import_node_fs2.readFileSync)(filePath, "utf8");
|
|
1692
1931
|
} catch {
|
|
1693
1932
|
return "skipped";
|
|
1694
1933
|
}
|
|
@@ -1726,14 +1965,14 @@ ${indent}HomeController,${content.slice(bracketPos)}`;
|
|
|
1726
1965
|
}
|
|
1727
1966
|
}
|
|
1728
1967
|
if (!changed) return "already";
|
|
1729
|
-
(0,
|
|
1968
|
+
(0, import_node_fs2.writeFileSync)(filePath, content, "utf8");
|
|
1730
1969
|
return "patched";
|
|
1731
1970
|
}
|
|
1732
1971
|
__name(patchAppModule, "patchAppModule");
|
|
1733
1972
|
function patchMainTs(filePath) {
|
|
1734
1973
|
let content;
|
|
1735
1974
|
try {
|
|
1736
|
-
content = (0,
|
|
1975
|
+
content = (0, import_node_fs2.readFileSync)(filePath, "utf8");
|
|
1737
1976
|
} catch {
|
|
1738
1977
|
return "skipped";
|
|
1739
1978
|
}
|
|
@@ -1757,7 +1996,7 @@ ${content.slice(insertAt)}`;
|
|
|
1757
1996
|
});`;
|
|
1758
1997
|
content = `${content.slice(0, insertAfterPos)}
|
|
1759
1998
|
${viteSetup}${content.slice(insertAfterPos)}`;
|
|
1760
|
-
(0,
|
|
1999
|
+
(0, import_node_fs2.writeFileSync)(filePath, content, "utf8");
|
|
1761
2000
|
return "patched";
|
|
1762
2001
|
}
|
|
1763
2002
|
__name(patchMainTs, "patchMainTs");
|
|
@@ -1806,11 +2045,19 @@ function htmlShellTemplate(framework, _engine) {
|
|
|
1806
2045
|
__name(htmlShellTemplate, "htmlShellTemplate");
|
|
1807
2046
|
function viteConfigTemplate(framework) {
|
|
1808
2047
|
const pluginOption = `{ ${framework}: true }`;
|
|
1809
|
-
return `import {
|
|
2048
|
+
return `import { resolve } from 'node:path';
|
|
2049
|
+
import { defineConfig } from 'vite';
|
|
1810
2050
|
import nestInertia from '@dudousxd/nestjs-inertia-vite/plugin';
|
|
1811
2051
|
|
|
1812
2052
|
export default defineConfig({
|
|
1813
2053
|
plugins: [nestInertia(${pluginOption})],
|
|
2054
|
+
resolve: {
|
|
2055
|
+
alias: {
|
|
2056
|
+
'@': resolve(__dirname, 'src'),
|
|
2057
|
+
'~': resolve(__dirname, 'inertia'),
|
|
2058
|
+
'~codegen': resolve(__dirname, '.nestjs-inertia'),
|
|
2059
|
+
},
|
|
2060
|
+
},
|
|
1814
2061
|
});
|
|
1815
2062
|
`;
|
|
1816
2063
|
}
|