@dudousxd/nestjs-inertia-codegen 1.0.7 → 1.2.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 +31 -0
- package/dist/cli/main.cjs +163 -18
- package/dist/cli/main.cjs.map +1 -1
- package/dist/cli/main.js +164 -19
- package/dist/cli/main.js.map +1 -1
- package/dist/index.cjs +163 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +164 -19
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
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,12 @@ function emitRouterTypeBlock(tree, indent) {
|
|
|
316
316
|
if (node.kind === "leaf") {
|
|
317
317
|
const c = node;
|
|
318
318
|
const method = c.method.toUpperCase();
|
|
319
|
-
const
|
|
320
|
-
const
|
|
321
|
-
const
|
|
319
|
+
const queryRef = c.contractSource.queryRef;
|
|
320
|
+
const query = queryRef ? queryRef.isArray ? `Array<${queryRef.name}>` : queryRef.name : c.contractSource.query ?? "never";
|
|
321
|
+
const bodyRef = c.contractSource.bodyRef;
|
|
322
|
+
const body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
|
|
323
|
+
const respRef = c.contractSource.responseRef;
|
|
324
|
+
const response = respRef ? respRef.isArray ? `Array<${respRef.name}>` : respRef.name : c.contractSource.response;
|
|
322
325
|
const safeMethod = JSON.stringify(method);
|
|
323
326
|
const safeUrl = JSON.stringify(c.path);
|
|
324
327
|
lines.push(`${pad}${objKey}: { method: ${safeMethod}; url: ${safeUrl}; query: ${query}; body: ${body}; response: ${response} };`);
|
|
@@ -345,15 +348,16 @@ function emitApiObjectBlock(tree, indent) {
|
|
|
345
348
|
if (method === "GET") {
|
|
346
349
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
347
350
|
lines.push(`${pad}${objKey}: {`);
|
|
348
|
-
lines.push(`${pad}
|
|
349
|
-
lines.push(`${pad}
|
|
350
|
-
lines.push(`${pad}
|
|
351
|
-
lines.push(`${pad}
|
|
352
|
-
lines.push(`${pad}
|
|
351
|
+
lines.push(`${pad} queryKey: (query?: ${typeAccess}['query']) => query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
352
|
+
lines.push(`${pad} queryOptions: (query?: ${typeAccess}['query']) => ({`);
|
|
353
|
+
lines.push(`${pad} queryKey: query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
354
|
+
lines.push(`${pad} queryFn: () => fetcher.get<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { query }),`);
|
|
355
|
+
lines.push(`${pad} }),`);
|
|
353
356
|
lines.push(`${pad}},`);
|
|
354
357
|
} else {
|
|
355
358
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
356
359
|
lines.push(`${pad}${objKey}: {`);
|
|
360
|
+
lines.push(`${pad} queryKey: () => [${flatName}] as const,`);
|
|
357
361
|
lines.push(`${pad} mutationOptions: () => ({`);
|
|
358
362
|
lines.push(`${pad} mutationFn: (body: ${typeAccess}['body']) => fetcher.${fetcherMethod}<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { body }),`);
|
|
359
363
|
lines.push(`${pad} }),`);
|
|
@@ -373,18 +377,43 @@ function buildRouterTypeAccess(name) {
|
|
|
373
377
|
return `ApiRouter${segments.map((s) => `[${JSON.stringify(s)}]`).join("")}`;
|
|
374
378
|
}
|
|
375
379
|
__name(buildRouterTypeAccess, "buildRouterTypeAccess");
|
|
376
|
-
function buildApiFile(routes) {
|
|
380
|
+
function buildApiFile(routes, outDir) {
|
|
377
381
|
const contracted = routes.filter((r) => r.contract);
|
|
378
|
-
const
|
|
382
|
+
const importsByFile = /* @__PURE__ */ new Map();
|
|
383
|
+
for (const r of contracted) {
|
|
384
|
+
const cs = r.contract?.contractSource;
|
|
385
|
+
if (!cs) continue;
|
|
386
|
+
for (const ref of [
|
|
387
|
+
cs.queryRef,
|
|
388
|
+
cs.bodyRef,
|
|
389
|
+
cs.responseRef
|
|
390
|
+
]) {
|
|
391
|
+
if (!ref) continue;
|
|
392
|
+
let names = importsByFile.get(ref.filePath);
|
|
393
|
+
if (!names) {
|
|
394
|
+
names = /* @__PURE__ */ new Set();
|
|
395
|
+
importsByFile.set(ref.filePath, names);
|
|
396
|
+
}
|
|
397
|
+
names.add(ref.name);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
379
400
|
const lines = [
|
|
380
401
|
"// Generated by @dudousxd/nestjs-inertia-codegen. Do not edit.",
|
|
381
402
|
""
|
|
382
403
|
];
|
|
383
|
-
if (hasGetRoutes) {
|
|
384
|
-
lines.push("import { queryOptions } from '@tanstack/query-core';");
|
|
385
|
-
}
|
|
386
404
|
lines.push("import { route } from './routes.js';");
|
|
387
405
|
lines.push("import { createFetcher } from '@dudousxd/nestjs-inertia-client';");
|
|
406
|
+
if (importsByFile.size > 0 && outDir) {
|
|
407
|
+
lines.push("");
|
|
408
|
+
for (const [filePath, names] of importsByFile) {
|
|
409
|
+
let relPath = (0, import_node_path3.relative)(outDir, filePath).replace(/\.ts$/, "");
|
|
410
|
+
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
411
|
+
const sortedNames = [
|
|
412
|
+
...names
|
|
413
|
+
].sort();
|
|
414
|
+
lines.push(`import type { ${sortedNames.join(", ")} } from '${relPath}';`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
388
417
|
lines.push("");
|
|
389
418
|
lines.push("export const fetcher = createFetcher();");
|
|
390
419
|
lines.push("");
|
|
@@ -981,7 +1010,8 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
981
1010
|
const name = import_ts_morph.Node.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
|
|
982
1011
|
if (name === "string" || name === "number" || name === "boolean") return name;
|
|
983
1012
|
if (name === "Date") return "string";
|
|
984
|
-
if (name === "unknown" || name === "any") return "unknown";
|
|
1013
|
+
if (name === "unknown" || name === "any" || name === "void") return "unknown";
|
|
1014
|
+
if (name === "StreamableFile" || name === "Observable" || name === "ReadableStream") return "unknown";
|
|
985
1015
|
if (name === "Array") {
|
|
986
1016
|
const typeArgs = typeNode.getTypeArguments();
|
|
987
1017
|
const firstTypeArg = typeArgs[0];
|
|
@@ -1129,6 +1159,68 @@ function resolveIdentifierToClassType(node, sourceFile, project, depth) {
|
|
|
1129
1159
|
return name;
|
|
1130
1160
|
}
|
|
1131
1161
|
__name(resolveIdentifierToClassType, "resolveIdentifierToClassType");
|
|
1162
|
+
function tryResolveTypeRef(typeNode, sourceFile, project) {
|
|
1163
|
+
if (import_ts_morph.Node.isTypeReference(typeNode)) {
|
|
1164
|
+
const typeName = typeNode.getTypeName();
|
|
1165
|
+
const name = import_ts_morph.Node.isIdentifier(typeName) ? typeName.getText() : null;
|
|
1166
|
+
if (!name) return null;
|
|
1167
|
+
if (name === "Promise") {
|
|
1168
|
+
const typeArgs = typeNode.getTypeArguments();
|
|
1169
|
+
const first = typeArgs[0];
|
|
1170
|
+
if (first) return tryResolveTypeRef(first, sourceFile, project);
|
|
1171
|
+
return null;
|
|
1172
|
+
}
|
|
1173
|
+
if (name === "Array") {
|
|
1174
|
+
const typeArgs = typeNode.getTypeArguments();
|
|
1175
|
+
const first = typeArgs[0];
|
|
1176
|
+
if (first) {
|
|
1177
|
+
const inner = tryResolveTypeRef(first, sourceFile, project);
|
|
1178
|
+
if (inner) return {
|
|
1179
|
+
...inner,
|
|
1180
|
+
isArray: true
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
return null;
|
|
1184
|
+
}
|
|
1185
|
+
if ([
|
|
1186
|
+
"string",
|
|
1187
|
+
"number",
|
|
1188
|
+
"boolean",
|
|
1189
|
+
"void",
|
|
1190
|
+
"unknown",
|
|
1191
|
+
"any",
|
|
1192
|
+
"Date"
|
|
1193
|
+
].includes(name)) {
|
|
1194
|
+
return null;
|
|
1195
|
+
}
|
|
1196
|
+
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1197
|
+
if (localDecl && localDecl.isExported()) {
|
|
1198
|
+
return {
|
|
1199
|
+
name,
|
|
1200
|
+
filePath: sourceFile.getFilePath()
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
const resolved = resolveImportedType(name, sourceFile, project);
|
|
1204
|
+
if (resolved && (resolved.kind === "class" || resolved.kind === "interface")) {
|
|
1205
|
+
const decl = resolved.decl;
|
|
1206
|
+
if (decl.isExported()) {
|
|
1207
|
+
return {
|
|
1208
|
+
name,
|
|
1209
|
+
filePath: resolved.file.getFilePath()
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
if (import_ts_morph.Node.isArrayTypeNode(typeNode)) {
|
|
1215
|
+
const inner = tryResolveTypeRef(typeNode.getElementTypeNode(), sourceFile, project);
|
|
1216
|
+
if (inner) return {
|
|
1217
|
+
...inner,
|
|
1218
|
+
isArray: true
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
return null;
|
|
1222
|
+
}
|
|
1223
|
+
__name(tryResolveTypeRef, "tryResolveTypeRef");
|
|
1132
1224
|
function extractDtoContract(method, sourceFile, project) {
|
|
1133
1225
|
const body = extractBodyType(method, sourceFile, project);
|
|
1134
1226
|
const query = extractQueryType(method, sourceFile, project);
|
|
@@ -1137,11 +1229,61 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
1137
1229
|
if (body === null && query === null && paramsType === null && response === "unknown") {
|
|
1138
1230
|
return null;
|
|
1139
1231
|
}
|
|
1232
|
+
let bodyRef = null;
|
|
1233
|
+
let queryRef = null;
|
|
1234
|
+
let responseRef = null;
|
|
1235
|
+
for (const param of method.getParameters()) {
|
|
1236
|
+
if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode()) {
|
|
1237
|
+
bodyRef = tryResolveTypeRef(param.getTypeNode(), sourceFile, project);
|
|
1238
|
+
}
|
|
1239
|
+
if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode()) {
|
|
1240
|
+
queryRef = tryResolveTypeRef(param.getTypeNode(), sourceFile, project);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
const returnTypeNode = method.getReturnTypeNode();
|
|
1244
|
+
if (returnTypeNode) {
|
|
1245
|
+
responseRef = tryResolveTypeRef(returnTypeNode, sourceFile, project);
|
|
1246
|
+
}
|
|
1247
|
+
if (!responseRef) {
|
|
1248
|
+
const apiResp = method.getDecorator("ApiResponse");
|
|
1249
|
+
if (apiResp) {
|
|
1250
|
+
const args = apiResp.getArguments();
|
|
1251
|
+
const optsArg = args[0];
|
|
1252
|
+
if (optsArg && import_ts_morph.Node.isObjectLiteralExpression(optsArg)) {
|
|
1253
|
+
for (const prop of optsArg.getProperties()) {
|
|
1254
|
+
if (import_ts_morph.Node.isPropertyAssignment(prop) && prop.getName() === "type") {
|
|
1255
|
+
const val = prop.getInitializer();
|
|
1256
|
+
if (val && import_ts_morph.Node.isIdentifier(val)) {
|
|
1257
|
+
const name = val.getText();
|
|
1258
|
+
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1259
|
+
if (localDecl && localDecl.isExported()) {
|
|
1260
|
+
responseRef = {
|
|
1261
|
+
name,
|
|
1262
|
+
filePath: sourceFile.getFilePath()
|
|
1263
|
+
};
|
|
1264
|
+
} else {
|
|
1265
|
+
const resolved = resolveImportedType(name, sourceFile, project);
|
|
1266
|
+
if (resolved && (resolved.kind === "class" || resolved.kind === "interface") && resolved.decl.isExported()) {
|
|
1267
|
+
responseRef = {
|
|
1268
|
+
name,
|
|
1269
|
+
filePath: resolved.file.getFilePath()
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1140
1279
|
return {
|
|
1141
1280
|
query,
|
|
1142
1281
|
body,
|
|
1143
1282
|
response,
|
|
1144
|
-
params: paramsType
|
|
1283
|
+
params: paramsType,
|
|
1284
|
+
queryRef,
|
|
1285
|
+
bodyRef,
|
|
1286
|
+
responseRef
|
|
1145
1287
|
};
|
|
1146
1288
|
}
|
|
1147
1289
|
__name(extractDtoContract, "extractDtoContract");
|
|
@@ -1280,7 +1422,10 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1280
1422
|
contractSource: {
|
|
1281
1423
|
query: dtoContract.query,
|
|
1282
1424
|
body: dtoContract.body,
|
|
1283
|
-
response: dtoContract.response
|
|
1425
|
+
response: dtoContract.response,
|
|
1426
|
+
queryRef: dtoContract.queryRef,
|
|
1427
|
+
bodyRef: dtoContract.bodyRef,
|
|
1428
|
+
responseRef: dtoContract.responseRef
|
|
1284
1429
|
}
|
|
1285
1430
|
}
|
|
1286
1431
|
} : {}
|
|
@@ -1453,7 +1598,7 @@ async function watch(config, onChange) {
|
|
|
1453
1598
|
__name(watch, "watch");
|
|
1454
1599
|
|
|
1455
1600
|
// src/index.ts
|
|
1456
|
-
var VERSION = "1.0
|
|
1601
|
+
var VERSION = "1.2.0";
|
|
1457
1602
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1458
1603
|
0 && (module.exports = {
|
|
1459
1604
|
CodegenError,
|