@boon4681/giri 0.0.2 → 0.0.3-alpha-1
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 +180 -17
- package/dist/adapters/hono.js.map +1 -1
- package/dist/cli.js +985 -252
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +30 -4
- package/dist/index.js +750 -130
- 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
|
@@ -48,10 +48,10 @@ var init_es5 = __esm({
|
|
|
48
48
|
// src/generator/schema/program.ts
|
|
49
49
|
function createSchemaProgram(paths, routeFiles) {
|
|
50
50
|
let options = { ...DEFAULT_OPTIONS };
|
|
51
|
-
const configPath =
|
|
51
|
+
const configPath = import_typescript2.default.findConfigFile(paths.cwd, import_typescript2.default.sys.fileExists, "tsconfig.json");
|
|
52
52
|
if (configPath) {
|
|
53
|
-
const parsed =
|
|
54
|
-
...
|
|
53
|
+
const parsed = import_typescript2.default.getParsedCommandLineOfConfigFile(configPath, {}, {
|
|
54
|
+
...import_typescript2.default.sys,
|
|
55
55
|
onUnRecoverableConfigFileDiagnostic: () => {
|
|
56
56
|
}
|
|
57
57
|
});
|
|
@@ -59,17 +59,17 @@ function createSchemaProgram(paths, routeFiles) {
|
|
|
59
59
|
options = { ...parsed.options, noEmit: true };
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
return
|
|
62
|
+
return import_typescript2.default.createProgram(routeFiles, options);
|
|
63
63
|
}
|
|
64
|
-
var
|
|
64
|
+
var import_typescript2, DEFAULT_OPTIONS;
|
|
65
65
|
var init_program = __esm({
|
|
66
66
|
"src/generator/schema/program.ts"() {
|
|
67
67
|
"use strict";
|
|
68
|
-
|
|
68
|
+
import_typescript2 = __toESM(require("typescript"));
|
|
69
69
|
DEFAULT_OPTIONS = {
|
|
70
|
-
target:
|
|
71
|
-
module:
|
|
72
|
-
moduleResolution:
|
|
70
|
+
target: import_typescript2.default.ScriptTarget.ES2022,
|
|
71
|
+
module: import_typescript2.default.ModuleKind.NodeNext,
|
|
72
|
+
moduleResolution: import_typescript2.default.ModuleResolutionKind.NodeNext,
|
|
73
73
|
strict: true,
|
|
74
74
|
skipLibCheck: true,
|
|
75
75
|
noEmit: true
|
|
@@ -103,7 +103,7 @@ function literalValuesOf(types) {
|
|
|
103
103
|
for (const member of types) {
|
|
104
104
|
if (member.isStringLiteral() || member.isNumberLiteral()) {
|
|
105
105
|
values.push(member.value);
|
|
106
|
-
} else if (member.flags &
|
|
106
|
+
} else if (member.flags & import_typescript3.default.TypeFlags.BooleanLiteral) {
|
|
107
107
|
values.push(intrinsicName(member) === "true");
|
|
108
108
|
} else {
|
|
109
109
|
return void 0;
|
|
@@ -112,7 +112,7 @@ function literalValuesOf(types) {
|
|
|
112
112
|
return values;
|
|
113
113
|
}
|
|
114
114
|
function walkUnion(type, ctx) {
|
|
115
|
-
const flag =
|
|
115
|
+
const flag = import_typescript3.default.TypeFlags.Undefined | import_typescript3.default.TypeFlags.Void | import_typescript3.default.TypeFlags.Never;
|
|
116
116
|
const members = type.types.filter((member) => !(member.flags & flag));
|
|
117
117
|
if (members.length === 1) {
|
|
118
118
|
return walkType(members[0], ctx);
|
|
@@ -125,13 +125,13 @@ function walkUnion(type, ctx) {
|
|
|
125
125
|
}
|
|
126
126
|
function buildObjectSchema(type, ctx) {
|
|
127
127
|
const { checker } = ctx;
|
|
128
|
-
const indexInfo = checker.getIndexInfoOfType(type,
|
|
128
|
+
const indexInfo = checker.getIndexInfoOfType(type, import_typescript3.default.IndexKind.String) ?? checker.getIndexInfoOfType(type, import_typescript3.default.IndexKind.Number);
|
|
129
129
|
const properties = {};
|
|
130
130
|
const required = [];
|
|
131
131
|
for (const symbol of checker.getPropertiesOfType(type)) {
|
|
132
132
|
const name = symbol.getName();
|
|
133
133
|
const propType = checker.getTypeOfSymbolAtLocation(symbol, ctx.location);
|
|
134
|
-
const optional = Boolean(symbol.getFlags() &
|
|
134
|
+
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
135
|
properties[name] = walkType(propType, ctx);
|
|
136
136
|
if (!optional) {
|
|
137
137
|
required.push(name);
|
|
@@ -190,16 +190,16 @@ function walkObject(type, ctx) {
|
|
|
190
190
|
}
|
|
191
191
|
function walkType(type, ctx) {
|
|
192
192
|
const flags = type.flags;
|
|
193
|
-
if (flags & (
|
|
193
|
+
if (flags & (import_typescript3.default.TypeFlags.Any | import_typescript3.default.TypeFlags.Unknown)) {
|
|
194
194
|
return {};
|
|
195
195
|
}
|
|
196
|
-
if (flags &
|
|
196
|
+
if (flags & import_typescript3.default.TypeFlags.Null) {
|
|
197
197
|
return { type: "null" };
|
|
198
198
|
}
|
|
199
|
-
if (flags & (
|
|
199
|
+
if (flags & (import_typescript3.default.TypeFlags.Undefined | import_typescript3.default.TypeFlags.Void)) {
|
|
200
200
|
return {};
|
|
201
201
|
}
|
|
202
|
-
if (flags & (
|
|
202
|
+
if (flags & (import_typescript3.default.TypeFlags.BigInt | import_typescript3.default.TypeFlags.BigIntLiteral)) {
|
|
203
203
|
ctx.warnings.push("bigint is not JSON-serializable (JSON.stringify throws); documented as string.");
|
|
204
204
|
return { type: "string" };
|
|
205
205
|
}
|
|
@@ -209,45 +209,45 @@ function walkType(type, ctx) {
|
|
|
209
209
|
if (type.isNumberLiteral()) {
|
|
210
210
|
return { type: "number", const: type.value };
|
|
211
211
|
}
|
|
212
|
-
if (flags &
|
|
212
|
+
if (flags & import_typescript3.default.TypeFlags.BooleanLiteral) {
|
|
213
213
|
return { type: "boolean", const: intrinsicName(type) === "true" };
|
|
214
214
|
}
|
|
215
|
-
if (flags &
|
|
215
|
+
if (flags & import_typescript3.default.TypeFlags.String) {
|
|
216
216
|
return { type: "string" };
|
|
217
217
|
}
|
|
218
|
-
if (flags &
|
|
218
|
+
if (flags & import_typescript3.default.TypeFlags.Number) {
|
|
219
219
|
return { type: "number" };
|
|
220
220
|
}
|
|
221
|
-
if (flags &
|
|
221
|
+
if (flags & import_typescript3.default.TypeFlags.Boolean) {
|
|
222
222
|
return { type: "boolean" };
|
|
223
223
|
}
|
|
224
224
|
if (type.isUnion()) {
|
|
225
225
|
return walkUnion(type, ctx);
|
|
226
226
|
}
|
|
227
|
-
if (flags &
|
|
227
|
+
if (flags & import_typescript3.default.TypeFlags.Object || type.isIntersection()) {
|
|
228
228
|
return walkObject(type, ctx);
|
|
229
229
|
}
|
|
230
230
|
return {};
|
|
231
231
|
}
|
|
232
|
-
var
|
|
232
|
+
var import_typescript3;
|
|
233
233
|
var init_json_schema = __esm({
|
|
234
234
|
"src/generator/schema/json-schema.ts"() {
|
|
235
235
|
"use strict";
|
|
236
|
-
|
|
236
|
+
import_typescript3 = __toESM(require("typescript"));
|
|
237
237
|
}
|
|
238
238
|
});
|
|
239
239
|
|
|
240
240
|
// src/generator/schema/responses.ts
|
|
241
241
|
function findHandleFunction(source) {
|
|
242
242
|
let found;
|
|
243
|
-
const isExported = (node) =>
|
|
243
|
+
const isExported = (node) => import_typescript4.default.canHaveModifiers(node) && (import_typescript4.default.getModifiers(node)?.some((m) => m.kind === import_typescript4.default.SyntaxKind.ExportKeyword) ?? false);
|
|
244
244
|
for (const statement of source.statements) {
|
|
245
|
-
if (
|
|
245
|
+
if (import_typescript4.default.isFunctionDeclaration(statement) && statement.name?.text === "handle" && isExported(statement)) {
|
|
246
246
|
found = statement;
|
|
247
247
|
}
|
|
248
|
-
if (
|
|
248
|
+
if (import_typescript4.default.isVariableStatement(statement) && isExported(statement)) {
|
|
249
249
|
for (const declaration of statement.declarationList.declarations) {
|
|
250
|
-
if (
|
|
250
|
+
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
251
|
found = declaration.initializer;
|
|
252
252
|
}
|
|
253
253
|
}
|
|
@@ -256,7 +256,7 @@ function findHandleFunction(source) {
|
|
|
256
256
|
return found;
|
|
257
257
|
}
|
|
258
258
|
function collectReturnExpressions(fn) {
|
|
259
|
-
if (
|
|
259
|
+
if (import_typescript4.default.isArrowFunction(fn) && !import_typescript4.default.isBlock(fn.body)) {
|
|
260
260
|
return [fn.body];
|
|
261
261
|
}
|
|
262
262
|
if (!fn.body) {
|
|
@@ -264,15 +264,15 @@ function collectReturnExpressions(fn) {
|
|
|
264
264
|
}
|
|
265
265
|
const expressions = [];
|
|
266
266
|
const visit = (node) => {
|
|
267
|
-
if (
|
|
267
|
+
if (import_typescript4.default.isFunctionDeclaration(node) || import_typescript4.default.isFunctionExpression(node) || import_typescript4.default.isArrowFunction(node)) {
|
|
268
268
|
return;
|
|
269
269
|
}
|
|
270
|
-
if (
|
|
270
|
+
if (import_typescript4.default.isReturnStatement(node) && node.expression) {
|
|
271
271
|
expressions.push(node.expression);
|
|
272
272
|
}
|
|
273
|
-
|
|
273
|
+
import_typescript4.default.forEachChild(node, visit);
|
|
274
274
|
};
|
|
275
|
-
|
|
275
|
+
import_typescript4.default.forEachChild(fn.body, visit);
|
|
276
276
|
return expressions;
|
|
277
277
|
}
|
|
278
278
|
function propertyType(checker, type, name, location) {
|
|
@@ -284,15 +284,21 @@ function isTypedResponse(checker, type) {
|
|
|
284
284
|
checker.getPropertyOfType(type, "data") && checker.getPropertyOfType(type, "status") && checker.getPropertyOfType(type, "format")
|
|
285
285
|
);
|
|
286
286
|
}
|
|
287
|
-
function
|
|
288
|
-
|
|
287
|
+
function firstParameterName(fn) {
|
|
288
|
+
const [first] = fn.parameters;
|
|
289
|
+
return first && import_typescript4.default.isIdentifier(first.name) ? first.name.text : void 0;
|
|
290
|
+
}
|
|
291
|
+
function readFromCall(checker, expression, contextName) {
|
|
292
|
+
if (!import_typescript4.default.isCallExpression(expression) || !import_typescript4.default.isPropertyAccessExpression(expression.expression)) {
|
|
289
293
|
return void 0;
|
|
290
294
|
}
|
|
291
295
|
const method = expression.expression.name.text;
|
|
292
296
|
if (method !== "json" && method !== "text") {
|
|
293
297
|
return void 0;
|
|
294
298
|
}
|
|
295
|
-
|
|
299
|
+
const target = expression.expression.expression;
|
|
300
|
+
const directContextCall = contextName && import_typescript4.default.isIdentifier(target) && target.text === contextName;
|
|
301
|
+
if (!directContextCall && !isTypedResponse(checker, checker.getTypeAtLocation(expression))) {
|
|
296
302
|
return void 0;
|
|
297
303
|
}
|
|
298
304
|
const [dataArg, statusArg] = expression.arguments;
|
|
@@ -332,6 +338,7 @@ function extractRouteResponses(program, file) {
|
|
|
332
338
|
return result;
|
|
333
339
|
}
|
|
334
340
|
const ctx = createWalkContext(checker, fn);
|
|
341
|
+
const contextName = firstParameterName(fn);
|
|
335
342
|
const byStatus = /* @__PURE__ */ new Map();
|
|
336
343
|
const record = (hit) => {
|
|
337
344
|
const schema = walkType(hit.data, ctx);
|
|
@@ -340,7 +347,7 @@ function extractRouteResponses(program, file) {
|
|
|
340
347
|
byStatus.set(hit.status, bucket);
|
|
341
348
|
};
|
|
342
349
|
for (const expression of collectReturnExpressions(fn)) {
|
|
343
|
-
const fromCall = readFromCall(checker, expression);
|
|
350
|
+
const fromCall = readFromCall(checker, expression, contextName);
|
|
344
351
|
if (fromCall) {
|
|
345
352
|
record(fromCall);
|
|
346
353
|
continue;
|
|
@@ -366,11 +373,11 @@ function extractRouteResponses(program, file) {
|
|
|
366
373
|
result.$defs = ctx.defs;
|
|
367
374
|
return result;
|
|
368
375
|
}
|
|
369
|
-
var
|
|
376
|
+
var import_typescript4;
|
|
370
377
|
var init_responses = __esm({
|
|
371
378
|
"src/generator/schema/responses.ts"() {
|
|
372
379
|
"use strict";
|
|
373
|
-
|
|
380
|
+
import_typescript4 = __toESM(require("typescript"));
|
|
374
381
|
init_json_schema();
|
|
375
382
|
}
|
|
376
383
|
});
|
|
@@ -394,9 +401,9 @@ var init_schema = __esm({
|
|
|
394
401
|
|
|
395
402
|
// src/cli.ts
|
|
396
403
|
var import_node_child_process = require("child_process");
|
|
397
|
-
var
|
|
398
|
-
var
|
|
399
|
-
var
|
|
404
|
+
var import_node_fs12 = require("fs");
|
|
405
|
+
var import_promises5 = require("fs/promises");
|
|
406
|
+
var import_node_path17 = require("path");
|
|
400
407
|
var prompts = __toESM(require("@clack/prompts"));
|
|
401
408
|
|
|
402
409
|
// src/app.ts
|
|
@@ -422,7 +429,8 @@ var configSchema = import_typebox.Type.Object({
|
|
|
422
429
|
port: import_typebox.Type.Optional(import_typebox.Type.Number()),
|
|
423
430
|
hostname: import_typebox.Type.Optional(import_typebox.Type.String())
|
|
424
431
|
}, { additionalProperties: false })),
|
|
425
|
-
errorSchema: import_typebox.Type.Optional(import_typebox.Type.Any())
|
|
432
|
+
errorSchema: import_typebox.Type.Optional(import_typebox.Type.Any()),
|
|
433
|
+
cookieSecret: import_typebox.Type.Optional(import_typebox.Type.String())
|
|
426
434
|
}, { additionalProperties: false });
|
|
427
435
|
|
|
428
436
|
// src/loader/loader.ts
|
|
@@ -461,29 +469,44 @@ var safeRegister = async () => {
|
|
|
461
469
|
await assertES5(res.unregister);
|
|
462
470
|
return res;
|
|
463
471
|
};
|
|
464
|
-
var
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
(0, import_node_process.exit)(1);
|
|
472
|
+
var findConfigPath = (cwd = (0, import_node_path.resolve)()) => {
|
|
473
|
+
for (const name of ["giri.config.ts", "giri.config.js"]) {
|
|
474
|
+
const path = (0, import_node_path.resolve)(cwd, name);
|
|
475
|
+
if ((0, import_node_fs.existsSync)(path)) {
|
|
476
|
+
return path;
|
|
477
|
+
}
|
|
471
478
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
479
|
+
return void 0;
|
|
480
|
+
};
|
|
481
|
+
var load = async (opts = {}) => {
|
|
482
|
+
const fail = (message) => {
|
|
483
|
+
if (opts.throwOnError) {
|
|
484
|
+
throw new Error(message);
|
|
485
|
+
}
|
|
486
|
+
import_prompts.log.error(message);
|
|
475
487
|
(0, import_node_process.exit)(1);
|
|
488
|
+
};
|
|
489
|
+
const path = findConfigPath();
|
|
490
|
+
if (!path) {
|
|
491
|
+
fail("Config file not found.");
|
|
476
492
|
}
|
|
477
493
|
const { unregister } = await safeRegister();
|
|
478
|
-
|
|
479
|
-
|
|
494
|
+
let content;
|
|
495
|
+
try {
|
|
496
|
+
const required = require(`${path}`);
|
|
497
|
+
content = required.default ?? required;
|
|
498
|
+
} finally {
|
|
499
|
+
}
|
|
480
500
|
unregister();
|
|
481
501
|
const res = import_value.Value.Check(configSchema, content);
|
|
482
502
|
if (!res) {
|
|
483
|
-
|
|
484
|
-
|
|
503
|
+
const messages = [...import_value.Value.Errors(configSchema, content)].map((error) => error.message);
|
|
504
|
+
if (!opts.throwOnError) {
|
|
505
|
+
for (const message of messages) {
|
|
506
|
+
import_prompts.log.error(message);
|
|
507
|
+
}
|
|
485
508
|
}
|
|
486
|
-
(
|
|
509
|
+
fail(messages.join("\n"));
|
|
487
510
|
}
|
|
488
511
|
return content;
|
|
489
512
|
};
|
|
@@ -510,13 +533,18 @@ function methodFromFile(fileName) {
|
|
|
510
533
|
const stem = fileName.replace(/\.(?:[cm]?[jt]s|[jt]sx)$/, "").toLowerCase();
|
|
511
534
|
return METHOD_FROM_FILE.get(stem);
|
|
512
535
|
}
|
|
513
|
-
function sharedFileIn(dir) {
|
|
536
|
+
function sharedFileIn(dir, cache) {
|
|
537
|
+
if (cache?.has(dir)) {
|
|
538
|
+
return cache.get(dir);
|
|
539
|
+
}
|
|
514
540
|
for (const ext of ["ts", "tsx", "js", "jsx", "mjs", "cjs", "mts", "cts"]) {
|
|
515
541
|
const file = (0, import_node_path2.join)(dir, `+shared.${ext}`);
|
|
516
542
|
if ((0, import_node_fs2.existsSync)(file)) {
|
|
543
|
+
cache?.set(dir, file);
|
|
517
544
|
return file;
|
|
518
545
|
}
|
|
519
546
|
}
|
|
547
|
+
cache?.set(dir, void 0);
|
|
520
548
|
return void 0;
|
|
521
549
|
}
|
|
522
550
|
function physicalRouteSegments(routesDir, routeDir) {
|
|
@@ -585,7 +613,7 @@ async function scanRouteFolders(routesDir) {
|
|
|
585
613
|
function routeParamsForDir(routesDir, dir) {
|
|
586
614
|
return pathFromSegments(physicalRouteSegments(routesDir, dir)).params;
|
|
587
615
|
}
|
|
588
|
-
function sharedFilesForDir(routesDir, dir) {
|
|
616
|
+
function sharedFilesForDir(routesDir, dir, cache) {
|
|
589
617
|
const segments = physicalRouteSegments(routesDir, dir);
|
|
590
618
|
const dirs = [routesDir];
|
|
591
619
|
let current = routesDir;
|
|
@@ -593,7 +621,7 @@ function sharedFilesForDir(routesDir, dir) {
|
|
|
593
621
|
current = (0, import_node_path2.join)(current, segment);
|
|
594
622
|
dirs.push(current);
|
|
595
623
|
}
|
|
596
|
-
return dirs.map(sharedFileIn).filter((file) => Boolean(file));
|
|
624
|
+
return dirs.map((currentDir) => sharedFileIn(currentDir, cache)).filter((file) => Boolean(file));
|
|
597
625
|
}
|
|
598
626
|
async function scanRoutes(routesDir) {
|
|
599
627
|
if (!(0, import_node_fs2.existsSync)(routesDir)) {
|
|
@@ -605,6 +633,7 @@ async function scanRoutes(routesDir) {
|
|
|
605
633
|
onlyFiles: true
|
|
606
634
|
});
|
|
607
635
|
const routes = [];
|
|
636
|
+
const sharedCache = /* @__PURE__ */ new Map();
|
|
608
637
|
for (const file of files) {
|
|
609
638
|
const method = methodFromFile((0, import_node_path2.basename)(file));
|
|
610
639
|
if (!method) {
|
|
@@ -620,7 +649,7 @@ async function scanRoutes(routesDir) {
|
|
|
620
649
|
routeDir,
|
|
621
650
|
routeSegments,
|
|
622
651
|
params,
|
|
623
|
-
sharedFiles: sharedFilesForDir(routesDir, routeDir)
|
|
652
|
+
sharedFiles: sharedFilesForDir(routesDir, routeDir, sharedCache)
|
|
624
653
|
});
|
|
625
654
|
}
|
|
626
655
|
return routes.sort((left, right) => {
|
|
@@ -649,9 +678,11 @@ function isGiriBodySchema(value) {
|
|
|
649
678
|
}
|
|
650
679
|
|
|
651
680
|
// src/app.ts
|
|
652
|
-
function loadModule(file) {
|
|
681
|
+
function loadModule(file, force = true) {
|
|
653
682
|
const resolved = require.resolve(file);
|
|
654
|
-
|
|
683
|
+
if (force) {
|
|
684
|
+
delete require.cache[resolved];
|
|
685
|
+
}
|
|
655
686
|
return require(resolved);
|
|
656
687
|
}
|
|
657
688
|
function interopDefault(value) {
|
|
@@ -713,20 +744,13 @@ function resolveAliasTarget(cwd, target, capture = "") {
|
|
|
713
744
|
}
|
|
714
745
|
function matchAlias(request, key) {
|
|
715
746
|
if (key.includes("*")) {
|
|
716
|
-
const [
|
|
717
|
-
if (request.startsWith(
|
|
718
|
-
return request.slice(
|
|
747
|
+
const [prefix, suffix = ""] = key.split("*");
|
|
748
|
+
if (request.startsWith(prefix) && request.endsWith(suffix)) {
|
|
749
|
+
return request.slice(prefix.length, request.length - suffix.length);
|
|
719
750
|
}
|
|
720
751
|
return void 0;
|
|
721
752
|
}
|
|
722
|
-
|
|
723
|
-
return "";
|
|
724
|
-
}
|
|
725
|
-
const prefix = `${key}/`;
|
|
726
|
-
if (request.startsWith(prefix)) {
|
|
727
|
-
return request.slice(prefix.length);
|
|
728
|
-
}
|
|
729
|
-
return void 0;
|
|
753
|
+
return request === key ? "" : void 0;
|
|
730
754
|
}
|
|
731
755
|
function resolveAliasRequest(request, alias, cwd) {
|
|
732
756
|
for (const [key, value] of Object.entries(alias ?? {})) {
|
|
@@ -790,38 +814,78 @@ async function buildGiriApp(config, options = {}) {
|
|
|
790
814
|
const routes = await scanRoutes(paths.routesDir);
|
|
791
815
|
const app = config.adapter.createApp();
|
|
792
816
|
ensureGiriAliasResolver(paths.outDir);
|
|
793
|
-
|
|
794
|
-
|
|
817
|
+
if (options.lazy && (!options.loaderRegistered || !options.aliasResolverRegistered)) {
|
|
818
|
+
throw new Error("Lazy route loading requires persistent loader and alias registrations.");
|
|
819
|
+
}
|
|
820
|
+
const loader = options.loaderRegistered ? void 0 : await safeRegister();
|
|
821
|
+
const unregisterAliasResolver = options.aliasResolverRegistered ? void 0 : registerAliasResolver(config.alias, paths.cwd);
|
|
795
822
|
try {
|
|
796
|
-
|
|
797
|
-
|
|
823
|
+
const dirty = options.dirty;
|
|
824
|
+
const forceReload = dirty === void 0;
|
|
825
|
+
const isDirty = (file) => forceReload || dirty.has(file);
|
|
826
|
+
const sharedCache = /* @__PURE__ */ new Map();
|
|
827
|
+
const loadShared = (file) => {
|
|
828
|
+
if (!sharedCache.has(file)) {
|
|
829
|
+
sharedCache.set(file, loadModule(file, isDirty(file)));
|
|
830
|
+
}
|
|
831
|
+
return sharedCache.get(file);
|
|
832
|
+
};
|
|
833
|
+
const runtimeFor = (route) => {
|
|
834
|
+
const routeModule = loadModule(route.file, isDirty(route.file));
|
|
798
835
|
if (typeof routeModule.handle !== "function") {
|
|
799
836
|
throw new Error(`${route.file} must export a named handle function.`);
|
|
800
837
|
}
|
|
801
838
|
const folderMiddleware = routeModule.config?.skipInherited ? [] : route.sharedFiles.flatMap(
|
|
802
|
-
(file) => normalizeMiddleware(
|
|
839
|
+
(file) => normalizeMiddleware(loadShared(file).middleware, file)
|
|
803
840
|
);
|
|
804
841
|
const verbMiddleware = normalizeMiddleware(routeModule.middleware, route.file);
|
|
805
|
-
|
|
842
|
+
return {
|
|
806
843
|
method: route.method,
|
|
807
844
|
path: route.path,
|
|
808
845
|
handle: routeModule.handle,
|
|
809
846
|
middleware: [...folderMiddleware, ...verbMiddleware],
|
|
810
847
|
input: routeInput(routeModule, route.file),
|
|
811
|
-
services: options.services
|
|
848
|
+
services: options.services,
|
|
849
|
+
cookieSecret: config.cookieSecret
|
|
850
|
+
};
|
|
851
|
+
};
|
|
852
|
+
for (const route of routes) {
|
|
853
|
+
if (!options.lazy) {
|
|
854
|
+
config.adapter.register(app, runtimeFor(route));
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
let runtime;
|
|
858
|
+
const getRuntime = () => {
|
|
859
|
+
runtime ??= runtimeFor(route);
|
|
860
|
+
return runtime;
|
|
861
|
+
};
|
|
862
|
+
config.adapter.register(app, {
|
|
863
|
+
method: route.method,
|
|
864
|
+
path: route.path,
|
|
865
|
+
get handle() {
|
|
866
|
+
return getRuntime().handle;
|
|
867
|
+
},
|
|
868
|
+
get middleware() {
|
|
869
|
+
return getRuntime().middleware;
|
|
870
|
+
},
|
|
871
|
+
get input() {
|
|
872
|
+
return getRuntime().input;
|
|
873
|
+
},
|
|
874
|
+
services: options.services,
|
|
875
|
+
cookieSecret: config.cookieSecret
|
|
812
876
|
});
|
|
813
877
|
}
|
|
814
878
|
} finally {
|
|
815
|
-
unregisterAliasResolver();
|
|
816
|
-
unregister();
|
|
879
|
+
unregisterAliasResolver?.();
|
|
880
|
+
loader?.unregister();
|
|
817
881
|
}
|
|
818
882
|
return { app, routes, paths };
|
|
819
883
|
}
|
|
820
884
|
|
|
821
885
|
// src/generator/sync.ts
|
|
822
|
-
var
|
|
823
|
-
var
|
|
824
|
-
var
|
|
886
|
+
var import_node_fs8 = require("fs");
|
|
887
|
+
var import_promises4 = require("fs/promises");
|
|
888
|
+
var import_node_path12 = require("path");
|
|
825
889
|
|
|
826
890
|
// src/generator/app-types.ts
|
|
827
891
|
var import_node_fs4 = require("fs");
|
|
@@ -1073,6 +1137,7 @@ function buildOpenApiDocument(paths, routes, data = {}) {
|
|
|
1073
1137
|
const documentPaths = {};
|
|
1074
1138
|
const schemas = {};
|
|
1075
1139
|
const securitySchemes = {};
|
|
1140
|
+
const tagOrder = [];
|
|
1076
1141
|
for (const route of routes) {
|
|
1077
1142
|
if (data.hiddenFiles?.has(route.file)) {
|
|
1078
1143
|
continue;
|
|
@@ -1080,10 +1145,32 @@ function buildOpenApiDocument(paths, routes, data = {}) {
|
|
|
1080
1145
|
const responses = data.responsesByFile?.get(route.file);
|
|
1081
1146
|
const input = data.inputsByFile?.get(route.file);
|
|
1082
1147
|
const security = data.securityByFile?.get(route.file);
|
|
1148
|
+
const meta = data.openapiByFile?.get(route.file);
|
|
1083
1149
|
for (const [name, schema] of Object.entries(responses?.$defs ?? {})) {
|
|
1084
1150
|
schemas[name] = rewriteRefs(schema);
|
|
1085
1151
|
}
|
|
1086
|
-
const operation = {
|
|
1152
|
+
const operation = {};
|
|
1153
|
+
if (meta?.tags && meta.tags.length > 0) {
|
|
1154
|
+
operation.tags = meta.tags;
|
|
1155
|
+
for (const tag2 of meta.tags) {
|
|
1156
|
+
if (!tagOrder.includes(tag2)) {
|
|
1157
|
+
tagOrder.push(tag2);
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
if (meta?.summary) {
|
|
1162
|
+
operation.summary = meta.summary;
|
|
1163
|
+
}
|
|
1164
|
+
if (meta?.description) {
|
|
1165
|
+
operation.description = meta.description;
|
|
1166
|
+
}
|
|
1167
|
+
if (meta?.operationId) {
|
|
1168
|
+
operation.operationId = meta.operationId;
|
|
1169
|
+
}
|
|
1170
|
+
if (meta?.deprecated) {
|
|
1171
|
+
operation.deprecated = true;
|
|
1172
|
+
}
|
|
1173
|
+
operation.responses = buildResponses(responses?.responses ?? []);
|
|
1087
1174
|
const parameters = [...pathParameters(route), ...queryParameters(input?.query)];
|
|
1088
1175
|
if (parameters.length > 0) {
|
|
1089
1176
|
operation.parameters = parameters;
|
|
@@ -1115,6 +1202,9 @@ function buildOpenApiDocument(paths, routes, data = {}) {
|
|
|
1115
1202
|
info: readProjectInfo(paths.cwd),
|
|
1116
1203
|
paths: documentPaths
|
|
1117
1204
|
};
|
|
1205
|
+
if (tagOrder.length > 0) {
|
|
1206
|
+
document.tags = tagOrder.map((name) => ({ name }));
|
|
1207
|
+
}
|
|
1118
1208
|
const components = {};
|
|
1119
1209
|
if (Object.keys(schemas).length > 0) {
|
|
1120
1210
|
components.schemas = schemas;
|
|
@@ -1146,14 +1236,29 @@ function paramsType(params) {
|
|
|
1146
1236
|
${fields}
|
|
1147
1237
|
}`;
|
|
1148
1238
|
}
|
|
1149
|
-
function
|
|
1150
|
-
|
|
1151
|
-
|
|
1239
|
+
function middlewareVarsType(typesDir, sharedFile) {
|
|
1240
|
+
const spec = JSON.stringify(moduleSpecifier(typesDir, sharedFile));
|
|
1241
|
+
return `(typeof import(${spec}) extends { middleware: infer M } ? import("@boon4681/giri").InferStackVars<M> : {})`;
|
|
1242
|
+
}
|
|
1243
|
+
function ownSharedFile(dir, sharedFiles) {
|
|
1244
|
+
for (let index = sharedFiles.length - 1; index >= 0; index -= 1) {
|
|
1245
|
+
if ((0, import_node_path8.dirname)(sharedFiles[index]) === dir) {
|
|
1246
|
+
return sharedFiles[index];
|
|
1247
|
+
}
|
|
1152
1248
|
}
|
|
1153
|
-
return
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1249
|
+
return void 0;
|
|
1250
|
+
}
|
|
1251
|
+
function varsType(paths, file, dir, sharedFiles) {
|
|
1252
|
+
const typesDir = (0, import_node_path8.dirname)(file);
|
|
1253
|
+
const parts = [];
|
|
1254
|
+
if (dir !== paths.routesDir) {
|
|
1255
|
+
parts.push(`import(${JSON.stringify(importPath(file, typeFilePath(paths, (0, import_node_path8.dirname)(dir))))}).Vars`);
|
|
1256
|
+
}
|
|
1257
|
+
const ownShared = ownSharedFile(dir, sharedFiles);
|
|
1258
|
+
if (ownShared) {
|
|
1259
|
+
parts.push(middlewareVarsType(typesDir, ownShared));
|
|
1260
|
+
}
|
|
1261
|
+
return parts.length > 0 ? parts.join("\n & ") : "{}";
|
|
1157
1262
|
}
|
|
1158
1263
|
function methodExports(typesDir, verbs) {
|
|
1159
1264
|
return verbs.map(({ method, file }) => {
|
|
@@ -1164,14 +1269,14 @@ function methodExports(typesDir, verbs) {
|
|
|
1164
1269
|
});
|
|
1165
1270
|
}
|
|
1166
1271
|
async function writeParamTypes(paths, folders) {
|
|
1167
|
-
|
|
1272
|
+
await Promise.all(folders.map(({ dir, params, sharedFiles, verbs }) => {
|
|
1168
1273
|
const file = typeFilePath(paths, dir);
|
|
1169
1274
|
const typesDir = (0, import_node_path8.dirname)(file);
|
|
1170
1275
|
const lines = [
|
|
1171
1276
|
GENERATED_HEADER,
|
|
1172
1277
|
`export type Params = ${paramsType(params)};`,
|
|
1173
1278
|
"export type RouteParams = Params;",
|
|
1174
|
-
`type Vars = ${varsType(
|
|
1279
|
+
`export type Vars = ${varsType(paths, file, dir, sharedFiles)};`,
|
|
1175
1280
|
"export type Middleware<Injects extends Record<string, unknown> = {}> =",
|
|
1176
1281
|
' import("@boon4681/giri").Middleware<Params, import("@boon4681/giri").ValidatedInput, Injects>;',
|
|
1177
1282
|
'export type Handle<Input extends import("@boon4681/giri").ValidatedInput = import("@boon4681/giri").ValidatedInput> =',
|
|
@@ -1181,10 +1286,14 @@ async function writeParamTypes(paths, folders) {
|
|
|
1181
1286
|
lines.push(...methodExports(typesDir, verbs));
|
|
1182
1287
|
}
|
|
1183
1288
|
lines.push("");
|
|
1184
|
-
|
|
1185
|
-
}
|
|
1289
|
+
return writeGenerated(file, lines.join("\n"));
|
|
1290
|
+
}));
|
|
1186
1291
|
}
|
|
1187
1292
|
|
|
1293
|
+
// src/generator/route-meta.ts
|
|
1294
|
+
var import_node_fs6 = require("fs");
|
|
1295
|
+
var import_typescript = __toESM(require("typescript"));
|
|
1296
|
+
|
|
1188
1297
|
// src/generator/inputs.ts
|
|
1189
1298
|
function sanitize(schema) {
|
|
1190
1299
|
const { $schema, ...rest } = schema;
|
|
@@ -1245,28 +1354,315 @@ function readInput(routeModule) {
|
|
|
1245
1354
|
}
|
|
1246
1355
|
return input.body || input.query ? input : void 0;
|
|
1247
1356
|
}
|
|
1248
|
-
function
|
|
1249
|
-
|
|
1357
|
+
function hasExportModifier(node) {
|
|
1358
|
+
return import_typescript.default.canHaveModifiers(node) && (import_typescript.default.getModifiers(node)?.some((modifier) => modifier.kind === import_typescript.default.SyntaxKind.ExportKeyword) ?? false);
|
|
1359
|
+
}
|
|
1360
|
+
function unwrapExpression(expression) {
|
|
1361
|
+
let current = expression;
|
|
1362
|
+
while (import_typescript.default.isParenthesizedExpression(current) || import_typescript.default.isAsExpression(current) || import_typescript.default.isSatisfiesExpression(current)) {
|
|
1363
|
+
current = current.expression;
|
|
1364
|
+
}
|
|
1365
|
+
return current;
|
|
1366
|
+
}
|
|
1367
|
+
function staticBoolean(expression) {
|
|
1368
|
+
const value = unwrapExpression(expression);
|
|
1369
|
+
if (value.kind === import_typescript.default.SyntaxKind.TrueKeyword) {
|
|
1250
1370
|
return true;
|
|
1251
1371
|
}
|
|
1252
|
-
if (value ===
|
|
1372
|
+
if (value.kind === import_typescript.default.SyntaxKind.FalseKeyword) {
|
|
1253
1373
|
return false;
|
|
1254
1374
|
}
|
|
1255
|
-
|
|
1256
|
-
|
|
1375
|
+
return void 0;
|
|
1376
|
+
}
|
|
1377
|
+
function staticString(expression) {
|
|
1378
|
+
const value = unwrapExpression(expression);
|
|
1379
|
+
return import_typescript.default.isStringLiteralLike(value) ? value.text : void 0;
|
|
1380
|
+
}
|
|
1381
|
+
function staticStringArray(expression) {
|
|
1382
|
+
const value = unwrapExpression(expression);
|
|
1383
|
+
if (!import_typescript.default.isArrayLiteralExpression(value)) {
|
|
1384
|
+
return void 0;
|
|
1385
|
+
}
|
|
1386
|
+
const strings = [];
|
|
1387
|
+
for (const element of value.elements) {
|
|
1388
|
+
const string = staticString(element);
|
|
1389
|
+
if (string === void 0) {
|
|
1390
|
+
return void 0;
|
|
1391
|
+
}
|
|
1392
|
+
strings.push(string);
|
|
1393
|
+
}
|
|
1394
|
+
return strings;
|
|
1395
|
+
}
|
|
1396
|
+
function propertyName(name) {
|
|
1397
|
+
if (import_typescript.default.isIdentifier(name) || import_typescript.default.isStringLiteral(name) || import_typescript.default.isNumericLiteral(name)) {
|
|
1398
|
+
return name.text;
|
|
1257
1399
|
}
|
|
1258
1400
|
return void 0;
|
|
1259
1401
|
}
|
|
1260
|
-
function
|
|
1402
|
+
function collectImportedNames(source) {
|
|
1403
|
+
const names = /* @__PURE__ */ new Set();
|
|
1404
|
+
for (const statement of source.statements) {
|
|
1405
|
+
if (!import_typescript.default.isImportDeclaration(statement)) {
|
|
1406
|
+
continue;
|
|
1407
|
+
}
|
|
1408
|
+
const clause = statement.importClause;
|
|
1409
|
+
if (!clause) {
|
|
1410
|
+
continue;
|
|
1411
|
+
}
|
|
1412
|
+
if (clause.name) {
|
|
1413
|
+
names.add(clause.name.text);
|
|
1414
|
+
}
|
|
1415
|
+
const bindings = clause.namedBindings;
|
|
1416
|
+
if (bindings && import_typescript.default.isNamespaceImport(bindings)) {
|
|
1417
|
+
names.add(bindings.name.text);
|
|
1418
|
+
}
|
|
1419
|
+
if (bindings && import_typescript.default.isNamedImports(bindings)) {
|
|
1420
|
+
for (const element of bindings.elements) {
|
|
1421
|
+
names.add(element.name.text);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
return names;
|
|
1426
|
+
}
|
|
1427
|
+
function expressionReferencesImportedMiddleware(expression, importedNames) {
|
|
1428
|
+
let found = false;
|
|
1429
|
+
const allowedImportedHelpers = /* @__PURE__ */ new Set(["stack", "fromHono"]);
|
|
1430
|
+
const visit = (node) => {
|
|
1431
|
+
if (found) {
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
if (import_typescript.default.isIdentifier(node) && importedNames.has(node.text) && !allowedImportedHelpers.has(node.text)) {
|
|
1435
|
+
found = true;
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
import_typescript.default.forEachChild(node, visit);
|
|
1439
|
+
};
|
|
1440
|
+
visit(expression);
|
|
1441
|
+
return found;
|
|
1442
|
+
}
|
|
1443
|
+
function parseStaticOpenApi(expression) {
|
|
1444
|
+
const value = unwrapExpression(expression);
|
|
1445
|
+
const boolean = staticBoolean(value);
|
|
1446
|
+
if (boolean !== void 0) {
|
|
1447
|
+
return boolean;
|
|
1448
|
+
}
|
|
1449
|
+
if (!import_typescript.default.isObjectLiteralExpression(value)) {
|
|
1450
|
+
return void 0;
|
|
1451
|
+
}
|
|
1452
|
+
const openapi = {};
|
|
1453
|
+
for (const property of value.properties) {
|
|
1454
|
+
if (!import_typescript.default.isPropertyAssignment(property)) {
|
|
1455
|
+
return void 0;
|
|
1456
|
+
}
|
|
1457
|
+
const name = propertyName(property.name);
|
|
1458
|
+
if (!name) {
|
|
1459
|
+
return void 0;
|
|
1460
|
+
}
|
|
1461
|
+
if (name === "hidden") {
|
|
1462
|
+
const hidden = staticBoolean(property.initializer);
|
|
1463
|
+
if (hidden === void 0) {
|
|
1464
|
+
return void 0;
|
|
1465
|
+
}
|
|
1466
|
+
openapi.hidden = hidden;
|
|
1467
|
+
} else if (name === "tags") {
|
|
1468
|
+
const tags = staticStringArray(property.initializer);
|
|
1469
|
+
if (!tags) {
|
|
1470
|
+
return void 0;
|
|
1471
|
+
}
|
|
1472
|
+
openapi.tags = tags;
|
|
1473
|
+
} else if (name === "summary" || name === "description" || name === "operationId") {
|
|
1474
|
+
const string = staticString(property.initializer);
|
|
1475
|
+
if (string === void 0) {
|
|
1476
|
+
return void 0;
|
|
1477
|
+
}
|
|
1478
|
+
openapi[name] = string;
|
|
1479
|
+
} else if (name === "deprecated") {
|
|
1480
|
+
const deprecated = staticBoolean(property.initializer);
|
|
1481
|
+
if (deprecated === void 0) {
|
|
1482
|
+
return void 0;
|
|
1483
|
+
}
|
|
1484
|
+
openapi.deprecated = deprecated;
|
|
1485
|
+
} else {
|
|
1486
|
+
return void 0;
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
return openapi;
|
|
1490
|
+
}
|
|
1491
|
+
function readStaticModuleMeta(file) {
|
|
1492
|
+
let source;
|
|
1493
|
+
try {
|
|
1494
|
+
source = import_typescript.default.createSourceFile(file, (0, import_node_fs6.readFileSync)(file, "utf8"), import_typescript.default.ScriptTarget.Latest, true);
|
|
1495
|
+
} catch {
|
|
1496
|
+
return void 0;
|
|
1497
|
+
}
|
|
1498
|
+
const importedNames = collectImportedNames(source);
|
|
1499
|
+
const sourceText = source.getFullText();
|
|
1500
|
+
const canSkipMiddlewareRuntime = !sourceText.includes("defineMiddleware") && !sourceText.includes(".openapi");
|
|
1501
|
+
const meta = { middlewareSecurity: false };
|
|
1502
|
+
for (const statement of source.statements) {
|
|
1503
|
+
if (import_typescript.default.isImportDeclaration(statement) || import_typescript.default.isInterfaceDeclaration(statement) || import_typescript.default.isTypeAliasDeclaration(statement) || import_typescript.default.isEmptyStatement(statement) || !hasExportModifier(statement)) {
|
|
1504
|
+
continue;
|
|
1505
|
+
}
|
|
1506
|
+
if (import_typescript.default.isFunctionDeclaration(statement) && statement.name?.text === "handle") {
|
|
1507
|
+
continue;
|
|
1508
|
+
}
|
|
1509
|
+
if (import_typescript.default.isVariableStatement(statement)) {
|
|
1510
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
1511
|
+
if (!import_typescript.default.isIdentifier(declaration.name)) {
|
|
1512
|
+
return void 0;
|
|
1513
|
+
}
|
|
1514
|
+
const name = declaration.name.text;
|
|
1515
|
+
if (name === "openapi") {
|
|
1516
|
+
if (!declaration.initializer) {
|
|
1517
|
+
return void 0;
|
|
1518
|
+
}
|
|
1519
|
+
const openapi = parseStaticOpenApi(declaration.initializer);
|
|
1520
|
+
if (openapi === void 0) {
|
|
1521
|
+
return void 0;
|
|
1522
|
+
}
|
|
1523
|
+
meta.openapi = openapi;
|
|
1524
|
+
} else if (name === "handle") {
|
|
1525
|
+
if (!declaration.initializer || !import_typescript.default.isArrowFunction(declaration.initializer) && !import_typescript.default.isFunctionExpression(declaration.initializer)) {
|
|
1526
|
+
return void 0;
|
|
1527
|
+
}
|
|
1528
|
+
continue;
|
|
1529
|
+
} else if (name === "middleware") {
|
|
1530
|
+
if (!declaration.initializer || !canSkipMiddlewareRuntime || expressionReferencesImportedMiddleware(declaration.initializer, importedNames)) {
|
|
1531
|
+
return void 0;
|
|
1532
|
+
}
|
|
1533
|
+
meta.middlewareSecurity = false;
|
|
1534
|
+
continue;
|
|
1535
|
+
} else {
|
|
1536
|
+
return void 0;
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
continue;
|
|
1540
|
+
}
|
|
1541
|
+
return void 0;
|
|
1542
|
+
}
|
|
1543
|
+
return meta;
|
|
1544
|
+
}
|
|
1545
|
+
function resolveStaticOpenApi(route, routeModule, loadShared) {
|
|
1261
1546
|
let hidden = false;
|
|
1547
|
+
const tags = [];
|
|
1548
|
+
const meta = {};
|
|
1549
|
+
const apply = (value, isVerb) => {
|
|
1550
|
+
if (value === false) {
|
|
1551
|
+
hidden = true;
|
|
1552
|
+
return;
|
|
1553
|
+
}
|
|
1554
|
+
if (value === true) {
|
|
1555
|
+
hidden = false;
|
|
1556
|
+
return;
|
|
1557
|
+
}
|
|
1558
|
+
if (!value) {
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
if (typeof value.hidden === "boolean") {
|
|
1562
|
+
hidden = value.hidden;
|
|
1563
|
+
}
|
|
1564
|
+
if (value.tags) {
|
|
1565
|
+
tags.push(...value.tags);
|
|
1566
|
+
}
|
|
1567
|
+
if (typeof value.summary === "string") {
|
|
1568
|
+
meta.summary = value.summary;
|
|
1569
|
+
}
|
|
1570
|
+
if (typeof value.description === "string") {
|
|
1571
|
+
meta.description = value.description;
|
|
1572
|
+
}
|
|
1573
|
+
if (typeof value.deprecated === "boolean") {
|
|
1574
|
+
meta.deprecated = value.deprecated;
|
|
1575
|
+
}
|
|
1576
|
+
if (isVerb && typeof value.operationId === "string") {
|
|
1577
|
+
meta.operationId = value.operationId;
|
|
1578
|
+
}
|
|
1579
|
+
};
|
|
1262
1580
|
for (const file of route.sharedFiles) {
|
|
1263
|
-
const
|
|
1264
|
-
if (
|
|
1265
|
-
|
|
1581
|
+
const shared = loadShared(file);
|
|
1582
|
+
if (!shared) {
|
|
1583
|
+
return void 0;
|
|
1584
|
+
}
|
|
1585
|
+
apply(shared.openapi, false);
|
|
1586
|
+
}
|
|
1587
|
+
apply(routeModule.openapi, true);
|
|
1588
|
+
if (tags.length > 0) {
|
|
1589
|
+
meta.tags = [...new Set(tags)];
|
|
1590
|
+
}
|
|
1591
|
+
return { hidden, meta };
|
|
1592
|
+
}
|
|
1593
|
+
function extractStaticMeta(route, routeModule, loadShared) {
|
|
1594
|
+
const openapi = resolveStaticOpenApi(route, routeModule, loadShared);
|
|
1595
|
+
if (!openapi) {
|
|
1596
|
+
return void 0;
|
|
1597
|
+
}
|
|
1598
|
+
const meta = {};
|
|
1599
|
+
if (openapi.hidden) {
|
|
1600
|
+
meta.hidden = true;
|
|
1601
|
+
}
|
|
1602
|
+
if (Object.keys(openapi.meta).length > 0) {
|
|
1603
|
+
meta.openapi = openapi.meta;
|
|
1604
|
+
}
|
|
1605
|
+
return meta;
|
|
1606
|
+
}
|
|
1607
|
+
function extractRuntimeSharedMeta(route, routeModule, loadShared) {
|
|
1608
|
+
const meta = {};
|
|
1609
|
+
const security = collectSecurity(route, {}, loadShared);
|
|
1610
|
+
const { hidden, meta: openapi } = resolveOpenApi(route, { openapi: routeModule.openapi }, loadShared);
|
|
1611
|
+
if (security) {
|
|
1612
|
+
meta.security = security;
|
|
1613
|
+
}
|
|
1614
|
+
if (hidden) {
|
|
1615
|
+
meta.hidden = true;
|
|
1616
|
+
}
|
|
1617
|
+
if (Object.keys(openapi).length > 0) {
|
|
1618
|
+
meta.openapi = openapi;
|
|
1619
|
+
}
|
|
1620
|
+
return meta;
|
|
1621
|
+
}
|
|
1622
|
+
function resolveOpenApi(route, routeModule, loadShared) {
|
|
1623
|
+
let hidden = false;
|
|
1624
|
+
const tags = [];
|
|
1625
|
+
const meta = {};
|
|
1626
|
+
const apply = (value, isVerb) => {
|
|
1627
|
+
if (value === false) {
|
|
1628
|
+
hidden = true;
|
|
1629
|
+
return;
|
|
1266
1630
|
}
|
|
1631
|
+
if (value === true) {
|
|
1632
|
+
hidden = false;
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
if (!value || typeof value !== "object") {
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
const o = value;
|
|
1639
|
+
if ("hidden" in o) {
|
|
1640
|
+
hidden = Boolean(o.hidden);
|
|
1641
|
+
}
|
|
1642
|
+
if (Array.isArray(o.tags)) {
|
|
1643
|
+
tags.push(...o.tags.filter((tag2) => typeof tag2 === "string"));
|
|
1644
|
+
}
|
|
1645
|
+
if (typeof o.summary === "string") {
|
|
1646
|
+
meta.summary = o.summary;
|
|
1647
|
+
}
|
|
1648
|
+
if (typeof o.description === "string") {
|
|
1649
|
+
meta.description = o.description;
|
|
1650
|
+
}
|
|
1651
|
+
if (typeof o.deprecated === "boolean") {
|
|
1652
|
+
meta.deprecated = o.deprecated;
|
|
1653
|
+
}
|
|
1654
|
+
if (isVerb && typeof o.operationId === "string") {
|
|
1655
|
+
meta.operationId = o.operationId;
|
|
1656
|
+
}
|
|
1657
|
+
};
|
|
1658
|
+
for (const file of route.sharedFiles) {
|
|
1659
|
+
apply(loadShared(file).openapi, false);
|
|
1660
|
+
}
|
|
1661
|
+
apply(routeModule.openapi, true);
|
|
1662
|
+
if (tags.length > 0) {
|
|
1663
|
+
meta.tags = [...new Set(tags)];
|
|
1267
1664
|
}
|
|
1268
|
-
|
|
1269
|
-
return verb ?? hidden;
|
|
1665
|
+
return { hidden, meta };
|
|
1270
1666
|
}
|
|
1271
1667
|
function collectSecurity(route, routeModule, loadShared) {
|
|
1272
1668
|
const skipInherited = Boolean(
|
|
@@ -1298,6 +1694,33 @@ function collectSecurity(route, routeModule, loadShared) {
|
|
|
1298
1694
|
}
|
|
1299
1695
|
async function extractRouteMeta(config, paths, routes) {
|
|
1300
1696
|
const byFile = /* @__PURE__ */ new Map();
|
|
1697
|
+
const remainingRoutes = [];
|
|
1698
|
+
const runtimeSharedRoutes = [];
|
|
1699
|
+
const staticCache = /* @__PURE__ */ new Map();
|
|
1700
|
+
const loadStatic = (file) => {
|
|
1701
|
+
if (!staticCache.has(file)) {
|
|
1702
|
+
staticCache.set(file, readStaticModuleMeta(file));
|
|
1703
|
+
}
|
|
1704
|
+
return staticCache.get(file);
|
|
1705
|
+
};
|
|
1706
|
+
for (const route of routes) {
|
|
1707
|
+
const routeModule = loadStatic(route.file);
|
|
1708
|
+
if (!routeModule) {
|
|
1709
|
+
remainingRoutes.push(route);
|
|
1710
|
+
continue;
|
|
1711
|
+
}
|
|
1712
|
+
const meta = extractStaticMeta(route, routeModule, loadStatic);
|
|
1713
|
+
if (meta) {
|
|
1714
|
+
if (meta.hidden || meta.openapi) {
|
|
1715
|
+
byFile.set(route.file, meta);
|
|
1716
|
+
}
|
|
1717
|
+
continue;
|
|
1718
|
+
}
|
|
1719
|
+
runtimeSharedRoutes.push({ route, routeModule });
|
|
1720
|
+
}
|
|
1721
|
+
if (remainingRoutes.length === 0 && runtimeSharedRoutes.length === 0) {
|
|
1722
|
+
return byFile;
|
|
1723
|
+
}
|
|
1301
1724
|
const { unregister } = await safeRegister();
|
|
1302
1725
|
const unregisterAlias = registerAliasResolver(config.alias, paths.cwd);
|
|
1303
1726
|
const sharedCache = /* @__PURE__ */ new Map();
|
|
@@ -1312,13 +1735,19 @@ async function extractRouteMeta(config, paths, routes) {
|
|
|
1312
1735
|
return sharedCache.get(file);
|
|
1313
1736
|
};
|
|
1314
1737
|
try {
|
|
1315
|
-
for (const route of
|
|
1738
|
+
for (const { route, routeModule } of runtimeSharedRoutes) {
|
|
1739
|
+
const meta = extractRuntimeSharedMeta(route, routeModule, loadShared);
|
|
1740
|
+
if (meta.input || meta.security || meta.hidden || meta.openapi) {
|
|
1741
|
+
byFile.set(route.file, meta);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
for (const route of remainingRoutes) {
|
|
1316
1745
|
try {
|
|
1317
1746
|
const routeModule = loadModule2(route.file);
|
|
1318
1747
|
const meta = {};
|
|
1319
1748
|
const input = readInput(routeModule);
|
|
1320
1749
|
const security = collectSecurity(route, routeModule, loadShared);
|
|
1321
|
-
const hidden =
|
|
1750
|
+
const { hidden, meta: openapi } = resolveOpenApi(route, routeModule, loadShared);
|
|
1322
1751
|
if (input) {
|
|
1323
1752
|
meta.input = input;
|
|
1324
1753
|
}
|
|
@@ -1328,7 +1757,10 @@ async function extractRouteMeta(config, paths, routes) {
|
|
|
1328
1757
|
if (hidden) {
|
|
1329
1758
|
meta.hidden = true;
|
|
1330
1759
|
}
|
|
1331
|
-
if (
|
|
1760
|
+
if (Object.keys(openapi).length > 0) {
|
|
1761
|
+
meta.openapi = openapi;
|
|
1762
|
+
}
|
|
1763
|
+
if (meta.input || meta.security || meta.hidden || meta.openapi) {
|
|
1332
1764
|
byFile.set(route.file, meta);
|
|
1333
1765
|
}
|
|
1334
1766
|
} catch {
|
|
@@ -1400,6 +1832,103 @@ async function writeTsConfig(paths, config) {
|
|
|
1400
1832
|
});
|
|
1401
1833
|
}
|
|
1402
1834
|
|
|
1835
|
+
// src/generator/cache.ts
|
|
1836
|
+
var import_node_crypto = require("crypto");
|
|
1837
|
+
var import_node_fs7 = require("fs");
|
|
1838
|
+
var import_promises3 = require("fs/promises");
|
|
1839
|
+
var import_node_path11 = require("path");
|
|
1840
|
+
var import_tinyglobby2 = require("tinyglobby");
|
|
1841
|
+
var CACHE_VERSION = 1;
|
|
1842
|
+
var SYNC_CACHE_NAME = ".sync-cache.json";
|
|
1843
|
+
function stableConfig(config) {
|
|
1844
|
+
const alias = Object.entries(config.alias ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => [key, Array.isArray(value) ? [...value] : value]);
|
|
1845
|
+
return { alias, outDir: config.outDir ?? ".giri" };
|
|
1846
|
+
}
|
|
1847
|
+
async function syncFingerprint(config, paths) {
|
|
1848
|
+
const outRelative = slash((0, import_node_path11.relative)(paths.cwd, paths.outDir));
|
|
1849
|
+
const ignore = ["**/node_modules/**", "**/.git/**"];
|
|
1850
|
+
if (outRelative && !outRelative.startsWith("..")) {
|
|
1851
|
+
ignore.push(`${outRelative}/**`);
|
|
1852
|
+
}
|
|
1853
|
+
const files = await (0, import_tinyglobby2.glob)([
|
|
1854
|
+
"src/**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs,json}",
|
|
1855
|
+
"giri.config.{ts,js,mts,cts,mjs,cjs}",
|
|
1856
|
+
"tsconfig*.json",
|
|
1857
|
+
"package.json",
|
|
1858
|
+
"package-lock.json",
|
|
1859
|
+
"npm-shrinkwrap.json",
|
|
1860
|
+
"yarn.lock",
|
|
1861
|
+
"pnpm-lock.yaml",
|
|
1862
|
+
"bun.lock",
|
|
1863
|
+
"bun.lockb"
|
|
1864
|
+
], {
|
|
1865
|
+
cwd: paths.cwd,
|
|
1866
|
+
absolute: false,
|
|
1867
|
+
onlyFiles: true,
|
|
1868
|
+
dot: true,
|
|
1869
|
+
ignore
|
|
1870
|
+
});
|
|
1871
|
+
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
1872
|
+
hash.update(JSON.stringify(stableConfig(config)));
|
|
1873
|
+
for (const file of files.sort()) {
|
|
1874
|
+
hash.update("\0");
|
|
1875
|
+
hash.update(slash(file));
|
|
1876
|
+
hash.update("\0");
|
|
1877
|
+
hash.update(await (0, import_promises3.readFile)((0, import_node_path11.resolve)(paths.cwd, file)));
|
|
1878
|
+
}
|
|
1879
|
+
return hash.digest("hex");
|
|
1880
|
+
}
|
|
1881
|
+
function cachePath(paths) {
|
|
1882
|
+
return (0, import_node_path11.join)(paths.outDir, SYNC_CACHE_NAME);
|
|
1883
|
+
}
|
|
1884
|
+
function serializePath(paths, file) {
|
|
1885
|
+
return slash((0, import_node_path11.relative)(paths.cwd, file));
|
|
1886
|
+
}
|
|
1887
|
+
function deserializePath(paths, file) {
|
|
1888
|
+
return slash((0, import_node_path11.resolve)(paths.cwd, file.split("/").join(import_node_path11.sep)));
|
|
1889
|
+
}
|
|
1890
|
+
function serializeMap(paths, values) {
|
|
1891
|
+
return [...values].map(([file, value]) => [serializePath(paths, file), value]);
|
|
1892
|
+
}
|
|
1893
|
+
function deserializeMap(paths, values) {
|
|
1894
|
+
return new Map(values.map(([file, value]) => [deserializePath(paths, file), value]));
|
|
1895
|
+
}
|
|
1896
|
+
async function readSyncCache(paths, fingerprint) {
|
|
1897
|
+
const file = cachePath(paths);
|
|
1898
|
+
if (!(0, import_node_fs7.existsSync)(file)) {
|
|
1899
|
+
return void 0;
|
|
1900
|
+
}
|
|
1901
|
+
try {
|
|
1902
|
+
const cache = JSON.parse(await (0, import_promises3.readFile)(file, "utf8"));
|
|
1903
|
+
if (cache.version !== CACHE_VERSION || cache.fingerprint !== fingerprint) {
|
|
1904
|
+
return void 0;
|
|
1905
|
+
}
|
|
1906
|
+
return {
|
|
1907
|
+
responsesByFile: deserializeMap(paths, cache.data.responsesByFile),
|
|
1908
|
+
inputsByFile: deserializeMap(paths, cache.data.inputsByFile),
|
|
1909
|
+
securityByFile: deserializeMap(paths, cache.data.securityByFile),
|
|
1910
|
+
hiddenFiles: new Set(cache.data.hiddenFiles.map((entry) => deserializePath(paths, entry))),
|
|
1911
|
+
openapiByFile: deserializeMap(paths, cache.data.openapiByFile)
|
|
1912
|
+
};
|
|
1913
|
+
} catch {
|
|
1914
|
+
return void 0;
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
async function writeSyncCache(paths, fingerprint, data) {
|
|
1918
|
+
const cache = {
|
|
1919
|
+
version: CACHE_VERSION,
|
|
1920
|
+
fingerprint,
|
|
1921
|
+
data: {
|
|
1922
|
+
responsesByFile: serializeMap(paths, data.responsesByFile),
|
|
1923
|
+
inputsByFile: serializeMap(paths, data.inputsByFile),
|
|
1924
|
+
securityByFile: serializeMap(paths, data.securityByFile),
|
|
1925
|
+
hiddenFiles: [...data.hiddenFiles].map((file) => serializePath(paths, file)),
|
|
1926
|
+
openapiByFile: serializeMap(paths, data.openapiByFile)
|
|
1927
|
+
}
|
|
1928
|
+
};
|
|
1929
|
+
await writeJson(cachePath(paths), cache);
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1403
1932
|
// src/generator/sync.ts
|
|
1404
1933
|
async function typeFolders(paths, routes) {
|
|
1405
1934
|
const verbsByDir = /* @__PURE__ */ new Map();
|
|
@@ -1410,10 +1939,11 @@ async function typeFolders(paths, routes) {
|
|
|
1410
1939
|
verbsByDir.set(key, list);
|
|
1411
1940
|
}
|
|
1412
1941
|
const dirs = await scanRouteFolders(paths.routesDir);
|
|
1942
|
+
const sharedCache = /* @__PURE__ */ new Map();
|
|
1413
1943
|
return dirs.map((dir) => ({
|
|
1414
1944
|
dir,
|
|
1415
1945
|
params: routeParamsForDir(paths.routesDir, dir),
|
|
1416
|
-
sharedFiles: sharedFilesForDir(paths.routesDir, dir),
|
|
1946
|
+
sharedFiles: sharedFilesForDir(paths.routesDir, dir, sharedCache),
|
|
1417
1947
|
verbs: verbsByDir.get(slash(dir)) ?? []
|
|
1418
1948
|
}));
|
|
1419
1949
|
}
|
|
@@ -1425,11 +1955,9 @@ async function extractResponses(paths, routes) {
|
|
|
1425
1955
|
try {
|
|
1426
1956
|
const { createSchemaProgram: createSchemaProgram2, extractRouteResponses: extractRouteResponses2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
|
|
1427
1957
|
const files = [...new Set(routes.map((route) => route.file))];
|
|
1428
|
-
const appTypes = (0,
|
|
1429
|
-
const
|
|
1430
|
-
|
|
1431
|
-
(0, import_node_fs6.existsSync)(appTypes) ? [...files, appTypes] : files
|
|
1432
|
-
);
|
|
1958
|
+
const appTypes = (0, import_node_path12.join)(paths.outDir, "types", "app.d.ts");
|
|
1959
|
+
const roots = (0, import_node_fs8.existsSync)(appTypes) ? [...files, appTypes] : files;
|
|
1960
|
+
const program = createSchemaProgram2(paths, roots);
|
|
1433
1961
|
for (const file of files) {
|
|
1434
1962
|
byFile.set(file, extractRouteResponses2(program, file));
|
|
1435
1963
|
}
|
|
@@ -1442,8 +1970,9 @@ async function extractMeta(config, paths, routes) {
|
|
|
1442
1970
|
const inputsByFile = /* @__PURE__ */ new Map();
|
|
1443
1971
|
const securityByFile = /* @__PURE__ */ new Map();
|
|
1444
1972
|
const hiddenFiles = /* @__PURE__ */ new Set();
|
|
1973
|
+
const openapiByFile = /* @__PURE__ */ new Map();
|
|
1445
1974
|
if (routes.length === 0) {
|
|
1446
|
-
return { inputsByFile, securityByFile, hiddenFiles };
|
|
1975
|
+
return { inputsByFile, securityByFile, hiddenFiles, openapiByFile };
|
|
1447
1976
|
}
|
|
1448
1977
|
try {
|
|
1449
1978
|
const meta = await extractRouteMeta(config, paths, routes);
|
|
@@ -1457,79 +1986,180 @@ async function extractMeta(config, paths, routes) {
|
|
|
1457
1986
|
if (entry.hidden) {
|
|
1458
1987
|
hiddenFiles.add(file);
|
|
1459
1988
|
}
|
|
1989
|
+
if (entry.openapi) {
|
|
1990
|
+
openapiByFile.set(file, entry.openapi);
|
|
1991
|
+
}
|
|
1460
1992
|
}
|
|
1461
1993
|
} catch (error) {
|
|
1462
1994
|
console.warn(`giri: skipped input/security generation (${error.message}).`);
|
|
1463
1995
|
}
|
|
1464
|
-
return { inputsByFile, securityByFile, hiddenFiles };
|
|
1996
|
+
return { inputsByFile, securityByFile, hiddenFiles, openapiByFile };
|
|
1465
1997
|
}
|
|
1466
1998
|
async function syncProject(config, options = {}) {
|
|
1467
1999
|
const paths = resolveGiriPaths(config, options.cwd);
|
|
1468
2000
|
assertSafeOutDir(paths);
|
|
2001
|
+
const hadOutDir = (0, import_node_fs8.existsSync)(paths.outDir);
|
|
1469
2002
|
const routes = await scanRoutes(paths.routesDir);
|
|
1470
2003
|
const folders = await typeFolders(paths, routes);
|
|
1471
|
-
await (
|
|
2004
|
+
const fingerprint = await syncFingerprint(config, paths);
|
|
2005
|
+
const cached = await readSyncCache(paths, fingerprint);
|
|
2006
|
+
const generatedFiles = [
|
|
2007
|
+
(0, import_node_path12.join)(paths.outDir, "tsconfig.json"),
|
|
2008
|
+
(0, import_node_path12.join)(paths.outDir, "manifest.json"),
|
|
2009
|
+
(0, import_node_path12.join)(paths.outDir, "openapi.json"),
|
|
2010
|
+
(0, import_node_path12.join)(paths.outDir, "routes.d.ts"),
|
|
2011
|
+
(0, import_node_path12.join)(paths.outDir, "types", "app.d.ts"),
|
|
2012
|
+
...folders.map((folder) => typeFilePath(paths, folder.dir))
|
|
2013
|
+
];
|
|
2014
|
+
if (cached && generatedFiles.every(import_node_fs8.existsSync)) {
|
|
2015
|
+
return { paths, routes, folders, data: cached };
|
|
2016
|
+
}
|
|
2017
|
+
await (0, import_promises4.mkdir)(paths.outDir, { recursive: true });
|
|
1472
2018
|
await writeParamTypes(paths, folders);
|
|
1473
2019
|
await writeRouteTypes(paths, routes);
|
|
1474
2020
|
await writeAppTypes(paths);
|
|
1475
2021
|
await writeTsConfig(paths, config);
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1478
|
-
|
|
2022
|
+
const data = cached ?? {
|
|
2023
|
+
responsesByFile: await extractResponses(paths, routes),
|
|
2024
|
+
...await extractMeta(config, paths, routes)
|
|
2025
|
+
};
|
|
1479
2026
|
await writeManifest(paths, routes, data);
|
|
1480
2027
|
await writeOpenApi(paths, routes, data);
|
|
1481
|
-
await
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
2028
|
+
await writeSyncCache(paths, fingerprint, data);
|
|
2029
|
+
if (hadOutDir) {
|
|
2030
|
+
await pruneDir(
|
|
2031
|
+
paths.outDir,
|
|
2032
|
+
/* @__PURE__ */ new Set([
|
|
2033
|
+
(0, import_node_path12.join)(paths.outDir, "tsconfig.json"),
|
|
2034
|
+
(0, import_node_path12.join)(paths.outDir, "manifest.json"),
|
|
2035
|
+
(0, import_node_path12.join)(paths.outDir, "openapi.json"),
|
|
2036
|
+
(0, import_node_path12.join)(paths.outDir, "routes.d.ts"),
|
|
2037
|
+
(0, import_node_path12.join)(paths.outDir, SYNC_CACHE_NAME),
|
|
2038
|
+
(0, import_node_path12.join)(paths.outDir, "types", "app.d.ts"),
|
|
2039
|
+
...folders.map((folder) => typeFilePath(paths, folder.dir))
|
|
2040
|
+
])
|
|
2041
|
+
);
|
|
2042
|
+
}
|
|
1492
2043
|
return { paths, routes, folders, data };
|
|
1493
2044
|
}
|
|
1494
2045
|
|
|
1495
2046
|
// src/generator/watch.ts
|
|
1496
|
-
var
|
|
1497
|
-
var
|
|
2047
|
+
var import_node_fs10 = require("fs");
|
|
2048
|
+
var import_node_path15 = require("path");
|
|
1498
2049
|
|
|
1499
|
-
// src/loader/
|
|
1500
|
-
var
|
|
1501
|
-
var
|
|
1502
|
-
var
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
var
|
|
1506
|
-
|
|
2050
|
+
// src/loader/import-graph.ts
|
|
2051
|
+
var import_node_fs9 = require("fs");
|
|
2052
|
+
var import_node_path13 = require("path");
|
|
2053
|
+
var import_typescript5 = __toESM(require("typescript"));
|
|
2054
|
+
var import_tinyglobby3 = require("tinyglobby");
|
|
2055
|
+
var RESOLVE_EXTS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
2056
|
+
var JS_EXT = /\.(?:c|m)?jsx?$/;
|
|
2057
|
+
var toSlash = (path) => path.split(import_node_path13.sep).join("/");
|
|
2058
|
+
function probeFile(base) {
|
|
2059
|
+
if ((0, import_node_fs9.existsSync)(base) && (0, import_node_fs9.statSync)(base).isFile()) {
|
|
2060
|
+
return base;
|
|
2061
|
+
}
|
|
2062
|
+
for (const ext of RESOLVE_EXTS) {
|
|
2063
|
+
const candidate = base + ext;
|
|
2064
|
+
if ((0, import_node_fs9.existsSync)(candidate)) {
|
|
2065
|
+
return candidate;
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
for (const ext of RESOLVE_EXTS) {
|
|
2069
|
+
const candidate = (0, import_node_path13.join)(base, `index${ext}`);
|
|
2070
|
+
if ((0, import_node_fs9.existsSync)(candidate)) {
|
|
2071
|
+
return candidate;
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
return void 0;
|
|
2075
|
+
}
|
|
2076
|
+
function resolveSpecifier(specifier, fromFile, alias, cwd) {
|
|
2077
|
+
let target;
|
|
2078
|
+
if (specifier.startsWith(".")) {
|
|
2079
|
+
target = (0, import_node_path13.resolve)((0, import_node_path13.dirname)(fromFile), specifier);
|
|
2080
|
+
} else {
|
|
2081
|
+
const aliased = resolveAliasRequest(specifier, alias, cwd);
|
|
2082
|
+
if (aliased === void 0) {
|
|
2083
|
+
return void 0;
|
|
2084
|
+
}
|
|
2085
|
+
target = aliased;
|
|
2086
|
+
}
|
|
2087
|
+
const resolved = probeFile(target);
|
|
2088
|
+
if (resolved) {
|
|
2089
|
+
return resolved;
|
|
2090
|
+
}
|
|
2091
|
+
if (JS_EXT.test(target)) {
|
|
2092
|
+
return probeFile(target.replace(JS_EXT, ""));
|
|
2093
|
+
}
|
|
2094
|
+
return void 0;
|
|
2095
|
+
}
|
|
2096
|
+
function importSpecifiers(file) {
|
|
2097
|
+
let source;
|
|
2098
|
+
try {
|
|
2099
|
+
source = import_typescript5.default.createSourceFile(file, (0, import_node_fs9.readFileSync)(file, "utf8"), import_typescript5.default.ScriptTarget.Latest, false);
|
|
2100
|
+
} catch {
|
|
2101
|
+
return [];
|
|
2102
|
+
}
|
|
2103
|
+
const specifiers = [];
|
|
2104
|
+
const visit = (node) => {
|
|
2105
|
+
if ((import_typescript5.default.isImportDeclaration(node) || import_typescript5.default.isExportDeclaration(node)) && node.moduleSpecifier && import_typescript5.default.isStringLiteral(node.moduleSpecifier)) {
|
|
2106
|
+
specifiers.push(node.moduleSpecifier.text);
|
|
2107
|
+
} else if (import_typescript5.default.isImportEqualsDeclaration(node) && import_typescript5.default.isExternalModuleReference(node.moduleReference) && import_typescript5.default.isStringLiteralLike(node.moduleReference.expression)) {
|
|
2108
|
+
specifiers.push(node.moduleReference.expression.text);
|
|
2109
|
+
} else if (import_typescript5.default.isCallExpression(node)) {
|
|
2110
|
+
const isRequire = import_typescript5.default.isIdentifier(node.expression) && node.expression.text === "require";
|
|
2111
|
+
const isDynamicImport = node.expression.kind === import_typescript5.default.SyntaxKind.ImportKeyword;
|
|
2112
|
+
const [first] = node.arguments;
|
|
2113
|
+
if ((isRequire || isDynamicImport) && first && import_typescript5.default.isStringLiteralLike(first)) {
|
|
2114
|
+
specifiers.push(first.text);
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
import_typescript5.default.forEachChild(node, visit);
|
|
2118
|
+
};
|
|
2119
|
+
visit(source);
|
|
2120
|
+
return specifiers;
|
|
2121
|
+
}
|
|
2122
|
+
async function buildImportGraph(config, cwd) {
|
|
2123
|
+
const root = (0, import_node_path13.resolve)(cwd);
|
|
2124
|
+
const outDir = (0, import_node_path13.resolve)(root, config.outDir ?? ".giri") + import_node_path13.sep;
|
|
2125
|
+
const outRel = (0, import_node_path13.relative)(root, outDir).split(import_node_path13.sep).join("/").replace(/\/$/, "");
|
|
2126
|
+
const files = await (0, import_tinyglobby3.glob)("**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}", {
|
|
2127
|
+
cwd: root,
|
|
2128
|
+
absolute: true,
|
|
2129
|
+
onlyFiles: true,
|
|
2130
|
+
ignore: ["**/node_modules/**", "**/.git/**", outRel ? `${outRel}/**` : ".giri/**"]
|
|
2131
|
+
});
|
|
1507
2132
|
const importers = /* @__PURE__ */ new Map();
|
|
1508
2133
|
const nodes = /* @__PURE__ */ new Set();
|
|
1509
|
-
for (const
|
|
1510
|
-
if (
|
|
1511
|
-
continue;
|
|
1512
|
-
}
|
|
1513
|
-
const mod = require.cache[id];
|
|
1514
|
-
if (!mod) {
|
|
2134
|
+
for (const file of files) {
|
|
2135
|
+
if (file.startsWith(outDir)) {
|
|
1515
2136
|
continue;
|
|
1516
2137
|
}
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
if (!
|
|
2138
|
+
for (const specifier of importSpecifiers(file)) {
|
|
2139
|
+
const dep = resolveSpecifier(specifier, file, config.alias, root);
|
|
2140
|
+
if (!dep || dep.startsWith(outDir)) {
|
|
1520
2141
|
continue;
|
|
1521
2142
|
}
|
|
1522
|
-
|
|
1523
|
-
const
|
|
1524
|
-
|
|
2143
|
+
const from = toSlash(file);
|
|
2144
|
+
const to = toSlash(dep);
|
|
2145
|
+
nodes.add(from);
|
|
2146
|
+
nodes.add(to);
|
|
2147
|
+
let set = importers.get(to);
|
|
1525
2148
|
if (!set) {
|
|
1526
2149
|
set = /* @__PURE__ */ new Set();
|
|
1527
|
-
importers.set(
|
|
2150
|
+
importers.set(to, set);
|
|
1528
2151
|
}
|
|
1529
|
-
set.add(
|
|
2152
|
+
set.add(from);
|
|
1530
2153
|
}
|
|
1531
2154
|
}
|
|
1532
2155
|
return { importers, nodes };
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
// src/loader/module-loader.ts
|
|
2159
|
+
var import_node_path14 = require("path");
|
|
2160
|
+
var toSlash2 = (path) => path.split(import_node_path14.sep).join("/");
|
|
2161
|
+
var isProjectModule = (id, root) => {
|
|
2162
|
+
return id.startsWith(root) && !id.includes(`${import_node_path14.sep}node_modules${import_node_path14.sep}`) && !id.includes(`${import_node_path14.sep}.giri${import_node_path14.sep}`);
|
|
1533
2163
|
};
|
|
1534
2164
|
var collectDependents = (graph, start) => {
|
|
1535
2165
|
const out = /* @__PURE__ */ new Set([start]);
|
|
@@ -1547,19 +2177,27 @@ var collectDependents = (graph, start) => {
|
|
|
1547
2177
|
};
|
|
1548
2178
|
var purgeModules = (files) => {
|
|
1549
2179
|
for (const id of Object.keys(require.cache)) {
|
|
1550
|
-
if (files.has(
|
|
2180
|
+
if (files.has(toSlash2(id))) {
|
|
1551
2181
|
delete require.cache[id];
|
|
1552
2182
|
}
|
|
1553
2183
|
}
|
|
1554
2184
|
};
|
|
1555
2185
|
var purgeProjectModules = (cwd) => {
|
|
1556
|
-
const root = (0,
|
|
2186
|
+
const root = (0, import_node_path14.resolve)(cwd) + import_node_path14.sep;
|
|
1557
2187
|
for (const id of Object.keys(require.cache)) {
|
|
1558
2188
|
if (isProjectModule(id, root)) {
|
|
1559
2189
|
delete require.cache[id];
|
|
1560
2190
|
}
|
|
1561
2191
|
}
|
|
1562
2192
|
};
|
|
2193
|
+
var purgeGeneratedModules = (outDir) => {
|
|
2194
|
+
const root = (0, import_node_path14.resolve)(outDir) + import_node_path14.sep;
|
|
2195
|
+
for (const id of Object.keys(require.cache)) {
|
|
2196
|
+
if ((0, import_node_path14.resolve)(id).startsWith(root)) {
|
|
2197
|
+
delete require.cache[id];
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
};
|
|
1563
2201
|
|
|
1564
2202
|
// src/generator/watch.ts
|
|
1565
2203
|
function createWatchUpdater(config, initial) {
|
|
@@ -1574,14 +2212,16 @@ function createWatchUpdater(config, initial) {
|
|
|
1574
2212
|
data.inputsByFile = result.data.inputsByFile;
|
|
1575
2213
|
data.securityByFile = result.data.securityByFile;
|
|
1576
2214
|
data.hiddenFiles = result.data.hiddenFiles;
|
|
2215
|
+
data.openapiByFile = result.data.openapiByFile;
|
|
2216
|
+
purgeGeneratedModules(paths.outDir);
|
|
1577
2217
|
return "full";
|
|
1578
2218
|
};
|
|
1579
2219
|
const reextractRoute = async (route) => {
|
|
1580
2220
|
const key = route.file;
|
|
1581
2221
|
try {
|
|
1582
2222
|
const { createSchemaProgram: createSchemaProgram2, extractRouteResponses: extractRouteResponses2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
|
|
1583
|
-
const appTypes = (0,
|
|
1584
|
-
const program = createSchemaProgram2(paths, (0,
|
|
2223
|
+
const appTypes = (0, import_node_path15.join)(paths.outDir, "types", "app.d.ts");
|
|
2224
|
+
const program = createSchemaProgram2(paths, (0, import_node_fs10.existsSync)(appTypes) ? [key, appTypes] : [key]);
|
|
1585
2225
|
data.responsesByFile.set(key, extractRouteResponses2(program, key));
|
|
1586
2226
|
} catch {
|
|
1587
2227
|
}
|
|
@@ -1591,6 +2231,7 @@ function createWatchUpdater(config, initial) {
|
|
|
1591
2231
|
data.inputsByFile.delete(key);
|
|
1592
2232
|
data.securityByFile.delete(key);
|
|
1593
2233
|
data.hiddenFiles.delete(key);
|
|
2234
|
+
data.openapiByFile.delete(key);
|
|
1594
2235
|
if (entry?.input) {
|
|
1595
2236
|
data.inputsByFile.set(key, entry.input);
|
|
1596
2237
|
}
|
|
@@ -1600,6 +2241,9 @@ function createWatchUpdater(config, initial) {
|
|
|
1600
2241
|
if (entry?.hidden) {
|
|
1601
2242
|
data.hiddenFiles.add(key);
|
|
1602
2243
|
}
|
|
2244
|
+
if (entry?.openapi) {
|
|
2245
|
+
data.openapiByFile.set(key, entry.openapi);
|
|
2246
|
+
}
|
|
1603
2247
|
} catch {
|
|
1604
2248
|
}
|
|
1605
2249
|
};
|
|
@@ -1608,12 +2252,15 @@ function createWatchUpdater(config, initial) {
|
|
|
1608
2252
|
if (!filename) {
|
|
1609
2253
|
return fullResync();
|
|
1610
2254
|
}
|
|
1611
|
-
const abs = (0,
|
|
2255
|
+
const abs = (0, import_node_path15.resolve)((0, import_node_path15.dirname)(paths.routesDir), filename);
|
|
1612
2256
|
const file = slash(abs);
|
|
1613
|
-
if (!(0,
|
|
2257
|
+
if (!(0, import_node_fs10.existsSync)(abs)) {
|
|
1614
2258
|
return fullResync();
|
|
1615
2259
|
}
|
|
1616
|
-
|
|
2260
|
+
if ((0, import_node_fs10.statSync)(abs).isDirectory()) {
|
|
2261
|
+
return "skip";
|
|
2262
|
+
}
|
|
2263
|
+
const graph = await buildImportGraph(config, paths.cwd);
|
|
1617
2264
|
const isRoute = routes.some((candidate) => slash(candidate.file) === file);
|
|
1618
2265
|
if (!graph.nodes.has(file) && !isRoute) {
|
|
1619
2266
|
return fullResync();
|
|
@@ -1628,26 +2275,28 @@ function createWatchUpdater(config, initial) {
|
|
|
1628
2275
|
}
|
|
1629
2276
|
await writeManifest(paths, routes, data);
|
|
1630
2277
|
await writeOpenApi(paths, routes, data);
|
|
2278
|
+
await writeSyncCache(paths, await syncFingerprint(config, paths), data);
|
|
2279
|
+
purgeGeneratedModules(paths.outDir);
|
|
1631
2280
|
return "incremental";
|
|
1632
2281
|
}
|
|
1633
2282
|
};
|
|
1634
2283
|
}
|
|
1635
2284
|
|
|
1636
2285
|
// src/lifecycle.ts
|
|
1637
|
-
var
|
|
1638
|
-
var
|
|
2286
|
+
var import_node_fs11 = require("fs");
|
|
2287
|
+
var import_node_path16 = require("path");
|
|
1639
2288
|
var MAIN_EXTENSIONS2 = ["ts", "tsx", "mts", "cts", "js", "jsx", "mjs", "cjs"];
|
|
1640
2289
|
function resolveMainFile(cwd) {
|
|
1641
2290
|
for (const ext of MAIN_EXTENSIONS2) {
|
|
1642
|
-
const file = (0,
|
|
1643
|
-
if ((0,
|
|
2291
|
+
const file = (0, import_node_path16.join)(cwd, "src", `main.${ext}`);
|
|
2292
|
+
if ((0, import_node_fs11.existsSync)(file)) {
|
|
1644
2293
|
return file;
|
|
1645
2294
|
}
|
|
1646
2295
|
}
|
|
1647
2296
|
return void 0;
|
|
1648
2297
|
}
|
|
1649
2298
|
async function loadLifecycle(cwd = process.cwd()) {
|
|
1650
|
-
const file = resolveMainFile((0,
|
|
2299
|
+
const file = resolveMainFile((0, import_node_path16.resolve)(cwd));
|
|
1651
2300
|
if (!file) {
|
|
1652
2301
|
return {};
|
|
1653
2302
|
}
|
|
@@ -1805,25 +2454,25 @@ function parseFlags(args) {
|
|
|
1805
2454
|
return flags;
|
|
1806
2455
|
}
|
|
1807
2456
|
async function ensureGitignore(cwd) {
|
|
1808
|
-
const file = (0,
|
|
2457
|
+
const file = (0, import_node_path17.join)(cwd, ".gitignore");
|
|
1809
2458
|
const entry = ".giri";
|
|
1810
|
-
if (!(0,
|
|
1811
|
-
await (0,
|
|
2459
|
+
if (!(0, import_node_fs12.existsSync)(file)) {
|
|
2460
|
+
await (0, import_promises5.writeFile)(file, `${entry}
|
|
1812
2461
|
`);
|
|
1813
2462
|
return;
|
|
1814
2463
|
}
|
|
1815
|
-
const content = await (0,
|
|
2464
|
+
const content = await (0, import_promises5.readFile)(file, "utf8");
|
|
1816
2465
|
if (!content.split(/\r?\n/).includes(entry)) {
|
|
1817
|
-
await (0,
|
|
2466
|
+
await (0, import_promises5.appendFile)(file, `${content.endsWith("\n") ? "" : "\n"}${entry}
|
|
1818
2467
|
`);
|
|
1819
2468
|
}
|
|
1820
2469
|
}
|
|
1821
2470
|
async function ensureTsConfig(cwd) {
|
|
1822
|
-
const file = (0,
|
|
1823
|
-
if ((0,
|
|
2471
|
+
const file = (0, import_node_path17.join)(cwd, "tsconfig.json");
|
|
2472
|
+
if ((0, import_node_fs12.existsSync)(file)) {
|
|
1824
2473
|
return;
|
|
1825
2474
|
}
|
|
1826
|
-
await (0,
|
|
2475
|
+
await (0, import_promises5.writeFile)(
|
|
1827
2476
|
file,
|
|
1828
2477
|
`${JSON.stringify(
|
|
1829
2478
|
{
|
|
@@ -1849,7 +2498,7 @@ async function ensureTsConfig(cwd) {
|
|
|
1849
2498
|
async function missingDeps(cwd, candidates) {
|
|
1850
2499
|
let pkg = {};
|
|
1851
2500
|
try {
|
|
1852
|
-
pkg = JSON.parse(await (0,
|
|
2501
|
+
pkg = JSON.parse(await (0, import_promises5.readFile)((0, import_node_path17.join)(cwd, "package.json"), "utf8"));
|
|
1853
2502
|
} catch {
|
|
1854
2503
|
}
|
|
1855
2504
|
const present = /* @__PURE__ */ new Set();
|
|
@@ -1918,7 +2567,7 @@ async function selectAdapter(interactive) {
|
|
|
1918
2567
|
return ADAPTERS.find((adapter) => adapter.value === picked) ?? null;
|
|
1919
2568
|
}
|
|
1920
2569
|
async function initProject(cwd, flags) {
|
|
1921
|
-
if (!(0,
|
|
2570
|
+
if (!(0, import_node_fs12.existsSync)((0, import_node_path17.join)(cwd, "package.json"))) {
|
|
1922
2571
|
throw new Error(
|
|
1923
2572
|
"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
2573
|
);
|
|
@@ -1943,14 +2592,14 @@ async function initProject(cwd, flags) {
|
|
|
1943
2592
|
prompts.cancel(`The ${adapter.label} adapter isn't available yet - only Hono ships today.`);
|
|
1944
2593
|
return;
|
|
1945
2594
|
}
|
|
1946
|
-
const configPath = (0,
|
|
1947
|
-
if (!(0,
|
|
1948
|
-
await (0,
|
|
2595
|
+
const configPath = (0, import_node_path17.join)(cwd, "giri.config.ts");
|
|
2596
|
+
if (!(0, import_node_fs12.existsSync)(configPath)) {
|
|
2597
|
+
await (0, import_promises5.writeFile)(configPath, configSource(adapter));
|
|
1949
2598
|
}
|
|
1950
|
-
const routePath = (0,
|
|
1951
|
-
if (!(0,
|
|
1952
|
-
await (0,
|
|
1953
|
-
await (0,
|
|
2599
|
+
const routePath = (0, import_node_path17.join)(cwd, "src", "routes", "+get.ts");
|
|
2600
|
+
if (!(0, import_node_fs12.existsSync)(routePath)) {
|
|
2601
|
+
await (0, import_promises5.mkdir)((0, import_node_path17.join)(cwd, "src", "routes"), { recursive: true });
|
|
2602
|
+
await (0, import_promises5.writeFile)(
|
|
1954
2603
|
routePath,
|
|
1955
2604
|
[
|
|
1956
2605
|
'import type { Handle } from "@boon4681/giri";',
|
|
@@ -2017,69 +2666,156 @@ function displayHost(address) {
|
|
|
2017
2666
|
return address.includes(":") ? `[${address}]` : address;
|
|
2018
2667
|
}
|
|
2019
2668
|
async function serveProject(config, flags) {
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2669
|
+
Error.stackTraceLimit = 30;
|
|
2670
|
+
const { watch } = await import("chokidar");
|
|
2671
|
+
let stop;
|
|
2672
|
+
const boot = async (cfg) => {
|
|
2673
|
+
const closers = [];
|
|
2674
|
+
closers.push(registerAliasResolver(cfg.alias, (0, import_node_path17.resolve)(process.cwd())));
|
|
2675
|
+
const lifecycle = await loadLifecycle();
|
|
2676
|
+
const sync = syncProject(cfg).then((initial2) => {
|
|
2677
|
+
log2.success(
|
|
2678
|
+
`synced ${initial2.routes.length} route${initial2.routes.length === 1 ? "" : "s"} ${muted(`at ${initial2.paths.outDir}`)}`,
|
|
2679
|
+
"sync"
|
|
2680
|
+
);
|
|
2681
|
+
return initial2;
|
|
2682
|
+
});
|
|
2683
|
+
const [initial, services] = await Promise.all([sync, runInit(lifecycle)]);
|
|
2684
|
+
const loader = await safeRegister();
|
|
2685
|
+
closers.push(loader.unregister);
|
|
2686
|
+
let current = await buildGiriApp(cfg, {
|
|
2687
|
+
services,
|
|
2688
|
+
lazy: true,
|
|
2689
|
+
loaderRegistered: true,
|
|
2690
|
+
aliasResolverRegistered: true
|
|
2691
|
+
});
|
|
2692
|
+
const port = flags.port ?? cfg.server?.port ?? 3e3;
|
|
2693
|
+
const hostname = flags.hostname ?? cfg.server?.hostname;
|
|
2694
|
+
if (flags.watch) {
|
|
2695
|
+
const srcDir = (0, import_node_path17.resolve)(current.paths.routesDir, "..");
|
|
2696
|
+
if ((0, import_node_fs12.existsSync)(srcDir)) {
|
|
2697
|
+
let timer;
|
|
2698
|
+
let syncing = false;
|
|
2699
|
+
const changed = /* @__PURE__ */ new Set();
|
|
2700
|
+
const updater = createWatchUpdater(cfg, initial);
|
|
2701
|
+
const hmrCount = /* @__PURE__ */ new Map();
|
|
2702
|
+
const bump = (key) => {
|
|
2703
|
+
const next = (hmrCount.get(key) ?? 0) + 1;
|
|
2704
|
+
hmrCount.set(key, next);
|
|
2705
|
+
return next;
|
|
2706
|
+
};
|
|
2707
|
+
const flush = async () => {
|
|
2708
|
+
if (syncing) {
|
|
2709
|
+
return;
|
|
2710
|
+
}
|
|
2711
|
+
syncing = true;
|
|
2712
|
+
try {
|
|
2713
|
+
while (changed.size > 0) {
|
|
2714
|
+
const batch = [...changed];
|
|
2715
|
+
changed.clear();
|
|
2716
|
+
let rebuild = false;
|
|
2717
|
+
let fullSync = false;
|
|
2718
|
+
const dirtySet = /* @__PURE__ */ new Set();
|
|
2719
|
+
for (const name of batch) {
|
|
2720
|
+
const outcome = await updater.apply(name || null);
|
|
2721
|
+
if (outcome === "skip") {
|
|
2722
|
+
continue;
|
|
2723
|
+
}
|
|
2724
|
+
rebuild = true;
|
|
2725
|
+
if (name) {
|
|
2726
|
+
dirtySet.add((0, import_node_path17.resolve)(srcDir, name));
|
|
2727
|
+
}
|
|
2728
|
+
const rel = name ? `src/${name.replace(/\\/g, "/")}` : "src";
|
|
2729
|
+
log2.change(outcome === "full" ? "sync" : "update", rel, bump(rel));
|
|
2730
|
+
if (outcome === "full") {
|
|
2731
|
+
fullSync = true;
|
|
2732
|
+
break;
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
if (rebuild) {
|
|
2736
|
+
current = await buildGiriApp(cfg, {
|
|
2737
|
+
services,
|
|
2738
|
+
dirty: fullSync ? void 0 : dirtySet,
|
|
2739
|
+
lazy: true,
|
|
2740
|
+
loaderRegistered: true,
|
|
2741
|
+
aliasResolverRegistered: true
|
|
2742
|
+
});
|
|
2743
|
+
}
|
|
2057
2744
|
}
|
|
2058
|
-
|
|
2745
|
+
} catch (error) {
|
|
2746
|
+
log2.error(error instanceof Error ? error.message : String(error), "watch");
|
|
2747
|
+
} finally {
|
|
2748
|
+
syncing = false;
|
|
2059
2749
|
}
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
}
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2750
|
+
if (changed.size > 0) {
|
|
2751
|
+
void flush();
|
|
2752
|
+
}
|
|
2753
|
+
};
|
|
2754
|
+
const watcher = watch(srcDir, { persistent: true, ignoreInitial: true });
|
|
2755
|
+
const onFileChange = (filename) => {
|
|
2756
|
+
changed.add((0, import_node_path17.isAbsolute)(filename) ? (0, import_node_path17.relative)(srcDir, filename) : filename);
|
|
2757
|
+
clearTimeout(timer);
|
|
2758
|
+
timer = setTimeout(() => void flush(), 150);
|
|
2759
|
+
};
|
|
2760
|
+
watcher.on("change", onFileChange);
|
|
2761
|
+
watcher.on("add", onFileChange);
|
|
2762
|
+
watcher.on("unlink", onFileChange);
|
|
2763
|
+
closers.push(() => {
|
|
2764
|
+
clearTimeout(timer);
|
|
2765
|
+
watcher.close();
|
|
2766
|
+
});
|
|
2767
|
+
}
|
|
2074
2768
|
}
|
|
2769
|
+
const handler = (request) => cfg.adapter.fetch(current.app, request);
|
|
2770
|
+
const server = cfg.adapter.serve(handler, { port, hostname }, (info) => {
|
|
2771
|
+
log2.ready(`http://${displayHost(info.address)}:${info.port}`);
|
|
2772
|
+
});
|
|
2773
|
+
stop = async () => {
|
|
2774
|
+
for (const close of closers) {
|
|
2775
|
+
await close();
|
|
2776
|
+
}
|
|
2777
|
+
try {
|
|
2778
|
+
await server.close();
|
|
2779
|
+
} catch {
|
|
2780
|
+
}
|
|
2781
|
+
if (lifecycle.teardown) {
|
|
2782
|
+
await lifecycle.teardown(services);
|
|
2783
|
+
}
|
|
2784
|
+
};
|
|
2785
|
+
};
|
|
2786
|
+
await boot(config);
|
|
2787
|
+
const configPath = flags.watch ? findConfigPath((0, import_node_path17.resolve)(process.cwd())) : void 0;
|
|
2788
|
+
if (configPath) {
|
|
2789
|
+
let timer;
|
|
2790
|
+
let restarting = false;
|
|
2791
|
+
const configName = (0, import_node_path17.basename)(configPath);
|
|
2792
|
+
const restart = async () => {
|
|
2793
|
+
if (restarting) {
|
|
2794
|
+
return;
|
|
2795
|
+
}
|
|
2796
|
+
restarting = true;
|
|
2797
|
+
try {
|
|
2798
|
+
log2.info(`${color.green("restart")} ${configName} changed`, "config");
|
|
2799
|
+
delete require.cache[configPath];
|
|
2800
|
+
const next = await load({ throwOnError: true });
|
|
2801
|
+
await stop?.();
|
|
2802
|
+
await boot(next);
|
|
2803
|
+
} catch (error) {
|
|
2804
|
+
log2.error(error instanceof Error ? error.message : String(error), "config");
|
|
2805
|
+
log2.info("kept the previous server running \u2014 fix the config and save again", "config");
|
|
2806
|
+
} finally {
|
|
2807
|
+
restarting = false;
|
|
2808
|
+
}
|
|
2809
|
+
};
|
|
2810
|
+
const configWatcher = watch(configPath, { persistent: true, ignoreInitial: true });
|
|
2811
|
+
configWatcher.on("all", () => {
|
|
2812
|
+
clearTimeout(timer);
|
|
2813
|
+
timer = setTimeout(() => void restart(), 150);
|
|
2814
|
+
});
|
|
2075
2815
|
}
|
|
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);
|
|
2816
|
+
registerShutdown(() => stop?.());
|
|
2081
2817
|
}
|
|
2082
|
-
function registerShutdown(
|
|
2818
|
+
function registerShutdown(cleanup) {
|
|
2083
2819
|
let shuttingDown = false;
|
|
2084
2820
|
const shutdown = async () => {
|
|
2085
2821
|
if (shuttingDown) {
|
|
@@ -2087,10 +2823,7 @@ function registerShutdown(server, lifecycle, services) {
|
|
|
2087
2823
|
}
|
|
2088
2824
|
shuttingDown = true;
|
|
2089
2825
|
try {
|
|
2090
|
-
await
|
|
2091
|
-
if (lifecycle.teardown) {
|
|
2092
|
-
await lifecycle.teardown(services);
|
|
2093
|
-
}
|
|
2826
|
+
await cleanup();
|
|
2094
2827
|
} catch (error) {
|
|
2095
2828
|
log2.error(error instanceof Error ? error.message : String(error));
|
|
2096
2829
|
process.exitCode = 1;
|
|
@@ -2103,7 +2836,7 @@ function registerShutdown(server, lifecycle, services) {
|
|
|
2103
2836
|
}
|
|
2104
2837
|
async function main() {
|
|
2105
2838
|
const [command = "help", ...args] = process.argv.slice(2);
|
|
2106
|
-
const cwd = (0,
|
|
2839
|
+
const cwd = (0, import_node_path17.resolve)(process.cwd());
|
|
2107
2840
|
if (command === "help" || command === "--help" || command === "-h") {
|
|
2108
2841
|
help();
|
|
2109
2842
|
return;
|