@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/dist/index.d.cts
CHANGED
|
@@ -62,14 +62,27 @@ declare class CodegenError extends Error {
|
|
|
62
62
|
constructor(message: string, options?: ErrorOptions);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
interface TypeRef {
|
|
66
|
+
name: string;
|
|
67
|
+
filePath: string;
|
|
68
|
+
isArray?: boolean;
|
|
69
|
+
}
|
|
65
70
|
interface ContractSource {
|
|
66
71
|
query: string | null;
|
|
67
72
|
body: string | null;
|
|
68
73
|
response: string;
|
|
74
|
+
queryRef?: TypeRef | null;
|
|
75
|
+
bodyRef?: TypeRef | null;
|
|
76
|
+
responseRef?: TypeRef | null;
|
|
69
77
|
}
|
|
70
78
|
interface ContractDescriptor {
|
|
71
79
|
contractSource: ContractSource;
|
|
72
80
|
}
|
|
81
|
+
interface ControllerRef {
|
|
82
|
+
className: string;
|
|
83
|
+
methodName: string;
|
|
84
|
+
filePath: string;
|
|
85
|
+
}
|
|
73
86
|
interface RouteDescriptor {
|
|
74
87
|
method: string;
|
|
75
88
|
path: string;
|
|
@@ -79,6 +92,7 @@ interface RouteDescriptor {
|
|
|
79
92
|
source: 'path' | 'query' | 'body' | 'header';
|
|
80
93
|
}>;
|
|
81
94
|
contract?: ContractDescriptor;
|
|
95
|
+
controllerRef?: ControllerRef;
|
|
82
96
|
}
|
|
83
97
|
|
|
84
98
|
/**
|
|
@@ -120,6 +134,6 @@ declare function acquireLock(outDir: string): Promise<{
|
|
|
120
134
|
release: () => Promise<void>;
|
|
121
135
|
} | null>;
|
|
122
136
|
|
|
123
|
-
declare const VERSION = "1.0
|
|
137
|
+
declare const VERSION = "1.3.0";
|
|
124
138
|
|
|
125
139
|
export { CodegenError, ConfigError, type ResolvedConfig, type ScopeConfig, type UserConfig, VERSION, type Watcher, acquireLock, defineConfig, generate, loadConfig, watch };
|
package/dist/index.d.ts
CHANGED
|
@@ -62,14 +62,27 @@ declare class CodegenError extends Error {
|
|
|
62
62
|
constructor(message: string, options?: ErrorOptions);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
interface TypeRef {
|
|
66
|
+
name: string;
|
|
67
|
+
filePath: string;
|
|
68
|
+
isArray?: boolean;
|
|
69
|
+
}
|
|
65
70
|
interface ContractSource {
|
|
66
71
|
query: string | null;
|
|
67
72
|
body: string | null;
|
|
68
73
|
response: string;
|
|
74
|
+
queryRef?: TypeRef | null;
|
|
75
|
+
bodyRef?: TypeRef | null;
|
|
76
|
+
responseRef?: TypeRef | null;
|
|
69
77
|
}
|
|
70
78
|
interface ContractDescriptor {
|
|
71
79
|
contractSource: ContractSource;
|
|
72
80
|
}
|
|
81
|
+
interface ControllerRef {
|
|
82
|
+
className: string;
|
|
83
|
+
methodName: string;
|
|
84
|
+
filePath: string;
|
|
85
|
+
}
|
|
73
86
|
interface RouteDescriptor {
|
|
74
87
|
method: string;
|
|
75
88
|
path: string;
|
|
@@ -79,6 +92,7 @@ interface RouteDescriptor {
|
|
|
79
92
|
source: 'path' | 'query' | 'body' | 'header';
|
|
80
93
|
}>;
|
|
81
94
|
contract?: ContractDescriptor;
|
|
95
|
+
controllerRef?: ControllerRef;
|
|
82
96
|
}
|
|
83
97
|
|
|
84
98
|
/**
|
|
@@ -120,6 +134,6 @@ declare function acquireLock(outDir: string): Promise<{
|
|
|
120
134
|
release: () => Promise<void>;
|
|
121
135
|
} | null>;
|
|
122
136
|
|
|
123
|
-
declare const VERSION = "1.0
|
|
137
|
+
declare const VERSION = "1.3.0";
|
|
124
138
|
|
|
125
139
|
export { CodegenError, ConfigError, type ResolvedConfig, type ScopeConfig, type UserConfig, VERSION, type Watcher, acquireLock, defineConfig, generate, loadConfig, watch };
|
package/dist/index.js
CHANGED
|
@@ -197,12 +197,12 @@ __name(extractPropsSource, "extractPropsSource");
|
|
|
197
197
|
|
|
198
198
|
// src/emit/emit-api.ts
|
|
199
199
|
import { mkdir, writeFile } from "fs/promises";
|
|
200
|
-
import { join as join2 } from "path";
|
|
200
|
+
import { join as join2, relative as relative3 } from "path";
|
|
201
201
|
async function emitApi(routes, outDir) {
|
|
202
202
|
await mkdir(outDir, {
|
|
203
203
|
recursive: true
|
|
204
204
|
});
|
|
205
|
-
const content = buildApiFile(routes);
|
|
205
|
+
const content = buildApiFile(routes, outDir);
|
|
206
206
|
await writeFile(join2(outDir, "api.ts"), content, "utf8");
|
|
207
207
|
}
|
|
208
208
|
__name(emitApi, "emitApi");
|
|
@@ -267,7 +267,20 @@ function insertIntoTree(tree, segments, leaf, fullName) {
|
|
|
267
267
|
}
|
|
268
268
|
}
|
|
269
269
|
__name(insertIntoTree, "insertIntoTree");
|
|
270
|
-
function
|
|
270
|
+
function buildResponseType(c, outDir) {
|
|
271
|
+
if (c.controllerRef) {
|
|
272
|
+
let relPath = relative3(outDir, c.controllerRef.filePath).replace(/\.ts$/, "");
|
|
273
|
+
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
274
|
+
return `Awaited<ReturnType<import('${relPath}').${c.controllerRef.className}['${c.controllerRef.methodName}']>>`;
|
|
275
|
+
}
|
|
276
|
+
const respRef = c.contractSource.responseRef;
|
|
277
|
+
if (respRef) {
|
|
278
|
+
return respRef.isArray ? `Array<${respRef.name}>` : respRef.name;
|
|
279
|
+
}
|
|
280
|
+
return c.contractSource.response;
|
|
281
|
+
}
|
|
282
|
+
__name(buildResponseType, "buildResponseType");
|
|
283
|
+
function emitRouterTypeBlock(tree, indent, outDir) {
|
|
271
284
|
const pad = " ".repeat(indent);
|
|
272
285
|
const lines = [];
|
|
273
286
|
for (const [key, node] of tree) {
|
|
@@ -275,15 +288,17 @@ function emitRouterTypeBlock(tree, indent) {
|
|
|
275
288
|
if (node.kind === "leaf") {
|
|
276
289
|
const c = node;
|
|
277
290
|
const method = c.method.toUpperCase();
|
|
278
|
-
const
|
|
279
|
-
const
|
|
280
|
-
const
|
|
291
|
+
const queryRef = c.contractSource.queryRef;
|
|
292
|
+
const query = queryRef ? queryRef.isArray ? `Array<${queryRef.name}>` : queryRef.name : c.contractSource.query ?? "never";
|
|
293
|
+
const bodyRef = c.contractSource.bodyRef;
|
|
294
|
+
const body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
|
|
295
|
+
const response = buildResponseType(c, outDir);
|
|
281
296
|
const safeMethod = JSON.stringify(method);
|
|
282
297
|
const safeUrl = JSON.stringify(c.path);
|
|
283
298
|
lines.push(`${pad}${objKey}: { method: ${safeMethod}; url: ${safeUrl}; query: ${query}; body: ${body}; response: ${response} };`);
|
|
284
299
|
} else {
|
|
285
300
|
lines.push(`${pad}${objKey}: {`);
|
|
286
|
-
lines.push(...emitRouterTypeBlock(node.children, indent + 2));
|
|
301
|
+
lines.push(...emitRouterTypeBlock(node.children, indent + 2, outDir));
|
|
287
302
|
lines.push(`${pad}};`);
|
|
288
303
|
}
|
|
289
304
|
}
|
|
@@ -304,18 +319,21 @@ function emitApiObjectBlock(tree, indent) {
|
|
|
304
319
|
if (method === "GET") {
|
|
305
320
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
306
321
|
lines.push(`${pad}${objKey}: {`);
|
|
322
|
+
lines.push(`${pad} queryKey: (query?: ${typeAccess}['query']) => query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
307
323
|
lines.push(`${pad} queryOptions: (query?: ${typeAccess}['query']) =>`);
|
|
308
|
-
lines.push(`${pad}
|
|
309
|
-
lines.push(`${pad} queryKey: [${flatName}, query],`);
|
|
324
|
+
lines.push(`${pad} _queryOptions({`);
|
|
325
|
+
lines.push(`${pad} queryKey: query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
310
326
|
lines.push(`${pad} queryFn: () => fetcher.get<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { query }),`);
|
|
311
327
|
lines.push(`${pad} }),`);
|
|
312
328
|
lines.push(`${pad}},`);
|
|
313
329
|
} else {
|
|
314
330
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
315
331
|
lines.push(`${pad}${objKey}: {`);
|
|
316
|
-
lines.push(`${pad}
|
|
317
|
-
lines.push(`${pad}
|
|
318
|
-
lines.push(`${pad}
|
|
332
|
+
lines.push(`${pad} queryKey: () => [${flatName}] as const,`);
|
|
333
|
+
lines.push(`${pad} mutationOptions: () =>`);
|
|
334
|
+
lines.push(`${pad} _mutationOptions({`);
|
|
335
|
+
lines.push(`${pad} mutationFn: (body: ${typeAccess}['body']) => fetcher.${fetcherMethod}<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { body }),`);
|
|
336
|
+
lines.push(`${pad} }),`);
|
|
319
337
|
lines.push(`${pad}},`);
|
|
320
338
|
}
|
|
321
339
|
} else {
|
|
@@ -332,18 +350,55 @@ function buildRouterTypeAccess(name) {
|
|
|
332
350
|
return `ApiRouter${segments.map((s) => `[${JSON.stringify(s)}]`).join("")}`;
|
|
333
351
|
}
|
|
334
352
|
__name(buildRouterTypeAccess, "buildRouterTypeAccess");
|
|
335
|
-
function buildApiFile(routes) {
|
|
353
|
+
function buildApiFile(routes, outDir) {
|
|
336
354
|
const contracted = routes.filter((r) => r.contract);
|
|
355
|
+
const importsByFile = /* @__PURE__ */ new Map();
|
|
356
|
+
for (const r of contracted) {
|
|
357
|
+
const cs = r.contract?.contractSource;
|
|
358
|
+
if (!cs) continue;
|
|
359
|
+
const refs = r.controllerRef ? [
|
|
360
|
+
cs.queryRef,
|
|
361
|
+
cs.bodyRef
|
|
362
|
+
] : [
|
|
363
|
+
cs.queryRef,
|
|
364
|
+
cs.bodyRef,
|
|
365
|
+
cs.responseRef
|
|
366
|
+
];
|
|
367
|
+
for (const ref of refs) {
|
|
368
|
+
if (!ref) continue;
|
|
369
|
+
let names = importsByFile.get(ref.filePath);
|
|
370
|
+
if (!names) {
|
|
371
|
+
names = /* @__PURE__ */ new Set();
|
|
372
|
+
importsByFile.set(ref.filePath, names);
|
|
373
|
+
}
|
|
374
|
+
names.add(ref.name);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
337
377
|
const hasGetRoutes = contracted.some((r) => r.method === "GET");
|
|
378
|
+
const hasMutationRoutes = contracted.some((r) => r.method !== "GET");
|
|
338
379
|
const lines = [
|
|
339
380
|
"// Generated by @dudousxd/nestjs-inertia-codegen. Do not edit.",
|
|
340
381
|
""
|
|
341
382
|
];
|
|
342
|
-
|
|
343
|
-
|
|
383
|
+
const tqImports = [];
|
|
384
|
+
if (hasGetRoutes) tqImports.push("queryOptions as _queryOptions");
|
|
385
|
+
if (hasMutationRoutes) tqImports.push("mutationOptions as _mutationOptions");
|
|
386
|
+
if (tqImports.length > 0) {
|
|
387
|
+
lines.push(`import { ${tqImports.join(", ")} } from '@tanstack/react-query';`);
|
|
344
388
|
}
|
|
345
389
|
lines.push("import { route } from './routes.js';");
|
|
346
390
|
lines.push("import { createFetcher } from '@dudousxd/nestjs-inertia-client';");
|
|
391
|
+
if (importsByFile.size > 0 && outDir) {
|
|
392
|
+
lines.push("");
|
|
393
|
+
for (const [filePath, names] of importsByFile) {
|
|
394
|
+
let relPath = relative3(outDir, filePath).replace(/\.ts$/, "");
|
|
395
|
+
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
396
|
+
const sortedNames = [
|
|
397
|
+
...names
|
|
398
|
+
].sort();
|
|
399
|
+
lines.push(`import type { ${sortedNames.join(", ")} } from '${relPath}';`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
347
402
|
lines.push("");
|
|
348
403
|
lines.push("export const fetcher = createFetcher();");
|
|
349
404
|
lines.push("");
|
|
@@ -384,13 +439,14 @@ function buildApiFile(routes) {
|
|
|
384
439
|
method: r.method,
|
|
385
440
|
name,
|
|
386
441
|
path: r.path,
|
|
442
|
+
controllerRef: r.controllerRef,
|
|
387
443
|
contractSource: c.contractSource
|
|
388
444
|
};
|
|
389
445
|
insertIntoTree(tree, segments, leaf, name);
|
|
390
446
|
}
|
|
391
447
|
void detectCollisions;
|
|
392
448
|
lines.push("export type ApiRouter = {");
|
|
393
|
-
lines.push(...emitRouterTypeBlock(tree, 2));
|
|
449
|
+
lines.push(...emitRouterTypeBlock(tree, 2, outDir ?? ""));
|
|
394
450
|
lines.push("};");
|
|
395
451
|
lines.push("");
|
|
396
452
|
lines.push("export const api = {");
|
|
@@ -660,9 +716,28 @@ import { join as join9 } from "path";
|
|
|
660
716
|
import chokidar from "chokidar";
|
|
661
717
|
|
|
662
718
|
// src/discovery/contracts-fast.ts
|
|
719
|
+
import { readFileSync } from "fs";
|
|
663
720
|
import { dirname, join as join7, resolve as resolve2 } from "path";
|
|
664
721
|
import fg2 from "fast-glob";
|
|
665
722
|
import { Node, Project, SyntaxKind } from "ts-morph";
|
|
723
|
+
var _projectRoot = "";
|
|
724
|
+
var _tsconfigPaths = null;
|
|
725
|
+
var _debug = process.env.NESTJS_INERTIA_DEBUG === "1";
|
|
726
|
+
function dbg(...args) {
|
|
727
|
+
if (_debug) console.log("[codegen:debug]", ...args);
|
|
728
|
+
}
|
|
729
|
+
__name(dbg, "dbg");
|
|
730
|
+
function loadTsconfigPaths(tsconfigPath) {
|
|
731
|
+
try {
|
|
732
|
+
const raw = readFileSync(tsconfigPath, "utf8");
|
|
733
|
+
const stripped = raw.replace(/\/\/.*$/gm, "");
|
|
734
|
+
const parsed = JSON.parse(stripped);
|
|
735
|
+
return parsed.compilerOptions?.paths ?? null;
|
|
736
|
+
} catch {
|
|
737
|
+
return null;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
__name(loadTsconfigPaths, "loadTsconfigPaths");
|
|
666
741
|
async function discoverContractsFast(opts) {
|
|
667
742
|
const { cwd, glob, tsconfig } = opts;
|
|
668
743
|
const tsconfigPath = tsconfig ? resolve2(tsconfig) : join7(cwd, "tsconfig.json");
|
|
@@ -695,6 +770,8 @@ async function discoverContractsFast(opts) {
|
|
|
695
770
|
project.addSourceFileAtPath(f);
|
|
696
771
|
}
|
|
697
772
|
const routes = [];
|
|
773
|
+
_projectRoot = cwd;
|
|
774
|
+
_tsconfigPaths = loadTsconfigPaths(tsconfigPath);
|
|
698
775
|
for (const sourceFile of project.getSourceFiles()) {
|
|
699
776
|
routes.push(...extractFromSourceFile(sourceFile, project));
|
|
700
777
|
}
|
|
@@ -896,17 +973,41 @@ function findTypeInFile(name, file) {
|
|
|
896
973
|
return null;
|
|
897
974
|
}
|
|
898
975
|
__name(findTypeInFile, "findTypeInFile");
|
|
976
|
+
function resolveModuleSpecifier(moduleSpecifier, sourceFile, project) {
|
|
977
|
+
if (moduleSpecifier.startsWith(".")) {
|
|
978
|
+
const dir = dirname(sourceFile.getFilePath());
|
|
979
|
+
return [
|
|
980
|
+
resolve2(dir, `${moduleSpecifier}.ts`),
|
|
981
|
+
resolve2(dir, moduleSpecifier, "index.ts")
|
|
982
|
+
];
|
|
983
|
+
}
|
|
984
|
+
const baseUrl = _projectRoot;
|
|
985
|
+
dbg("resolveModuleSpecifier", moduleSpecifier, "paths:", JSON.stringify(_tsconfigPaths), "baseUrl:", baseUrl);
|
|
986
|
+
if (_tsconfigPaths) {
|
|
987
|
+
for (const [pattern, mappings] of Object.entries(_tsconfigPaths)) {
|
|
988
|
+
const prefix = pattern.replace("*", "");
|
|
989
|
+
if (moduleSpecifier.startsWith(prefix)) {
|
|
990
|
+
const rest = moduleSpecifier.slice(prefix.length);
|
|
991
|
+
const candidates = [];
|
|
992
|
+
for (const mapping of mappings) {
|
|
993
|
+
const resolved = resolve2(baseUrl, mapping.replace("*", rest));
|
|
994
|
+
candidates.push(`${resolved}.ts`, resolve2(resolved, "index.ts"));
|
|
995
|
+
}
|
|
996
|
+
dbg(" resolved candidates:", candidates);
|
|
997
|
+
return candidates;
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return [];
|
|
1002
|
+
}
|
|
1003
|
+
__name(resolveModuleSpecifier, "resolveModuleSpecifier");
|
|
899
1004
|
function resolveImportedType(name, sourceFile, project) {
|
|
900
1005
|
for (const importDecl of sourceFile.getImportDeclarations()) {
|
|
901
1006
|
const namedImport = importDecl.getNamedImports().find((n) => n.getName() === name);
|
|
902
1007
|
if (!namedImport) continue;
|
|
903
1008
|
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
const candidates = [
|
|
907
|
-
resolve2(dir, `${moduleSpecifier}.ts`),
|
|
908
|
-
resolve2(dir, moduleSpecifier, "index.ts")
|
|
909
|
-
];
|
|
1009
|
+
const candidates = resolveModuleSpecifier(moduleSpecifier, sourceFile, project);
|
|
1010
|
+
if (candidates.length === 0) continue;
|
|
910
1011
|
for (const candidate of candidates) {
|
|
911
1012
|
let importedFile = project.getSourceFile(candidate);
|
|
912
1013
|
if (!importedFile) {
|
|
@@ -940,7 +1041,8 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
940
1041
|
const name = Node.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
|
|
941
1042
|
if (name === "string" || name === "number" || name === "boolean") return name;
|
|
942
1043
|
if (name === "Date") return "string";
|
|
943
|
-
if (name === "unknown" || name === "any") return "unknown";
|
|
1044
|
+
if (name === "unknown" || name === "any" || name === "void") return "unknown";
|
|
1045
|
+
if (name === "StreamableFile" || name === "Observable" || name === "ReadableStream") return "unknown";
|
|
944
1046
|
if (name === "Array") {
|
|
945
1047
|
const typeArgs = typeNode.getTypeArguments();
|
|
946
1048
|
const firstTypeArg = typeArgs[0];
|
|
@@ -949,6 +1051,18 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
949
1051
|
}
|
|
950
1052
|
return "Array<unknown>";
|
|
951
1053
|
}
|
|
1054
|
+
if ([
|
|
1055
|
+
"Record",
|
|
1056
|
+
"Omit",
|
|
1057
|
+
"Pick",
|
|
1058
|
+
"Partial",
|
|
1059
|
+
"Required",
|
|
1060
|
+
"Readonly",
|
|
1061
|
+
"Map",
|
|
1062
|
+
"Set"
|
|
1063
|
+
].includes(name)) {
|
|
1064
|
+
return typeNode.getText();
|
|
1065
|
+
}
|
|
952
1066
|
if (name === "Promise") {
|
|
953
1067
|
const typeArgs = typeNode.getTypeArguments();
|
|
954
1068
|
const firstTypeArg = typeArgs[0];
|
|
@@ -961,7 +1075,8 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
961
1075
|
if (resolved) {
|
|
962
1076
|
return expandTypeDecl(resolved, project, depth - 1);
|
|
963
1077
|
}
|
|
964
|
-
|
|
1078
|
+
dbg("unresolvable type:", name, "in", sourceFile.getFilePath());
|
|
1079
|
+
return "unknown";
|
|
965
1080
|
}
|
|
966
1081
|
const kind = typeNode.getKind();
|
|
967
1082
|
if (kind === SyntaxKind.StringKeyword) return "string";
|
|
@@ -1088,6 +1203,68 @@ function resolveIdentifierToClassType(node, sourceFile, project, depth) {
|
|
|
1088
1203
|
return name;
|
|
1089
1204
|
}
|
|
1090
1205
|
__name(resolveIdentifierToClassType, "resolveIdentifierToClassType");
|
|
1206
|
+
function tryResolveTypeRef(typeNode, sourceFile, project) {
|
|
1207
|
+
if (Node.isTypeReference(typeNode)) {
|
|
1208
|
+
const typeName = typeNode.getTypeName();
|
|
1209
|
+
const name = Node.isIdentifier(typeName) ? typeName.getText() : null;
|
|
1210
|
+
if (!name) return null;
|
|
1211
|
+
if (name === "Promise") {
|
|
1212
|
+
const typeArgs = typeNode.getTypeArguments();
|
|
1213
|
+
const first = typeArgs[0];
|
|
1214
|
+
if (first) return tryResolveTypeRef(first, sourceFile, project);
|
|
1215
|
+
return null;
|
|
1216
|
+
}
|
|
1217
|
+
if (name === "Array") {
|
|
1218
|
+
const typeArgs = typeNode.getTypeArguments();
|
|
1219
|
+
const first = typeArgs[0];
|
|
1220
|
+
if (first) {
|
|
1221
|
+
const inner = tryResolveTypeRef(first, sourceFile, project);
|
|
1222
|
+
if (inner) return {
|
|
1223
|
+
...inner,
|
|
1224
|
+
isArray: true
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
return null;
|
|
1228
|
+
}
|
|
1229
|
+
if ([
|
|
1230
|
+
"string",
|
|
1231
|
+
"number",
|
|
1232
|
+
"boolean",
|
|
1233
|
+
"void",
|
|
1234
|
+
"unknown",
|
|
1235
|
+
"any",
|
|
1236
|
+
"Date"
|
|
1237
|
+
].includes(name)) {
|
|
1238
|
+
return null;
|
|
1239
|
+
}
|
|
1240
|
+
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1241
|
+
if (localDecl?.isExported()) {
|
|
1242
|
+
return {
|
|
1243
|
+
name,
|
|
1244
|
+
filePath: sourceFile.getFilePath()
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
const resolved = resolveImportedType(name, sourceFile, project);
|
|
1248
|
+
if (resolved && (resolved.kind === "class" || resolved.kind === "interface")) {
|
|
1249
|
+
const decl = resolved.decl;
|
|
1250
|
+
if (decl.isExported()) {
|
|
1251
|
+
return {
|
|
1252
|
+
name,
|
|
1253
|
+
filePath: resolved.file.getFilePath()
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
if (Node.isArrayTypeNode(typeNode)) {
|
|
1259
|
+
const inner = tryResolveTypeRef(typeNode.getElementTypeNode(), sourceFile, project);
|
|
1260
|
+
if (inner) return {
|
|
1261
|
+
...inner,
|
|
1262
|
+
isArray: true
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
return null;
|
|
1266
|
+
}
|
|
1267
|
+
__name(tryResolveTypeRef, "tryResolveTypeRef");
|
|
1091
1268
|
function extractDtoContract(method, sourceFile, project) {
|
|
1092
1269
|
const body = extractBodyType(method, sourceFile, project);
|
|
1093
1270
|
const query = extractQueryType(method, sourceFile, project);
|
|
@@ -1096,11 +1273,61 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
1096
1273
|
if (body === null && query === null && paramsType === null && response === "unknown") {
|
|
1097
1274
|
return null;
|
|
1098
1275
|
}
|
|
1276
|
+
let bodyRef = null;
|
|
1277
|
+
let queryRef = null;
|
|
1278
|
+
let responseRef = null;
|
|
1279
|
+
for (const param of method.getParameters()) {
|
|
1280
|
+
if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode()) {
|
|
1281
|
+
bodyRef = tryResolveTypeRef(param.getTypeNode(), sourceFile, project);
|
|
1282
|
+
}
|
|
1283
|
+
if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode()) {
|
|
1284
|
+
queryRef = tryResolveTypeRef(param.getTypeNode(), sourceFile, project);
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
const returnTypeNode = method.getReturnTypeNode();
|
|
1288
|
+
if (returnTypeNode) {
|
|
1289
|
+
responseRef = tryResolveTypeRef(returnTypeNode, sourceFile, project);
|
|
1290
|
+
}
|
|
1291
|
+
if (!responseRef) {
|
|
1292
|
+
const apiResp = method.getDecorator("ApiResponse");
|
|
1293
|
+
if (apiResp) {
|
|
1294
|
+
const args = apiResp.getArguments();
|
|
1295
|
+
const optsArg = args[0];
|
|
1296
|
+
if (optsArg && Node.isObjectLiteralExpression(optsArg)) {
|
|
1297
|
+
for (const prop of optsArg.getProperties()) {
|
|
1298
|
+
if (Node.isPropertyAssignment(prop) && prop.getName() === "type") {
|
|
1299
|
+
const val = prop.getInitializer();
|
|
1300
|
+
if (val && Node.isIdentifier(val)) {
|
|
1301
|
+
const name = val.getText();
|
|
1302
|
+
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1303
|
+
if (localDecl?.isExported()) {
|
|
1304
|
+
responseRef = {
|
|
1305
|
+
name,
|
|
1306
|
+
filePath: sourceFile.getFilePath()
|
|
1307
|
+
};
|
|
1308
|
+
} else {
|
|
1309
|
+
const resolved = resolveImportedType(name, sourceFile, project);
|
|
1310
|
+
if (resolved && (resolved.kind === "class" || resolved.kind === "interface") && resolved.decl.isExported()) {
|
|
1311
|
+
responseRef = {
|
|
1312
|
+
name,
|
|
1313
|
+
filePath: resolved.file.getFilePath()
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1099
1323
|
return {
|
|
1100
1324
|
query,
|
|
1101
1325
|
body,
|
|
1102
1326
|
response,
|
|
1103
|
-
params: paramsType
|
|
1327
|
+
params: paramsType,
|
|
1328
|
+
queryRef,
|
|
1329
|
+
bodyRef,
|
|
1330
|
+
responseRef
|
|
1104
1331
|
};
|
|
1105
1332
|
}
|
|
1106
1333
|
__name(extractDtoContract, "extractDtoContract");
|
|
@@ -1199,6 +1426,11 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1199
1426
|
path: combined,
|
|
1200
1427
|
name: routeName,
|
|
1201
1428
|
params,
|
|
1429
|
+
controllerRef: {
|
|
1430
|
+
className,
|
|
1431
|
+
methodName,
|
|
1432
|
+
filePath: sourceFile.getFilePath()
|
|
1433
|
+
},
|
|
1202
1434
|
contract: {
|
|
1203
1435
|
contractSource: {
|
|
1204
1436
|
query: contractDef.query,
|
|
@@ -1233,13 +1465,20 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1233
1465
|
path: combined,
|
|
1234
1466
|
name: routeName,
|
|
1235
1467
|
params,
|
|
1236
|
-
|
|
1468
|
+
controllerRef: {
|
|
1469
|
+
className,
|
|
1470
|
+
methodName,
|
|
1471
|
+
filePath: sourceFile.getFilePath()
|
|
1472
|
+
},
|
|
1237
1473
|
...dtoContract ? {
|
|
1238
1474
|
contract: {
|
|
1239
1475
|
contractSource: {
|
|
1240
1476
|
query: dtoContract.query,
|
|
1241
1477
|
body: dtoContract.body,
|
|
1242
|
-
response: dtoContract.response
|
|
1478
|
+
response: dtoContract.response,
|
|
1479
|
+
queryRef: dtoContract.queryRef,
|
|
1480
|
+
bodyRef: dtoContract.bodyRef,
|
|
1481
|
+
responseRef: dtoContract.responseRef
|
|
1243
1482
|
}
|
|
1244
1483
|
}
|
|
1245
1484
|
} : {}
|
|
@@ -1412,7 +1651,7 @@ async function watch(config, onChange) {
|
|
|
1412
1651
|
__name(watch, "watch");
|
|
1413
1652
|
|
|
1414
1653
|
// src/index.ts
|
|
1415
|
-
var VERSION = "1.0
|
|
1654
|
+
var VERSION = "1.3.0";
|
|
1416
1655
|
export {
|
|
1417
1656
|
CodegenError,
|
|
1418
1657
|
ConfigError,
|