@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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# Changelog — @dudousxd/nestjs-inertia-codegen
|
|
2
2
|
|
|
3
|
+
## 3.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- feat(codegen): import type references from source instead of inline expansion — eliminates unknown fields from depth limits
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies []:
|
|
12
|
+
- @dudousxd/nestjs-inertia@3.0.0
|
|
13
|
+
|
|
14
|
+
## 2.0.1
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- fix(codegen): remove @tanstack/query-core dependency — generated api.ts uses plain object literals
|
|
19
|
+
|
|
20
|
+
- Updated dependencies []:
|
|
21
|
+
- @dudousxd/nestjs-inertia@2.0.1
|
|
22
|
+
|
|
23
|
+
## 2.0.0
|
|
24
|
+
|
|
25
|
+
### Minor Changes
|
|
26
|
+
|
|
27
|
+
- feat(codegen): add queryKey() helper for typed cache invalidation — api.crew.getCrew.queryKey()
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- Updated dependencies []:
|
|
32
|
+
- @dudousxd/nestjs-inertia@2.0.0
|
|
33
|
+
|
|
3
34
|
## 1.0.7
|
|
4
35
|
|
|
5
36
|
### 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");
|
|
@@ -295,9 +295,12 @@ function emitRouterTypeBlock(tree, indent) {
|
|
|
295
295
|
if (node.kind === "leaf") {
|
|
296
296
|
const c = node;
|
|
297
297
|
const method = c.method.toUpperCase();
|
|
298
|
-
const
|
|
299
|
-
const
|
|
300
|
-
const
|
|
298
|
+
const queryRef = c.contractSource.queryRef;
|
|
299
|
+
const query = queryRef ? queryRef.isArray ? `Array<${queryRef.name}>` : queryRef.name : c.contractSource.query ?? "never";
|
|
300
|
+
const bodyRef = c.contractSource.bodyRef;
|
|
301
|
+
const body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
|
|
302
|
+
const respRef = c.contractSource.responseRef;
|
|
303
|
+
const response = respRef ? respRef.isArray ? `Array<${respRef.name}>` : respRef.name : c.contractSource.response;
|
|
301
304
|
const safeMethod = JSON.stringify(method);
|
|
302
305
|
const safeUrl = JSON.stringify(c.path);
|
|
303
306
|
lines.push(`${pad}${objKey}: { method: ${safeMethod}; url: ${safeUrl}; query: ${query}; body: ${body}; response: ${response} };`);
|
|
@@ -324,15 +327,16 @@ function emitApiObjectBlock(tree, indent) {
|
|
|
324
327
|
if (method === "GET") {
|
|
325
328
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
326
329
|
lines.push(`${pad}${objKey}: {`);
|
|
327
|
-
lines.push(`${pad}
|
|
328
|
-
lines.push(`${pad}
|
|
329
|
-
lines.push(`${pad}
|
|
330
|
-
lines.push(`${pad}
|
|
331
|
-
lines.push(`${pad}
|
|
330
|
+
lines.push(`${pad} queryKey: (query?: ${typeAccess}['query']) => query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
331
|
+
lines.push(`${pad} queryOptions: (query?: ${typeAccess}['query']) => ({`);
|
|
332
|
+
lines.push(`${pad} queryKey: query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
333
|
+
lines.push(`${pad} queryFn: () => fetcher.get<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { query }),`);
|
|
334
|
+
lines.push(`${pad} }),`);
|
|
332
335
|
lines.push(`${pad}},`);
|
|
333
336
|
} else {
|
|
334
337
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
335
338
|
lines.push(`${pad}${objKey}: {`);
|
|
339
|
+
lines.push(`${pad} queryKey: () => [${flatName}] as const,`);
|
|
336
340
|
lines.push(`${pad} mutationOptions: () => ({`);
|
|
337
341
|
lines.push(`${pad} mutationFn: (body: ${typeAccess}['body']) => fetcher.${fetcherMethod}<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { body }),`);
|
|
338
342
|
lines.push(`${pad} }),`);
|
|
@@ -352,18 +356,43 @@ function buildRouterTypeAccess(name) {
|
|
|
352
356
|
return `ApiRouter${segments.map((s) => `[${JSON.stringify(s)}]`).join("")}`;
|
|
353
357
|
}
|
|
354
358
|
__name(buildRouterTypeAccess, "buildRouterTypeAccess");
|
|
355
|
-
function buildApiFile(routes) {
|
|
359
|
+
function buildApiFile(routes, outDir) {
|
|
356
360
|
const contracted = routes.filter((r) => r.contract);
|
|
357
|
-
const
|
|
361
|
+
const importsByFile = /* @__PURE__ */ new Map();
|
|
362
|
+
for (const r of contracted) {
|
|
363
|
+
const cs = r.contract?.contractSource;
|
|
364
|
+
if (!cs) continue;
|
|
365
|
+
for (const ref of [
|
|
366
|
+
cs.queryRef,
|
|
367
|
+
cs.bodyRef,
|
|
368
|
+
cs.responseRef
|
|
369
|
+
]) {
|
|
370
|
+
if (!ref) continue;
|
|
371
|
+
let names = importsByFile.get(ref.filePath);
|
|
372
|
+
if (!names) {
|
|
373
|
+
names = /* @__PURE__ */ new Set();
|
|
374
|
+
importsByFile.set(ref.filePath, names);
|
|
375
|
+
}
|
|
376
|
+
names.add(ref.name);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
358
379
|
const lines = [
|
|
359
380
|
"// Generated by @dudousxd/nestjs-inertia-codegen. Do not edit.",
|
|
360
381
|
""
|
|
361
382
|
];
|
|
362
|
-
if (hasGetRoutes) {
|
|
363
|
-
lines.push("import { queryOptions } from '@tanstack/query-core';");
|
|
364
|
-
}
|
|
365
383
|
lines.push("import { route } from './routes.js';");
|
|
366
384
|
lines.push("import { createFetcher } from '@dudousxd/nestjs-inertia-client';");
|
|
385
|
+
if (importsByFile.size > 0 && outDir) {
|
|
386
|
+
lines.push("");
|
|
387
|
+
for (const [filePath, names] of importsByFile) {
|
|
388
|
+
let relPath = (0, import_node_path3.relative)(outDir, filePath).replace(/\.ts$/, "");
|
|
389
|
+
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
390
|
+
const sortedNames = [
|
|
391
|
+
...names
|
|
392
|
+
].sort();
|
|
393
|
+
lines.push(`import type { ${sortedNames.join(", ")} } from '${relPath}';`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
367
396
|
lines.push("");
|
|
368
397
|
lines.push("export const fetcher = createFetcher();");
|
|
369
398
|
lines.push("");
|
|
@@ -960,7 +989,8 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
960
989
|
const name = import_ts_morph.Node.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
|
|
961
990
|
if (name === "string" || name === "number" || name === "boolean") return name;
|
|
962
991
|
if (name === "Date") return "string";
|
|
963
|
-
if (name === "unknown" || name === "any") return "unknown";
|
|
992
|
+
if (name === "unknown" || name === "any" || name === "void") return "unknown";
|
|
993
|
+
if (name === "StreamableFile" || name === "Observable" || name === "ReadableStream") return "unknown";
|
|
964
994
|
if (name === "Array") {
|
|
965
995
|
const typeArgs = typeNode.getTypeArguments();
|
|
966
996
|
const firstTypeArg = typeArgs[0];
|
|
@@ -1108,6 +1138,68 @@ function resolveIdentifierToClassType(node, sourceFile, project, depth) {
|
|
|
1108
1138
|
return name;
|
|
1109
1139
|
}
|
|
1110
1140
|
__name(resolveIdentifierToClassType, "resolveIdentifierToClassType");
|
|
1141
|
+
function tryResolveTypeRef(typeNode, sourceFile, project) {
|
|
1142
|
+
if (import_ts_morph.Node.isTypeReference(typeNode)) {
|
|
1143
|
+
const typeName = typeNode.getTypeName();
|
|
1144
|
+
const name = import_ts_morph.Node.isIdentifier(typeName) ? typeName.getText() : null;
|
|
1145
|
+
if (!name) return null;
|
|
1146
|
+
if (name === "Promise") {
|
|
1147
|
+
const typeArgs = typeNode.getTypeArguments();
|
|
1148
|
+
const first = typeArgs[0];
|
|
1149
|
+
if (first) return tryResolveTypeRef(first, sourceFile, project);
|
|
1150
|
+
return null;
|
|
1151
|
+
}
|
|
1152
|
+
if (name === "Array") {
|
|
1153
|
+
const typeArgs = typeNode.getTypeArguments();
|
|
1154
|
+
const first = typeArgs[0];
|
|
1155
|
+
if (first) {
|
|
1156
|
+
const inner = tryResolveTypeRef(first, sourceFile, project);
|
|
1157
|
+
if (inner) return {
|
|
1158
|
+
...inner,
|
|
1159
|
+
isArray: true
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
return null;
|
|
1163
|
+
}
|
|
1164
|
+
if ([
|
|
1165
|
+
"string",
|
|
1166
|
+
"number",
|
|
1167
|
+
"boolean",
|
|
1168
|
+
"void",
|
|
1169
|
+
"unknown",
|
|
1170
|
+
"any",
|
|
1171
|
+
"Date"
|
|
1172
|
+
].includes(name)) {
|
|
1173
|
+
return null;
|
|
1174
|
+
}
|
|
1175
|
+
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1176
|
+
if (localDecl && localDecl.isExported()) {
|
|
1177
|
+
return {
|
|
1178
|
+
name,
|
|
1179
|
+
filePath: sourceFile.getFilePath()
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
const resolved = resolveImportedType(name, sourceFile, project);
|
|
1183
|
+
if (resolved && (resolved.kind === "class" || resolved.kind === "interface")) {
|
|
1184
|
+
const decl = resolved.decl;
|
|
1185
|
+
if (decl.isExported()) {
|
|
1186
|
+
return {
|
|
1187
|
+
name,
|
|
1188
|
+
filePath: resolved.file.getFilePath()
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
if (import_ts_morph.Node.isArrayTypeNode(typeNode)) {
|
|
1194
|
+
const inner = tryResolveTypeRef(typeNode.getElementTypeNode(), sourceFile, project);
|
|
1195
|
+
if (inner) return {
|
|
1196
|
+
...inner,
|
|
1197
|
+
isArray: true
|
|
1198
|
+
};
|
|
1199
|
+
}
|
|
1200
|
+
return null;
|
|
1201
|
+
}
|
|
1202
|
+
__name(tryResolveTypeRef, "tryResolveTypeRef");
|
|
1111
1203
|
function extractDtoContract(method, sourceFile, project) {
|
|
1112
1204
|
const body = extractBodyType(method, sourceFile, project);
|
|
1113
1205
|
const query = extractQueryType(method, sourceFile, project);
|
|
@@ -1116,11 +1208,61 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
1116
1208
|
if (body === null && query === null && paramsType === null && response === "unknown") {
|
|
1117
1209
|
return null;
|
|
1118
1210
|
}
|
|
1211
|
+
let bodyRef = null;
|
|
1212
|
+
let queryRef = null;
|
|
1213
|
+
let responseRef = null;
|
|
1214
|
+
for (const param of method.getParameters()) {
|
|
1215
|
+
if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode()) {
|
|
1216
|
+
bodyRef = tryResolveTypeRef(param.getTypeNode(), sourceFile, project);
|
|
1217
|
+
}
|
|
1218
|
+
if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode()) {
|
|
1219
|
+
queryRef = tryResolveTypeRef(param.getTypeNode(), sourceFile, project);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
const returnTypeNode = method.getReturnTypeNode();
|
|
1223
|
+
if (returnTypeNode) {
|
|
1224
|
+
responseRef = tryResolveTypeRef(returnTypeNode, sourceFile, project);
|
|
1225
|
+
}
|
|
1226
|
+
if (!responseRef) {
|
|
1227
|
+
const apiResp = method.getDecorator("ApiResponse");
|
|
1228
|
+
if (apiResp) {
|
|
1229
|
+
const args = apiResp.getArguments();
|
|
1230
|
+
const optsArg = args[0];
|
|
1231
|
+
if (optsArg && import_ts_morph.Node.isObjectLiteralExpression(optsArg)) {
|
|
1232
|
+
for (const prop of optsArg.getProperties()) {
|
|
1233
|
+
if (import_ts_morph.Node.isPropertyAssignment(prop) && prop.getName() === "type") {
|
|
1234
|
+
const val = prop.getInitializer();
|
|
1235
|
+
if (val && import_ts_morph.Node.isIdentifier(val)) {
|
|
1236
|
+
const name = val.getText();
|
|
1237
|
+
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1238
|
+
if (localDecl && localDecl.isExported()) {
|
|
1239
|
+
responseRef = {
|
|
1240
|
+
name,
|
|
1241
|
+
filePath: sourceFile.getFilePath()
|
|
1242
|
+
};
|
|
1243
|
+
} else {
|
|
1244
|
+
const resolved = resolveImportedType(name, sourceFile, project);
|
|
1245
|
+
if (resolved && (resolved.kind === "class" || resolved.kind === "interface") && resolved.decl.isExported()) {
|
|
1246
|
+
responseRef = {
|
|
1247
|
+
name,
|
|
1248
|
+
filePath: resolved.file.getFilePath()
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1119
1258
|
return {
|
|
1120
1259
|
query,
|
|
1121
1260
|
body,
|
|
1122
1261
|
response,
|
|
1123
|
-
params: paramsType
|
|
1262
|
+
params: paramsType,
|
|
1263
|
+
queryRef,
|
|
1264
|
+
bodyRef,
|
|
1265
|
+
responseRef
|
|
1124
1266
|
};
|
|
1125
1267
|
}
|
|
1126
1268
|
__name(extractDtoContract, "extractDtoContract");
|
|
@@ -1259,7 +1401,10 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1259
1401
|
contractSource: {
|
|
1260
1402
|
query: dtoContract.query,
|
|
1261
1403
|
body: dtoContract.body,
|
|
1262
|
-
response: dtoContract.response
|
|
1404
|
+
response: dtoContract.response,
|
|
1405
|
+
queryRef: dtoContract.queryRef,
|
|
1406
|
+
bodyRef: dtoContract.bodyRef,
|
|
1407
|
+
responseRef: dtoContract.responseRef
|
|
1263
1408
|
}
|
|
1264
1409
|
}
|
|
1265
1410
|
} : {}
|
|
@@ -1432,7 +1577,7 @@ async function watch(config, onChange) {
|
|
|
1432
1577
|
__name(watch, "watch");
|
|
1433
1578
|
|
|
1434
1579
|
// src/index.ts
|
|
1435
|
-
var VERSION = "1.0
|
|
1580
|
+
var VERSION = "1.2.0";
|
|
1436
1581
|
|
|
1437
1582
|
// src/cli/codegen.ts
|
|
1438
1583
|
async function runCodegen(opts = {}) {
|