@boon4681/giri 0.0.2 → 0.0.3
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 +25 -4
- package/dist/adapters/hono.js +171 -8
- package/dist/adapters/hono.js.map +1 -1
- package/dist/cli.js +862 -234
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +24 -4
- package/dist/index.js +649 -122
- package/dist/index.js.map +1 -1
- package/dist/{types-DkrKD1S4.d.ts → types-BvRph0mx.d.ts} +92 -2
- 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/cli.js
CHANGED
|
@@ -46,12 +46,18 @@ var init_es5 = __esm({
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
// src/generator/schema/program.ts
|
|
49
|
-
function
|
|
49
|
+
function leanOptions(options) {
|
|
50
|
+
return {
|
|
51
|
+
...options,
|
|
52
|
+
types: []
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function createSchemaProgram(paths, routeFiles, programOptions = {}) {
|
|
50
56
|
let options = { ...DEFAULT_OPTIONS };
|
|
51
|
-
const configPath =
|
|
57
|
+
const configPath = import_typescript2.default.findConfigFile(paths.cwd, import_typescript2.default.sys.fileExists, "tsconfig.json");
|
|
52
58
|
if (configPath) {
|
|
53
|
-
const parsed =
|
|
54
|
-
...
|
|
59
|
+
const parsed = import_typescript2.default.getParsedCommandLineOfConfigFile(configPath, {}, {
|
|
60
|
+
...import_typescript2.default.sys,
|
|
55
61
|
onUnRecoverableConfigFileDiagnostic: () => {
|
|
56
62
|
}
|
|
57
63
|
});
|
|
@@ -59,17 +65,20 @@ function createSchemaProgram(paths, routeFiles) {
|
|
|
59
65
|
options = { ...parsed.options, noEmit: true };
|
|
60
66
|
}
|
|
61
67
|
}
|
|
62
|
-
|
|
68
|
+
if (programOptions.lean) {
|
|
69
|
+
options = leanOptions(options);
|
|
70
|
+
}
|
|
71
|
+
return import_typescript2.default.createProgram(routeFiles, options);
|
|
63
72
|
}
|
|
64
|
-
var
|
|
73
|
+
var import_typescript2, DEFAULT_OPTIONS;
|
|
65
74
|
var init_program = __esm({
|
|
66
75
|
"src/generator/schema/program.ts"() {
|
|
67
76
|
"use strict";
|
|
68
|
-
|
|
77
|
+
import_typescript2 = __toESM(require("typescript"));
|
|
69
78
|
DEFAULT_OPTIONS = {
|
|
70
|
-
target:
|
|
71
|
-
module:
|
|
72
|
-
moduleResolution:
|
|
79
|
+
target: import_typescript2.default.ScriptTarget.ES2022,
|
|
80
|
+
module: import_typescript2.default.ModuleKind.NodeNext,
|
|
81
|
+
moduleResolution: import_typescript2.default.ModuleResolutionKind.NodeNext,
|
|
73
82
|
strict: true,
|
|
74
83
|
skipLibCheck: true,
|
|
75
84
|
noEmit: true
|
|
@@ -103,7 +112,7 @@ function literalValuesOf(types) {
|
|
|
103
112
|
for (const member of types) {
|
|
104
113
|
if (member.isStringLiteral() || member.isNumberLiteral()) {
|
|
105
114
|
values.push(member.value);
|
|
106
|
-
} else if (member.flags &
|
|
115
|
+
} else if (member.flags & import_typescript3.default.TypeFlags.BooleanLiteral) {
|
|
107
116
|
values.push(intrinsicName(member) === "true");
|
|
108
117
|
} else {
|
|
109
118
|
return void 0;
|
|
@@ -112,7 +121,7 @@ function literalValuesOf(types) {
|
|
|
112
121
|
return values;
|
|
113
122
|
}
|
|
114
123
|
function walkUnion(type, ctx) {
|
|
115
|
-
const flag =
|
|
124
|
+
const flag = import_typescript3.default.TypeFlags.Undefined | import_typescript3.default.TypeFlags.Void | import_typescript3.default.TypeFlags.Never;
|
|
116
125
|
const members = type.types.filter((member) => !(member.flags & flag));
|
|
117
126
|
if (members.length === 1) {
|
|
118
127
|
return walkType(members[0], ctx);
|
|
@@ -125,13 +134,13 @@ function walkUnion(type, ctx) {
|
|
|
125
134
|
}
|
|
126
135
|
function buildObjectSchema(type, ctx) {
|
|
127
136
|
const { checker } = ctx;
|
|
128
|
-
const indexInfo = checker.getIndexInfoOfType(type,
|
|
137
|
+
const indexInfo = checker.getIndexInfoOfType(type, import_typescript3.default.IndexKind.String) ?? checker.getIndexInfoOfType(type, import_typescript3.default.IndexKind.Number);
|
|
129
138
|
const properties = {};
|
|
130
139
|
const required = [];
|
|
131
140
|
for (const symbol of checker.getPropertiesOfType(type)) {
|
|
132
141
|
const name = symbol.getName();
|
|
133
142
|
const propType = checker.getTypeOfSymbolAtLocation(symbol, ctx.location);
|
|
134
|
-
const optional = Boolean(symbol.getFlags() &
|
|
143
|
+
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));
|
|
135
144
|
properties[name] = walkType(propType, ctx);
|
|
136
145
|
if (!optional) {
|
|
137
146
|
required.push(name);
|
|
@@ -190,16 +199,16 @@ function walkObject(type, ctx) {
|
|
|
190
199
|
}
|
|
191
200
|
function walkType(type, ctx) {
|
|
192
201
|
const flags = type.flags;
|
|
193
|
-
if (flags & (
|
|
202
|
+
if (flags & (import_typescript3.default.TypeFlags.Any | import_typescript3.default.TypeFlags.Unknown)) {
|
|
194
203
|
return {};
|
|
195
204
|
}
|
|
196
|
-
if (flags &
|
|
205
|
+
if (flags & import_typescript3.default.TypeFlags.Null) {
|
|
197
206
|
return { type: "null" };
|
|
198
207
|
}
|
|
199
|
-
if (flags & (
|
|
208
|
+
if (flags & (import_typescript3.default.TypeFlags.Undefined | import_typescript3.default.TypeFlags.Void)) {
|
|
200
209
|
return {};
|
|
201
210
|
}
|
|
202
|
-
if (flags & (
|
|
211
|
+
if (flags & (import_typescript3.default.TypeFlags.BigInt | import_typescript3.default.TypeFlags.BigIntLiteral)) {
|
|
203
212
|
ctx.warnings.push("bigint is not JSON-serializable (JSON.stringify throws); documented as string.");
|
|
204
213
|
return { type: "string" };
|
|
205
214
|
}
|
|
@@ -209,45 +218,45 @@ function walkType(type, ctx) {
|
|
|
209
218
|
if (type.isNumberLiteral()) {
|
|
210
219
|
return { type: "number", const: type.value };
|
|
211
220
|
}
|
|
212
|
-
if (flags &
|
|
221
|
+
if (flags & import_typescript3.default.TypeFlags.BooleanLiteral) {
|
|
213
222
|
return { type: "boolean", const: intrinsicName(type) === "true" };
|
|
214
223
|
}
|
|
215
|
-
if (flags &
|
|
224
|
+
if (flags & import_typescript3.default.TypeFlags.String) {
|
|
216
225
|
return { type: "string" };
|
|
217
226
|
}
|
|
218
|
-
if (flags &
|
|
227
|
+
if (flags & import_typescript3.default.TypeFlags.Number) {
|
|
219
228
|
return { type: "number" };
|
|
220
229
|
}
|
|
221
|
-
if (flags &
|
|
230
|
+
if (flags & import_typescript3.default.TypeFlags.Boolean) {
|
|
222
231
|
return { type: "boolean" };
|
|
223
232
|
}
|
|
224
233
|
if (type.isUnion()) {
|
|
225
234
|
return walkUnion(type, ctx);
|
|
226
235
|
}
|
|
227
|
-
if (flags &
|
|
236
|
+
if (flags & import_typescript3.default.TypeFlags.Object || type.isIntersection()) {
|
|
228
237
|
return walkObject(type, ctx);
|
|
229
238
|
}
|
|
230
239
|
return {};
|
|
231
240
|
}
|
|
232
|
-
var
|
|
241
|
+
var import_typescript3;
|
|
233
242
|
var init_json_schema = __esm({
|
|
234
243
|
"src/generator/schema/json-schema.ts"() {
|
|
235
244
|
"use strict";
|
|
236
|
-
|
|
245
|
+
import_typescript3 = __toESM(require("typescript"));
|
|
237
246
|
}
|
|
238
247
|
});
|
|
239
248
|
|
|
240
249
|
// src/generator/schema/responses.ts
|
|
241
250
|
function findHandleFunction(source) {
|
|
242
251
|
let found;
|
|
243
|
-
const isExported = (node) =>
|
|
252
|
+
const isExported = (node) => import_typescript4.default.canHaveModifiers(node) && (import_typescript4.default.getModifiers(node)?.some((m) => m.kind === import_typescript4.default.SyntaxKind.ExportKeyword) ?? false);
|
|
244
253
|
for (const statement of source.statements) {
|
|
245
|
-
if (
|
|
254
|
+
if (import_typescript4.default.isFunctionDeclaration(statement) && statement.name?.text === "handle" && isExported(statement)) {
|
|
246
255
|
found = statement;
|
|
247
256
|
}
|
|
248
|
-
if (
|
|
257
|
+
if (import_typescript4.default.isVariableStatement(statement) && isExported(statement)) {
|
|
249
258
|
for (const declaration of statement.declarationList.declarations) {
|
|
250
|
-
if (
|
|
259
|
+
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))) {
|
|
251
260
|
found = declaration.initializer;
|
|
252
261
|
}
|
|
253
262
|
}
|
|
@@ -256,7 +265,7 @@ function findHandleFunction(source) {
|
|
|
256
265
|
return found;
|
|
257
266
|
}
|
|
258
267
|
function collectReturnExpressions(fn) {
|
|
259
|
-
if (
|
|
268
|
+
if (import_typescript4.default.isArrowFunction(fn) && !import_typescript4.default.isBlock(fn.body)) {
|
|
260
269
|
return [fn.body];
|
|
261
270
|
}
|
|
262
271
|
if (!fn.body) {
|
|
@@ -264,15 +273,15 @@ function collectReturnExpressions(fn) {
|
|
|
264
273
|
}
|
|
265
274
|
const expressions = [];
|
|
266
275
|
const visit = (node) => {
|
|
267
|
-
if (
|
|
276
|
+
if (import_typescript4.default.isFunctionDeclaration(node) || import_typescript4.default.isFunctionExpression(node) || import_typescript4.default.isArrowFunction(node)) {
|
|
268
277
|
return;
|
|
269
278
|
}
|
|
270
|
-
if (
|
|
279
|
+
if (import_typescript4.default.isReturnStatement(node) && node.expression) {
|
|
271
280
|
expressions.push(node.expression);
|
|
272
281
|
}
|
|
273
|
-
|
|
282
|
+
import_typescript4.default.forEachChild(node, visit);
|
|
274
283
|
};
|
|
275
|
-
|
|
284
|
+
import_typescript4.default.forEachChild(fn.body, visit);
|
|
276
285
|
return expressions;
|
|
277
286
|
}
|
|
278
287
|
function propertyType(checker, type, name, location) {
|
|
@@ -284,15 +293,21 @@ function isTypedResponse(checker, type) {
|
|
|
284
293
|
checker.getPropertyOfType(type, "data") && checker.getPropertyOfType(type, "status") && checker.getPropertyOfType(type, "format")
|
|
285
294
|
);
|
|
286
295
|
}
|
|
287
|
-
function
|
|
288
|
-
|
|
296
|
+
function firstParameterName(fn) {
|
|
297
|
+
const [first] = fn.parameters;
|
|
298
|
+
return first && import_typescript4.default.isIdentifier(first.name) ? first.name.text : void 0;
|
|
299
|
+
}
|
|
300
|
+
function readFromCall(checker, expression, contextName) {
|
|
301
|
+
if (!import_typescript4.default.isCallExpression(expression) || !import_typescript4.default.isPropertyAccessExpression(expression.expression)) {
|
|
289
302
|
return void 0;
|
|
290
303
|
}
|
|
291
304
|
const method = expression.expression.name.text;
|
|
292
305
|
if (method !== "json" && method !== "text") {
|
|
293
306
|
return void 0;
|
|
294
307
|
}
|
|
295
|
-
|
|
308
|
+
const target = expression.expression.expression;
|
|
309
|
+
const directContextCall = contextName && import_typescript4.default.isIdentifier(target) && target.text === contextName;
|
|
310
|
+
if (!directContextCall && !isTypedResponse(checker, checker.getTypeAtLocation(expression))) {
|
|
296
311
|
return void 0;
|
|
297
312
|
}
|
|
298
313
|
const [dataArg, statusArg] = expression.arguments;
|
|
@@ -332,6 +347,7 @@ function extractRouteResponses(program, file) {
|
|
|
332
347
|
return result;
|
|
333
348
|
}
|
|
334
349
|
const ctx = createWalkContext(checker, fn);
|
|
350
|
+
const contextName = firstParameterName(fn);
|
|
335
351
|
const byStatus = /* @__PURE__ */ new Map();
|
|
336
352
|
const record = (hit) => {
|
|
337
353
|
const schema = walkType(hit.data, ctx);
|
|
@@ -340,7 +356,7 @@ function extractRouteResponses(program, file) {
|
|
|
340
356
|
byStatus.set(hit.status, bucket);
|
|
341
357
|
};
|
|
342
358
|
for (const expression of collectReturnExpressions(fn)) {
|
|
343
|
-
const fromCall = readFromCall(checker, expression);
|
|
359
|
+
const fromCall = readFromCall(checker, expression, contextName);
|
|
344
360
|
if (fromCall) {
|
|
345
361
|
record(fromCall);
|
|
346
362
|
continue;
|
|
@@ -366,11 +382,11 @@ function extractRouteResponses(program, file) {
|
|
|
366
382
|
result.$defs = ctx.defs;
|
|
367
383
|
return result;
|
|
368
384
|
}
|
|
369
|
-
var
|
|
385
|
+
var import_typescript4;
|
|
370
386
|
var init_responses = __esm({
|
|
371
387
|
"src/generator/schema/responses.ts"() {
|
|
372
388
|
"use strict";
|
|
373
|
-
|
|
389
|
+
import_typescript4 = __toESM(require("typescript"));
|
|
374
390
|
init_json_schema();
|
|
375
391
|
}
|
|
376
392
|
});
|
|
@@ -394,9 +410,9 @@ var init_schema = __esm({
|
|
|
394
410
|
|
|
395
411
|
// src/cli.ts
|
|
396
412
|
var import_node_child_process = require("child_process");
|
|
397
|
-
var
|
|
413
|
+
var import_node_fs11 = require("fs");
|
|
398
414
|
var import_promises4 = require("fs/promises");
|
|
399
|
-
var
|
|
415
|
+
var import_node_path16 = require("path");
|
|
400
416
|
var prompts = __toESM(require("@clack/prompts"));
|
|
401
417
|
|
|
402
418
|
// src/app.ts
|
|
@@ -422,7 +438,8 @@ var configSchema = import_typebox.Type.Object({
|
|
|
422
438
|
port: import_typebox.Type.Optional(import_typebox.Type.Number()),
|
|
423
439
|
hostname: import_typebox.Type.Optional(import_typebox.Type.String())
|
|
424
440
|
}, { additionalProperties: false })),
|
|
425
|
-
errorSchema: import_typebox.Type.Optional(import_typebox.Type.Any())
|
|
441
|
+
errorSchema: import_typebox.Type.Optional(import_typebox.Type.Any()),
|
|
442
|
+
cookieSecret: import_typebox.Type.Optional(import_typebox.Type.String())
|
|
426
443
|
}, { additionalProperties: false });
|
|
427
444
|
|
|
428
445
|
// src/loader/loader.ts
|
|
@@ -461,29 +478,44 @@ var safeRegister = async () => {
|
|
|
461
478
|
await assertES5(res.unregister);
|
|
462
479
|
return res;
|
|
463
480
|
};
|
|
464
|
-
var
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
(0, import_node_process.exit)(1);
|
|
481
|
+
var findConfigPath = (cwd = (0, import_node_path.resolve)()) => {
|
|
482
|
+
for (const name of ["giri.config.ts", "giri.config.js"]) {
|
|
483
|
+
const path = (0, import_node_path.resolve)(cwd, name);
|
|
484
|
+
if ((0, import_node_fs.existsSync)(path)) {
|
|
485
|
+
return path;
|
|
486
|
+
}
|
|
471
487
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
488
|
+
return void 0;
|
|
489
|
+
};
|
|
490
|
+
var load = async (opts = {}) => {
|
|
491
|
+
const fail = (message) => {
|
|
492
|
+
if (opts.throwOnError) {
|
|
493
|
+
throw new Error(message);
|
|
494
|
+
}
|
|
495
|
+
import_prompts.log.error(message);
|
|
475
496
|
(0, import_node_process.exit)(1);
|
|
497
|
+
};
|
|
498
|
+
const path = findConfigPath();
|
|
499
|
+
if (!path) {
|
|
500
|
+
fail("Config file not found.");
|
|
476
501
|
}
|
|
477
502
|
const { unregister } = await safeRegister();
|
|
478
|
-
|
|
479
|
-
|
|
503
|
+
let content;
|
|
504
|
+
try {
|
|
505
|
+
const required = require(`${path}`);
|
|
506
|
+
content = required.default ?? required;
|
|
507
|
+
} finally {
|
|
508
|
+
}
|
|
480
509
|
unregister();
|
|
481
510
|
const res = import_value.Value.Check(configSchema, content);
|
|
482
511
|
if (!res) {
|
|
483
|
-
|
|
484
|
-
|
|
512
|
+
const messages = [...import_value.Value.Errors(configSchema, content)].map((error) => error.message);
|
|
513
|
+
if (!opts.throwOnError) {
|
|
514
|
+
for (const message of messages) {
|
|
515
|
+
import_prompts.log.error(message);
|
|
516
|
+
}
|
|
485
517
|
}
|
|
486
|
-
(
|
|
518
|
+
fail(messages.join("\n"));
|
|
487
519
|
}
|
|
488
520
|
return content;
|
|
489
521
|
};
|
|
@@ -510,13 +542,18 @@ function methodFromFile(fileName) {
|
|
|
510
542
|
const stem = fileName.replace(/\.(?:[cm]?[jt]s|[jt]sx)$/, "").toLowerCase();
|
|
511
543
|
return METHOD_FROM_FILE.get(stem);
|
|
512
544
|
}
|
|
513
|
-
function sharedFileIn(dir) {
|
|
545
|
+
function sharedFileIn(dir, cache) {
|
|
546
|
+
if (cache?.has(dir)) {
|
|
547
|
+
return cache.get(dir);
|
|
548
|
+
}
|
|
514
549
|
for (const ext of ["ts", "tsx", "js", "jsx", "mjs", "cjs", "mts", "cts"]) {
|
|
515
550
|
const file = (0, import_node_path2.join)(dir, `+shared.${ext}`);
|
|
516
551
|
if ((0, import_node_fs2.existsSync)(file)) {
|
|
552
|
+
cache?.set(dir, file);
|
|
517
553
|
return file;
|
|
518
554
|
}
|
|
519
555
|
}
|
|
556
|
+
cache?.set(dir, void 0);
|
|
520
557
|
return void 0;
|
|
521
558
|
}
|
|
522
559
|
function physicalRouteSegments(routesDir, routeDir) {
|
|
@@ -585,7 +622,7 @@ async function scanRouteFolders(routesDir) {
|
|
|
585
622
|
function routeParamsForDir(routesDir, dir) {
|
|
586
623
|
return pathFromSegments(physicalRouteSegments(routesDir, dir)).params;
|
|
587
624
|
}
|
|
588
|
-
function sharedFilesForDir(routesDir, dir) {
|
|
625
|
+
function sharedFilesForDir(routesDir, dir, cache) {
|
|
589
626
|
const segments = physicalRouteSegments(routesDir, dir);
|
|
590
627
|
const dirs = [routesDir];
|
|
591
628
|
let current = routesDir;
|
|
@@ -593,7 +630,7 @@ function sharedFilesForDir(routesDir, dir) {
|
|
|
593
630
|
current = (0, import_node_path2.join)(current, segment);
|
|
594
631
|
dirs.push(current);
|
|
595
632
|
}
|
|
596
|
-
return dirs.map(sharedFileIn).filter((file) => Boolean(file));
|
|
633
|
+
return dirs.map((currentDir) => sharedFileIn(currentDir, cache)).filter((file) => Boolean(file));
|
|
597
634
|
}
|
|
598
635
|
async function scanRoutes(routesDir) {
|
|
599
636
|
if (!(0, import_node_fs2.existsSync)(routesDir)) {
|
|
@@ -605,6 +642,7 @@ async function scanRoutes(routesDir) {
|
|
|
605
642
|
onlyFiles: true
|
|
606
643
|
});
|
|
607
644
|
const routes = [];
|
|
645
|
+
const sharedCache = /* @__PURE__ */ new Map();
|
|
608
646
|
for (const file of files) {
|
|
609
647
|
const method = methodFromFile((0, import_node_path2.basename)(file));
|
|
610
648
|
if (!method) {
|
|
@@ -620,7 +658,7 @@ async function scanRoutes(routesDir) {
|
|
|
620
658
|
routeDir,
|
|
621
659
|
routeSegments,
|
|
622
660
|
params,
|
|
623
|
-
sharedFiles: sharedFilesForDir(routesDir, routeDir)
|
|
661
|
+
sharedFiles: sharedFilesForDir(routesDir, routeDir, sharedCache)
|
|
624
662
|
});
|
|
625
663
|
}
|
|
626
664
|
return routes.sort((left, right) => {
|
|
@@ -649,9 +687,11 @@ function isGiriBodySchema(value) {
|
|
|
649
687
|
}
|
|
650
688
|
|
|
651
689
|
// src/app.ts
|
|
652
|
-
function loadModule(file) {
|
|
690
|
+
function loadModule(file, force = true) {
|
|
653
691
|
const resolved = require.resolve(file);
|
|
654
|
-
|
|
692
|
+
if (force) {
|
|
693
|
+
delete require.cache[resolved];
|
|
694
|
+
}
|
|
655
695
|
return require(resolved);
|
|
656
696
|
}
|
|
657
697
|
function interopDefault(value) {
|
|
@@ -713,20 +753,13 @@ function resolveAliasTarget(cwd, target, capture = "") {
|
|
|
713
753
|
}
|
|
714
754
|
function matchAlias(request, key) {
|
|
715
755
|
if (key.includes("*")) {
|
|
716
|
-
const [
|
|
717
|
-
if (request.startsWith(
|
|
718
|
-
return request.slice(
|
|
756
|
+
const [prefix, suffix = ""] = key.split("*");
|
|
757
|
+
if (request.startsWith(prefix) && request.endsWith(suffix)) {
|
|
758
|
+
return request.slice(prefix.length, request.length - suffix.length);
|
|
719
759
|
}
|
|
720
760
|
return void 0;
|
|
721
761
|
}
|
|
722
|
-
|
|
723
|
-
return "";
|
|
724
|
-
}
|
|
725
|
-
const prefix = `${key}/`;
|
|
726
|
-
if (request.startsWith(prefix)) {
|
|
727
|
-
return request.slice(prefix.length);
|
|
728
|
-
}
|
|
729
|
-
return void 0;
|
|
762
|
+
return request === key ? "" : void 0;
|
|
730
763
|
}
|
|
731
764
|
function resolveAliasRequest(request, alias, cwd) {
|
|
732
765
|
for (const [key, value] of Object.entries(alias ?? {})) {
|
|
@@ -793,13 +826,23 @@ async function buildGiriApp(config, options = {}) {
|
|
|
793
826
|
const { unregister } = await safeRegister();
|
|
794
827
|
const unregisterAliasResolver = registerAliasResolver(config.alias, paths.cwd);
|
|
795
828
|
try {
|
|
829
|
+
const dirty = options.dirty;
|
|
830
|
+
const forceReload = dirty === void 0;
|
|
831
|
+
const isDirty = (file) => forceReload || dirty.has(file);
|
|
832
|
+
const sharedCache = /* @__PURE__ */ new Map();
|
|
833
|
+
const loadShared = (file) => {
|
|
834
|
+
if (!sharedCache.has(file)) {
|
|
835
|
+
sharedCache.set(file, loadModule(file, isDirty(file)));
|
|
836
|
+
}
|
|
837
|
+
return sharedCache.get(file);
|
|
838
|
+
};
|
|
796
839
|
for (const route of routes) {
|
|
797
|
-
const routeModule = loadModule(route.file);
|
|
840
|
+
const routeModule = loadModule(route.file, isDirty(route.file));
|
|
798
841
|
if (typeof routeModule.handle !== "function") {
|
|
799
842
|
throw new Error(`${route.file} must export a named handle function.`);
|
|
800
843
|
}
|
|
801
844
|
const folderMiddleware = routeModule.config?.skipInherited ? [] : route.sharedFiles.flatMap(
|
|
802
|
-
(file) => normalizeMiddleware(
|
|
845
|
+
(file) => normalizeMiddleware(loadShared(file).middleware, file)
|
|
803
846
|
);
|
|
804
847
|
const verbMiddleware = normalizeMiddleware(routeModule.middleware, route.file);
|
|
805
848
|
config.adapter.register(app, {
|
|
@@ -808,7 +851,8 @@ async function buildGiriApp(config, options = {}) {
|
|
|
808
851
|
handle: routeModule.handle,
|
|
809
852
|
middleware: [...folderMiddleware, ...verbMiddleware],
|
|
810
853
|
input: routeInput(routeModule, route.file),
|
|
811
|
-
services: options.services
|
|
854
|
+
services: options.services,
|
|
855
|
+
cookieSecret: config.cookieSecret
|
|
812
856
|
});
|
|
813
857
|
}
|
|
814
858
|
} finally {
|
|
@@ -819,7 +863,7 @@ async function buildGiriApp(config, options = {}) {
|
|
|
819
863
|
}
|
|
820
864
|
|
|
821
865
|
// src/generator/sync.ts
|
|
822
|
-
var
|
|
866
|
+
var import_node_fs7 = require("fs");
|
|
823
867
|
var import_promises3 = require("fs/promises");
|
|
824
868
|
var import_node_path11 = require("path");
|
|
825
869
|
|
|
@@ -1073,6 +1117,7 @@ function buildOpenApiDocument(paths, routes, data = {}) {
|
|
|
1073
1117
|
const documentPaths = {};
|
|
1074
1118
|
const schemas = {};
|
|
1075
1119
|
const securitySchemes = {};
|
|
1120
|
+
const tagOrder = [];
|
|
1076
1121
|
for (const route of routes) {
|
|
1077
1122
|
if (data.hiddenFiles?.has(route.file)) {
|
|
1078
1123
|
continue;
|
|
@@ -1080,10 +1125,32 @@ function buildOpenApiDocument(paths, routes, data = {}) {
|
|
|
1080
1125
|
const responses = data.responsesByFile?.get(route.file);
|
|
1081
1126
|
const input = data.inputsByFile?.get(route.file);
|
|
1082
1127
|
const security = data.securityByFile?.get(route.file);
|
|
1128
|
+
const meta = data.openapiByFile?.get(route.file);
|
|
1083
1129
|
for (const [name, schema] of Object.entries(responses?.$defs ?? {})) {
|
|
1084
1130
|
schemas[name] = rewriteRefs(schema);
|
|
1085
1131
|
}
|
|
1086
|
-
const operation = {
|
|
1132
|
+
const operation = {};
|
|
1133
|
+
if (meta?.tags && meta.tags.length > 0) {
|
|
1134
|
+
operation.tags = meta.tags;
|
|
1135
|
+
for (const tag2 of meta.tags) {
|
|
1136
|
+
if (!tagOrder.includes(tag2)) {
|
|
1137
|
+
tagOrder.push(tag2);
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
if (meta?.summary) {
|
|
1142
|
+
operation.summary = meta.summary;
|
|
1143
|
+
}
|
|
1144
|
+
if (meta?.description) {
|
|
1145
|
+
operation.description = meta.description;
|
|
1146
|
+
}
|
|
1147
|
+
if (meta?.operationId) {
|
|
1148
|
+
operation.operationId = meta.operationId;
|
|
1149
|
+
}
|
|
1150
|
+
if (meta?.deprecated) {
|
|
1151
|
+
operation.deprecated = true;
|
|
1152
|
+
}
|
|
1153
|
+
operation.responses = buildResponses(responses?.responses ?? []);
|
|
1087
1154
|
const parameters = [...pathParameters(route), ...queryParameters(input?.query)];
|
|
1088
1155
|
if (parameters.length > 0) {
|
|
1089
1156
|
operation.parameters = parameters;
|
|
@@ -1115,6 +1182,9 @@ function buildOpenApiDocument(paths, routes, data = {}) {
|
|
|
1115
1182
|
info: readProjectInfo(paths.cwd),
|
|
1116
1183
|
paths: documentPaths
|
|
1117
1184
|
};
|
|
1185
|
+
if (tagOrder.length > 0) {
|
|
1186
|
+
document.tags = tagOrder.map((name) => ({ name }));
|
|
1187
|
+
}
|
|
1118
1188
|
const components = {};
|
|
1119
1189
|
if (Object.keys(schemas).length > 0) {
|
|
1120
1190
|
components.schemas = schemas;
|
|
@@ -1146,14 +1216,29 @@ function paramsType(params) {
|
|
|
1146
1216
|
${fields}
|
|
1147
1217
|
}`;
|
|
1148
1218
|
}
|
|
1149
|
-
function
|
|
1150
|
-
|
|
1151
|
-
|
|
1219
|
+
function middlewareVarsType(typesDir, sharedFile) {
|
|
1220
|
+
const spec = JSON.stringify(moduleSpecifier(typesDir, sharedFile));
|
|
1221
|
+
return `(typeof import(${spec}) extends { middleware: infer M } ? import("@boon4681/giri").InferStackVars<M> : {})`;
|
|
1222
|
+
}
|
|
1223
|
+
function ownSharedFile(dir, sharedFiles) {
|
|
1224
|
+
for (let index = sharedFiles.length - 1; index >= 0; index -= 1) {
|
|
1225
|
+
if ((0, import_node_path8.dirname)(sharedFiles[index]) === dir) {
|
|
1226
|
+
return sharedFiles[index];
|
|
1227
|
+
}
|
|
1152
1228
|
}
|
|
1153
|
-
return
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1229
|
+
return void 0;
|
|
1230
|
+
}
|
|
1231
|
+
function varsType(paths, file, dir, sharedFiles) {
|
|
1232
|
+
const typesDir = (0, import_node_path8.dirname)(file);
|
|
1233
|
+
const parts = [];
|
|
1234
|
+
if (dir !== paths.routesDir) {
|
|
1235
|
+
parts.push(`import(${JSON.stringify(importPath(file, typeFilePath(paths, (0, import_node_path8.dirname)(dir))))}).Vars`);
|
|
1236
|
+
}
|
|
1237
|
+
const ownShared = ownSharedFile(dir, sharedFiles);
|
|
1238
|
+
if (ownShared) {
|
|
1239
|
+
parts.push(middlewareVarsType(typesDir, ownShared));
|
|
1240
|
+
}
|
|
1241
|
+
return parts.length > 0 ? parts.join("\n & ") : "{}";
|
|
1157
1242
|
}
|
|
1158
1243
|
function methodExports(typesDir, verbs) {
|
|
1159
1244
|
return verbs.map(({ method, file }) => {
|
|
@@ -1164,14 +1249,14 @@ function methodExports(typesDir, verbs) {
|
|
|
1164
1249
|
});
|
|
1165
1250
|
}
|
|
1166
1251
|
async function writeParamTypes(paths, folders) {
|
|
1167
|
-
|
|
1252
|
+
await Promise.all(folders.map(({ dir, params, sharedFiles, verbs }) => {
|
|
1168
1253
|
const file = typeFilePath(paths, dir);
|
|
1169
1254
|
const typesDir = (0, import_node_path8.dirname)(file);
|
|
1170
1255
|
const lines = [
|
|
1171
1256
|
GENERATED_HEADER,
|
|
1172
1257
|
`export type Params = ${paramsType(params)};`,
|
|
1173
1258
|
"export type RouteParams = Params;",
|
|
1174
|
-
`type Vars = ${varsType(
|
|
1259
|
+
`export type Vars = ${varsType(paths, file, dir, sharedFiles)};`,
|
|
1175
1260
|
"export type Middleware<Injects extends Record<string, unknown> = {}> =",
|
|
1176
1261
|
' import("@boon4681/giri").Middleware<Params, import("@boon4681/giri").ValidatedInput, Injects>;',
|
|
1177
1262
|
'export type Handle<Input extends import("@boon4681/giri").ValidatedInput = import("@boon4681/giri").ValidatedInput> =',
|
|
@@ -1181,10 +1266,14 @@ async function writeParamTypes(paths, folders) {
|
|
|
1181
1266
|
lines.push(...methodExports(typesDir, verbs));
|
|
1182
1267
|
}
|
|
1183
1268
|
lines.push("");
|
|
1184
|
-
|
|
1185
|
-
}
|
|
1269
|
+
return writeGenerated(file, lines.join("\n"));
|
|
1270
|
+
}));
|
|
1186
1271
|
}
|
|
1187
1272
|
|
|
1273
|
+
// src/generator/route-meta.ts
|
|
1274
|
+
var import_node_fs6 = require("fs");
|
|
1275
|
+
var import_typescript = __toESM(require("typescript"));
|
|
1276
|
+
|
|
1188
1277
|
// src/generator/inputs.ts
|
|
1189
1278
|
function sanitize(schema) {
|
|
1190
1279
|
const { $schema, ...rest } = schema;
|
|
@@ -1245,28 +1334,315 @@ function readInput(routeModule) {
|
|
|
1245
1334
|
}
|
|
1246
1335
|
return input.body || input.query ? input : void 0;
|
|
1247
1336
|
}
|
|
1248
|
-
function
|
|
1249
|
-
|
|
1337
|
+
function hasExportModifier(node) {
|
|
1338
|
+
return import_typescript.default.canHaveModifiers(node) && (import_typescript.default.getModifiers(node)?.some((modifier) => modifier.kind === import_typescript.default.SyntaxKind.ExportKeyword) ?? false);
|
|
1339
|
+
}
|
|
1340
|
+
function unwrapExpression(expression) {
|
|
1341
|
+
let current = expression;
|
|
1342
|
+
while (import_typescript.default.isParenthesizedExpression(current) || import_typescript.default.isAsExpression(current) || import_typescript.default.isSatisfiesExpression(current)) {
|
|
1343
|
+
current = current.expression;
|
|
1344
|
+
}
|
|
1345
|
+
return current;
|
|
1346
|
+
}
|
|
1347
|
+
function staticBoolean(expression) {
|
|
1348
|
+
const value = unwrapExpression(expression);
|
|
1349
|
+
if (value.kind === import_typescript.default.SyntaxKind.TrueKeyword) {
|
|
1250
1350
|
return true;
|
|
1251
1351
|
}
|
|
1252
|
-
if (value ===
|
|
1352
|
+
if (value.kind === import_typescript.default.SyntaxKind.FalseKeyword) {
|
|
1253
1353
|
return false;
|
|
1254
1354
|
}
|
|
1255
|
-
|
|
1256
|
-
|
|
1355
|
+
return void 0;
|
|
1356
|
+
}
|
|
1357
|
+
function staticString(expression) {
|
|
1358
|
+
const value = unwrapExpression(expression);
|
|
1359
|
+
return import_typescript.default.isStringLiteralLike(value) ? value.text : void 0;
|
|
1360
|
+
}
|
|
1361
|
+
function staticStringArray(expression) {
|
|
1362
|
+
const value = unwrapExpression(expression);
|
|
1363
|
+
if (!import_typescript.default.isArrayLiteralExpression(value)) {
|
|
1364
|
+
return void 0;
|
|
1365
|
+
}
|
|
1366
|
+
const strings = [];
|
|
1367
|
+
for (const element of value.elements) {
|
|
1368
|
+
const string = staticString(element);
|
|
1369
|
+
if (string === void 0) {
|
|
1370
|
+
return void 0;
|
|
1371
|
+
}
|
|
1372
|
+
strings.push(string);
|
|
1373
|
+
}
|
|
1374
|
+
return strings;
|
|
1375
|
+
}
|
|
1376
|
+
function propertyName(name) {
|
|
1377
|
+
if (import_typescript.default.isIdentifier(name) || import_typescript.default.isStringLiteral(name) || import_typescript.default.isNumericLiteral(name)) {
|
|
1378
|
+
return name.text;
|
|
1257
1379
|
}
|
|
1258
1380
|
return void 0;
|
|
1259
1381
|
}
|
|
1260
|
-
function
|
|
1382
|
+
function collectImportedNames(source) {
|
|
1383
|
+
const names = /* @__PURE__ */ new Set();
|
|
1384
|
+
for (const statement of source.statements) {
|
|
1385
|
+
if (!import_typescript.default.isImportDeclaration(statement)) {
|
|
1386
|
+
continue;
|
|
1387
|
+
}
|
|
1388
|
+
const clause = statement.importClause;
|
|
1389
|
+
if (!clause) {
|
|
1390
|
+
continue;
|
|
1391
|
+
}
|
|
1392
|
+
if (clause.name) {
|
|
1393
|
+
names.add(clause.name.text);
|
|
1394
|
+
}
|
|
1395
|
+
const bindings = clause.namedBindings;
|
|
1396
|
+
if (bindings && import_typescript.default.isNamespaceImport(bindings)) {
|
|
1397
|
+
names.add(bindings.name.text);
|
|
1398
|
+
}
|
|
1399
|
+
if (bindings && import_typescript.default.isNamedImports(bindings)) {
|
|
1400
|
+
for (const element of bindings.elements) {
|
|
1401
|
+
names.add(element.name.text);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
return names;
|
|
1406
|
+
}
|
|
1407
|
+
function expressionReferencesImportedMiddleware(expression, importedNames) {
|
|
1408
|
+
let found = false;
|
|
1409
|
+
const allowedImportedHelpers = /* @__PURE__ */ new Set(["stack", "fromHono"]);
|
|
1410
|
+
const visit = (node) => {
|
|
1411
|
+
if (found) {
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
if (import_typescript.default.isIdentifier(node) && importedNames.has(node.text) && !allowedImportedHelpers.has(node.text)) {
|
|
1415
|
+
found = true;
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
1418
|
+
import_typescript.default.forEachChild(node, visit);
|
|
1419
|
+
};
|
|
1420
|
+
visit(expression);
|
|
1421
|
+
return found;
|
|
1422
|
+
}
|
|
1423
|
+
function parseStaticOpenApi(expression) {
|
|
1424
|
+
const value = unwrapExpression(expression);
|
|
1425
|
+
const boolean = staticBoolean(value);
|
|
1426
|
+
if (boolean !== void 0) {
|
|
1427
|
+
return boolean;
|
|
1428
|
+
}
|
|
1429
|
+
if (!import_typescript.default.isObjectLiteralExpression(value)) {
|
|
1430
|
+
return void 0;
|
|
1431
|
+
}
|
|
1432
|
+
const openapi = {};
|
|
1433
|
+
for (const property of value.properties) {
|
|
1434
|
+
if (!import_typescript.default.isPropertyAssignment(property)) {
|
|
1435
|
+
return void 0;
|
|
1436
|
+
}
|
|
1437
|
+
const name = propertyName(property.name);
|
|
1438
|
+
if (!name) {
|
|
1439
|
+
return void 0;
|
|
1440
|
+
}
|
|
1441
|
+
if (name === "hidden") {
|
|
1442
|
+
const hidden = staticBoolean(property.initializer);
|
|
1443
|
+
if (hidden === void 0) {
|
|
1444
|
+
return void 0;
|
|
1445
|
+
}
|
|
1446
|
+
openapi.hidden = hidden;
|
|
1447
|
+
} else if (name === "tags") {
|
|
1448
|
+
const tags = staticStringArray(property.initializer);
|
|
1449
|
+
if (!tags) {
|
|
1450
|
+
return void 0;
|
|
1451
|
+
}
|
|
1452
|
+
openapi.tags = tags;
|
|
1453
|
+
} else if (name === "summary" || name === "description" || name === "operationId") {
|
|
1454
|
+
const string = staticString(property.initializer);
|
|
1455
|
+
if (string === void 0) {
|
|
1456
|
+
return void 0;
|
|
1457
|
+
}
|
|
1458
|
+
openapi[name] = string;
|
|
1459
|
+
} else if (name === "deprecated") {
|
|
1460
|
+
const deprecated = staticBoolean(property.initializer);
|
|
1461
|
+
if (deprecated === void 0) {
|
|
1462
|
+
return void 0;
|
|
1463
|
+
}
|
|
1464
|
+
openapi.deprecated = deprecated;
|
|
1465
|
+
} else {
|
|
1466
|
+
return void 0;
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
return openapi;
|
|
1470
|
+
}
|
|
1471
|
+
function readStaticModuleMeta(file) {
|
|
1472
|
+
let source;
|
|
1473
|
+
try {
|
|
1474
|
+
source = import_typescript.default.createSourceFile(file, (0, import_node_fs6.readFileSync)(file, "utf8"), import_typescript.default.ScriptTarget.Latest, true);
|
|
1475
|
+
} catch {
|
|
1476
|
+
return void 0;
|
|
1477
|
+
}
|
|
1478
|
+
const importedNames = collectImportedNames(source);
|
|
1479
|
+
const sourceText = source.getFullText();
|
|
1480
|
+
const canSkipMiddlewareRuntime = !sourceText.includes("defineMiddleware") && !sourceText.includes(".openapi");
|
|
1481
|
+
const meta = { middlewareSecurity: false };
|
|
1482
|
+
for (const statement of source.statements) {
|
|
1483
|
+
if (import_typescript.default.isImportDeclaration(statement) || import_typescript.default.isInterfaceDeclaration(statement) || import_typescript.default.isTypeAliasDeclaration(statement) || import_typescript.default.isEmptyStatement(statement) || !hasExportModifier(statement)) {
|
|
1484
|
+
continue;
|
|
1485
|
+
}
|
|
1486
|
+
if (import_typescript.default.isFunctionDeclaration(statement) && statement.name?.text === "handle") {
|
|
1487
|
+
continue;
|
|
1488
|
+
}
|
|
1489
|
+
if (import_typescript.default.isVariableStatement(statement)) {
|
|
1490
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
1491
|
+
if (!import_typescript.default.isIdentifier(declaration.name)) {
|
|
1492
|
+
return void 0;
|
|
1493
|
+
}
|
|
1494
|
+
const name = declaration.name.text;
|
|
1495
|
+
if (name === "openapi") {
|
|
1496
|
+
if (!declaration.initializer) {
|
|
1497
|
+
return void 0;
|
|
1498
|
+
}
|
|
1499
|
+
const openapi = parseStaticOpenApi(declaration.initializer);
|
|
1500
|
+
if (openapi === void 0) {
|
|
1501
|
+
return void 0;
|
|
1502
|
+
}
|
|
1503
|
+
meta.openapi = openapi;
|
|
1504
|
+
} else if (name === "handle") {
|
|
1505
|
+
if (!declaration.initializer || !import_typescript.default.isArrowFunction(declaration.initializer) && !import_typescript.default.isFunctionExpression(declaration.initializer)) {
|
|
1506
|
+
return void 0;
|
|
1507
|
+
}
|
|
1508
|
+
continue;
|
|
1509
|
+
} else if (name === "middleware") {
|
|
1510
|
+
if (!declaration.initializer || !canSkipMiddlewareRuntime || expressionReferencesImportedMiddleware(declaration.initializer, importedNames)) {
|
|
1511
|
+
return void 0;
|
|
1512
|
+
}
|
|
1513
|
+
meta.middlewareSecurity = false;
|
|
1514
|
+
continue;
|
|
1515
|
+
} else {
|
|
1516
|
+
return void 0;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
continue;
|
|
1520
|
+
}
|
|
1521
|
+
return void 0;
|
|
1522
|
+
}
|
|
1523
|
+
return meta;
|
|
1524
|
+
}
|
|
1525
|
+
function resolveStaticOpenApi(route, routeModule, loadShared) {
|
|
1261
1526
|
let hidden = false;
|
|
1527
|
+
const tags = [];
|
|
1528
|
+
const meta = {};
|
|
1529
|
+
const apply = (value, isVerb) => {
|
|
1530
|
+
if (value === false) {
|
|
1531
|
+
hidden = true;
|
|
1532
|
+
return;
|
|
1533
|
+
}
|
|
1534
|
+
if (value === true) {
|
|
1535
|
+
hidden = false;
|
|
1536
|
+
return;
|
|
1537
|
+
}
|
|
1538
|
+
if (!value) {
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
if (typeof value.hidden === "boolean") {
|
|
1542
|
+
hidden = value.hidden;
|
|
1543
|
+
}
|
|
1544
|
+
if (value.tags) {
|
|
1545
|
+
tags.push(...value.tags);
|
|
1546
|
+
}
|
|
1547
|
+
if (typeof value.summary === "string") {
|
|
1548
|
+
meta.summary = value.summary;
|
|
1549
|
+
}
|
|
1550
|
+
if (typeof value.description === "string") {
|
|
1551
|
+
meta.description = value.description;
|
|
1552
|
+
}
|
|
1553
|
+
if (typeof value.deprecated === "boolean") {
|
|
1554
|
+
meta.deprecated = value.deprecated;
|
|
1555
|
+
}
|
|
1556
|
+
if (isVerb && typeof value.operationId === "string") {
|
|
1557
|
+
meta.operationId = value.operationId;
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1262
1560
|
for (const file of route.sharedFiles) {
|
|
1263
|
-
const
|
|
1264
|
-
if (
|
|
1265
|
-
|
|
1561
|
+
const shared = loadShared(file);
|
|
1562
|
+
if (!shared) {
|
|
1563
|
+
return void 0;
|
|
1266
1564
|
}
|
|
1565
|
+
apply(shared.openapi, false);
|
|
1267
1566
|
}
|
|
1268
|
-
|
|
1269
|
-
|
|
1567
|
+
apply(routeModule.openapi, true);
|
|
1568
|
+
if (tags.length > 0) {
|
|
1569
|
+
meta.tags = [...new Set(tags)];
|
|
1570
|
+
}
|
|
1571
|
+
return { hidden, meta };
|
|
1572
|
+
}
|
|
1573
|
+
function extractStaticMeta(route, routeModule, loadShared) {
|
|
1574
|
+
const openapi = resolveStaticOpenApi(route, routeModule, loadShared);
|
|
1575
|
+
if (!openapi) {
|
|
1576
|
+
return void 0;
|
|
1577
|
+
}
|
|
1578
|
+
const meta = {};
|
|
1579
|
+
if (openapi.hidden) {
|
|
1580
|
+
meta.hidden = true;
|
|
1581
|
+
}
|
|
1582
|
+
if (Object.keys(openapi.meta).length > 0) {
|
|
1583
|
+
meta.openapi = openapi.meta;
|
|
1584
|
+
}
|
|
1585
|
+
return meta;
|
|
1586
|
+
}
|
|
1587
|
+
function extractRuntimeSharedMeta(route, routeModule, loadShared) {
|
|
1588
|
+
const meta = {};
|
|
1589
|
+
const security = collectSecurity(route, {}, loadShared);
|
|
1590
|
+
const { hidden, meta: openapi } = resolveOpenApi(route, { openapi: routeModule.openapi }, loadShared);
|
|
1591
|
+
if (security) {
|
|
1592
|
+
meta.security = security;
|
|
1593
|
+
}
|
|
1594
|
+
if (hidden) {
|
|
1595
|
+
meta.hidden = true;
|
|
1596
|
+
}
|
|
1597
|
+
if (Object.keys(openapi).length > 0) {
|
|
1598
|
+
meta.openapi = openapi;
|
|
1599
|
+
}
|
|
1600
|
+
return meta;
|
|
1601
|
+
}
|
|
1602
|
+
function resolveOpenApi(route, routeModule, loadShared) {
|
|
1603
|
+
let hidden = false;
|
|
1604
|
+
const tags = [];
|
|
1605
|
+
const meta = {};
|
|
1606
|
+
const apply = (value, isVerb) => {
|
|
1607
|
+
if (value === false) {
|
|
1608
|
+
hidden = true;
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
if (value === true) {
|
|
1612
|
+
hidden = false;
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
if (!value || typeof value !== "object") {
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
const o = value;
|
|
1619
|
+
if ("hidden" in o) {
|
|
1620
|
+
hidden = Boolean(o.hidden);
|
|
1621
|
+
}
|
|
1622
|
+
if (Array.isArray(o.tags)) {
|
|
1623
|
+
tags.push(...o.tags.filter((tag2) => typeof tag2 === "string"));
|
|
1624
|
+
}
|
|
1625
|
+
if (typeof o.summary === "string") {
|
|
1626
|
+
meta.summary = o.summary;
|
|
1627
|
+
}
|
|
1628
|
+
if (typeof o.description === "string") {
|
|
1629
|
+
meta.description = o.description;
|
|
1630
|
+
}
|
|
1631
|
+
if (typeof o.deprecated === "boolean") {
|
|
1632
|
+
meta.deprecated = o.deprecated;
|
|
1633
|
+
}
|
|
1634
|
+
if (isVerb && typeof o.operationId === "string") {
|
|
1635
|
+
meta.operationId = o.operationId;
|
|
1636
|
+
}
|
|
1637
|
+
};
|
|
1638
|
+
for (const file of route.sharedFiles) {
|
|
1639
|
+
apply(loadShared(file).openapi, false);
|
|
1640
|
+
}
|
|
1641
|
+
apply(routeModule.openapi, true);
|
|
1642
|
+
if (tags.length > 0) {
|
|
1643
|
+
meta.tags = [...new Set(tags)];
|
|
1644
|
+
}
|
|
1645
|
+
return { hidden, meta };
|
|
1270
1646
|
}
|
|
1271
1647
|
function collectSecurity(route, routeModule, loadShared) {
|
|
1272
1648
|
const skipInherited = Boolean(
|
|
@@ -1298,6 +1674,33 @@ function collectSecurity(route, routeModule, loadShared) {
|
|
|
1298
1674
|
}
|
|
1299
1675
|
async function extractRouteMeta(config, paths, routes) {
|
|
1300
1676
|
const byFile = /* @__PURE__ */ new Map();
|
|
1677
|
+
const remainingRoutes = [];
|
|
1678
|
+
const runtimeSharedRoutes = [];
|
|
1679
|
+
const staticCache = /* @__PURE__ */ new Map();
|
|
1680
|
+
const loadStatic = (file) => {
|
|
1681
|
+
if (!staticCache.has(file)) {
|
|
1682
|
+
staticCache.set(file, readStaticModuleMeta(file));
|
|
1683
|
+
}
|
|
1684
|
+
return staticCache.get(file);
|
|
1685
|
+
};
|
|
1686
|
+
for (const route of routes) {
|
|
1687
|
+
const routeModule = loadStatic(route.file);
|
|
1688
|
+
if (!routeModule) {
|
|
1689
|
+
remainingRoutes.push(route);
|
|
1690
|
+
continue;
|
|
1691
|
+
}
|
|
1692
|
+
const meta = extractStaticMeta(route, routeModule, loadStatic);
|
|
1693
|
+
if (meta) {
|
|
1694
|
+
if (meta.hidden || meta.openapi) {
|
|
1695
|
+
byFile.set(route.file, meta);
|
|
1696
|
+
}
|
|
1697
|
+
continue;
|
|
1698
|
+
}
|
|
1699
|
+
runtimeSharedRoutes.push({ route, routeModule });
|
|
1700
|
+
}
|
|
1701
|
+
if (remainingRoutes.length === 0 && runtimeSharedRoutes.length === 0) {
|
|
1702
|
+
return byFile;
|
|
1703
|
+
}
|
|
1301
1704
|
const { unregister } = await safeRegister();
|
|
1302
1705
|
const unregisterAlias = registerAliasResolver(config.alias, paths.cwd);
|
|
1303
1706
|
const sharedCache = /* @__PURE__ */ new Map();
|
|
@@ -1312,13 +1715,19 @@ async function extractRouteMeta(config, paths, routes) {
|
|
|
1312
1715
|
return sharedCache.get(file);
|
|
1313
1716
|
};
|
|
1314
1717
|
try {
|
|
1315
|
-
for (const route of
|
|
1718
|
+
for (const { route, routeModule } of runtimeSharedRoutes) {
|
|
1719
|
+
const meta = extractRuntimeSharedMeta(route, routeModule, loadShared);
|
|
1720
|
+
if (meta.input || meta.security || meta.hidden || meta.openapi) {
|
|
1721
|
+
byFile.set(route.file, meta);
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
for (const route of remainingRoutes) {
|
|
1316
1725
|
try {
|
|
1317
1726
|
const routeModule = loadModule2(route.file);
|
|
1318
1727
|
const meta = {};
|
|
1319
1728
|
const input = readInput(routeModule);
|
|
1320
1729
|
const security = collectSecurity(route, routeModule, loadShared);
|
|
1321
|
-
const hidden =
|
|
1730
|
+
const { hidden, meta: openapi } = resolveOpenApi(route, routeModule, loadShared);
|
|
1322
1731
|
if (input) {
|
|
1323
1732
|
meta.input = input;
|
|
1324
1733
|
}
|
|
@@ -1328,7 +1737,10 @@ async function extractRouteMeta(config, paths, routes) {
|
|
|
1328
1737
|
if (hidden) {
|
|
1329
1738
|
meta.hidden = true;
|
|
1330
1739
|
}
|
|
1331
|
-
if (
|
|
1740
|
+
if (Object.keys(openapi).length > 0) {
|
|
1741
|
+
meta.openapi = openapi;
|
|
1742
|
+
}
|
|
1743
|
+
if (meta.input || meta.security || meta.hidden || meta.openapi) {
|
|
1332
1744
|
byFile.set(route.file, meta);
|
|
1333
1745
|
}
|
|
1334
1746
|
} catch {
|
|
@@ -1410,10 +1822,11 @@ async function typeFolders(paths, routes) {
|
|
|
1410
1822
|
verbsByDir.set(key, list);
|
|
1411
1823
|
}
|
|
1412
1824
|
const dirs = await scanRouteFolders(paths.routesDir);
|
|
1825
|
+
const sharedCache = /* @__PURE__ */ new Map();
|
|
1413
1826
|
return dirs.map((dir) => ({
|
|
1414
1827
|
dir,
|
|
1415
1828
|
params: routeParamsForDir(paths.routesDir, dir),
|
|
1416
|
-
sharedFiles: sharedFilesForDir(paths.routesDir, dir),
|
|
1829
|
+
sharedFiles: sharedFilesForDir(paths.routesDir, dir, sharedCache),
|
|
1417
1830
|
verbs: verbsByDir.get(slash(dir)) ?? []
|
|
1418
1831
|
}));
|
|
1419
1832
|
}
|
|
@@ -1426,24 +1839,63 @@ async function extractResponses(paths, routes) {
|
|
|
1426
1839
|
const { createSchemaProgram: createSchemaProgram2, extractRouteResponses: extractRouteResponses2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
|
|
1427
1840
|
const files = [...new Set(routes.map((route) => route.file))];
|
|
1428
1841
|
const appTypes = (0, import_node_path11.join)(paths.outDir, "types", "app.d.ts");
|
|
1429
|
-
const
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
);
|
|
1842
|
+
const roots = (0, import_node_fs7.existsSync)(appTypes) ? [...files, appTypes] : files;
|
|
1843
|
+
const program = createSchemaProgram2(paths, roots, { lean: true });
|
|
1844
|
+
const fallbackFiles = [];
|
|
1433
1845
|
for (const file of files) {
|
|
1434
|
-
|
|
1846
|
+
const responses = extractRouteResponses2(program, file);
|
|
1847
|
+
byFile.set(file, responses);
|
|
1848
|
+
if (hasLooseResponseSchema(responses)) {
|
|
1849
|
+
fallbackFiles.push(file);
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
if (fallbackFiles.length > 0) {
|
|
1853
|
+
const fullProgram = createSchemaProgram2(
|
|
1854
|
+
paths,
|
|
1855
|
+
(0, import_node_fs7.existsSync)(appTypes) ? [...fallbackFiles, appTypes] : fallbackFiles
|
|
1856
|
+
);
|
|
1857
|
+
for (const file of fallbackFiles) {
|
|
1858
|
+
byFile.set(file, extractRouteResponses2(fullProgram, file));
|
|
1859
|
+
}
|
|
1435
1860
|
}
|
|
1436
1861
|
} catch (error) {
|
|
1437
1862
|
console.warn(`giri: skipped response schema generation (${error.message}).`);
|
|
1438
1863
|
}
|
|
1439
1864
|
return byFile;
|
|
1440
1865
|
}
|
|
1866
|
+
function isLooseSchema(value) {
|
|
1867
|
+
if (!value || typeof value !== "object") {
|
|
1868
|
+
return false;
|
|
1869
|
+
}
|
|
1870
|
+
const schema = value;
|
|
1871
|
+
const keys = Object.keys(schema);
|
|
1872
|
+
if (keys.length === 0) {
|
|
1873
|
+
return true;
|
|
1874
|
+
}
|
|
1875
|
+
if (typeof schema.$ref === "string") {
|
|
1876
|
+
return false;
|
|
1877
|
+
}
|
|
1878
|
+
if (Array.isArray(schema.anyOf) && schema.anyOf.some(isLooseSchema)) {
|
|
1879
|
+
return true;
|
|
1880
|
+
}
|
|
1881
|
+
if (schema.items && isLooseSchema(schema.items)) {
|
|
1882
|
+
return true;
|
|
1883
|
+
}
|
|
1884
|
+
if (schema.properties && typeof schema.properties === "object") {
|
|
1885
|
+
return Object.values(schema.properties).some(isLooseSchema);
|
|
1886
|
+
}
|
|
1887
|
+
return false;
|
|
1888
|
+
}
|
|
1889
|
+
function hasLooseResponseSchema(responses) {
|
|
1890
|
+
return responses.responses.some((response) => isLooseSchema(response.schema));
|
|
1891
|
+
}
|
|
1441
1892
|
async function extractMeta(config, paths, routes) {
|
|
1442
1893
|
const inputsByFile = /* @__PURE__ */ new Map();
|
|
1443
1894
|
const securityByFile = /* @__PURE__ */ new Map();
|
|
1444
1895
|
const hiddenFiles = /* @__PURE__ */ new Set();
|
|
1896
|
+
const openapiByFile = /* @__PURE__ */ new Map();
|
|
1445
1897
|
if (routes.length === 0) {
|
|
1446
|
-
return { inputsByFile, securityByFile, hiddenFiles };
|
|
1898
|
+
return { inputsByFile, securityByFile, hiddenFiles, openapiByFile };
|
|
1447
1899
|
}
|
|
1448
1900
|
try {
|
|
1449
1901
|
const meta = await extractRouteMeta(config, paths, routes);
|
|
@@ -1457,15 +1909,19 @@ async function extractMeta(config, paths, routes) {
|
|
|
1457
1909
|
if (entry.hidden) {
|
|
1458
1910
|
hiddenFiles.add(file);
|
|
1459
1911
|
}
|
|
1912
|
+
if (entry.openapi) {
|
|
1913
|
+
openapiByFile.set(file, entry.openapi);
|
|
1914
|
+
}
|
|
1460
1915
|
}
|
|
1461
1916
|
} catch (error) {
|
|
1462
1917
|
console.warn(`giri: skipped input/security generation (${error.message}).`);
|
|
1463
1918
|
}
|
|
1464
|
-
return { inputsByFile, securityByFile, hiddenFiles };
|
|
1919
|
+
return { inputsByFile, securityByFile, hiddenFiles, openapiByFile };
|
|
1465
1920
|
}
|
|
1466
1921
|
async function syncProject(config, options = {}) {
|
|
1467
1922
|
const paths = resolveGiriPaths(config, options.cwd);
|
|
1468
1923
|
assertSafeOutDir(paths);
|
|
1924
|
+
const hadOutDir = (0, import_node_fs7.existsSync)(paths.outDir);
|
|
1469
1925
|
const routes = await scanRoutes(paths.routesDir);
|
|
1470
1926
|
const folders = await typeFolders(paths, routes);
|
|
1471
1927
|
await (0, import_promises3.mkdir)(paths.outDir, { recursive: true });
|
|
@@ -1474,62 +1930,142 @@ async function syncProject(config, options = {}) {
|
|
|
1474
1930
|
await writeAppTypes(paths);
|
|
1475
1931
|
await writeTsConfig(paths, config);
|
|
1476
1932
|
const responsesByFile = await extractResponses(paths, routes);
|
|
1477
|
-
const { inputsByFile, securityByFile, hiddenFiles } = await extractMeta(config, paths, routes);
|
|
1478
|
-
const data = { responsesByFile, inputsByFile, securityByFile, hiddenFiles };
|
|
1933
|
+
const { inputsByFile, securityByFile, hiddenFiles, openapiByFile } = await extractMeta(config, paths, routes);
|
|
1934
|
+
const data = { responsesByFile, inputsByFile, securityByFile, hiddenFiles, openapiByFile };
|
|
1479
1935
|
await writeManifest(paths, routes, data);
|
|
1480
1936
|
await writeOpenApi(paths, routes, data);
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1937
|
+
if (hadOutDir) {
|
|
1938
|
+
await pruneDir(
|
|
1939
|
+
paths.outDir,
|
|
1940
|
+
/* @__PURE__ */ new Set([
|
|
1941
|
+
(0, import_node_path11.join)(paths.outDir, "tsconfig.json"),
|
|
1942
|
+
(0, import_node_path11.join)(paths.outDir, "manifest.json"),
|
|
1943
|
+
(0, import_node_path11.join)(paths.outDir, "openapi.json"),
|
|
1944
|
+
(0, import_node_path11.join)(paths.outDir, "routes.d.ts"),
|
|
1945
|
+
(0, import_node_path11.join)(paths.outDir, "types", "app.d.ts"),
|
|
1946
|
+
...folders.map((folder) => typeFilePath(paths, folder.dir))
|
|
1947
|
+
])
|
|
1948
|
+
);
|
|
1949
|
+
}
|
|
1492
1950
|
return { paths, routes, folders, data };
|
|
1493
1951
|
}
|
|
1494
1952
|
|
|
1495
1953
|
// src/generator/watch.ts
|
|
1496
|
-
var
|
|
1497
|
-
var
|
|
1954
|
+
var import_node_fs9 = require("fs");
|
|
1955
|
+
var import_node_path14 = require("path");
|
|
1498
1956
|
|
|
1499
|
-
// src/loader/
|
|
1957
|
+
// src/loader/import-graph.ts
|
|
1958
|
+
var import_node_fs8 = require("fs");
|
|
1500
1959
|
var import_node_path12 = require("path");
|
|
1960
|
+
var import_typescript5 = __toESM(require("typescript"));
|
|
1961
|
+
var import_tinyglobby2 = require("tinyglobby");
|
|
1962
|
+
var RESOLVE_EXTS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
1963
|
+
var JS_EXT = /\.(?:c|m)?jsx?$/;
|
|
1501
1964
|
var toSlash = (path) => path.split(import_node_path12.sep).join("/");
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
const
|
|
1965
|
+
function probeFile(base) {
|
|
1966
|
+
if ((0, import_node_fs8.existsSync)(base) && (0, import_node_fs8.statSync)(base).isFile()) {
|
|
1967
|
+
return base;
|
|
1968
|
+
}
|
|
1969
|
+
for (const ext of RESOLVE_EXTS) {
|
|
1970
|
+
const candidate = base + ext;
|
|
1971
|
+
if ((0, import_node_fs8.existsSync)(candidate)) {
|
|
1972
|
+
return candidate;
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
for (const ext of RESOLVE_EXTS) {
|
|
1976
|
+
const candidate = (0, import_node_path12.join)(base, `index${ext}`);
|
|
1977
|
+
if ((0, import_node_fs8.existsSync)(candidate)) {
|
|
1978
|
+
return candidate;
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
return void 0;
|
|
1982
|
+
}
|
|
1983
|
+
function resolveSpecifier(specifier, fromFile, alias, cwd) {
|
|
1984
|
+
let target;
|
|
1985
|
+
if (specifier.startsWith(".")) {
|
|
1986
|
+
target = (0, import_node_path12.resolve)((0, import_node_path12.dirname)(fromFile), specifier);
|
|
1987
|
+
} else {
|
|
1988
|
+
const aliased = resolveAliasRequest(specifier, alias, cwd);
|
|
1989
|
+
if (aliased === void 0) {
|
|
1990
|
+
return void 0;
|
|
1991
|
+
}
|
|
1992
|
+
target = aliased;
|
|
1993
|
+
}
|
|
1994
|
+
const resolved = probeFile(target);
|
|
1995
|
+
if (resolved) {
|
|
1996
|
+
return resolved;
|
|
1997
|
+
}
|
|
1998
|
+
if (JS_EXT.test(target)) {
|
|
1999
|
+
return probeFile(target.replace(JS_EXT, ""));
|
|
2000
|
+
}
|
|
2001
|
+
return void 0;
|
|
2002
|
+
}
|
|
2003
|
+
function importSpecifiers(file) {
|
|
2004
|
+
let source;
|
|
2005
|
+
try {
|
|
2006
|
+
source = import_typescript5.default.createSourceFile(file, (0, import_node_fs8.readFileSync)(file, "utf8"), import_typescript5.default.ScriptTarget.Latest, false);
|
|
2007
|
+
} catch {
|
|
2008
|
+
return [];
|
|
2009
|
+
}
|
|
2010
|
+
const specifiers = [];
|
|
2011
|
+
const visit = (node) => {
|
|
2012
|
+
if ((import_typescript5.default.isImportDeclaration(node) || import_typescript5.default.isExportDeclaration(node)) && node.moduleSpecifier && import_typescript5.default.isStringLiteral(node.moduleSpecifier)) {
|
|
2013
|
+
specifiers.push(node.moduleSpecifier.text);
|
|
2014
|
+
} else if (import_typescript5.default.isImportEqualsDeclaration(node) && import_typescript5.default.isExternalModuleReference(node.moduleReference) && import_typescript5.default.isStringLiteralLike(node.moduleReference.expression)) {
|
|
2015
|
+
specifiers.push(node.moduleReference.expression.text);
|
|
2016
|
+
} else if (import_typescript5.default.isCallExpression(node)) {
|
|
2017
|
+
const isRequire = import_typescript5.default.isIdentifier(node.expression) && node.expression.text === "require";
|
|
2018
|
+
const isDynamicImport = node.expression.kind === import_typescript5.default.SyntaxKind.ImportKeyword;
|
|
2019
|
+
const [first] = node.arguments;
|
|
2020
|
+
if ((isRequire || isDynamicImport) && first && import_typescript5.default.isStringLiteralLike(first)) {
|
|
2021
|
+
specifiers.push(first.text);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
import_typescript5.default.forEachChild(node, visit);
|
|
2025
|
+
};
|
|
2026
|
+
visit(source);
|
|
2027
|
+
return specifiers;
|
|
2028
|
+
}
|
|
2029
|
+
async function buildImportGraph(config, cwd) {
|
|
2030
|
+
const root = (0, import_node_path12.resolve)(cwd);
|
|
2031
|
+
const outDir = (0, import_node_path12.resolve)(root, config.outDir ?? ".giri") + import_node_path12.sep;
|
|
2032
|
+
const files = await (0, import_tinyglobby2.glob)("**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}", {
|
|
2033
|
+
cwd: root,
|
|
2034
|
+
absolute: true,
|
|
2035
|
+
onlyFiles: true,
|
|
2036
|
+
ignore: ["**/node_modules/**"]
|
|
2037
|
+
});
|
|
1507
2038
|
const importers = /* @__PURE__ */ new Map();
|
|
1508
2039
|
const nodes = /* @__PURE__ */ new Set();
|
|
1509
|
-
for (const
|
|
1510
|
-
if (
|
|
1511
|
-
continue;
|
|
1512
|
-
}
|
|
1513
|
-
const mod = require.cache[id];
|
|
1514
|
-
if (!mod) {
|
|
2040
|
+
for (const file of files) {
|
|
2041
|
+
if (file.startsWith(outDir)) {
|
|
1515
2042
|
continue;
|
|
1516
2043
|
}
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
if (!
|
|
2044
|
+
for (const specifier of importSpecifiers(file)) {
|
|
2045
|
+
const dep = resolveSpecifier(specifier, file, config.alias, root);
|
|
2046
|
+
if (!dep || dep.startsWith(outDir)) {
|
|
1520
2047
|
continue;
|
|
1521
2048
|
}
|
|
1522
|
-
|
|
1523
|
-
const
|
|
1524
|
-
|
|
2049
|
+
const from = toSlash(file);
|
|
2050
|
+
const to = toSlash(dep);
|
|
2051
|
+
nodes.add(from);
|
|
2052
|
+
nodes.add(to);
|
|
2053
|
+
let set = importers.get(to);
|
|
1525
2054
|
if (!set) {
|
|
1526
2055
|
set = /* @__PURE__ */ new Set();
|
|
1527
|
-
importers.set(
|
|
2056
|
+
importers.set(to, set);
|
|
1528
2057
|
}
|
|
1529
|
-
set.add(
|
|
2058
|
+
set.add(from);
|
|
1530
2059
|
}
|
|
1531
2060
|
}
|
|
1532
2061
|
return { importers, nodes };
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
// src/loader/module-loader.ts
|
|
2065
|
+
var import_node_path13 = require("path");
|
|
2066
|
+
var toSlash2 = (path) => path.split(import_node_path13.sep).join("/");
|
|
2067
|
+
var isProjectModule = (id, root) => {
|
|
2068
|
+
return id.startsWith(root) && !id.includes(`${import_node_path13.sep}node_modules${import_node_path13.sep}`) && !id.includes(`${import_node_path13.sep}.giri${import_node_path13.sep}`);
|
|
1533
2069
|
};
|
|
1534
2070
|
var collectDependents = (graph, start) => {
|
|
1535
2071
|
const out = /* @__PURE__ */ new Set([start]);
|
|
@@ -1547,19 +2083,27 @@ var collectDependents = (graph, start) => {
|
|
|
1547
2083
|
};
|
|
1548
2084
|
var purgeModules = (files) => {
|
|
1549
2085
|
for (const id of Object.keys(require.cache)) {
|
|
1550
|
-
if (files.has(
|
|
2086
|
+
if (files.has(toSlash2(id))) {
|
|
1551
2087
|
delete require.cache[id];
|
|
1552
2088
|
}
|
|
1553
2089
|
}
|
|
1554
2090
|
};
|
|
1555
2091
|
var purgeProjectModules = (cwd) => {
|
|
1556
|
-
const root = (0,
|
|
2092
|
+
const root = (0, import_node_path13.resolve)(cwd) + import_node_path13.sep;
|
|
1557
2093
|
for (const id of Object.keys(require.cache)) {
|
|
1558
2094
|
if (isProjectModule(id, root)) {
|
|
1559
2095
|
delete require.cache[id];
|
|
1560
2096
|
}
|
|
1561
2097
|
}
|
|
1562
2098
|
};
|
|
2099
|
+
var purgeGeneratedModules = (outDir) => {
|
|
2100
|
+
const root = (0, import_node_path13.resolve)(outDir) + import_node_path13.sep;
|
|
2101
|
+
for (const id of Object.keys(require.cache)) {
|
|
2102
|
+
if ((0, import_node_path13.resolve)(id).startsWith(root)) {
|
|
2103
|
+
delete require.cache[id];
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
};
|
|
1563
2107
|
|
|
1564
2108
|
// src/generator/watch.ts
|
|
1565
2109
|
function createWatchUpdater(config, initial) {
|
|
@@ -1574,14 +2118,16 @@ function createWatchUpdater(config, initial) {
|
|
|
1574
2118
|
data.inputsByFile = result.data.inputsByFile;
|
|
1575
2119
|
data.securityByFile = result.data.securityByFile;
|
|
1576
2120
|
data.hiddenFiles = result.data.hiddenFiles;
|
|
2121
|
+
data.openapiByFile = result.data.openapiByFile;
|
|
2122
|
+
purgeGeneratedModules(paths.outDir);
|
|
1577
2123
|
return "full";
|
|
1578
2124
|
};
|
|
1579
2125
|
const reextractRoute = async (route) => {
|
|
1580
2126
|
const key = route.file;
|
|
1581
2127
|
try {
|
|
1582
2128
|
const { createSchemaProgram: createSchemaProgram2, extractRouteResponses: extractRouteResponses2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
|
|
1583
|
-
const appTypes = (0,
|
|
1584
|
-
const program = createSchemaProgram2(paths, (0,
|
|
2129
|
+
const appTypes = (0, import_node_path14.join)(paths.outDir, "types", "app.d.ts");
|
|
2130
|
+
const program = createSchemaProgram2(paths, (0, import_node_fs9.existsSync)(appTypes) ? [key, appTypes] : [key]);
|
|
1585
2131
|
data.responsesByFile.set(key, extractRouteResponses2(program, key));
|
|
1586
2132
|
} catch {
|
|
1587
2133
|
}
|
|
@@ -1591,6 +2137,7 @@ function createWatchUpdater(config, initial) {
|
|
|
1591
2137
|
data.inputsByFile.delete(key);
|
|
1592
2138
|
data.securityByFile.delete(key);
|
|
1593
2139
|
data.hiddenFiles.delete(key);
|
|
2140
|
+
data.openapiByFile.delete(key);
|
|
1594
2141
|
if (entry?.input) {
|
|
1595
2142
|
data.inputsByFile.set(key, entry.input);
|
|
1596
2143
|
}
|
|
@@ -1600,6 +2147,9 @@ function createWatchUpdater(config, initial) {
|
|
|
1600
2147
|
if (entry?.hidden) {
|
|
1601
2148
|
data.hiddenFiles.add(key);
|
|
1602
2149
|
}
|
|
2150
|
+
if (entry?.openapi) {
|
|
2151
|
+
data.openapiByFile.set(key, entry.openapi);
|
|
2152
|
+
}
|
|
1603
2153
|
} catch {
|
|
1604
2154
|
}
|
|
1605
2155
|
};
|
|
@@ -1608,12 +2158,15 @@ function createWatchUpdater(config, initial) {
|
|
|
1608
2158
|
if (!filename) {
|
|
1609
2159
|
return fullResync();
|
|
1610
2160
|
}
|
|
1611
|
-
const abs = (0,
|
|
2161
|
+
const abs = (0, import_node_path14.resolve)((0, import_node_path14.dirname)(paths.routesDir), filename);
|
|
1612
2162
|
const file = slash(abs);
|
|
1613
|
-
if (!(0,
|
|
2163
|
+
if (!(0, import_node_fs9.existsSync)(abs)) {
|
|
1614
2164
|
return fullResync();
|
|
1615
2165
|
}
|
|
1616
|
-
|
|
2166
|
+
if ((0, import_node_fs9.statSync)(abs).isDirectory()) {
|
|
2167
|
+
return "skip";
|
|
2168
|
+
}
|
|
2169
|
+
const graph = await buildImportGraph(config, paths.cwd);
|
|
1617
2170
|
const isRoute = routes.some((candidate) => slash(candidate.file) === file);
|
|
1618
2171
|
if (!graph.nodes.has(file) && !isRoute) {
|
|
1619
2172
|
return fullResync();
|
|
@@ -1628,26 +2181,27 @@ function createWatchUpdater(config, initial) {
|
|
|
1628
2181
|
}
|
|
1629
2182
|
await writeManifest(paths, routes, data);
|
|
1630
2183
|
await writeOpenApi(paths, routes, data);
|
|
2184
|
+
purgeGeneratedModules(paths.outDir);
|
|
1631
2185
|
return "incremental";
|
|
1632
2186
|
}
|
|
1633
2187
|
};
|
|
1634
2188
|
}
|
|
1635
2189
|
|
|
1636
2190
|
// src/lifecycle.ts
|
|
1637
|
-
var
|
|
1638
|
-
var
|
|
2191
|
+
var import_node_fs10 = require("fs");
|
|
2192
|
+
var import_node_path15 = require("path");
|
|
1639
2193
|
var MAIN_EXTENSIONS2 = ["ts", "tsx", "mts", "cts", "js", "jsx", "mjs", "cjs"];
|
|
1640
2194
|
function resolveMainFile(cwd) {
|
|
1641
2195
|
for (const ext of MAIN_EXTENSIONS2) {
|
|
1642
|
-
const file = (0,
|
|
1643
|
-
if ((0,
|
|
2196
|
+
const file = (0, import_node_path15.join)(cwd, "src", `main.${ext}`);
|
|
2197
|
+
if ((0, import_node_fs10.existsSync)(file)) {
|
|
1644
2198
|
return file;
|
|
1645
2199
|
}
|
|
1646
2200
|
}
|
|
1647
2201
|
return void 0;
|
|
1648
2202
|
}
|
|
1649
2203
|
async function loadLifecycle(cwd = process.cwd()) {
|
|
1650
|
-
const file = resolveMainFile((0,
|
|
2204
|
+
const file = resolveMainFile((0, import_node_path15.resolve)(cwd));
|
|
1651
2205
|
if (!file) {
|
|
1652
2206
|
return {};
|
|
1653
2207
|
}
|
|
@@ -1805,9 +2359,9 @@ function parseFlags(args) {
|
|
|
1805
2359
|
return flags;
|
|
1806
2360
|
}
|
|
1807
2361
|
async function ensureGitignore(cwd) {
|
|
1808
|
-
const file = (0,
|
|
2362
|
+
const file = (0, import_node_path16.join)(cwd, ".gitignore");
|
|
1809
2363
|
const entry = ".giri";
|
|
1810
|
-
if (!(0,
|
|
2364
|
+
if (!(0, import_node_fs11.existsSync)(file)) {
|
|
1811
2365
|
await (0, import_promises4.writeFile)(file, `${entry}
|
|
1812
2366
|
`);
|
|
1813
2367
|
return;
|
|
@@ -1819,8 +2373,8 @@ async function ensureGitignore(cwd) {
|
|
|
1819
2373
|
}
|
|
1820
2374
|
}
|
|
1821
2375
|
async function ensureTsConfig(cwd) {
|
|
1822
|
-
const file = (0,
|
|
1823
|
-
if ((0,
|
|
2376
|
+
const file = (0, import_node_path16.join)(cwd, "tsconfig.json");
|
|
2377
|
+
if ((0, import_node_fs11.existsSync)(file)) {
|
|
1824
2378
|
return;
|
|
1825
2379
|
}
|
|
1826
2380
|
await (0, import_promises4.writeFile)(
|
|
@@ -1849,7 +2403,7 @@ async function ensureTsConfig(cwd) {
|
|
|
1849
2403
|
async function missingDeps(cwd, candidates) {
|
|
1850
2404
|
let pkg = {};
|
|
1851
2405
|
try {
|
|
1852
|
-
pkg = JSON.parse(await (0, import_promises4.readFile)((0,
|
|
2406
|
+
pkg = JSON.parse(await (0, import_promises4.readFile)((0, import_node_path16.join)(cwd, "package.json"), "utf8"));
|
|
1853
2407
|
} catch {
|
|
1854
2408
|
}
|
|
1855
2409
|
const present = /* @__PURE__ */ new Set();
|
|
@@ -1918,7 +2472,7 @@ async function selectAdapter(interactive) {
|
|
|
1918
2472
|
return ADAPTERS.find((adapter) => adapter.value === picked) ?? null;
|
|
1919
2473
|
}
|
|
1920
2474
|
async function initProject(cwd, flags) {
|
|
1921
|
-
if (!(0,
|
|
2475
|
+
if (!(0, import_node_fs11.existsSync)((0, import_node_path16.join)(cwd, "package.json"))) {
|
|
1922
2476
|
throw new Error(
|
|
1923
2477
|
"No package.json found. Run `giri init` inside an existing project - set one up first (e.g. `npm init -y` and install typescript), then re-run."
|
|
1924
2478
|
);
|
|
@@ -1943,13 +2497,13 @@ async function initProject(cwd, flags) {
|
|
|
1943
2497
|
prompts.cancel(`The ${adapter.label} adapter isn't available yet - only Hono ships today.`);
|
|
1944
2498
|
return;
|
|
1945
2499
|
}
|
|
1946
|
-
const configPath = (0,
|
|
1947
|
-
if (!(0,
|
|
2500
|
+
const configPath = (0, import_node_path16.join)(cwd, "giri.config.ts");
|
|
2501
|
+
if (!(0, import_node_fs11.existsSync)(configPath)) {
|
|
1948
2502
|
await (0, import_promises4.writeFile)(configPath, configSource(adapter));
|
|
1949
2503
|
}
|
|
1950
|
-
const routePath = (0,
|
|
1951
|
-
if (!(0,
|
|
1952
|
-
await (0, import_promises4.mkdir)((0,
|
|
2504
|
+
const routePath = (0, import_node_path16.join)(cwd, "src", "routes", "+get.ts");
|
|
2505
|
+
if (!(0, import_node_fs11.existsSync)(routePath)) {
|
|
2506
|
+
await (0, import_promises4.mkdir)((0, import_node_path16.join)(cwd, "src", "routes"), { recursive: true });
|
|
1953
2507
|
await (0, import_promises4.writeFile)(
|
|
1954
2508
|
routePath,
|
|
1955
2509
|
[
|
|
@@ -2017,69 +2571,146 @@ function displayHost(address) {
|
|
|
2017
2571
|
return address.includes(":") ? `[${address}]` : address;
|
|
2018
2572
|
}
|
|
2019
2573
|
async function serveProject(config, flags) {
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
const
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
const
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2574
|
+
Error.stackTraceLimit = 30;
|
|
2575
|
+
const { watch } = await import("chokidar");
|
|
2576
|
+
let stop;
|
|
2577
|
+
const boot = async (cfg) => {
|
|
2578
|
+
const initial = await syncProject(cfg);
|
|
2579
|
+
log2.success(
|
|
2580
|
+
`synced ${initial.routes.length} route${initial.routes.length === 1 ? "" : "s"} ${muted(`at ${initial.paths.outDir}`)}`,
|
|
2581
|
+
"sync"
|
|
2582
|
+
);
|
|
2583
|
+
const closers = [];
|
|
2584
|
+
closers.push(registerAliasResolver(cfg.alias, initial.paths.cwd));
|
|
2585
|
+
const lifecycle = await loadLifecycle();
|
|
2586
|
+
const services = await runInit(lifecycle);
|
|
2587
|
+
let current = await buildGiriApp(cfg, { services });
|
|
2588
|
+
const port = flags.port ?? cfg.server?.port ?? 3e3;
|
|
2589
|
+
const hostname = flags.hostname ?? cfg.server?.hostname;
|
|
2590
|
+
if (flags.watch) {
|
|
2591
|
+
const srcDir = (0, import_node_path16.resolve)(current.paths.routesDir, "..");
|
|
2592
|
+
if ((0, import_node_fs11.existsSync)(srcDir)) {
|
|
2593
|
+
let timer;
|
|
2594
|
+
let syncing = false;
|
|
2595
|
+
const changed = /* @__PURE__ */ new Set();
|
|
2596
|
+
const updater = createWatchUpdater(cfg, initial);
|
|
2597
|
+
const hmrCount = /* @__PURE__ */ new Map();
|
|
2598
|
+
const bump = (key) => {
|
|
2599
|
+
const next = (hmrCount.get(key) ?? 0) + 1;
|
|
2600
|
+
hmrCount.set(key, next);
|
|
2601
|
+
return next;
|
|
2602
|
+
};
|
|
2603
|
+
const flush = async () => {
|
|
2604
|
+
if (syncing) {
|
|
2605
|
+
return;
|
|
2606
|
+
}
|
|
2607
|
+
syncing = true;
|
|
2608
|
+
try {
|
|
2609
|
+
while (changed.size > 0) {
|
|
2610
|
+
const batch = [...changed];
|
|
2611
|
+
changed.clear();
|
|
2612
|
+
let rebuild = false;
|
|
2613
|
+
let fullSync = false;
|
|
2614
|
+
const dirtySet = /* @__PURE__ */ new Set();
|
|
2615
|
+
for (const name of batch) {
|
|
2616
|
+
const outcome = await updater.apply(name || null);
|
|
2617
|
+
if (outcome === "skip") {
|
|
2618
|
+
continue;
|
|
2619
|
+
}
|
|
2620
|
+
rebuild = true;
|
|
2621
|
+
if (name) {
|
|
2622
|
+
dirtySet.add((0, import_node_path16.resolve)(srcDir, name));
|
|
2623
|
+
}
|
|
2624
|
+
const rel = name ? `src/${name.replace(/\\/g, "/")}` : "src";
|
|
2625
|
+
log2.change(outcome === "full" ? "sync" : "update", rel, bump(rel));
|
|
2626
|
+
if (outcome === "full") {
|
|
2627
|
+
fullSync = true;
|
|
2628
|
+
break;
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2631
|
+
if (rebuild) {
|
|
2632
|
+
current = await buildGiriApp(cfg, {
|
|
2633
|
+
services,
|
|
2634
|
+
dirty: fullSync ? void 0 : dirtySet
|
|
2635
|
+
});
|
|
2636
|
+
}
|
|
2057
2637
|
}
|
|
2058
|
-
|
|
2638
|
+
} catch (error) {
|
|
2639
|
+
log2.error(error instanceof Error ? error.message : String(error), "watch");
|
|
2640
|
+
} finally {
|
|
2641
|
+
syncing = false;
|
|
2059
2642
|
}
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
}
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2643
|
+
if (changed.size > 0) {
|
|
2644
|
+
void flush();
|
|
2645
|
+
}
|
|
2646
|
+
};
|
|
2647
|
+
const watcher = watch(srcDir, { persistent: true });
|
|
2648
|
+
const onFileChange = (filename) => {
|
|
2649
|
+
changed.add(filename);
|
|
2650
|
+
clearTimeout(timer);
|
|
2651
|
+
timer = setTimeout(() => void flush(), 150);
|
|
2652
|
+
};
|
|
2653
|
+
watcher.on("change", onFileChange);
|
|
2654
|
+
watcher.on("add", onFileChange);
|
|
2655
|
+
watcher.on("unlink", onFileChange);
|
|
2656
|
+
closers.push(() => {
|
|
2657
|
+
clearTimeout(timer);
|
|
2658
|
+
watcher.close();
|
|
2659
|
+
});
|
|
2660
|
+
}
|
|
2074
2661
|
}
|
|
2662
|
+
const handler = (request) => cfg.adapter.fetch(current.app, request);
|
|
2663
|
+
const server = cfg.adapter.serve(handler, { port, hostname }, (info) => {
|
|
2664
|
+
log2.ready(`http://${displayHost(info.address)}:${info.port}`);
|
|
2665
|
+
});
|
|
2666
|
+
stop = async () => {
|
|
2667
|
+
for (const close of closers) {
|
|
2668
|
+
await close();
|
|
2669
|
+
}
|
|
2670
|
+
try {
|
|
2671
|
+
await server.close();
|
|
2672
|
+
} catch {
|
|
2673
|
+
}
|
|
2674
|
+
if (lifecycle.teardown) {
|
|
2675
|
+
await lifecycle.teardown(services);
|
|
2676
|
+
}
|
|
2677
|
+
};
|
|
2678
|
+
};
|
|
2679
|
+
await boot(config);
|
|
2680
|
+
const configPath = flags.watch ? findConfigPath((0, import_node_path16.resolve)(process.cwd())) : void 0;
|
|
2681
|
+
if (configPath) {
|
|
2682
|
+
let timer;
|
|
2683
|
+
let restarting = false;
|
|
2684
|
+
const configName = (0, import_node_path16.basename)(configPath);
|
|
2685
|
+
const restart = async () => {
|
|
2686
|
+
if (restarting) {
|
|
2687
|
+
return;
|
|
2688
|
+
}
|
|
2689
|
+
restarting = true;
|
|
2690
|
+
try {
|
|
2691
|
+
log2.info(`${color.green("restart")} ${configName} changed`, "config");
|
|
2692
|
+
delete require.cache[configPath];
|
|
2693
|
+
const next = await load({ throwOnError: true });
|
|
2694
|
+
await stop?.();
|
|
2695
|
+
await boot(next);
|
|
2696
|
+
} catch (error) {
|
|
2697
|
+
log2.error(error instanceof Error ? error.message : String(error), "config");
|
|
2698
|
+
log2.info("kept the previous server running \u2014 fix the config and save again", "config");
|
|
2699
|
+
} finally {
|
|
2700
|
+
restarting = false;
|
|
2701
|
+
}
|
|
2702
|
+
};
|
|
2703
|
+
const configWatcher = watch((0, import_node_path16.dirname)(configPath), { persistent: true });
|
|
2704
|
+
configWatcher.on("all", (_event, filename) => {
|
|
2705
|
+
if (filename && (0, import_node_path16.basename)(filename.toString()) === configName) {
|
|
2706
|
+
clearTimeout(timer);
|
|
2707
|
+
timer = setTimeout(() => void restart(), 150);
|
|
2708
|
+
}
|
|
2709
|
+
});
|
|
2075
2710
|
}
|
|
2076
|
-
|
|
2077
|
-
const server = config.adapter.serve(handler, { port, hostname }, (info) => {
|
|
2078
|
-
log2.ready(`http://${displayHost(info.address)}:${info.port}`);
|
|
2079
|
-
});
|
|
2080
|
-
registerShutdown(server, lifecycle, services);
|
|
2711
|
+
registerShutdown(() => stop?.());
|
|
2081
2712
|
}
|
|
2082
|
-
function registerShutdown(
|
|
2713
|
+
function registerShutdown(cleanup) {
|
|
2083
2714
|
let shuttingDown = false;
|
|
2084
2715
|
const shutdown = async () => {
|
|
2085
2716
|
if (shuttingDown) {
|
|
@@ -2087,10 +2718,7 @@ function registerShutdown(server, lifecycle, services) {
|
|
|
2087
2718
|
}
|
|
2088
2719
|
shuttingDown = true;
|
|
2089
2720
|
try {
|
|
2090
|
-
await
|
|
2091
|
-
if (lifecycle.teardown) {
|
|
2092
|
-
await lifecycle.teardown(services);
|
|
2093
|
-
}
|
|
2721
|
+
await cleanup();
|
|
2094
2722
|
} catch (error) {
|
|
2095
2723
|
log2.error(error instanceof Error ? error.message : String(error));
|
|
2096
2724
|
process.exitCode = 1;
|
|
@@ -2103,7 +2731,7 @@ function registerShutdown(server, lifecycle, services) {
|
|
|
2103
2731
|
}
|
|
2104
2732
|
async function main() {
|
|
2105
2733
|
const [command = "help", ...args] = process.argv.slice(2);
|
|
2106
|
-
const cwd = (0,
|
|
2734
|
+
const cwd = (0, import_node_path16.resolve)(process.cwd());
|
|
2107
2735
|
if (command === "help" || command === "--help" || command === "-h") {
|
|
2108
2736
|
help();
|
|
2109
2737
|
return;
|