@dudousxd/nestjs-inertia-codegen 2.0.0 → 3.0.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/dist/index.d.cts CHANGED
@@ -62,10 +62,17 @@ 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
+ }
65
69
  interface ContractSource {
66
70
  query: string | null;
67
71
  body: string | null;
68
72
  response: string;
73
+ queryRef?: TypeRef | null;
74
+ bodyRef?: TypeRef | null;
75
+ responseRef?: TypeRef | null;
69
76
  }
70
77
  interface ContractDescriptor {
71
78
  contractSource: ContractSource;
@@ -120,6 +127,6 @@ declare function acquireLock(outDir: string): Promise<{
120
127
  release: () => Promise<void>;
121
128
  } | null>;
122
129
 
123
- declare const VERSION = "2.0.0";
130
+ declare const VERSION = "3.0.0";
124
131
 
125
132
  export { CodegenError, ConfigError, type ResolvedConfig, type ScopeConfig, type UserConfig, VERSION, type Watcher, acquireLock, defineConfig, generate, loadConfig, watch };
package/dist/index.d.ts CHANGED
@@ -62,10 +62,17 @@ 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
+ }
65
69
  interface ContractSource {
66
70
  query: string | null;
67
71
  body: string | null;
68
72
  response: string;
73
+ queryRef?: TypeRef | null;
74
+ bodyRef?: TypeRef | null;
75
+ responseRef?: TypeRef | null;
69
76
  }
70
77
  interface ContractDescriptor {
71
78
  contractSource: ContractSource;
@@ -120,6 +127,6 @@ declare function acquireLock(outDir: string): Promise<{
120
127
  release: () => Promise<void>;
121
128
  } | null>;
122
129
 
123
- declare const VERSION = "2.0.0";
130
+ declare const VERSION = "3.0.0";
124
131
 
125
132
  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");
@@ -275,9 +275,9 @@ function emitRouterTypeBlock(tree, indent) {
275
275
  if (node.kind === "leaf") {
276
276
  const c = node;
277
277
  const method = c.method.toUpperCase();
278
- const query = c.contractSource.query ?? "never";
279
- const body = method === "GET" ? "never" : c.contractSource.body ?? "never";
280
- const response = c.contractSource.response;
278
+ const query = c.contractSource.queryRef ? c.contractSource.queryRef.name : c.contractSource.query ?? "never";
279
+ const body = method === "GET" ? "never" : c.contractSource.bodyRef ? c.contractSource.bodyRef.name : c.contractSource.body ?? "never";
280
+ const response = c.contractSource.responseRef ? c.contractSource.responseRef.name : c.contractSource.response;
281
281
  const safeMethod = JSON.stringify(method);
282
282
  const safeUrl = JSON.stringify(c.path);
283
283
  lines.push(`${pad}${objKey}: { method: ${safeMethod}; url: ${safeUrl}; query: ${query}; body: ${body}; response: ${response} };`);
@@ -305,11 +305,10 @@ function emitApiObjectBlock(tree, indent) {
305
305
  const typeAccess = buildRouterTypeAccess(c.name);
306
306
  lines.push(`${pad}${objKey}: {`);
307
307
  lines.push(`${pad} queryKey: (query?: ${typeAccess}['query']) => query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
308
- lines.push(`${pad} queryOptions: (query?: ${typeAccess}['query']) =>`);
309
- lines.push(`${pad} queryOptions({`);
310
- lines.push(`${pad} queryKey: query !== undefined ? [${flatName}, query] : [${flatName}],`);
311
- lines.push(`${pad} queryFn: () => fetcher.get<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { query }),`);
312
- lines.push(`${pad} }),`);
308
+ lines.push(`${pad} queryOptions: (query?: ${typeAccess}['query']) => ({`);
309
+ lines.push(`${pad} queryKey: query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
310
+ lines.push(`${pad} queryFn: () => fetcher.get<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { query }),`);
311
+ lines.push(`${pad} }),`);
313
312
  lines.push(`${pad}},`);
314
313
  } else {
315
314
  const typeAccess = buildRouterTypeAccess(c.name);
@@ -334,18 +333,43 @@ function buildRouterTypeAccess(name) {
334
333
  return `ApiRouter${segments.map((s) => `[${JSON.stringify(s)}]`).join("")}`;
335
334
  }
336
335
  __name(buildRouterTypeAccess, "buildRouterTypeAccess");
337
- function buildApiFile(routes) {
336
+ function buildApiFile(routes, outDir) {
338
337
  const contracted = routes.filter((r) => r.contract);
339
- const hasGetRoutes = contracted.some((r) => r.method === "GET");
338
+ const importsByFile = /* @__PURE__ */ new Map();
339
+ for (const r of contracted) {
340
+ const cs = r.contract?.contractSource;
341
+ if (!cs) continue;
342
+ for (const ref of [
343
+ cs.queryRef,
344
+ cs.bodyRef,
345
+ cs.responseRef
346
+ ]) {
347
+ if (!ref) continue;
348
+ let names = importsByFile.get(ref.filePath);
349
+ if (!names) {
350
+ names = /* @__PURE__ */ new Set();
351
+ importsByFile.set(ref.filePath, names);
352
+ }
353
+ names.add(ref.name);
354
+ }
355
+ }
340
356
  const lines = [
341
357
  "// Generated by @dudousxd/nestjs-inertia-codegen. Do not edit.",
342
358
  ""
343
359
  ];
344
- if (hasGetRoutes) {
345
- lines.push("import { queryOptions } from '@tanstack/query-core';");
346
- }
347
360
  lines.push("import { route } from './routes.js';");
348
361
  lines.push("import { createFetcher } from '@dudousxd/nestjs-inertia-client';");
362
+ if (importsByFile.size > 0 && outDir) {
363
+ lines.push("");
364
+ for (const [filePath, names] of importsByFile) {
365
+ let relPath = relative3(outDir, filePath).replace(/\.ts$/, "");
366
+ if (!relPath.startsWith(".")) relPath = `./${relPath}`;
367
+ const sortedNames = [
368
+ ...names
369
+ ].sort();
370
+ lines.push(`import type { ${sortedNames.join(", ")} } from '${relPath}';`);
371
+ }
372
+ }
349
373
  lines.push("");
350
374
  lines.push("export const fetcher = createFetcher();");
351
375
  lines.push("");
@@ -1090,6 +1114,53 @@ function resolveIdentifierToClassType(node, sourceFile, project, depth) {
1090
1114
  return name;
1091
1115
  }
1092
1116
  __name(resolveIdentifierToClassType, "resolveIdentifierToClassType");
1117
+ function tryResolveTypeRef(typeNode, sourceFile, project) {
1118
+ if (Node.isTypeReference(typeNode)) {
1119
+ const typeName = typeNode.getTypeName();
1120
+ const name = Node.isIdentifier(typeName) ? typeName.getText() : null;
1121
+ if (!name) return null;
1122
+ if (name === "Promise") {
1123
+ const typeArgs = typeNode.getTypeArguments();
1124
+ const first = typeArgs[0];
1125
+ if (first) return tryResolveTypeRef(first, sourceFile, project);
1126
+ return null;
1127
+ }
1128
+ if ([
1129
+ "string",
1130
+ "number",
1131
+ "boolean",
1132
+ "void",
1133
+ "unknown",
1134
+ "any",
1135
+ "Date",
1136
+ "Array"
1137
+ ].includes(name)) {
1138
+ return null;
1139
+ }
1140
+ const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
1141
+ if (localDecl && localDecl.isExported()) {
1142
+ return {
1143
+ name,
1144
+ filePath: sourceFile.getFilePath()
1145
+ };
1146
+ }
1147
+ const resolved = resolveImportedType(name, sourceFile, project);
1148
+ if (resolved && (resolved.kind === "class" || resolved.kind === "interface")) {
1149
+ const decl = resolved.decl;
1150
+ if (decl.isExported()) {
1151
+ return {
1152
+ name,
1153
+ filePath: resolved.file.getFilePath()
1154
+ };
1155
+ }
1156
+ }
1157
+ }
1158
+ if (Node.isArrayTypeNode(typeNode)) {
1159
+ return tryResolveTypeRef(typeNode.getElementTypeNode(), sourceFile, project);
1160
+ }
1161
+ return null;
1162
+ }
1163
+ __name(tryResolveTypeRef, "tryResolveTypeRef");
1093
1164
  function extractDtoContract(method, sourceFile, project) {
1094
1165
  const body = extractBodyType(method, sourceFile, project);
1095
1166
  const query = extractQueryType(method, sourceFile, project);
@@ -1098,11 +1169,61 @@ function extractDtoContract(method, sourceFile, project) {
1098
1169
  if (body === null && query === null && paramsType === null && response === "unknown") {
1099
1170
  return null;
1100
1171
  }
1172
+ let bodyRef = null;
1173
+ let queryRef = null;
1174
+ let responseRef = null;
1175
+ for (const param of method.getParameters()) {
1176
+ if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode()) {
1177
+ bodyRef = tryResolveTypeRef(param.getTypeNode(), sourceFile, project);
1178
+ }
1179
+ if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode()) {
1180
+ queryRef = tryResolveTypeRef(param.getTypeNode(), sourceFile, project);
1181
+ }
1182
+ }
1183
+ const returnTypeNode = method.getReturnTypeNode();
1184
+ if (returnTypeNode) {
1185
+ responseRef = tryResolveTypeRef(returnTypeNode, sourceFile, project);
1186
+ }
1187
+ if (!responseRef) {
1188
+ const apiResp = method.getDecorator("ApiResponse");
1189
+ if (apiResp) {
1190
+ const args = apiResp.getArguments();
1191
+ const optsArg = args[0];
1192
+ if (optsArg && Node.isObjectLiteralExpression(optsArg)) {
1193
+ for (const prop of optsArg.getProperties()) {
1194
+ if (Node.isPropertyAssignment(prop) && prop.getName() === "type") {
1195
+ const val = prop.getInitializer();
1196
+ if (val && Node.isIdentifier(val)) {
1197
+ const name = val.getText();
1198
+ const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
1199
+ if (localDecl && localDecl.isExported()) {
1200
+ responseRef = {
1201
+ name,
1202
+ filePath: sourceFile.getFilePath()
1203
+ };
1204
+ } else {
1205
+ const resolved = resolveImportedType(name, sourceFile, project);
1206
+ if (resolved && (resolved.kind === "class" || resolved.kind === "interface") && resolved.decl.isExported()) {
1207
+ responseRef = {
1208
+ name,
1209
+ filePath: resolved.file.getFilePath()
1210
+ };
1211
+ }
1212
+ }
1213
+ }
1214
+ }
1215
+ }
1216
+ }
1217
+ }
1218
+ }
1101
1219
  return {
1102
1220
  query,
1103
1221
  body,
1104
1222
  response,
1105
- params: paramsType
1223
+ params: paramsType,
1224
+ queryRef,
1225
+ bodyRef,
1226
+ responseRef
1106
1227
  };
1107
1228
  }
1108
1229
  __name(extractDtoContract, "extractDtoContract");
@@ -1241,7 +1362,10 @@ function extractFromSourceFile(sourceFile, project) {
1241
1362
  contractSource: {
1242
1363
  query: dtoContract.query,
1243
1364
  body: dtoContract.body,
1244
- response: dtoContract.response
1365
+ response: dtoContract.response,
1366
+ queryRef: dtoContract.queryRef,
1367
+ bodyRef: dtoContract.bodyRef,
1368
+ responseRef: dtoContract.responseRef
1245
1369
  }
1246
1370
  }
1247
1371
  } : {}
@@ -1414,7 +1538,7 @@ async function watch(config, onChange) {
1414
1538
  __name(watch, "watch");
1415
1539
 
1416
1540
  // src/index.ts
1417
- var VERSION = "2.0.0";
1541
+ var VERSION = "3.0.0";
1418
1542
  export {
1419
1543
  CodegenError,
1420
1544
  ConfigError,