@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.cjs CHANGED
@@ -243,7 +243,7 @@ async function emitApi(routes, outDir) {
243
243
  await (0, import_promises3.mkdir)(outDir, {
244
244
  recursive: true
245
245
  });
246
- const content = buildApiFile(routes);
246
+ const content = buildApiFile(routes, outDir);
247
247
  await (0, import_promises3.writeFile)((0, import_node_path3.join)(outDir, "api.ts"), content, "utf8");
248
248
  }
249
249
  __name(emitApi, "emitApi");
@@ -316,9 +316,9 @@ function emitRouterTypeBlock(tree, indent) {
316
316
  if (node.kind === "leaf") {
317
317
  const c = node;
318
318
  const method = c.method.toUpperCase();
319
- const query = c.contractSource.query ?? "never";
320
- const body = method === "GET" ? "never" : c.contractSource.body ?? "never";
321
- const response = c.contractSource.response;
319
+ const query = c.contractSource.queryRef ? c.contractSource.queryRef.name : c.contractSource.query ?? "never";
320
+ const body = method === "GET" ? "never" : c.contractSource.bodyRef ? c.contractSource.bodyRef.name : c.contractSource.body ?? "never";
321
+ const response = c.contractSource.responseRef ? c.contractSource.responseRef.name : c.contractSource.response;
322
322
  const safeMethod = JSON.stringify(method);
323
323
  const safeUrl = JSON.stringify(c.path);
324
324
  lines.push(`${pad}${objKey}: { method: ${safeMethod}; url: ${safeUrl}; query: ${query}; body: ${body}; response: ${response} };`);
@@ -346,11 +346,10 @@ function emitApiObjectBlock(tree, indent) {
346
346
  const typeAccess = buildRouterTypeAccess(c.name);
347
347
  lines.push(`${pad}${objKey}: {`);
348
348
  lines.push(`${pad} queryKey: (query?: ${typeAccess}['query']) => query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
349
- lines.push(`${pad} queryOptions: (query?: ${typeAccess}['query']) =>`);
350
- lines.push(`${pad} queryOptions({`);
351
- lines.push(`${pad} queryKey: query !== undefined ? [${flatName}, query] : [${flatName}],`);
352
- lines.push(`${pad} queryFn: () => fetcher.get<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { query }),`);
353
- lines.push(`${pad} }),`);
349
+ lines.push(`${pad} queryOptions: (query?: ${typeAccess}['query']) => ({`);
350
+ lines.push(`${pad} queryKey: query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
351
+ lines.push(`${pad} queryFn: () => fetcher.get<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { query }),`);
352
+ lines.push(`${pad} }),`);
354
353
  lines.push(`${pad}},`);
355
354
  } else {
356
355
  const typeAccess = buildRouterTypeAccess(c.name);
@@ -375,18 +374,43 @@ function buildRouterTypeAccess(name) {
375
374
  return `ApiRouter${segments.map((s) => `[${JSON.stringify(s)}]`).join("")}`;
376
375
  }
377
376
  __name(buildRouterTypeAccess, "buildRouterTypeAccess");
378
- function buildApiFile(routes) {
377
+ function buildApiFile(routes, outDir) {
379
378
  const contracted = routes.filter((r) => r.contract);
380
- const hasGetRoutes = contracted.some((r) => r.method === "GET");
379
+ const importsByFile = /* @__PURE__ */ new Map();
380
+ for (const r of contracted) {
381
+ const cs = r.contract?.contractSource;
382
+ if (!cs) continue;
383
+ for (const ref of [
384
+ cs.queryRef,
385
+ cs.bodyRef,
386
+ cs.responseRef
387
+ ]) {
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
+ }
381
397
  const lines = [
382
398
  "// Generated by @dudousxd/nestjs-inertia-codegen. Do not edit.",
383
399
  ""
384
400
  ];
385
- if (hasGetRoutes) {
386
- lines.push("import { queryOptions } from '@tanstack/query-core';");
387
- }
388
401
  lines.push("import { route } from './routes.js';");
389
402
  lines.push("import { createFetcher } from '@dudousxd/nestjs-inertia-client';");
403
+ if (importsByFile.size > 0 && outDir) {
404
+ lines.push("");
405
+ for (const [filePath, names] of importsByFile) {
406
+ let relPath = (0, import_node_path3.relative)(outDir, filePath).replace(/\.ts$/, "");
407
+ if (!relPath.startsWith(".")) relPath = `./${relPath}`;
408
+ const sortedNames = [
409
+ ...names
410
+ ].sort();
411
+ lines.push(`import type { ${sortedNames.join(", ")} } from '${relPath}';`);
412
+ }
413
+ }
390
414
  lines.push("");
391
415
  lines.push("export const fetcher = createFetcher();");
392
416
  lines.push("");
@@ -1131,6 +1155,53 @@ function resolveIdentifierToClassType(node, sourceFile, project, depth) {
1131
1155
  return name;
1132
1156
  }
1133
1157
  __name(resolveIdentifierToClassType, "resolveIdentifierToClassType");
1158
+ function tryResolveTypeRef(typeNode, sourceFile, project) {
1159
+ if (import_ts_morph.Node.isTypeReference(typeNode)) {
1160
+ const typeName = typeNode.getTypeName();
1161
+ const name = import_ts_morph.Node.isIdentifier(typeName) ? typeName.getText() : null;
1162
+ if (!name) return null;
1163
+ if (name === "Promise") {
1164
+ const typeArgs = typeNode.getTypeArguments();
1165
+ const first = typeArgs[0];
1166
+ if (first) return tryResolveTypeRef(first, sourceFile, project);
1167
+ return null;
1168
+ }
1169
+ if ([
1170
+ "string",
1171
+ "number",
1172
+ "boolean",
1173
+ "void",
1174
+ "unknown",
1175
+ "any",
1176
+ "Date",
1177
+ "Array"
1178
+ ].includes(name)) {
1179
+ return null;
1180
+ }
1181
+ const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
1182
+ if (localDecl && localDecl.isExported()) {
1183
+ return {
1184
+ name,
1185
+ filePath: sourceFile.getFilePath()
1186
+ };
1187
+ }
1188
+ const resolved = resolveImportedType(name, sourceFile, project);
1189
+ if (resolved && (resolved.kind === "class" || resolved.kind === "interface")) {
1190
+ const decl = resolved.decl;
1191
+ if (decl.isExported()) {
1192
+ return {
1193
+ name,
1194
+ filePath: resolved.file.getFilePath()
1195
+ };
1196
+ }
1197
+ }
1198
+ }
1199
+ if (import_ts_morph.Node.isArrayTypeNode(typeNode)) {
1200
+ return tryResolveTypeRef(typeNode.getElementTypeNode(), sourceFile, project);
1201
+ }
1202
+ return null;
1203
+ }
1204
+ __name(tryResolveTypeRef, "tryResolveTypeRef");
1134
1205
  function extractDtoContract(method, sourceFile, project) {
1135
1206
  const body = extractBodyType(method, sourceFile, project);
1136
1207
  const query = extractQueryType(method, sourceFile, project);
@@ -1139,11 +1210,61 @@ function extractDtoContract(method, sourceFile, project) {
1139
1210
  if (body === null && query === null && paramsType === null && response === "unknown") {
1140
1211
  return null;
1141
1212
  }
1213
+ let bodyRef = null;
1214
+ let queryRef = null;
1215
+ let responseRef = null;
1216
+ for (const param of method.getParameters()) {
1217
+ if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode()) {
1218
+ bodyRef = tryResolveTypeRef(param.getTypeNode(), sourceFile, project);
1219
+ }
1220
+ if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode()) {
1221
+ queryRef = tryResolveTypeRef(param.getTypeNode(), sourceFile, project);
1222
+ }
1223
+ }
1224
+ const returnTypeNode = method.getReturnTypeNode();
1225
+ if (returnTypeNode) {
1226
+ responseRef = tryResolveTypeRef(returnTypeNode, sourceFile, project);
1227
+ }
1228
+ if (!responseRef) {
1229
+ const apiResp = method.getDecorator("ApiResponse");
1230
+ if (apiResp) {
1231
+ const args = apiResp.getArguments();
1232
+ const optsArg = args[0];
1233
+ if (optsArg && import_ts_morph.Node.isObjectLiteralExpression(optsArg)) {
1234
+ for (const prop of optsArg.getProperties()) {
1235
+ if (import_ts_morph.Node.isPropertyAssignment(prop) && prop.getName() === "type") {
1236
+ const val = prop.getInitializer();
1237
+ if (val && import_ts_morph.Node.isIdentifier(val)) {
1238
+ const name = val.getText();
1239
+ const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
1240
+ if (localDecl && localDecl.isExported()) {
1241
+ responseRef = {
1242
+ name,
1243
+ filePath: sourceFile.getFilePath()
1244
+ };
1245
+ } else {
1246
+ const resolved = resolveImportedType(name, sourceFile, project);
1247
+ if (resolved && (resolved.kind === "class" || resolved.kind === "interface") && resolved.decl.isExported()) {
1248
+ responseRef = {
1249
+ name,
1250
+ filePath: resolved.file.getFilePath()
1251
+ };
1252
+ }
1253
+ }
1254
+ }
1255
+ }
1256
+ }
1257
+ }
1258
+ }
1259
+ }
1142
1260
  return {
1143
1261
  query,
1144
1262
  body,
1145
1263
  response,
1146
- params: paramsType
1264
+ params: paramsType,
1265
+ queryRef,
1266
+ bodyRef,
1267
+ responseRef
1147
1268
  };
1148
1269
  }
1149
1270
  __name(extractDtoContract, "extractDtoContract");
@@ -1282,7 +1403,10 @@ function extractFromSourceFile(sourceFile, project) {
1282
1403
  contractSource: {
1283
1404
  query: dtoContract.query,
1284
1405
  body: dtoContract.body,
1285
- response: dtoContract.response
1406
+ response: dtoContract.response,
1407
+ queryRef: dtoContract.queryRef,
1408
+ bodyRef: dtoContract.bodyRef,
1409
+ responseRef: dtoContract.responseRef
1286
1410
  }
1287
1411
  }
1288
1412
  } : {}
@@ -1455,7 +1579,7 @@ async function watch(config, onChange) {
1455
1579
  __name(watch, "watch");
1456
1580
 
1457
1581
  // src/index.ts
1458
- var VERSION = "2.0.0";
1582
+ var VERSION = "3.0.0";
1459
1583
  // Annotate the CommonJS export names for ESM import in node:
1460
1584
  0 && (module.exports = {
1461
1585
  CodegenError,