@boon4681/giri 0.0.2-alpha-7 → 0.0.2-alpha-8
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/adapters/hono.d.ts +1 -1
- package/dist/adapters/hono.js.map +1 -1
- package/dist/cli.js +585 -118
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +13 -2
- package/dist/index.js +547 -99
- package/dist/index.js.map +1 -1
- package/dist/{types-NVvedshc.d.ts → types-BvRph0mx.d.ts} +15 -1
- package/dist/validators/valibot.d.ts +1 -1
- package/dist/validators/valibot.js.map +1 -1
- package/dist/validators/zod.d.ts +1 -1
- package/dist/validators/zod.js.map +1 -1
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -45,12 +45,18 @@ var init_es5 = __esm({
|
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
// src/generator/schema/program.ts
|
|
48
|
-
function
|
|
48
|
+
function leanOptions(options) {
|
|
49
|
+
return {
|
|
50
|
+
...options,
|
|
51
|
+
types: []
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function createSchemaProgram(paths, routeFiles, programOptions = {}) {
|
|
49
55
|
let options = { ...DEFAULT_OPTIONS };
|
|
50
|
-
const configPath =
|
|
56
|
+
const configPath = import_typescript2.default.findConfigFile(paths.cwd, import_typescript2.default.sys.fileExists, "tsconfig.json");
|
|
51
57
|
if (configPath) {
|
|
52
|
-
const parsed =
|
|
53
|
-
...
|
|
58
|
+
const parsed = import_typescript2.default.getParsedCommandLineOfConfigFile(configPath, {}, {
|
|
59
|
+
...import_typescript2.default.sys,
|
|
54
60
|
onUnRecoverableConfigFileDiagnostic: () => {
|
|
55
61
|
}
|
|
56
62
|
});
|
|
@@ -58,17 +64,20 @@ function createSchemaProgram(paths, routeFiles) {
|
|
|
58
64
|
options = { ...parsed.options, noEmit: true };
|
|
59
65
|
}
|
|
60
66
|
}
|
|
61
|
-
|
|
67
|
+
if (programOptions.lean) {
|
|
68
|
+
options = leanOptions(options);
|
|
69
|
+
}
|
|
70
|
+
return import_typescript2.default.createProgram(routeFiles, options);
|
|
62
71
|
}
|
|
63
|
-
var
|
|
72
|
+
var import_typescript2, DEFAULT_OPTIONS;
|
|
64
73
|
var init_program = __esm({
|
|
65
74
|
"src/generator/schema/program.ts"() {
|
|
66
75
|
"use strict";
|
|
67
|
-
|
|
76
|
+
import_typescript2 = __toESM(require("typescript"));
|
|
68
77
|
DEFAULT_OPTIONS = {
|
|
69
|
-
target:
|
|
70
|
-
module:
|
|
71
|
-
moduleResolution:
|
|
78
|
+
target: import_typescript2.default.ScriptTarget.ES2022,
|
|
79
|
+
module: import_typescript2.default.ModuleKind.NodeNext,
|
|
80
|
+
moduleResolution: import_typescript2.default.ModuleResolutionKind.NodeNext,
|
|
72
81
|
strict: true,
|
|
73
82
|
skipLibCheck: true,
|
|
74
83
|
noEmit: true
|
|
@@ -102,7 +111,7 @@ function literalValuesOf(types) {
|
|
|
102
111
|
for (const member of types) {
|
|
103
112
|
if (member.isStringLiteral() || member.isNumberLiteral()) {
|
|
104
113
|
values.push(member.value);
|
|
105
|
-
} else if (member.flags &
|
|
114
|
+
} else if (member.flags & import_typescript3.default.TypeFlags.BooleanLiteral) {
|
|
106
115
|
values.push(intrinsicName(member) === "true");
|
|
107
116
|
} else {
|
|
108
117
|
return void 0;
|
|
@@ -111,7 +120,7 @@ function literalValuesOf(types) {
|
|
|
111
120
|
return values;
|
|
112
121
|
}
|
|
113
122
|
function walkUnion(type, ctx) {
|
|
114
|
-
const flag =
|
|
123
|
+
const flag = import_typescript3.default.TypeFlags.Undefined | import_typescript3.default.TypeFlags.Void | import_typescript3.default.TypeFlags.Never;
|
|
115
124
|
const members = type.types.filter((member) => !(member.flags & flag));
|
|
116
125
|
if (members.length === 1) {
|
|
117
126
|
return walkType(members[0], ctx);
|
|
@@ -124,13 +133,13 @@ function walkUnion(type, ctx) {
|
|
|
124
133
|
}
|
|
125
134
|
function buildObjectSchema(type, ctx) {
|
|
126
135
|
const { checker } = ctx;
|
|
127
|
-
const indexInfo = checker.getIndexInfoOfType(type,
|
|
136
|
+
const indexInfo = checker.getIndexInfoOfType(type, import_typescript3.default.IndexKind.String) ?? checker.getIndexInfoOfType(type, import_typescript3.default.IndexKind.Number);
|
|
128
137
|
const properties = {};
|
|
129
138
|
const required = [];
|
|
130
139
|
for (const symbol of checker.getPropertiesOfType(type)) {
|
|
131
140
|
const name = symbol.getName();
|
|
132
141
|
const propType = checker.getTypeOfSymbolAtLocation(symbol, ctx.location);
|
|
133
|
-
const optional = Boolean(symbol.getFlags() &
|
|
142
|
+
const optional = Boolean(symbol.getFlags() & import_typescript3.default.SymbolFlags.Optional) || Boolean(propType.flags & import_typescript3.default.TypeFlags.Union && propType.types.some((t) => t.flags & import_typescript3.default.TypeFlags.Undefined));
|
|
134
143
|
properties[name] = walkType(propType, ctx);
|
|
135
144
|
if (!optional) {
|
|
136
145
|
required.push(name);
|
|
@@ -189,16 +198,16 @@ function walkObject(type, ctx) {
|
|
|
189
198
|
}
|
|
190
199
|
function walkType(type, ctx) {
|
|
191
200
|
const flags = type.flags;
|
|
192
|
-
if (flags & (
|
|
201
|
+
if (flags & (import_typescript3.default.TypeFlags.Any | import_typescript3.default.TypeFlags.Unknown)) {
|
|
193
202
|
return {};
|
|
194
203
|
}
|
|
195
|
-
if (flags &
|
|
204
|
+
if (flags & import_typescript3.default.TypeFlags.Null) {
|
|
196
205
|
return { type: "null" };
|
|
197
206
|
}
|
|
198
|
-
if (flags & (
|
|
207
|
+
if (flags & (import_typescript3.default.TypeFlags.Undefined | import_typescript3.default.TypeFlags.Void)) {
|
|
199
208
|
return {};
|
|
200
209
|
}
|
|
201
|
-
if (flags & (
|
|
210
|
+
if (flags & (import_typescript3.default.TypeFlags.BigInt | import_typescript3.default.TypeFlags.BigIntLiteral)) {
|
|
202
211
|
ctx.warnings.push("bigint is not JSON-serializable (JSON.stringify throws); documented as string.");
|
|
203
212
|
return { type: "string" };
|
|
204
213
|
}
|
|
@@ -208,45 +217,45 @@ function walkType(type, ctx) {
|
|
|
208
217
|
if (type.isNumberLiteral()) {
|
|
209
218
|
return { type: "number", const: type.value };
|
|
210
219
|
}
|
|
211
|
-
if (flags &
|
|
220
|
+
if (flags & import_typescript3.default.TypeFlags.BooleanLiteral) {
|
|
212
221
|
return { type: "boolean", const: intrinsicName(type) === "true" };
|
|
213
222
|
}
|
|
214
|
-
if (flags &
|
|
223
|
+
if (flags & import_typescript3.default.TypeFlags.String) {
|
|
215
224
|
return { type: "string" };
|
|
216
225
|
}
|
|
217
|
-
if (flags &
|
|
226
|
+
if (flags & import_typescript3.default.TypeFlags.Number) {
|
|
218
227
|
return { type: "number" };
|
|
219
228
|
}
|
|
220
|
-
if (flags &
|
|
229
|
+
if (flags & import_typescript3.default.TypeFlags.Boolean) {
|
|
221
230
|
return { type: "boolean" };
|
|
222
231
|
}
|
|
223
232
|
if (type.isUnion()) {
|
|
224
233
|
return walkUnion(type, ctx);
|
|
225
234
|
}
|
|
226
|
-
if (flags &
|
|
235
|
+
if (flags & import_typescript3.default.TypeFlags.Object || type.isIntersection()) {
|
|
227
236
|
return walkObject(type, ctx);
|
|
228
237
|
}
|
|
229
238
|
return {};
|
|
230
239
|
}
|
|
231
|
-
var
|
|
240
|
+
var import_typescript3;
|
|
232
241
|
var init_json_schema = __esm({
|
|
233
242
|
"src/generator/schema/json-schema.ts"() {
|
|
234
243
|
"use strict";
|
|
235
|
-
|
|
244
|
+
import_typescript3 = __toESM(require("typescript"));
|
|
236
245
|
}
|
|
237
246
|
});
|
|
238
247
|
|
|
239
248
|
// src/generator/schema/responses.ts
|
|
240
249
|
function findHandleFunction(source) {
|
|
241
250
|
let found;
|
|
242
|
-
const isExported = (node) =>
|
|
251
|
+
const isExported = (node) => import_typescript4.default.canHaveModifiers(node) && (import_typescript4.default.getModifiers(node)?.some((m) => m.kind === import_typescript4.default.SyntaxKind.ExportKeyword) ?? false);
|
|
243
252
|
for (const statement of source.statements) {
|
|
244
|
-
if (
|
|
253
|
+
if (import_typescript4.default.isFunctionDeclaration(statement) && statement.name?.text === "handle" && isExported(statement)) {
|
|
245
254
|
found = statement;
|
|
246
255
|
}
|
|
247
|
-
if (
|
|
256
|
+
if (import_typescript4.default.isVariableStatement(statement) && isExported(statement)) {
|
|
248
257
|
for (const declaration of statement.declarationList.declarations) {
|
|
249
|
-
if (
|
|
258
|
+
if (import_typescript4.default.isIdentifier(declaration.name) && declaration.name.text === "handle" && declaration.initializer && (import_typescript4.default.isArrowFunction(declaration.initializer) || import_typescript4.default.isFunctionExpression(declaration.initializer))) {
|
|
250
259
|
found = declaration.initializer;
|
|
251
260
|
}
|
|
252
261
|
}
|
|
@@ -255,7 +264,7 @@ function findHandleFunction(source) {
|
|
|
255
264
|
return found;
|
|
256
265
|
}
|
|
257
266
|
function collectReturnExpressions(fn) {
|
|
258
|
-
if (
|
|
267
|
+
if (import_typescript4.default.isArrowFunction(fn) && !import_typescript4.default.isBlock(fn.body)) {
|
|
259
268
|
return [fn.body];
|
|
260
269
|
}
|
|
261
270
|
if (!fn.body) {
|
|
@@ -263,15 +272,15 @@ function collectReturnExpressions(fn) {
|
|
|
263
272
|
}
|
|
264
273
|
const expressions = [];
|
|
265
274
|
const visit = (node) => {
|
|
266
|
-
if (
|
|
275
|
+
if (import_typescript4.default.isFunctionDeclaration(node) || import_typescript4.default.isFunctionExpression(node) || import_typescript4.default.isArrowFunction(node)) {
|
|
267
276
|
return;
|
|
268
277
|
}
|
|
269
|
-
if (
|
|
278
|
+
if (import_typescript4.default.isReturnStatement(node) && node.expression) {
|
|
270
279
|
expressions.push(node.expression);
|
|
271
280
|
}
|
|
272
|
-
|
|
281
|
+
import_typescript4.default.forEachChild(node, visit);
|
|
273
282
|
};
|
|
274
|
-
|
|
283
|
+
import_typescript4.default.forEachChild(fn.body, visit);
|
|
275
284
|
return expressions;
|
|
276
285
|
}
|
|
277
286
|
function propertyType(checker, type, name, location) {
|
|
@@ -283,15 +292,21 @@ function isTypedResponse2(checker, type) {
|
|
|
283
292
|
checker.getPropertyOfType(type, "data") && checker.getPropertyOfType(type, "status") && checker.getPropertyOfType(type, "format")
|
|
284
293
|
);
|
|
285
294
|
}
|
|
286
|
-
function
|
|
287
|
-
|
|
295
|
+
function firstParameterName(fn) {
|
|
296
|
+
const [first] = fn.parameters;
|
|
297
|
+
return first && import_typescript4.default.isIdentifier(first.name) ? first.name.text : void 0;
|
|
298
|
+
}
|
|
299
|
+
function readFromCall(checker, expression, contextName) {
|
|
300
|
+
if (!import_typescript4.default.isCallExpression(expression) || !import_typescript4.default.isPropertyAccessExpression(expression.expression)) {
|
|
288
301
|
return void 0;
|
|
289
302
|
}
|
|
290
303
|
const method = expression.expression.name.text;
|
|
291
304
|
if (method !== "json" && method !== "text") {
|
|
292
305
|
return void 0;
|
|
293
306
|
}
|
|
294
|
-
|
|
307
|
+
const target = expression.expression.expression;
|
|
308
|
+
const directContextCall = contextName && import_typescript4.default.isIdentifier(target) && target.text === contextName;
|
|
309
|
+
if (!directContextCall && !isTypedResponse2(checker, checker.getTypeAtLocation(expression))) {
|
|
295
310
|
return void 0;
|
|
296
311
|
}
|
|
297
312
|
const [dataArg, statusArg] = expression.arguments;
|
|
@@ -331,6 +346,7 @@ function extractRouteResponses(program, file) {
|
|
|
331
346
|
return result;
|
|
332
347
|
}
|
|
333
348
|
const ctx = createWalkContext(checker, fn);
|
|
349
|
+
const contextName = firstParameterName(fn);
|
|
334
350
|
const byStatus = /* @__PURE__ */ new Map();
|
|
335
351
|
const record = (hit) => {
|
|
336
352
|
const schema = walkType(hit.data, ctx);
|
|
@@ -339,7 +355,7 @@ function extractRouteResponses(program, file) {
|
|
|
339
355
|
byStatus.set(hit.status, bucket);
|
|
340
356
|
};
|
|
341
357
|
for (const expression of collectReturnExpressions(fn)) {
|
|
342
|
-
const fromCall = readFromCall(checker, expression);
|
|
358
|
+
const fromCall = readFromCall(checker, expression, contextName);
|
|
343
359
|
if (fromCall) {
|
|
344
360
|
record(fromCall);
|
|
345
361
|
continue;
|
|
@@ -365,11 +381,11 @@ function extractRouteResponses(program, file) {
|
|
|
365
381
|
result.$defs = ctx.defs;
|
|
366
382
|
return result;
|
|
367
383
|
}
|
|
368
|
-
var
|
|
384
|
+
var import_typescript4;
|
|
369
385
|
var init_responses = __esm({
|
|
370
386
|
"src/generator/schema/responses.ts"() {
|
|
371
387
|
"use strict";
|
|
372
|
-
|
|
388
|
+
import_typescript4 = __toESM(require("typescript"));
|
|
373
389
|
init_json_schema();
|
|
374
390
|
}
|
|
375
391
|
});
|
|
@@ -827,13 +843,18 @@ function methodFromFile(fileName) {
|
|
|
827
843
|
const stem = fileName.replace(/\.(?:[cm]?[jt]s|[jt]sx)$/, "").toLowerCase();
|
|
828
844
|
return METHOD_FROM_FILE.get(stem);
|
|
829
845
|
}
|
|
830
|
-
function sharedFileIn(dir) {
|
|
846
|
+
function sharedFileIn(dir, cache) {
|
|
847
|
+
if (cache?.has(dir)) {
|
|
848
|
+
return cache.get(dir);
|
|
849
|
+
}
|
|
831
850
|
for (const ext of ["ts", "tsx", "js", "jsx", "mjs", "cjs", "mts", "cts"]) {
|
|
832
851
|
const file = (0, import_node_path2.join)(dir, `+shared.${ext}`);
|
|
833
852
|
if ((0, import_node_fs2.existsSync)(file)) {
|
|
853
|
+
cache?.set(dir, file);
|
|
834
854
|
return file;
|
|
835
855
|
}
|
|
836
856
|
}
|
|
857
|
+
cache?.set(dir, void 0);
|
|
837
858
|
return void 0;
|
|
838
859
|
}
|
|
839
860
|
function physicalRouteSegments(routesDir, routeDir) {
|
|
@@ -902,7 +923,7 @@ async function scanRouteFolders(routesDir) {
|
|
|
902
923
|
function routeParamsForDir(routesDir, dir) {
|
|
903
924
|
return pathFromSegments(physicalRouteSegments(routesDir, dir)).params;
|
|
904
925
|
}
|
|
905
|
-
function sharedFilesForDir(routesDir, dir) {
|
|
926
|
+
function sharedFilesForDir(routesDir, dir, cache) {
|
|
906
927
|
const segments = physicalRouteSegments(routesDir, dir);
|
|
907
928
|
const dirs = [routesDir];
|
|
908
929
|
let current = routesDir;
|
|
@@ -910,7 +931,7 @@ function sharedFilesForDir(routesDir, dir) {
|
|
|
910
931
|
current = (0, import_node_path2.join)(current, segment);
|
|
911
932
|
dirs.push(current);
|
|
912
933
|
}
|
|
913
|
-
return dirs.map(sharedFileIn).filter((file) => Boolean(file));
|
|
934
|
+
return dirs.map((currentDir) => sharedFileIn(currentDir, cache)).filter((file) => Boolean(file));
|
|
914
935
|
}
|
|
915
936
|
async function scanRoutes(routesDir) {
|
|
916
937
|
if (!(0, import_node_fs2.existsSync)(routesDir)) {
|
|
@@ -922,6 +943,7 @@ async function scanRoutes(routesDir) {
|
|
|
922
943
|
onlyFiles: true
|
|
923
944
|
});
|
|
924
945
|
const routes = [];
|
|
946
|
+
const sharedCache = /* @__PURE__ */ new Map();
|
|
925
947
|
for (const file of files) {
|
|
926
948
|
const method = methodFromFile((0, import_node_path2.basename)(file));
|
|
927
949
|
if (!method) {
|
|
@@ -937,7 +959,7 @@ async function scanRoutes(routesDir) {
|
|
|
937
959
|
routeDir,
|
|
938
960
|
routeSegments,
|
|
939
961
|
params,
|
|
940
|
-
sharedFiles: sharedFilesForDir(routesDir, routeDir)
|
|
962
|
+
sharedFiles: sharedFilesForDir(routesDir, routeDir, sharedCache)
|
|
941
963
|
});
|
|
942
964
|
}
|
|
943
965
|
return routes.sort((left, right) => {
|
|
@@ -950,9 +972,11 @@ async function scanRoutes(routesDir) {
|
|
|
950
972
|
}
|
|
951
973
|
|
|
952
974
|
// src/app.ts
|
|
953
|
-
function loadModule(file) {
|
|
975
|
+
function loadModule(file, force = true) {
|
|
954
976
|
const resolved = require.resolve(file);
|
|
955
|
-
|
|
977
|
+
if (force) {
|
|
978
|
+
delete require.cache[resolved];
|
|
979
|
+
}
|
|
956
980
|
return require(resolved);
|
|
957
981
|
}
|
|
958
982
|
function interopDefault(value) {
|
|
@@ -1087,13 +1111,23 @@ async function buildGiriApp(config, options = {}) {
|
|
|
1087
1111
|
const { unregister } = await safeRegister();
|
|
1088
1112
|
const unregisterAliasResolver = registerAliasResolver(config.alias, paths.cwd);
|
|
1089
1113
|
try {
|
|
1114
|
+
const dirty = options.dirty;
|
|
1115
|
+
const forceReload = dirty === void 0;
|
|
1116
|
+
const isDirty = (file) => forceReload || dirty.has(file);
|
|
1117
|
+
const sharedCache = /* @__PURE__ */ new Map();
|
|
1118
|
+
const loadShared = (file) => {
|
|
1119
|
+
if (!sharedCache.has(file)) {
|
|
1120
|
+
sharedCache.set(file, loadModule(file, isDirty(file)));
|
|
1121
|
+
}
|
|
1122
|
+
return sharedCache.get(file);
|
|
1123
|
+
};
|
|
1090
1124
|
for (const route of routes) {
|
|
1091
|
-
const routeModule = loadModule(route.file);
|
|
1125
|
+
const routeModule = loadModule(route.file, isDirty(route.file));
|
|
1092
1126
|
if (typeof routeModule.handle !== "function") {
|
|
1093
1127
|
throw new Error(`${route.file} must export a named handle function.`);
|
|
1094
1128
|
}
|
|
1095
1129
|
const folderMiddleware = routeModule.config?.skipInherited ? [] : route.sharedFiles.flatMap(
|
|
1096
|
-
(file) => normalizeMiddleware(
|
|
1130
|
+
(file) => normalizeMiddleware(loadShared(file).middleware, file)
|
|
1097
1131
|
);
|
|
1098
1132
|
const verbMiddleware = normalizeMiddleware(routeModule.middleware, route.file);
|
|
1099
1133
|
config.adapter.register(app, {
|
|
@@ -1114,7 +1148,7 @@ async function buildGiriApp(config, options = {}) {
|
|
|
1114
1148
|
}
|
|
1115
1149
|
|
|
1116
1150
|
// src/generator/sync.ts
|
|
1117
|
-
var
|
|
1151
|
+
var import_node_fs7 = require("fs");
|
|
1118
1152
|
var import_promises3 = require("fs/promises");
|
|
1119
1153
|
var import_node_path11 = require("path");
|
|
1120
1154
|
|
|
@@ -1368,6 +1402,7 @@ function buildOpenApiDocument(paths, routes, data = {}) {
|
|
|
1368
1402
|
const documentPaths = {};
|
|
1369
1403
|
const schemas = {};
|
|
1370
1404
|
const securitySchemes = {};
|
|
1405
|
+
const tagOrder = [];
|
|
1371
1406
|
for (const route of routes) {
|
|
1372
1407
|
if (data.hiddenFiles?.has(route.file)) {
|
|
1373
1408
|
continue;
|
|
@@ -1375,10 +1410,32 @@ function buildOpenApiDocument(paths, routes, data = {}) {
|
|
|
1375
1410
|
const responses = data.responsesByFile?.get(route.file);
|
|
1376
1411
|
const input = data.inputsByFile?.get(route.file);
|
|
1377
1412
|
const security = data.securityByFile?.get(route.file);
|
|
1413
|
+
const meta = data.openapiByFile?.get(route.file);
|
|
1378
1414
|
for (const [name, schema] of Object.entries(responses?.$defs ?? {})) {
|
|
1379
1415
|
schemas[name] = rewriteRefs(schema);
|
|
1380
1416
|
}
|
|
1381
|
-
const operation = {
|
|
1417
|
+
const operation = {};
|
|
1418
|
+
if (meta?.tags && meta.tags.length > 0) {
|
|
1419
|
+
operation.tags = meta.tags;
|
|
1420
|
+
for (const tag of meta.tags) {
|
|
1421
|
+
if (!tagOrder.includes(tag)) {
|
|
1422
|
+
tagOrder.push(tag);
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
if (meta?.summary) {
|
|
1427
|
+
operation.summary = meta.summary;
|
|
1428
|
+
}
|
|
1429
|
+
if (meta?.description) {
|
|
1430
|
+
operation.description = meta.description;
|
|
1431
|
+
}
|
|
1432
|
+
if (meta?.operationId) {
|
|
1433
|
+
operation.operationId = meta.operationId;
|
|
1434
|
+
}
|
|
1435
|
+
if (meta?.deprecated) {
|
|
1436
|
+
operation.deprecated = true;
|
|
1437
|
+
}
|
|
1438
|
+
operation.responses = buildResponses(responses?.responses ?? []);
|
|
1382
1439
|
const parameters = [...pathParameters(route), ...queryParameters(input?.query)];
|
|
1383
1440
|
if (parameters.length > 0) {
|
|
1384
1441
|
operation.parameters = parameters;
|
|
@@ -1410,6 +1467,9 @@ function buildOpenApiDocument(paths, routes, data = {}) {
|
|
|
1410
1467
|
info: readProjectInfo(paths.cwd),
|
|
1411
1468
|
paths: documentPaths
|
|
1412
1469
|
};
|
|
1470
|
+
if (tagOrder.length > 0) {
|
|
1471
|
+
document.tags = tagOrder.map((name) => ({ name }));
|
|
1472
|
+
}
|
|
1413
1473
|
const components = {};
|
|
1414
1474
|
if (Object.keys(schemas).length > 0) {
|
|
1415
1475
|
components.schemas = schemas;
|
|
@@ -1441,14 +1501,29 @@ function paramsType(params) {
|
|
|
1441
1501
|
${fields}
|
|
1442
1502
|
}`;
|
|
1443
1503
|
}
|
|
1444
|
-
function
|
|
1445
|
-
|
|
1446
|
-
|
|
1504
|
+
function middlewareVarsType(typesDir, sharedFile) {
|
|
1505
|
+
const spec = JSON.stringify(moduleSpecifier(typesDir, sharedFile));
|
|
1506
|
+
return `(typeof import(${spec}) extends { middleware: infer M } ? import("@boon4681/giri").InferStackVars<M> : {})`;
|
|
1507
|
+
}
|
|
1508
|
+
function ownSharedFile(dir, sharedFiles) {
|
|
1509
|
+
for (let index = sharedFiles.length - 1; index >= 0; index -= 1) {
|
|
1510
|
+
if ((0, import_node_path8.dirname)(sharedFiles[index]) === dir) {
|
|
1511
|
+
return sharedFiles[index];
|
|
1512
|
+
}
|
|
1447
1513
|
}
|
|
1448
|
-
return
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1514
|
+
return void 0;
|
|
1515
|
+
}
|
|
1516
|
+
function varsType(paths, file, dir, sharedFiles) {
|
|
1517
|
+
const typesDir = (0, import_node_path8.dirname)(file);
|
|
1518
|
+
const parts = [];
|
|
1519
|
+
if (dir !== paths.routesDir) {
|
|
1520
|
+
parts.push(`import(${JSON.stringify(importPath(file, typeFilePath(paths, (0, import_node_path8.dirname)(dir))))}).Vars`);
|
|
1521
|
+
}
|
|
1522
|
+
const ownShared = ownSharedFile(dir, sharedFiles);
|
|
1523
|
+
if (ownShared) {
|
|
1524
|
+
parts.push(middlewareVarsType(typesDir, ownShared));
|
|
1525
|
+
}
|
|
1526
|
+
return parts.length > 0 ? parts.join("\n & ") : "{}";
|
|
1452
1527
|
}
|
|
1453
1528
|
function methodExports(typesDir, verbs) {
|
|
1454
1529
|
return verbs.map(({ method, file }) => {
|
|
@@ -1459,14 +1534,14 @@ function methodExports(typesDir, verbs) {
|
|
|
1459
1534
|
});
|
|
1460
1535
|
}
|
|
1461
1536
|
async function writeParamTypes(paths, folders) {
|
|
1462
|
-
|
|
1537
|
+
await Promise.all(folders.map(({ dir, params, sharedFiles, verbs }) => {
|
|
1463
1538
|
const file = typeFilePath(paths, dir);
|
|
1464
1539
|
const typesDir = (0, import_node_path8.dirname)(file);
|
|
1465
1540
|
const lines = [
|
|
1466
1541
|
GENERATED_HEADER,
|
|
1467
1542
|
`export type Params = ${paramsType(params)};`,
|
|
1468
1543
|
"export type RouteParams = Params;",
|
|
1469
|
-
`type Vars = ${varsType(
|
|
1544
|
+
`export type Vars = ${varsType(paths, file, dir, sharedFiles)};`,
|
|
1470
1545
|
"export type Middleware<Injects extends Record<string, unknown> = {}> =",
|
|
1471
1546
|
' import("@boon4681/giri").Middleware<Params, import("@boon4681/giri").ValidatedInput, Injects>;',
|
|
1472
1547
|
'export type Handle<Input extends import("@boon4681/giri").ValidatedInput = import("@boon4681/giri").ValidatedInput> =',
|
|
@@ -1476,10 +1551,14 @@ async function writeParamTypes(paths, folders) {
|
|
|
1476
1551
|
lines.push(...methodExports(typesDir, verbs));
|
|
1477
1552
|
}
|
|
1478
1553
|
lines.push("");
|
|
1479
|
-
|
|
1480
|
-
}
|
|
1554
|
+
return writeGenerated(file, lines.join("\n"));
|
|
1555
|
+
}));
|
|
1481
1556
|
}
|
|
1482
1557
|
|
|
1558
|
+
// src/generator/route-meta.ts
|
|
1559
|
+
var import_node_fs6 = require("fs");
|
|
1560
|
+
var import_typescript = __toESM(require("typescript"));
|
|
1561
|
+
|
|
1483
1562
|
// src/generator/inputs.ts
|
|
1484
1563
|
function sanitize(schema) {
|
|
1485
1564
|
const { $schema, ...rest } = schema;
|
|
@@ -1540,28 +1619,315 @@ function readInput(routeModule) {
|
|
|
1540
1619
|
}
|
|
1541
1620
|
return input.body || input.query ? input : void 0;
|
|
1542
1621
|
}
|
|
1543
|
-
function
|
|
1544
|
-
|
|
1622
|
+
function hasExportModifier(node) {
|
|
1623
|
+
return import_typescript.default.canHaveModifiers(node) && (import_typescript.default.getModifiers(node)?.some((modifier) => modifier.kind === import_typescript.default.SyntaxKind.ExportKeyword) ?? false);
|
|
1624
|
+
}
|
|
1625
|
+
function unwrapExpression(expression) {
|
|
1626
|
+
let current = expression;
|
|
1627
|
+
while (import_typescript.default.isParenthesizedExpression(current) || import_typescript.default.isAsExpression(current) || import_typescript.default.isSatisfiesExpression(current)) {
|
|
1628
|
+
current = current.expression;
|
|
1629
|
+
}
|
|
1630
|
+
return current;
|
|
1631
|
+
}
|
|
1632
|
+
function staticBoolean(expression) {
|
|
1633
|
+
const value = unwrapExpression(expression);
|
|
1634
|
+
if (value.kind === import_typescript.default.SyntaxKind.TrueKeyword) {
|
|
1545
1635
|
return true;
|
|
1546
1636
|
}
|
|
1547
|
-
if (value ===
|
|
1637
|
+
if (value.kind === import_typescript.default.SyntaxKind.FalseKeyword) {
|
|
1548
1638
|
return false;
|
|
1549
1639
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1640
|
+
return void 0;
|
|
1641
|
+
}
|
|
1642
|
+
function staticString(expression) {
|
|
1643
|
+
const value = unwrapExpression(expression);
|
|
1644
|
+
return import_typescript.default.isStringLiteralLike(value) ? value.text : void 0;
|
|
1645
|
+
}
|
|
1646
|
+
function staticStringArray(expression) {
|
|
1647
|
+
const value = unwrapExpression(expression);
|
|
1648
|
+
if (!import_typescript.default.isArrayLiteralExpression(value)) {
|
|
1649
|
+
return void 0;
|
|
1650
|
+
}
|
|
1651
|
+
const strings = [];
|
|
1652
|
+
for (const element of value.elements) {
|
|
1653
|
+
const string = staticString(element);
|
|
1654
|
+
if (string === void 0) {
|
|
1655
|
+
return void 0;
|
|
1656
|
+
}
|
|
1657
|
+
strings.push(string);
|
|
1658
|
+
}
|
|
1659
|
+
return strings;
|
|
1660
|
+
}
|
|
1661
|
+
function propertyName(name) {
|
|
1662
|
+
if (import_typescript.default.isIdentifier(name) || import_typescript.default.isStringLiteral(name) || import_typescript.default.isNumericLiteral(name)) {
|
|
1663
|
+
return name.text;
|
|
1552
1664
|
}
|
|
1553
1665
|
return void 0;
|
|
1554
1666
|
}
|
|
1555
|
-
function
|
|
1667
|
+
function collectImportedNames(source) {
|
|
1668
|
+
const names = /* @__PURE__ */ new Set();
|
|
1669
|
+
for (const statement of source.statements) {
|
|
1670
|
+
if (!import_typescript.default.isImportDeclaration(statement)) {
|
|
1671
|
+
continue;
|
|
1672
|
+
}
|
|
1673
|
+
const clause = statement.importClause;
|
|
1674
|
+
if (!clause) {
|
|
1675
|
+
continue;
|
|
1676
|
+
}
|
|
1677
|
+
if (clause.name) {
|
|
1678
|
+
names.add(clause.name.text);
|
|
1679
|
+
}
|
|
1680
|
+
const bindings = clause.namedBindings;
|
|
1681
|
+
if (bindings && import_typescript.default.isNamespaceImport(bindings)) {
|
|
1682
|
+
names.add(bindings.name.text);
|
|
1683
|
+
}
|
|
1684
|
+
if (bindings && import_typescript.default.isNamedImports(bindings)) {
|
|
1685
|
+
for (const element of bindings.elements) {
|
|
1686
|
+
names.add(element.name.text);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
return names;
|
|
1691
|
+
}
|
|
1692
|
+
function expressionReferencesImportedMiddleware(expression, importedNames) {
|
|
1693
|
+
let found = false;
|
|
1694
|
+
const allowedImportedHelpers = /* @__PURE__ */ new Set(["stack", "fromHono"]);
|
|
1695
|
+
const visit = (node) => {
|
|
1696
|
+
if (found) {
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
if (import_typescript.default.isIdentifier(node) && importedNames.has(node.text) && !allowedImportedHelpers.has(node.text)) {
|
|
1700
|
+
found = true;
|
|
1701
|
+
return;
|
|
1702
|
+
}
|
|
1703
|
+
import_typescript.default.forEachChild(node, visit);
|
|
1704
|
+
};
|
|
1705
|
+
visit(expression);
|
|
1706
|
+
return found;
|
|
1707
|
+
}
|
|
1708
|
+
function parseStaticOpenApi(expression) {
|
|
1709
|
+
const value = unwrapExpression(expression);
|
|
1710
|
+
const boolean = staticBoolean(value);
|
|
1711
|
+
if (boolean !== void 0) {
|
|
1712
|
+
return boolean;
|
|
1713
|
+
}
|
|
1714
|
+
if (!import_typescript.default.isObjectLiteralExpression(value)) {
|
|
1715
|
+
return void 0;
|
|
1716
|
+
}
|
|
1717
|
+
const openapi = {};
|
|
1718
|
+
for (const property of value.properties) {
|
|
1719
|
+
if (!import_typescript.default.isPropertyAssignment(property)) {
|
|
1720
|
+
return void 0;
|
|
1721
|
+
}
|
|
1722
|
+
const name = propertyName(property.name);
|
|
1723
|
+
if (!name) {
|
|
1724
|
+
return void 0;
|
|
1725
|
+
}
|
|
1726
|
+
if (name === "hidden") {
|
|
1727
|
+
const hidden = staticBoolean(property.initializer);
|
|
1728
|
+
if (hidden === void 0) {
|
|
1729
|
+
return void 0;
|
|
1730
|
+
}
|
|
1731
|
+
openapi.hidden = hidden;
|
|
1732
|
+
} else if (name === "tags") {
|
|
1733
|
+
const tags = staticStringArray(property.initializer);
|
|
1734
|
+
if (!tags) {
|
|
1735
|
+
return void 0;
|
|
1736
|
+
}
|
|
1737
|
+
openapi.tags = tags;
|
|
1738
|
+
} else if (name === "summary" || name === "description" || name === "operationId") {
|
|
1739
|
+
const string = staticString(property.initializer);
|
|
1740
|
+
if (string === void 0) {
|
|
1741
|
+
return void 0;
|
|
1742
|
+
}
|
|
1743
|
+
openapi[name] = string;
|
|
1744
|
+
} else if (name === "deprecated") {
|
|
1745
|
+
const deprecated = staticBoolean(property.initializer);
|
|
1746
|
+
if (deprecated === void 0) {
|
|
1747
|
+
return void 0;
|
|
1748
|
+
}
|
|
1749
|
+
openapi.deprecated = deprecated;
|
|
1750
|
+
} else {
|
|
1751
|
+
return void 0;
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
return openapi;
|
|
1755
|
+
}
|
|
1756
|
+
function readStaticModuleMeta(file) {
|
|
1757
|
+
let source;
|
|
1758
|
+
try {
|
|
1759
|
+
source = import_typescript.default.createSourceFile(file, (0, import_node_fs6.readFileSync)(file, "utf8"), import_typescript.default.ScriptTarget.Latest, true);
|
|
1760
|
+
} catch {
|
|
1761
|
+
return void 0;
|
|
1762
|
+
}
|
|
1763
|
+
const importedNames = collectImportedNames(source);
|
|
1764
|
+
const sourceText = source.getFullText();
|
|
1765
|
+
const canSkipMiddlewareRuntime = !sourceText.includes("defineMiddleware") && !sourceText.includes(".openapi");
|
|
1766
|
+
const meta = { middlewareSecurity: false };
|
|
1767
|
+
for (const statement of source.statements) {
|
|
1768
|
+
if (import_typescript.default.isImportDeclaration(statement) || import_typescript.default.isInterfaceDeclaration(statement) || import_typescript.default.isTypeAliasDeclaration(statement) || import_typescript.default.isEmptyStatement(statement) || !hasExportModifier(statement)) {
|
|
1769
|
+
continue;
|
|
1770
|
+
}
|
|
1771
|
+
if (import_typescript.default.isFunctionDeclaration(statement) && statement.name?.text === "handle") {
|
|
1772
|
+
continue;
|
|
1773
|
+
}
|
|
1774
|
+
if (import_typescript.default.isVariableStatement(statement)) {
|
|
1775
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
1776
|
+
if (!import_typescript.default.isIdentifier(declaration.name)) {
|
|
1777
|
+
return void 0;
|
|
1778
|
+
}
|
|
1779
|
+
const name = declaration.name.text;
|
|
1780
|
+
if (name === "openapi") {
|
|
1781
|
+
if (!declaration.initializer) {
|
|
1782
|
+
return void 0;
|
|
1783
|
+
}
|
|
1784
|
+
const openapi = parseStaticOpenApi(declaration.initializer);
|
|
1785
|
+
if (openapi === void 0) {
|
|
1786
|
+
return void 0;
|
|
1787
|
+
}
|
|
1788
|
+
meta.openapi = openapi;
|
|
1789
|
+
} else if (name === "handle") {
|
|
1790
|
+
if (!declaration.initializer || !import_typescript.default.isArrowFunction(declaration.initializer) && !import_typescript.default.isFunctionExpression(declaration.initializer)) {
|
|
1791
|
+
return void 0;
|
|
1792
|
+
}
|
|
1793
|
+
continue;
|
|
1794
|
+
} else if (name === "middleware") {
|
|
1795
|
+
if (!declaration.initializer || !canSkipMiddlewareRuntime || expressionReferencesImportedMiddleware(declaration.initializer, importedNames)) {
|
|
1796
|
+
return void 0;
|
|
1797
|
+
}
|
|
1798
|
+
meta.middlewareSecurity = false;
|
|
1799
|
+
continue;
|
|
1800
|
+
} else {
|
|
1801
|
+
return void 0;
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
continue;
|
|
1805
|
+
}
|
|
1806
|
+
return void 0;
|
|
1807
|
+
}
|
|
1808
|
+
return meta;
|
|
1809
|
+
}
|
|
1810
|
+
function resolveStaticOpenApi(route, routeModule, loadShared) {
|
|
1556
1811
|
let hidden = false;
|
|
1812
|
+
const tags = [];
|
|
1813
|
+
const meta = {};
|
|
1814
|
+
const apply = (value, isVerb) => {
|
|
1815
|
+
if (value === false) {
|
|
1816
|
+
hidden = true;
|
|
1817
|
+
return;
|
|
1818
|
+
}
|
|
1819
|
+
if (value === true) {
|
|
1820
|
+
hidden = false;
|
|
1821
|
+
return;
|
|
1822
|
+
}
|
|
1823
|
+
if (!value) {
|
|
1824
|
+
return;
|
|
1825
|
+
}
|
|
1826
|
+
if (typeof value.hidden === "boolean") {
|
|
1827
|
+
hidden = value.hidden;
|
|
1828
|
+
}
|
|
1829
|
+
if (value.tags) {
|
|
1830
|
+
tags.push(...value.tags);
|
|
1831
|
+
}
|
|
1832
|
+
if (typeof value.summary === "string") {
|
|
1833
|
+
meta.summary = value.summary;
|
|
1834
|
+
}
|
|
1835
|
+
if (typeof value.description === "string") {
|
|
1836
|
+
meta.description = value.description;
|
|
1837
|
+
}
|
|
1838
|
+
if (typeof value.deprecated === "boolean") {
|
|
1839
|
+
meta.deprecated = value.deprecated;
|
|
1840
|
+
}
|
|
1841
|
+
if (isVerb && typeof value.operationId === "string") {
|
|
1842
|
+
meta.operationId = value.operationId;
|
|
1843
|
+
}
|
|
1844
|
+
};
|
|
1557
1845
|
for (const file of route.sharedFiles) {
|
|
1558
|
-
const
|
|
1559
|
-
if (
|
|
1560
|
-
|
|
1846
|
+
const shared = loadShared(file);
|
|
1847
|
+
if (!shared) {
|
|
1848
|
+
return void 0;
|
|
1849
|
+
}
|
|
1850
|
+
apply(shared.openapi, false);
|
|
1851
|
+
}
|
|
1852
|
+
apply(routeModule.openapi, true);
|
|
1853
|
+
if (tags.length > 0) {
|
|
1854
|
+
meta.tags = [...new Set(tags)];
|
|
1855
|
+
}
|
|
1856
|
+
return { hidden, meta };
|
|
1857
|
+
}
|
|
1858
|
+
function extractStaticMeta(route, routeModule, loadShared) {
|
|
1859
|
+
const openapi = resolveStaticOpenApi(route, routeModule, loadShared);
|
|
1860
|
+
if (!openapi) {
|
|
1861
|
+
return void 0;
|
|
1862
|
+
}
|
|
1863
|
+
const meta = {};
|
|
1864
|
+
if (openapi.hidden) {
|
|
1865
|
+
meta.hidden = true;
|
|
1866
|
+
}
|
|
1867
|
+
if (Object.keys(openapi.meta).length > 0) {
|
|
1868
|
+
meta.openapi = openapi.meta;
|
|
1869
|
+
}
|
|
1870
|
+
return meta;
|
|
1871
|
+
}
|
|
1872
|
+
function extractRuntimeSharedMeta(route, routeModule, loadShared) {
|
|
1873
|
+
const meta = {};
|
|
1874
|
+
const security = collectSecurity(route, {}, loadShared);
|
|
1875
|
+
const { hidden, meta: openapi } = resolveOpenApi(route, { openapi: routeModule.openapi }, loadShared);
|
|
1876
|
+
if (security) {
|
|
1877
|
+
meta.security = security;
|
|
1878
|
+
}
|
|
1879
|
+
if (hidden) {
|
|
1880
|
+
meta.hidden = true;
|
|
1881
|
+
}
|
|
1882
|
+
if (Object.keys(openapi).length > 0) {
|
|
1883
|
+
meta.openapi = openapi;
|
|
1884
|
+
}
|
|
1885
|
+
return meta;
|
|
1886
|
+
}
|
|
1887
|
+
function resolveOpenApi(route, routeModule, loadShared) {
|
|
1888
|
+
let hidden = false;
|
|
1889
|
+
const tags = [];
|
|
1890
|
+
const meta = {};
|
|
1891
|
+
const apply = (value, isVerb) => {
|
|
1892
|
+
if (value === false) {
|
|
1893
|
+
hidden = true;
|
|
1894
|
+
return;
|
|
1895
|
+
}
|
|
1896
|
+
if (value === true) {
|
|
1897
|
+
hidden = false;
|
|
1898
|
+
return;
|
|
1899
|
+
}
|
|
1900
|
+
if (!value || typeof value !== "object") {
|
|
1901
|
+
return;
|
|
1902
|
+
}
|
|
1903
|
+
const o = value;
|
|
1904
|
+
if ("hidden" in o) {
|
|
1905
|
+
hidden = Boolean(o.hidden);
|
|
1906
|
+
}
|
|
1907
|
+
if (Array.isArray(o.tags)) {
|
|
1908
|
+
tags.push(...o.tags.filter((tag) => typeof tag === "string"));
|
|
1909
|
+
}
|
|
1910
|
+
if (typeof o.summary === "string") {
|
|
1911
|
+
meta.summary = o.summary;
|
|
1561
1912
|
}
|
|
1913
|
+
if (typeof o.description === "string") {
|
|
1914
|
+
meta.description = o.description;
|
|
1915
|
+
}
|
|
1916
|
+
if (typeof o.deprecated === "boolean") {
|
|
1917
|
+
meta.deprecated = o.deprecated;
|
|
1918
|
+
}
|
|
1919
|
+
if (isVerb && typeof o.operationId === "string") {
|
|
1920
|
+
meta.operationId = o.operationId;
|
|
1921
|
+
}
|
|
1922
|
+
};
|
|
1923
|
+
for (const file of route.sharedFiles) {
|
|
1924
|
+
apply(loadShared(file).openapi, false);
|
|
1925
|
+
}
|
|
1926
|
+
apply(routeModule.openapi, true);
|
|
1927
|
+
if (tags.length > 0) {
|
|
1928
|
+
meta.tags = [...new Set(tags)];
|
|
1562
1929
|
}
|
|
1563
|
-
|
|
1564
|
-
return verb ?? hidden;
|
|
1930
|
+
return { hidden, meta };
|
|
1565
1931
|
}
|
|
1566
1932
|
function collectSecurity(route, routeModule, loadShared) {
|
|
1567
1933
|
const skipInherited = Boolean(
|
|
@@ -1593,6 +1959,33 @@ function collectSecurity(route, routeModule, loadShared) {
|
|
|
1593
1959
|
}
|
|
1594
1960
|
async function extractRouteMeta(config, paths, routes) {
|
|
1595
1961
|
const byFile = /* @__PURE__ */ new Map();
|
|
1962
|
+
const remainingRoutes = [];
|
|
1963
|
+
const runtimeSharedRoutes = [];
|
|
1964
|
+
const staticCache = /* @__PURE__ */ new Map();
|
|
1965
|
+
const loadStatic = (file) => {
|
|
1966
|
+
if (!staticCache.has(file)) {
|
|
1967
|
+
staticCache.set(file, readStaticModuleMeta(file));
|
|
1968
|
+
}
|
|
1969
|
+
return staticCache.get(file);
|
|
1970
|
+
};
|
|
1971
|
+
for (const route of routes) {
|
|
1972
|
+
const routeModule = loadStatic(route.file);
|
|
1973
|
+
if (!routeModule) {
|
|
1974
|
+
remainingRoutes.push(route);
|
|
1975
|
+
continue;
|
|
1976
|
+
}
|
|
1977
|
+
const meta = extractStaticMeta(route, routeModule, loadStatic);
|
|
1978
|
+
if (meta) {
|
|
1979
|
+
if (meta.hidden || meta.openapi) {
|
|
1980
|
+
byFile.set(route.file, meta);
|
|
1981
|
+
}
|
|
1982
|
+
continue;
|
|
1983
|
+
}
|
|
1984
|
+
runtimeSharedRoutes.push({ route, routeModule });
|
|
1985
|
+
}
|
|
1986
|
+
if (remainingRoutes.length === 0 && runtimeSharedRoutes.length === 0) {
|
|
1987
|
+
return byFile;
|
|
1988
|
+
}
|
|
1596
1989
|
const { unregister } = await safeRegister();
|
|
1597
1990
|
const unregisterAlias = registerAliasResolver(config.alias, paths.cwd);
|
|
1598
1991
|
const sharedCache = /* @__PURE__ */ new Map();
|
|
@@ -1607,13 +2000,19 @@ async function extractRouteMeta(config, paths, routes) {
|
|
|
1607
2000
|
return sharedCache.get(file);
|
|
1608
2001
|
};
|
|
1609
2002
|
try {
|
|
1610
|
-
for (const route of
|
|
2003
|
+
for (const { route, routeModule } of runtimeSharedRoutes) {
|
|
2004
|
+
const meta = extractRuntimeSharedMeta(route, routeModule, loadShared);
|
|
2005
|
+
if (meta.input || meta.security || meta.hidden || meta.openapi) {
|
|
2006
|
+
byFile.set(route.file, meta);
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
for (const route of remainingRoutes) {
|
|
1611
2010
|
try {
|
|
1612
2011
|
const routeModule = loadModule2(route.file);
|
|
1613
2012
|
const meta = {};
|
|
1614
2013
|
const input = readInput(routeModule);
|
|
1615
2014
|
const security = collectSecurity(route, routeModule, loadShared);
|
|
1616
|
-
const hidden =
|
|
2015
|
+
const { hidden, meta: openapi } = resolveOpenApi(route, routeModule, loadShared);
|
|
1617
2016
|
if (input) {
|
|
1618
2017
|
meta.input = input;
|
|
1619
2018
|
}
|
|
@@ -1623,7 +2022,10 @@ async function extractRouteMeta(config, paths, routes) {
|
|
|
1623
2022
|
if (hidden) {
|
|
1624
2023
|
meta.hidden = true;
|
|
1625
2024
|
}
|
|
1626
|
-
if (
|
|
2025
|
+
if (Object.keys(openapi).length > 0) {
|
|
2026
|
+
meta.openapi = openapi;
|
|
2027
|
+
}
|
|
2028
|
+
if (meta.input || meta.security || meta.hidden || meta.openapi) {
|
|
1627
2029
|
byFile.set(route.file, meta);
|
|
1628
2030
|
}
|
|
1629
2031
|
} catch {
|
|
@@ -1705,10 +2107,11 @@ async function typeFolders(paths, routes) {
|
|
|
1705
2107
|
verbsByDir.set(key, list);
|
|
1706
2108
|
}
|
|
1707
2109
|
const dirs = await scanRouteFolders(paths.routesDir);
|
|
2110
|
+
const sharedCache = /* @__PURE__ */ new Map();
|
|
1708
2111
|
return dirs.map((dir) => ({
|
|
1709
2112
|
dir,
|
|
1710
2113
|
params: routeParamsForDir(paths.routesDir, dir),
|
|
1711
|
-
sharedFiles: sharedFilesForDir(paths.routesDir, dir),
|
|
2114
|
+
sharedFiles: sharedFilesForDir(paths.routesDir, dir, sharedCache),
|
|
1712
2115
|
verbs: verbsByDir.get(slash(dir)) ?? []
|
|
1713
2116
|
}));
|
|
1714
2117
|
}
|
|
@@ -1721,24 +2124,63 @@ async function extractResponses(paths, routes) {
|
|
|
1721
2124
|
const { createSchemaProgram: createSchemaProgram2, extractRouteResponses: extractRouteResponses2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
|
|
1722
2125
|
const files = [...new Set(routes.map((route) => route.file))];
|
|
1723
2126
|
const appTypes = (0, import_node_path11.join)(paths.outDir, "types", "app.d.ts");
|
|
1724
|
-
const
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
);
|
|
2127
|
+
const roots = (0, import_node_fs7.existsSync)(appTypes) ? [...files, appTypes] : files;
|
|
2128
|
+
const program = createSchemaProgram2(paths, roots, { lean: true });
|
|
2129
|
+
const fallbackFiles = [];
|
|
1728
2130
|
for (const file of files) {
|
|
1729
|
-
|
|
2131
|
+
const responses = extractRouteResponses2(program, file);
|
|
2132
|
+
byFile.set(file, responses);
|
|
2133
|
+
if (hasLooseResponseSchema(responses)) {
|
|
2134
|
+
fallbackFiles.push(file);
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
if (fallbackFiles.length > 0) {
|
|
2138
|
+
const fullProgram = createSchemaProgram2(
|
|
2139
|
+
paths,
|
|
2140
|
+
(0, import_node_fs7.existsSync)(appTypes) ? [...fallbackFiles, appTypes] : fallbackFiles
|
|
2141
|
+
);
|
|
2142
|
+
for (const file of fallbackFiles) {
|
|
2143
|
+
byFile.set(file, extractRouteResponses2(fullProgram, file));
|
|
2144
|
+
}
|
|
1730
2145
|
}
|
|
1731
2146
|
} catch (error) {
|
|
1732
2147
|
console.warn(`giri: skipped response schema generation (${error.message}).`);
|
|
1733
2148
|
}
|
|
1734
2149
|
return byFile;
|
|
1735
2150
|
}
|
|
2151
|
+
function isLooseSchema(value) {
|
|
2152
|
+
if (!value || typeof value !== "object") {
|
|
2153
|
+
return false;
|
|
2154
|
+
}
|
|
2155
|
+
const schema = value;
|
|
2156
|
+
const keys = Object.keys(schema);
|
|
2157
|
+
if (keys.length === 0) {
|
|
2158
|
+
return true;
|
|
2159
|
+
}
|
|
2160
|
+
if (typeof schema.$ref === "string") {
|
|
2161
|
+
return false;
|
|
2162
|
+
}
|
|
2163
|
+
if (Array.isArray(schema.anyOf) && schema.anyOf.some(isLooseSchema)) {
|
|
2164
|
+
return true;
|
|
2165
|
+
}
|
|
2166
|
+
if (schema.items && isLooseSchema(schema.items)) {
|
|
2167
|
+
return true;
|
|
2168
|
+
}
|
|
2169
|
+
if (schema.properties && typeof schema.properties === "object") {
|
|
2170
|
+
return Object.values(schema.properties).some(isLooseSchema);
|
|
2171
|
+
}
|
|
2172
|
+
return false;
|
|
2173
|
+
}
|
|
2174
|
+
function hasLooseResponseSchema(responses) {
|
|
2175
|
+
return responses.responses.some((response) => isLooseSchema(response.schema));
|
|
2176
|
+
}
|
|
1736
2177
|
async function extractMeta(config, paths, routes) {
|
|
1737
2178
|
const inputsByFile = /* @__PURE__ */ new Map();
|
|
1738
2179
|
const securityByFile = /* @__PURE__ */ new Map();
|
|
1739
2180
|
const hiddenFiles = /* @__PURE__ */ new Set();
|
|
2181
|
+
const openapiByFile = /* @__PURE__ */ new Map();
|
|
1740
2182
|
if (routes.length === 0) {
|
|
1741
|
-
return { inputsByFile, securityByFile, hiddenFiles };
|
|
2183
|
+
return { inputsByFile, securityByFile, hiddenFiles, openapiByFile };
|
|
1742
2184
|
}
|
|
1743
2185
|
try {
|
|
1744
2186
|
const meta = await extractRouteMeta(config, paths, routes);
|
|
@@ -1752,15 +2194,19 @@ async function extractMeta(config, paths, routes) {
|
|
|
1752
2194
|
if (entry.hidden) {
|
|
1753
2195
|
hiddenFiles.add(file);
|
|
1754
2196
|
}
|
|
2197
|
+
if (entry.openapi) {
|
|
2198
|
+
openapiByFile.set(file, entry.openapi);
|
|
2199
|
+
}
|
|
1755
2200
|
}
|
|
1756
2201
|
} catch (error) {
|
|
1757
2202
|
console.warn(`giri: skipped input/security generation (${error.message}).`);
|
|
1758
2203
|
}
|
|
1759
|
-
return { inputsByFile, securityByFile, hiddenFiles };
|
|
2204
|
+
return { inputsByFile, securityByFile, hiddenFiles, openapiByFile };
|
|
1760
2205
|
}
|
|
1761
2206
|
async function syncProject(config, options = {}) {
|
|
1762
2207
|
const paths = resolveGiriPaths(config, options.cwd);
|
|
1763
2208
|
assertSafeOutDir(paths);
|
|
2209
|
+
const hadOutDir = (0, import_node_fs7.existsSync)(paths.outDir);
|
|
1764
2210
|
const routes = await scanRoutes(paths.routesDir);
|
|
1765
2211
|
const folders = await typeFolders(paths, routes);
|
|
1766
2212
|
await (0, import_promises3.mkdir)(paths.outDir, { recursive: true });
|
|
@@ -1769,39 +2215,41 @@ async function syncProject(config, options = {}) {
|
|
|
1769
2215
|
await writeAppTypes(paths);
|
|
1770
2216
|
await writeTsConfig(paths, config);
|
|
1771
2217
|
const responsesByFile = await extractResponses(paths, routes);
|
|
1772
|
-
const { inputsByFile, securityByFile, hiddenFiles } = await extractMeta(config, paths, routes);
|
|
1773
|
-
const data = { responsesByFile, inputsByFile, securityByFile, hiddenFiles };
|
|
2218
|
+
const { inputsByFile, securityByFile, hiddenFiles, openapiByFile } = await extractMeta(config, paths, routes);
|
|
2219
|
+
const data = { responsesByFile, inputsByFile, securityByFile, hiddenFiles, openapiByFile };
|
|
1774
2220
|
await writeManifest(paths, routes, data);
|
|
1775
2221
|
await writeOpenApi(paths, routes, data);
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
2222
|
+
if (hadOutDir) {
|
|
2223
|
+
await pruneDir(
|
|
2224
|
+
paths.outDir,
|
|
2225
|
+
/* @__PURE__ */ new Set([
|
|
2226
|
+
(0, import_node_path11.join)(paths.outDir, "tsconfig.json"),
|
|
2227
|
+
(0, import_node_path11.join)(paths.outDir, "manifest.json"),
|
|
2228
|
+
(0, import_node_path11.join)(paths.outDir, "openapi.json"),
|
|
2229
|
+
(0, import_node_path11.join)(paths.outDir, "routes.d.ts"),
|
|
2230
|
+
(0, import_node_path11.join)(paths.outDir, "types", "app.d.ts"),
|
|
2231
|
+
...folders.map((folder) => typeFilePath(paths, folder.dir))
|
|
2232
|
+
])
|
|
2233
|
+
);
|
|
2234
|
+
}
|
|
1787
2235
|
return { paths, routes, folders, data };
|
|
1788
2236
|
}
|
|
1789
2237
|
|
|
1790
2238
|
// src/generator/watch.ts
|
|
1791
|
-
var
|
|
2239
|
+
var import_node_fs8 = require("fs");
|
|
1792
2240
|
var import_node_path13 = require("path");
|
|
1793
2241
|
|
|
1794
2242
|
// src/loader/module-loader.ts
|
|
1795
2243
|
var import_node_path12 = require("path");
|
|
1796
2244
|
|
|
1797
2245
|
// src/lifecycle.ts
|
|
1798
|
-
var
|
|
2246
|
+
var import_node_fs9 = require("fs");
|
|
1799
2247
|
var import_node_path14 = require("path");
|
|
1800
2248
|
var MAIN_EXTENSIONS2 = ["ts", "tsx", "mts", "cts", "js", "jsx", "mjs", "cjs"];
|
|
1801
2249
|
function resolveMainFile(cwd) {
|
|
1802
2250
|
for (const ext of MAIN_EXTENSIONS2) {
|
|
1803
2251
|
const file = (0, import_node_path14.join)(cwd, "src", `main.${ext}`);
|
|
1804
|
-
if ((0,
|
|
2252
|
+
if ((0, import_node_fs9.existsSync)(file)) {
|
|
1805
2253
|
return file;
|
|
1806
2254
|
}
|
|
1807
2255
|
}
|