@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/cli/main.js
CHANGED
|
@@ -185,12 +185,12 @@ __name(extractPropsSource, "extractPropsSource");
|
|
|
185
185
|
|
|
186
186
|
// src/emit/emit-api.ts
|
|
187
187
|
import { mkdir, writeFile } from "fs/promises";
|
|
188
|
-
import { join as join2 } from "path";
|
|
188
|
+
import { join as join2, relative as relative3 } from "path";
|
|
189
189
|
async function emitApi(routes, outDir) {
|
|
190
190
|
await mkdir(outDir, {
|
|
191
191
|
recursive: true
|
|
192
192
|
});
|
|
193
|
-
const content = buildApiFile(routes);
|
|
193
|
+
const content = buildApiFile(routes, outDir);
|
|
194
194
|
await writeFile(join2(outDir, "api.ts"), content, "utf8");
|
|
195
195
|
}
|
|
196
196
|
__name(emitApi, "emitApi");
|
|
@@ -263,9 +263,12 @@ function emitRouterTypeBlock(tree, indent) {
|
|
|
263
263
|
if (node.kind === "leaf") {
|
|
264
264
|
const c = node;
|
|
265
265
|
const method = c.method.toUpperCase();
|
|
266
|
-
const
|
|
267
|
-
const
|
|
268
|
-
const
|
|
266
|
+
const queryRef = c.contractSource.queryRef;
|
|
267
|
+
const query = queryRef ? queryRef.isArray ? `Array<${queryRef.name}>` : queryRef.name : c.contractSource.query ?? "never";
|
|
268
|
+
const bodyRef = c.contractSource.bodyRef;
|
|
269
|
+
const body = method === "GET" ? "never" : bodyRef ? bodyRef.isArray ? `Array<${bodyRef.name}>` : bodyRef.name : c.contractSource.body ?? "never";
|
|
270
|
+
const respRef = c.contractSource.responseRef;
|
|
271
|
+
const response = respRef ? respRef.isArray ? `Array<${respRef.name}>` : respRef.name : c.contractSource.response;
|
|
269
272
|
const safeMethod = JSON.stringify(method);
|
|
270
273
|
const safeUrl = JSON.stringify(c.path);
|
|
271
274
|
lines.push(`${pad}${objKey}: { method: ${safeMethod}; url: ${safeUrl}; query: ${query}; body: ${body}; response: ${response} };`);
|
|
@@ -292,15 +295,16 @@ function emitApiObjectBlock(tree, indent) {
|
|
|
292
295
|
if (method === "GET") {
|
|
293
296
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
294
297
|
lines.push(`${pad}${objKey}: {`);
|
|
295
|
-
lines.push(`${pad}
|
|
296
|
-
lines.push(`${pad}
|
|
297
|
-
lines.push(`${pad}
|
|
298
|
-
lines.push(`${pad}
|
|
299
|
-
lines.push(`${pad}
|
|
298
|
+
lines.push(`${pad} queryKey: (query?: ${typeAccess}['query']) => query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
299
|
+
lines.push(`${pad} queryOptions: (query?: ${typeAccess}['query']) => ({`);
|
|
300
|
+
lines.push(`${pad} queryKey: query !== undefined ? [${flatName}, query] as const : [${flatName}] as const,`);
|
|
301
|
+
lines.push(`${pad} queryFn: () => fetcher.get<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { query }),`);
|
|
302
|
+
lines.push(`${pad} }),`);
|
|
300
303
|
lines.push(`${pad}},`);
|
|
301
304
|
} else {
|
|
302
305
|
const typeAccess = buildRouterTypeAccess(c.name);
|
|
303
306
|
lines.push(`${pad}${objKey}: {`);
|
|
307
|
+
lines.push(`${pad} queryKey: () => [${flatName}] as const,`);
|
|
304
308
|
lines.push(`${pad} mutationOptions: () => ({`);
|
|
305
309
|
lines.push(`${pad} mutationFn: (body: ${typeAccess}['body']) => fetcher.${fetcherMethod}<${typeAccess}['response']>(route(${flatName} as never) || ${safePath}, { body }),`);
|
|
306
310
|
lines.push(`${pad} }),`);
|
|
@@ -320,18 +324,43 @@ function buildRouterTypeAccess(name) {
|
|
|
320
324
|
return `ApiRouter${segments.map((s) => `[${JSON.stringify(s)}]`).join("")}`;
|
|
321
325
|
}
|
|
322
326
|
__name(buildRouterTypeAccess, "buildRouterTypeAccess");
|
|
323
|
-
function buildApiFile(routes) {
|
|
327
|
+
function buildApiFile(routes, outDir) {
|
|
324
328
|
const contracted = routes.filter((r) => r.contract);
|
|
325
|
-
const
|
|
329
|
+
const importsByFile = /* @__PURE__ */ new Map();
|
|
330
|
+
for (const r of contracted) {
|
|
331
|
+
const cs = r.contract?.contractSource;
|
|
332
|
+
if (!cs) continue;
|
|
333
|
+
for (const ref of [
|
|
334
|
+
cs.queryRef,
|
|
335
|
+
cs.bodyRef,
|
|
336
|
+
cs.responseRef
|
|
337
|
+
]) {
|
|
338
|
+
if (!ref) continue;
|
|
339
|
+
let names = importsByFile.get(ref.filePath);
|
|
340
|
+
if (!names) {
|
|
341
|
+
names = /* @__PURE__ */ new Set();
|
|
342
|
+
importsByFile.set(ref.filePath, names);
|
|
343
|
+
}
|
|
344
|
+
names.add(ref.name);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
326
347
|
const lines = [
|
|
327
348
|
"// Generated by @dudousxd/nestjs-inertia-codegen. Do not edit.",
|
|
328
349
|
""
|
|
329
350
|
];
|
|
330
|
-
if (hasGetRoutes) {
|
|
331
|
-
lines.push("import { queryOptions } from '@tanstack/query-core';");
|
|
332
|
-
}
|
|
333
351
|
lines.push("import { route } from './routes.js';");
|
|
334
352
|
lines.push("import { createFetcher } from '@dudousxd/nestjs-inertia-client';");
|
|
353
|
+
if (importsByFile.size > 0 && outDir) {
|
|
354
|
+
lines.push("");
|
|
355
|
+
for (const [filePath, names] of importsByFile) {
|
|
356
|
+
let relPath = relative3(outDir, filePath).replace(/\.ts$/, "");
|
|
357
|
+
if (!relPath.startsWith(".")) relPath = `./${relPath}`;
|
|
358
|
+
const sortedNames = [
|
|
359
|
+
...names
|
|
360
|
+
].sort();
|
|
361
|
+
lines.push(`import type { ${sortedNames.join(", ")} } from '${relPath}';`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
335
364
|
lines.push("");
|
|
336
365
|
lines.push("export const fetcher = createFetcher();");
|
|
337
366
|
lines.push("");
|
|
@@ -928,7 +957,8 @@ function resolveTypeNodeToString(typeNode, sourceFile, project, depth) {
|
|
|
928
957
|
const name = Node.isIdentifier(typeName) ? typeName.getText() : typeNode.getText();
|
|
929
958
|
if (name === "string" || name === "number" || name === "boolean") return name;
|
|
930
959
|
if (name === "Date") return "string";
|
|
931
|
-
if (name === "unknown" || name === "any") return "unknown";
|
|
960
|
+
if (name === "unknown" || name === "any" || name === "void") return "unknown";
|
|
961
|
+
if (name === "StreamableFile" || name === "Observable" || name === "ReadableStream") return "unknown";
|
|
932
962
|
if (name === "Array") {
|
|
933
963
|
const typeArgs = typeNode.getTypeArguments();
|
|
934
964
|
const firstTypeArg = typeArgs[0];
|
|
@@ -1076,6 +1106,68 @@ function resolveIdentifierToClassType(node, sourceFile, project, depth) {
|
|
|
1076
1106
|
return name;
|
|
1077
1107
|
}
|
|
1078
1108
|
__name(resolveIdentifierToClassType, "resolveIdentifierToClassType");
|
|
1109
|
+
function tryResolveTypeRef(typeNode, sourceFile, project) {
|
|
1110
|
+
if (Node.isTypeReference(typeNode)) {
|
|
1111
|
+
const typeName = typeNode.getTypeName();
|
|
1112
|
+
const name = Node.isIdentifier(typeName) ? typeName.getText() : null;
|
|
1113
|
+
if (!name) return null;
|
|
1114
|
+
if (name === "Promise") {
|
|
1115
|
+
const typeArgs = typeNode.getTypeArguments();
|
|
1116
|
+
const first = typeArgs[0];
|
|
1117
|
+
if (first) return tryResolveTypeRef(first, sourceFile, project);
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
if (name === "Array") {
|
|
1121
|
+
const typeArgs = typeNode.getTypeArguments();
|
|
1122
|
+
const first = typeArgs[0];
|
|
1123
|
+
if (first) {
|
|
1124
|
+
const inner = tryResolveTypeRef(first, sourceFile, project);
|
|
1125
|
+
if (inner) return {
|
|
1126
|
+
...inner,
|
|
1127
|
+
isArray: true
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
return null;
|
|
1131
|
+
}
|
|
1132
|
+
if ([
|
|
1133
|
+
"string",
|
|
1134
|
+
"number",
|
|
1135
|
+
"boolean",
|
|
1136
|
+
"void",
|
|
1137
|
+
"unknown",
|
|
1138
|
+
"any",
|
|
1139
|
+
"Date"
|
|
1140
|
+
].includes(name)) {
|
|
1141
|
+
return null;
|
|
1142
|
+
}
|
|
1143
|
+
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1144
|
+
if (localDecl && localDecl.isExported()) {
|
|
1145
|
+
return {
|
|
1146
|
+
name,
|
|
1147
|
+
filePath: sourceFile.getFilePath()
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
const resolved = resolveImportedType(name, sourceFile, project);
|
|
1151
|
+
if (resolved && (resolved.kind === "class" || resolved.kind === "interface")) {
|
|
1152
|
+
const decl = resolved.decl;
|
|
1153
|
+
if (decl.isExported()) {
|
|
1154
|
+
return {
|
|
1155
|
+
name,
|
|
1156
|
+
filePath: resolved.file.getFilePath()
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
if (Node.isArrayTypeNode(typeNode)) {
|
|
1162
|
+
const inner = tryResolveTypeRef(typeNode.getElementTypeNode(), sourceFile, project);
|
|
1163
|
+
if (inner) return {
|
|
1164
|
+
...inner,
|
|
1165
|
+
isArray: true
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
return null;
|
|
1169
|
+
}
|
|
1170
|
+
__name(tryResolveTypeRef, "tryResolveTypeRef");
|
|
1079
1171
|
function extractDtoContract(method, sourceFile, project) {
|
|
1080
1172
|
const body = extractBodyType(method, sourceFile, project);
|
|
1081
1173
|
const query = extractQueryType(method, sourceFile, project);
|
|
@@ -1084,11 +1176,61 @@ function extractDtoContract(method, sourceFile, project) {
|
|
|
1084
1176
|
if (body === null && query === null && paramsType === null && response === "unknown") {
|
|
1085
1177
|
return null;
|
|
1086
1178
|
}
|
|
1179
|
+
let bodyRef = null;
|
|
1180
|
+
let queryRef = null;
|
|
1181
|
+
let responseRef = null;
|
|
1182
|
+
for (const param of method.getParameters()) {
|
|
1183
|
+
if (param.getDecorators().some((d) => d.getName() === "Body") && param.getTypeNode()) {
|
|
1184
|
+
bodyRef = tryResolveTypeRef(param.getTypeNode(), sourceFile, project);
|
|
1185
|
+
}
|
|
1186
|
+
if (param.getDecorators().some((d) => d.getName() === "Query") && param.getTypeNode()) {
|
|
1187
|
+
queryRef = tryResolveTypeRef(param.getTypeNode(), sourceFile, project);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
const returnTypeNode = method.getReturnTypeNode();
|
|
1191
|
+
if (returnTypeNode) {
|
|
1192
|
+
responseRef = tryResolveTypeRef(returnTypeNode, sourceFile, project);
|
|
1193
|
+
}
|
|
1194
|
+
if (!responseRef) {
|
|
1195
|
+
const apiResp = method.getDecorator("ApiResponse");
|
|
1196
|
+
if (apiResp) {
|
|
1197
|
+
const args = apiResp.getArguments();
|
|
1198
|
+
const optsArg = args[0];
|
|
1199
|
+
if (optsArg && Node.isObjectLiteralExpression(optsArg)) {
|
|
1200
|
+
for (const prop of optsArg.getProperties()) {
|
|
1201
|
+
if (Node.isPropertyAssignment(prop) && prop.getName() === "type") {
|
|
1202
|
+
const val = prop.getInitializer();
|
|
1203
|
+
if (val && Node.isIdentifier(val)) {
|
|
1204
|
+
const name = val.getText();
|
|
1205
|
+
const localDecl = sourceFile.getInterface(name) || sourceFile.getClass(name) || sourceFile.getTypeAlias(name);
|
|
1206
|
+
if (localDecl && localDecl.isExported()) {
|
|
1207
|
+
responseRef = {
|
|
1208
|
+
name,
|
|
1209
|
+
filePath: sourceFile.getFilePath()
|
|
1210
|
+
};
|
|
1211
|
+
} else {
|
|
1212
|
+
const resolved = resolveImportedType(name, sourceFile, project);
|
|
1213
|
+
if (resolved && (resolved.kind === "class" || resolved.kind === "interface") && resolved.decl.isExported()) {
|
|
1214
|
+
responseRef = {
|
|
1215
|
+
name,
|
|
1216
|
+
filePath: resolved.file.getFilePath()
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1087
1226
|
return {
|
|
1088
1227
|
query,
|
|
1089
1228
|
body,
|
|
1090
1229
|
response,
|
|
1091
|
-
params: paramsType
|
|
1230
|
+
params: paramsType,
|
|
1231
|
+
queryRef,
|
|
1232
|
+
bodyRef,
|
|
1233
|
+
responseRef
|
|
1092
1234
|
};
|
|
1093
1235
|
}
|
|
1094
1236
|
__name(extractDtoContract, "extractDtoContract");
|
|
@@ -1227,7 +1369,10 @@ function extractFromSourceFile(sourceFile, project) {
|
|
|
1227
1369
|
contractSource: {
|
|
1228
1370
|
query: dtoContract.query,
|
|
1229
1371
|
body: dtoContract.body,
|
|
1230
|
-
response: dtoContract.response
|
|
1372
|
+
response: dtoContract.response,
|
|
1373
|
+
queryRef: dtoContract.queryRef,
|
|
1374
|
+
bodyRef: dtoContract.bodyRef,
|
|
1375
|
+
responseRef: dtoContract.responseRef
|
|
1231
1376
|
}
|
|
1232
1377
|
}
|
|
1233
1378
|
} : {}
|
|
@@ -1400,7 +1545,7 @@ async function watch(config, onChange) {
|
|
|
1400
1545
|
__name(watch, "watch");
|
|
1401
1546
|
|
|
1402
1547
|
// src/index.ts
|
|
1403
|
-
var VERSION = "1.0
|
|
1548
|
+
var VERSION = "1.2.0";
|
|
1404
1549
|
|
|
1405
1550
|
// src/cli/codegen.ts
|
|
1406
1551
|
async function runCodegen(opts = {}) {
|