@boon4681/giri 0.0.2 → 0.0.3-alpha-2
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 +1168 -274
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +30 -4
- package/dist/index.js +858 -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_typescript3.default.findConfigFile(paths.cwd, import_typescript3.default.sys.fileExists, "tsconfig.json");
|
|
52
52
|
if (configPath) {
|
|
53
|
-
const parsed =
|
|
54
|
-
...
|
|
53
|
+
const parsed = import_typescript3.default.getParsedCommandLineOfConfigFile(configPath, {}, {
|
|
54
|
+
...import_typescript3.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_typescript3.default.createProgram(routeFiles, options);
|
|
63
63
|
}
|
|
64
|
-
var
|
|
64
|
+
var import_typescript3, DEFAULT_OPTIONS;
|
|
65
65
|
var init_program = __esm({
|
|
66
66
|
"src/generator/schema/program.ts"() {
|
|
67
67
|
"use strict";
|
|
68
|
-
|
|
68
|
+
import_typescript3 = __toESM(require("typescript"));
|
|
69
69
|
DEFAULT_OPTIONS = {
|
|
70
|
-
target:
|
|
71
|
-
module:
|
|
72
|
-
moduleResolution:
|
|
70
|
+
target: import_typescript3.default.ScriptTarget.ES2022,
|
|
71
|
+
module: import_typescript3.default.ModuleKind.NodeNext,
|
|
72
|
+
moduleResolution: import_typescript3.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_typescript4.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_typescript4.default.TypeFlags.Undefined | import_typescript4.default.TypeFlags.Void | import_typescript4.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_typescript4.default.IndexKind.String) ?? checker.getIndexInfoOfType(type, import_typescript4.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_typescript4.default.SymbolFlags.Optional) || Boolean(propType.flags & import_typescript4.default.TypeFlags.Union && propType.types.some((t) => t.flags & import_typescript4.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_typescript4.default.TypeFlags.Any | import_typescript4.default.TypeFlags.Unknown)) {
|
|
194
194
|
return {};
|
|
195
195
|
}
|
|
196
|
-
if (flags &
|
|
196
|
+
if (flags & import_typescript4.default.TypeFlags.Null) {
|
|
197
197
|
return { type: "null" };
|
|
198
198
|
}
|
|
199
|
-
if (flags & (
|
|
199
|
+
if (flags & (import_typescript4.default.TypeFlags.Undefined | import_typescript4.default.TypeFlags.Void)) {
|
|
200
200
|
return {};
|
|
201
201
|
}
|
|
202
|
-
if (flags & (
|
|
202
|
+
if (flags & (import_typescript4.default.TypeFlags.BigInt | import_typescript4.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_typescript4.default.TypeFlags.BooleanLiteral) {
|
|
213
213
|
return { type: "boolean", const: intrinsicName(type) === "true" };
|
|
214
214
|
}
|
|
215
|
-
if (flags &
|
|
215
|
+
if (flags & import_typescript4.default.TypeFlags.String) {
|
|
216
216
|
return { type: "string" };
|
|
217
217
|
}
|
|
218
|
-
if (flags &
|
|
218
|
+
if (flags & import_typescript4.default.TypeFlags.Number) {
|
|
219
219
|
return { type: "number" };
|
|
220
220
|
}
|
|
221
|
-
if (flags &
|
|
221
|
+
if (flags & import_typescript4.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_typescript4.default.TypeFlags.Object || type.isIntersection()) {
|
|
228
228
|
return walkObject(type, ctx);
|
|
229
229
|
}
|
|
230
230
|
return {};
|
|
231
231
|
}
|
|
232
|
-
var
|
|
232
|
+
var import_typescript4;
|
|
233
233
|
var init_json_schema = __esm({
|
|
234
234
|
"src/generator/schema/json-schema.ts"() {
|
|
235
235
|
"use strict";
|
|
236
|
-
|
|
236
|
+
import_typescript4 = __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_typescript5.default.canHaveModifiers(node) && (import_typescript5.default.getModifiers(node)?.some((m) => m.kind === import_typescript5.default.SyntaxKind.ExportKeyword) ?? false);
|
|
244
244
|
for (const statement of source.statements) {
|
|
245
|
-
if (
|
|
245
|
+
if (import_typescript5.default.isFunctionDeclaration(statement) && statement.name?.text === "handle" && isExported(statement)) {
|
|
246
246
|
found = statement;
|
|
247
247
|
}
|
|
248
|
-
if (
|
|
248
|
+
if (import_typescript5.default.isVariableStatement(statement) && isExported(statement)) {
|
|
249
249
|
for (const declaration of statement.declarationList.declarations) {
|
|
250
|
-
if (
|
|
250
|
+
if (import_typescript5.default.isIdentifier(declaration.name) && declaration.name.text === "handle" && declaration.initializer && (import_typescript5.default.isArrowFunction(declaration.initializer) || import_typescript5.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_typescript5.default.isArrowFunction(fn) && !import_typescript5.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_typescript5.default.isFunctionDeclaration(node) || import_typescript5.default.isFunctionExpression(node) || import_typescript5.default.isArrowFunction(node)) {
|
|
268
268
|
return;
|
|
269
269
|
}
|
|
270
|
-
if (
|
|
270
|
+
if (import_typescript5.default.isReturnStatement(node) && node.expression) {
|
|
271
271
|
expressions.push(node.expression);
|
|
272
272
|
}
|
|
273
|
-
|
|
273
|
+
import_typescript5.default.forEachChild(node, visit);
|
|
274
274
|
};
|
|
275
|
-
|
|
275
|
+
import_typescript5.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_typescript5.default.isIdentifier(first.name) ? first.name.text : void 0;
|
|
290
|
+
}
|
|
291
|
+
function readFromCall(checker, expression, contextName) {
|
|
292
|
+
if (!import_typescript5.default.isCallExpression(expression) || !import_typescript5.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_typescript5.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_typescript5;
|
|
370
377
|
var init_responses = __esm({
|
|
371
378
|
"src/generator/schema/responses.ts"() {
|
|
372
379
|
"use strict";
|
|
373
|
-
|
|
380
|
+
import_typescript5 = __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
|
};
|
|
@@ -493,6 +516,7 @@ var import_node_fs2 = require("fs");
|
|
|
493
516
|
var import_promises = require("fs/promises");
|
|
494
517
|
var import_node_path2 = require("path");
|
|
495
518
|
var import_tinyglobby = require("tinyglobby");
|
|
519
|
+
var import_typescript = __toESM(require("typescript"));
|
|
496
520
|
var METHOD_ORDER = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"];
|
|
497
521
|
var METHOD_FROM_FILE = new Map(
|
|
498
522
|
METHOD_ORDER.map((method) => [`+${method.toLowerCase()}`, method])
|
|
@@ -510,13 +534,129 @@ function methodFromFile(fileName) {
|
|
|
510
534
|
const stem = fileName.replace(/\.(?:[cm]?[jt]s|[jt]sx)$/, "").toLowerCase();
|
|
511
535
|
return METHOD_FROM_FILE.get(stem);
|
|
512
536
|
}
|
|
513
|
-
function
|
|
537
|
+
function hasExportModifier(node) {
|
|
538
|
+
return import_typescript.default.canHaveModifiers(node) && (import_typescript.default.getModifiers(node)?.some((modifier) => modifier.kind === import_typescript.default.SyntaxKind.ExportKeyword) ?? false);
|
|
539
|
+
}
|
|
540
|
+
function hasDeclareModifier(node) {
|
|
541
|
+
return import_typescript.default.canHaveModifiers(node) && (import_typescript.default.getModifiers(node)?.some((modifier) => modifier.kind === import_typescript.default.SyntaxKind.DeclareKeyword) ?? false);
|
|
542
|
+
}
|
|
543
|
+
function propertyName(node) {
|
|
544
|
+
if (import_typescript.default.isIdentifier(node) || import_typescript.default.isStringLiteralLike(node) || import_typescript.default.isNumericLiteral(node)) {
|
|
545
|
+
return node.text;
|
|
546
|
+
}
|
|
547
|
+
if (import_typescript.default.isPropertyAccessExpression(node)) {
|
|
548
|
+
return node.name.text;
|
|
549
|
+
}
|
|
550
|
+
if (import_typescript.default.isElementAccessExpression(node) && node.argumentExpression) {
|
|
551
|
+
const argument = node.argumentExpression;
|
|
552
|
+
if (import_typescript.default.isStringLiteralLike(argument)) {
|
|
553
|
+
return argument.text;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return void 0;
|
|
557
|
+
}
|
|
558
|
+
function isExportsObject(node) {
|
|
559
|
+
return import_typescript.default.isIdentifier(node) && node.text === "exports";
|
|
560
|
+
}
|
|
561
|
+
function isModuleExports(node) {
|
|
562
|
+
return import_typescript.default.isPropertyAccessExpression(node) && import_typescript.default.isIdentifier(node.expression) && node.expression.text === "module" && node.name.text === "exports";
|
|
563
|
+
}
|
|
564
|
+
function isCommonJsHandleTarget(node) {
|
|
565
|
+
if (!import_typescript.default.isPropertyAccessExpression(node) && !import_typescript.default.isElementAccessExpression(node)) {
|
|
566
|
+
return false;
|
|
567
|
+
}
|
|
568
|
+
return propertyName(node) === "handle" && (isExportsObject(node.expression) || isModuleExports(node.expression));
|
|
569
|
+
}
|
|
570
|
+
function objectExportsHandle(node) {
|
|
571
|
+
if (!import_typescript.default.isObjectLiteralExpression(node)) {
|
|
572
|
+
return false;
|
|
573
|
+
}
|
|
574
|
+
return node.properties.some((property) => {
|
|
575
|
+
if (import_typescript.default.isShorthandPropertyAssignment(property)) {
|
|
576
|
+
return property.name.text === "handle";
|
|
577
|
+
}
|
|
578
|
+
return (import_typescript.default.isPropertyAssignment(property) || import_typescript.default.isMethodDeclaration(property)) && propertyName(property.name) === "handle";
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
function hasNamedHandleExport(source) {
|
|
582
|
+
for (const statement of source.statements) {
|
|
583
|
+
if (hasExportModifier(statement) && !hasDeclareModifier(statement) && import_typescript.default.isFunctionDeclaration(statement) && statement.name?.text === "handle") {
|
|
584
|
+
return true;
|
|
585
|
+
}
|
|
586
|
+
if (hasExportModifier(statement) && !hasDeclareModifier(statement) && import_typescript.default.isVariableStatement(statement)) {
|
|
587
|
+
if (statement.declarationList.declarations.some(
|
|
588
|
+
(declaration) => import_typescript.default.isIdentifier(declaration.name) && declaration.name.text === "handle"
|
|
589
|
+
)) {
|
|
590
|
+
return true;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
if (import_typescript.default.isExportDeclaration(statement) && !statement.isTypeOnly && statement.exportClause && import_typescript.default.isNamedExports(statement.exportClause)) {
|
|
594
|
+
if (statement.exportClause.elements.some(
|
|
595
|
+
(element) => !element.isTypeOnly && element.name.text === "handle"
|
|
596
|
+
)) {
|
|
597
|
+
return true;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
if (!import_typescript.default.isExpressionStatement(statement) || !import_typescript.default.isBinaryExpression(statement.expression)) {
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
const assignment = statement.expression;
|
|
604
|
+
if (assignment.operatorToken.kind !== import_typescript.default.SyntaxKind.EqualsToken) {
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
607
|
+
if (isCommonJsHandleTarget(assignment.left) || isModuleExports(assignment.left) && objectExportsHandle(assignment.right)) {
|
|
608
|
+
return true;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return false;
|
|
612
|
+
}
|
|
613
|
+
function parseSource(file) {
|
|
614
|
+
return import_typescript.default.createSourceFile(
|
|
615
|
+
file,
|
|
616
|
+
(0, import_node_fs2.readFileSync)(file, "utf8"),
|
|
617
|
+
import_typescript.default.ScriptTarget.Latest,
|
|
618
|
+
true
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
function parseDiagnostics(source) {
|
|
622
|
+
return source.parseDiagnostics ?? [];
|
|
623
|
+
}
|
|
624
|
+
function formatSyntaxDiagnostic(diagnostic) {
|
|
625
|
+
const position = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
|
626
|
+
const message = import_typescript.default.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
627
|
+
return `${diagnostic.file.fileName}:${position.line + 1}:${position.character + 1} - error TS${diagnostic.code}: ${message}`;
|
|
628
|
+
}
|
|
629
|
+
function assertSourceSyntax(file) {
|
|
630
|
+
if (!/\.(?:[cm]?[jt]s|[jt]sx)$/i.test(file)) {
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
const diagnostics = parseDiagnostics(parseSource(file));
|
|
634
|
+
if (diagnostics.length > 0) {
|
|
635
|
+
throw new SyntaxError(diagnostics.map(formatSyntaxDiagnostic).join("\n"));
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
function assertRouteHandleExport(file) {
|
|
639
|
+
const source = parseSource(file);
|
|
640
|
+
const diagnostics = parseDiagnostics(source);
|
|
641
|
+
if (diagnostics.length > 0) {
|
|
642
|
+
throw new SyntaxError(diagnostics.map(formatSyntaxDiagnostic).join("\n"));
|
|
643
|
+
}
|
|
644
|
+
if (!hasNamedHandleExport(source)) {
|
|
645
|
+
throw new Error(`${file} must export a named handle function.`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
function sharedFileIn(dir, cache) {
|
|
649
|
+
if (cache?.has(dir)) {
|
|
650
|
+
return cache.get(dir);
|
|
651
|
+
}
|
|
514
652
|
for (const ext of ["ts", "tsx", "js", "jsx", "mjs", "cjs", "mts", "cts"]) {
|
|
515
653
|
const file = (0, import_node_path2.join)(dir, `+shared.${ext}`);
|
|
516
654
|
if ((0, import_node_fs2.existsSync)(file)) {
|
|
655
|
+
cache?.set(dir, file);
|
|
517
656
|
return file;
|
|
518
657
|
}
|
|
519
658
|
}
|
|
659
|
+
cache?.set(dir, void 0);
|
|
520
660
|
return void 0;
|
|
521
661
|
}
|
|
522
662
|
function physicalRouteSegments(routesDir, routeDir) {
|
|
@@ -585,7 +725,7 @@ async function scanRouteFolders(routesDir) {
|
|
|
585
725
|
function routeParamsForDir(routesDir, dir) {
|
|
586
726
|
return pathFromSegments(physicalRouteSegments(routesDir, dir)).params;
|
|
587
727
|
}
|
|
588
|
-
function sharedFilesForDir(routesDir, dir) {
|
|
728
|
+
function sharedFilesForDir(routesDir, dir, cache) {
|
|
589
729
|
const segments = physicalRouteSegments(routesDir, dir);
|
|
590
730
|
const dirs = [routesDir];
|
|
591
731
|
let current = routesDir;
|
|
@@ -593,7 +733,7 @@ function sharedFilesForDir(routesDir, dir) {
|
|
|
593
733
|
current = (0, import_node_path2.join)(current, segment);
|
|
594
734
|
dirs.push(current);
|
|
595
735
|
}
|
|
596
|
-
return dirs.map(sharedFileIn).filter((file) => Boolean(file));
|
|
736
|
+
return dirs.map((currentDir) => sharedFileIn(currentDir, cache)).filter((file) => Boolean(file));
|
|
597
737
|
}
|
|
598
738
|
async function scanRoutes(routesDir) {
|
|
599
739
|
if (!(0, import_node_fs2.existsSync)(routesDir)) {
|
|
@@ -605,6 +745,7 @@ async function scanRoutes(routesDir) {
|
|
|
605
745
|
onlyFiles: true
|
|
606
746
|
});
|
|
607
747
|
const routes = [];
|
|
748
|
+
const sharedCache = /* @__PURE__ */ new Map();
|
|
608
749
|
for (const file of files) {
|
|
609
750
|
const method = methodFromFile((0, import_node_path2.basename)(file));
|
|
610
751
|
if (!method) {
|
|
@@ -620,7 +761,7 @@ async function scanRoutes(routesDir) {
|
|
|
620
761
|
routeDir,
|
|
621
762
|
routeSegments,
|
|
622
763
|
params,
|
|
623
|
-
sharedFiles: sharedFilesForDir(routesDir, routeDir)
|
|
764
|
+
sharedFiles: sharedFilesForDir(routesDir, routeDir, sharedCache)
|
|
624
765
|
});
|
|
625
766
|
}
|
|
626
767
|
return routes.sort((left, right) => {
|
|
@@ -649,9 +790,11 @@ function isGiriBodySchema(value) {
|
|
|
649
790
|
}
|
|
650
791
|
|
|
651
792
|
// src/app.ts
|
|
652
|
-
function loadModule(file) {
|
|
793
|
+
function loadModule(file, force = true) {
|
|
653
794
|
const resolved = require.resolve(file);
|
|
654
|
-
|
|
795
|
+
if (force) {
|
|
796
|
+
delete require.cache[resolved];
|
|
797
|
+
}
|
|
655
798
|
return require(resolved);
|
|
656
799
|
}
|
|
657
800
|
function interopDefault(value) {
|
|
@@ -713,20 +856,13 @@ function resolveAliasTarget(cwd, target, capture = "") {
|
|
|
713
856
|
}
|
|
714
857
|
function matchAlias(request, key) {
|
|
715
858
|
if (key.includes("*")) {
|
|
716
|
-
const [
|
|
717
|
-
if (request.startsWith(
|
|
718
|
-
return request.slice(
|
|
859
|
+
const [prefix, suffix = ""] = key.split("*");
|
|
860
|
+
if (request.startsWith(prefix) && request.endsWith(suffix)) {
|
|
861
|
+
return request.slice(prefix.length, request.length - suffix.length);
|
|
719
862
|
}
|
|
720
863
|
return void 0;
|
|
721
864
|
}
|
|
722
|
-
|
|
723
|
-
return "";
|
|
724
|
-
}
|
|
725
|
-
const prefix = `${key}/`;
|
|
726
|
-
if (request.startsWith(prefix)) {
|
|
727
|
-
return request.slice(prefix.length);
|
|
728
|
-
}
|
|
729
|
-
return void 0;
|
|
865
|
+
return request === key ? "" : void 0;
|
|
730
866
|
}
|
|
731
867
|
function resolveAliasRequest(request, alias, cwd) {
|
|
732
868
|
for (const [key, value] of Object.entries(alias ?? {})) {
|
|
@@ -788,40 +924,85 @@ function resolveGiriPaths(config, cwd = process.cwd()) {
|
|
|
788
924
|
async function buildGiriApp(config, options = {}) {
|
|
789
925
|
const paths = resolveGiriPaths(config, options.cwd);
|
|
790
926
|
const routes = await scanRoutes(paths.routesDir);
|
|
927
|
+
if (options.lazy) {
|
|
928
|
+
for (const route of routes) {
|
|
929
|
+
assertRouteHandleExport(route.file);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
791
932
|
const app = config.adapter.createApp();
|
|
792
933
|
ensureGiriAliasResolver(paths.outDir);
|
|
793
|
-
|
|
794
|
-
|
|
934
|
+
if (options.lazy && (!options.loaderRegistered || !options.aliasResolverRegistered)) {
|
|
935
|
+
throw new Error("Lazy route loading requires persistent loader and alias registrations.");
|
|
936
|
+
}
|
|
937
|
+
const loader = options.loaderRegistered ? void 0 : await safeRegister();
|
|
938
|
+
const unregisterAliasResolver = options.aliasResolverRegistered ? void 0 : registerAliasResolver(config.alias, paths.cwd);
|
|
795
939
|
try {
|
|
796
|
-
|
|
797
|
-
|
|
940
|
+
const dirty = options.dirty;
|
|
941
|
+
const forceReload = dirty === void 0;
|
|
942
|
+
const isDirty = (file) => forceReload || dirty.has(file);
|
|
943
|
+
const sharedCache = /* @__PURE__ */ new Map();
|
|
944
|
+
const loadShared = (file) => {
|
|
945
|
+
if (!sharedCache.has(file)) {
|
|
946
|
+
sharedCache.set(file, loadModule(file, isDirty(file)));
|
|
947
|
+
}
|
|
948
|
+
return sharedCache.get(file);
|
|
949
|
+
};
|
|
950
|
+
const runtimeFor = (route) => {
|
|
951
|
+
const routeModule = loadModule(route.file, isDirty(route.file));
|
|
798
952
|
if (typeof routeModule.handle !== "function") {
|
|
799
953
|
throw new Error(`${route.file} must export a named handle function.`);
|
|
800
954
|
}
|
|
801
955
|
const folderMiddleware = routeModule.config?.skipInherited ? [] : route.sharedFiles.flatMap(
|
|
802
|
-
(file) => normalizeMiddleware(
|
|
956
|
+
(file) => normalizeMiddleware(loadShared(file).middleware, file)
|
|
803
957
|
);
|
|
804
958
|
const verbMiddleware = normalizeMiddleware(routeModule.middleware, route.file);
|
|
805
|
-
|
|
959
|
+
return {
|
|
806
960
|
method: route.method,
|
|
807
961
|
path: route.path,
|
|
808
962
|
handle: routeModule.handle,
|
|
809
963
|
middleware: [...folderMiddleware, ...verbMiddleware],
|
|
810
964
|
input: routeInput(routeModule, route.file),
|
|
811
|
-
services: options.services
|
|
965
|
+
services: options.services,
|
|
966
|
+
cookieSecret: config.cookieSecret
|
|
967
|
+
};
|
|
968
|
+
};
|
|
969
|
+
for (const route of routes) {
|
|
970
|
+
if (!options.lazy) {
|
|
971
|
+
config.adapter.register(app, runtimeFor(route));
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
let runtime;
|
|
975
|
+
const getRuntime = () => {
|
|
976
|
+
runtime ??= runtimeFor(route);
|
|
977
|
+
return runtime;
|
|
978
|
+
};
|
|
979
|
+
config.adapter.register(app, {
|
|
980
|
+
method: route.method,
|
|
981
|
+
path: route.path,
|
|
982
|
+
get handle() {
|
|
983
|
+
return getRuntime().handle;
|
|
984
|
+
},
|
|
985
|
+
get middleware() {
|
|
986
|
+
return getRuntime().middleware;
|
|
987
|
+
},
|
|
988
|
+
get input() {
|
|
989
|
+
return getRuntime().input;
|
|
990
|
+
},
|
|
991
|
+
services: options.services,
|
|
992
|
+
cookieSecret: config.cookieSecret
|
|
812
993
|
});
|
|
813
994
|
}
|
|
814
995
|
} finally {
|
|
815
|
-
unregisterAliasResolver();
|
|
816
|
-
unregister();
|
|
996
|
+
unregisterAliasResolver?.();
|
|
997
|
+
loader?.unregister();
|
|
817
998
|
}
|
|
818
999
|
return { app, routes, paths };
|
|
819
1000
|
}
|
|
820
1001
|
|
|
821
1002
|
// src/generator/sync.ts
|
|
822
|
-
var
|
|
823
|
-
var
|
|
824
|
-
var
|
|
1003
|
+
var import_node_fs8 = require("fs");
|
|
1004
|
+
var import_promises4 = require("fs/promises");
|
|
1005
|
+
var import_node_path12 = require("path");
|
|
825
1006
|
|
|
826
1007
|
// src/generator/app-types.ts
|
|
827
1008
|
var import_node_fs4 = require("fs");
|
|
@@ -1073,6 +1254,7 @@ function buildOpenApiDocument(paths, routes, data = {}) {
|
|
|
1073
1254
|
const documentPaths = {};
|
|
1074
1255
|
const schemas = {};
|
|
1075
1256
|
const securitySchemes = {};
|
|
1257
|
+
const tagOrder = [];
|
|
1076
1258
|
for (const route of routes) {
|
|
1077
1259
|
if (data.hiddenFiles?.has(route.file)) {
|
|
1078
1260
|
continue;
|
|
@@ -1080,10 +1262,32 @@ function buildOpenApiDocument(paths, routes, data = {}) {
|
|
|
1080
1262
|
const responses = data.responsesByFile?.get(route.file);
|
|
1081
1263
|
const input = data.inputsByFile?.get(route.file);
|
|
1082
1264
|
const security = data.securityByFile?.get(route.file);
|
|
1265
|
+
const meta = data.openapiByFile?.get(route.file);
|
|
1083
1266
|
for (const [name, schema] of Object.entries(responses?.$defs ?? {})) {
|
|
1084
1267
|
schemas[name] = rewriteRefs(schema);
|
|
1085
1268
|
}
|
|
1086
|
-
const operation = {
|
|
1269
|
+
const operation = {};
|
|
1270
|
+
if (meta?.tags && meta.tags.length > 0) {
|
|
1271
|
+
operation.tags = meta.tags;
|
|
1272
|
+
for (const tag2 of meta.tags) {
|
|
1273
|
+
if (!tagOrder.includes(tag2)) {
|
|
1274
|
+
tagOrder.push(tag2);
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
if (meta?.summary) {
|
|
1279
|
+
operation.summary = meta.summary;
|
|
1280
|
+
}
|
|
1281
|
+
if (meta?.description) {
|
|
1282
|
+
operation.description = meta.description;
|
|
1283
|
+
}
|
|
1284
|
+
if (meta?.operationId) {
|
|
1285
|
+
operation.operationId = meta.operationId;
|
|
1286
|
+
}
|
|
1287
|
+
if (meta?.deprecated) {
|
|
1288
|
+
operation.deprecated = true;
|
|
1289
|
+
}
|
|
1290
|
+
operation.responses = buildResponses(responses?.responses ?? []);
|
|
1087
1291
|
const parameters = [...pathParameters(route), ...queryParameters(input?.query)];
|
|
1088
1292
|
if (parameters.length > 0) {
|
|
1089
1293
|
operation.parameters = parameters;
|
|
@@ -1115,6 +1319,9 @@ function buildOpenApiDocument(paths, routes, data = {}) {
|
|
|
1115
1319
|
info: readProjectInfo(paths.cwd),
|
|
1116
1320
|
paths: documentPaths
|
|
1117
1321
|
};
|
|
1322
|
+
if (tagOrder.length > 0) {
|
|
1323
|
+
document.tags = tagOrder.map((name) => ({ name }));
|
|
1324
|
+
}
|
|
1118
1325
|
const components = {};
|
|
1119
1326
|
if (Object.keys(schemas).length > 0) {
|
|
1120
1327
|
components.schemas = schemas;
|
|
@@ -1146,14 +1353,29 @@ function paramsType(params) {
|
|
|
1146
1353
|
${fields}
|
|
1147
1354
|
}`;
|
|
1148
1355
|
}
|
|
1149
|
-
function
|
|
1150
|
-
|
|
1151
|
-
|
|
1356
|
+
function middlewareVarsType(typesDir, sharedFile) {
|
|
1357
|
+
const spec = JSON.stringify(moduleSpecifier(typesDir, sharedFile));
|
|
1358
|
+
return `(typeof import(${spec}) extends { middleware: infer M } ? import("@boon4681/giri").InferStackVars<M> : {})`;
|
|
1359
|
+
}
|
|
1360
|
+
function ownSharedFile(dir, sharedFiles) {
|
|
1361
|
+
for (let index = sharedFiles.length - 1; index >= 0; index -= 1) {
|
|
1362
|
+
if ((0, import_node_path8.dirname)(sharedFiles[index]) === dir) {
|
|
1363
|
+
return sharedFiles[index];
|
|
1364
|
+
}
|
|
1152
1365
|
}
|
|
1153
|
-
return
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1366
|
+
return void 0;
|
|
1367
|
+
}
|
|
1368
|
+
function varsType(paths, file, dir, sharedFiles) {
|
|
1369
|
+
const typesDir = (0, import_node_path8.dirname)(file);
|
|
1370
|
+
const parts = [];
|
|
1371
|
+
if (dir !== paths.routesDir) {
|
|
1372
|
+
parts.push(`import(${JSON.stringify(importPath(file, typeFilePath(paths, (0, import_node_path8.dirname)(dir))))}).Vars`);
|
|
1373
|
+
}
|
|
1374
|
+
const ownShared = ownSharedFile(dir, sharedFiles);
|
|
1375
|
+
if (ownShared) {
|
|
1376
|
+
parts.push(middlewareVarsType(typesDir, ownShared));
|
|
1377
|
+
}
|
|
1378
|
+
return parts.length > 0 ? parts.join("\n & ") : "{}";
|
|
1157
1379
|
}
|
|
1158
1380
|
function methodExports(typesDir, verbs) {
|
|
1159
1381
|
return verbs.map(({ method, file }) => {
|
|
@@ -1164,14 +1386,14 @@ function methodExports(typesDir, verbs) {
|
|
|
1164
1386
|
});
|
|
1165
1387
|
}
|
|
1166
1388
|
async function writeParamTypes(paths, folders) {
|
|
1167
|
-
|
|
1389
|
+
await Promise.all(folders.map(({ dir, params, sharedFiles, verbs }) => {
|
|
1168
1390
|
const file = typeFilePath(paths, dir);
|
|
1169
1391
|
const typesDir = (0, import_node_path8.dirname)(file);
|
|
1170
1392
|
const lines = [
|
|
1171
1393
|
GENERATED_HEADER,
|
|
1172
1394
|
`export type Params = ${paramsType(params)};`,
|
|
1173
1395
|
"export type RouteParams = Params;",
|
|
1174
|
-
`type Vars = ${varsType(
|
|
1396
|
+
`export type Vars = ${varsType(paths, file, dir, sharedFiles)};`,
|
|
1175
1397
|
"export type Middleware<Injects extends Record<string, unknown> = {}> =",
|
|
1176
1398
|
' import("@boon4681/giri").Middleware<Params, import("@boon4681/giri").ValidatedInput, Injects>;',
|
|
1177
1399
|
'export type Handle<Input extends import("@boon4681/giri").ValidatedInput = import("@boon4681/giri").ValidatedInput> =',
|
|
@@ -1181,10 +1403,14 @@ async function writeParamTypes(paths, folders) {
|
|
|
1181
1403
|
lines.push(...methodExports(typesDir, verbs));
|
|
1182
1404
|
}
|
|
1183
1405
|
lines.push("");
|
|
1184
|
-
|
|
1185
|
-
}
|
|
1406
|
+
return writeGenerated(file, lines.join("\n"));
|
|
1407
|
+
}));
|
|
1186
1408
|
}
|
|
1187
1409
|
|
|
1410
|
+
// src/generator/route-meta.ts
|
|
1411
|
+
var import_node_fs6 = require("fs");
|
|
1412
|
+
var import_typescript2 = __toESM(require("typescript"));
|
|
1413
|
+
|
|
1188
1414
|
// src/generator/inputs.ts
|
|
1189
1415
|
function sanitize(schema) {
|
|
1190
1416
|
const { $schema, ...rest } = schema;
|
|
@@ -1245,28 +1471,315 @@ function readInput(routeModule) {
|
|
|
1245
1471
|
}
|
|
1246
1472
|
return input.body || input.query ? input : void 0;
|
|
1247
1473
|
}
|
|
1248
|
-
function
|
|
1249
|
-
|
|
1474
|
+
function hasExportModifier2(node) {
|
|
1475
|
+
return import_typescript2.default.canHaveModifiers(node) && (import_typescript2.default.getModifiers(node)?.some((modifier) => modifier.kind === import_typescript2.default.SyntaxKind.ExportKeyword) ?? false);
|
|
1476
|
+
}
|
|
1477
|
+
function unwrapExpression(expression) {
|
|
1478
|
+
let current = expression;
|
|
1479
|
+
while (import_typescript2.default.isParenthesizedExpression(current) || import_typescript2.default.isAsExpression(current) || import_typescript2.default.isSatisfiesExpression(current)) {
|
|
1480
|
+
current = current.expression;
|
|
1481
|
+
}
|
|
1482
|
+
return current;
|
|
1483
|
+
}
|
|
1484
|
+
function staticBoolean(expression) {
|
|
1485
|
+
const value = unwrapExpression(expression);
|
|
1486
|
+
if (value.kind === import_typescript2.default.SyntaxKind.TrueKeyword) {
|
|
1250
1487
|
return true;
|
|
1251
1488
|
}
|
|
1252
|
-
if (value ===
|
|
1489
|
+
if (value.kind === import_typescript2.default.SyntaxKind.FalseKeyword) {
|
|
1253
1490
|
return false;
|
|
1254
1491
|
}
|
|
1255
|
-
|
|
1256
|
-
|
|
1492
|
+
return void 0;
|
|
1493
|
+
}
|
|
1494
|
+
function staticString(expression) {
|
|
1495
|
+
const value = unwrapExpression(expression);
|
|
1496
|
+
return import_typescript2.default.isStringLiteralLike(value) ? value.text : void 0;
|
|
1497
|
+
}
|
|
1498
|
+
function staticStringArray(expression) {
|
|
1499
|
+
const value = unwrapExpression(expression);
|
|
1500
|
+
if (!import_typescript2.default.isArrayLiteralExpression(value)) {
|
|
1501
|
+
return void 0;
|
|
1502
|
+
}
|
|
1503
|
+
const strings = [];
|
|
1504
|
+
for (const element of value.elements) {
|
|
1505
|
+
const string = staticString(element);
|
|
1506
|
+
if (string === void 0) {
|
|
1507
|
+
return void 0;
|
|
1508
|
+
}
|
|
1509
|
+
strings.push(string);
|
|
1510
|
+
}
|
|
1511
|
+
return strings;
|
|
1512
|
+
}
|
|
1513
|
+
function propertyName2(name) {
|
|
1514
|
+
if (import_typescript2.default.isIdentifier(name) || import_typescript2.default.isStringLiteral(name) || import_typescript2.default.isNumericLiteral(name)) {
|
|
1515
|
+
return name.text;
|
|
1257
1516
|
}
|
|
1258
1517
|
return void 0;
|
|
1259
1518
|
}
|
|
1260
|
-
function
|
|
1519
|
+
function collectImportedNames(source) {
|
|
1520
|
+
const names = /* @__PURE__ */ new Set();
|
|
1521
|
+
for (const statement of source.statements) {
|
|
1522
|
+
if (!import_typescript2.default.isImportDeclaration(statement)) {
|
|
1523
|
+
continue;
|
|
1524
|
+
}
|
|
1525
|
+
const clause = statement.importClause;
|
|
1526
|
+
if (!clause) {
|
|
1527
|
+
continue;
|
|
1528
|
+
}
|
|
1529
|
+
if (clause.name) {
|
|
1530
|
+
names.add(clause.name.text);
|
|
1531
|
+
}
|
|
1532
|
+
const bindings = clause.namedBindings;
|
|
1533
|
+
if (bindings && import_typescript2.default.isNamespaceImport(bindings)) {
|
|
1534
|
+
names.add(bindings.name.text);
|
|
1535
|
+
}
|
|
1536
|
+
if (bindings && import_typescript2.default.isNamedImports(bindings)) {
|
|
1537
|
+
for (const element of bindings.elements) {
|
|
1538
|
+
names.add(element.name.text);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
return names;
|
|
1543
|
+
}
|
|
1544
|
+
function expressionReferencesImportedMiddleware(expression, importedNames) {
|
|
1545
|
+
let found = false;
|
|
1546
|
+
const allowedImportedHelpers = /* @__PURE__ */ new Set(["stack", "fromHono"]);
|
|
1547
|
+
const visit = (node) => {
|
|
1548
|
+
if (found) {
|
|
1549
|
+
return;
|
|
1550
|
+
}
|
|
1551
|
+
if (import_typescript2.default.isIdentifier(node) && importedNames.has(node.text) && !allowedImportedHelpers.has(node.text)) {
|
|
1552
|
+
found = true;
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
import_typescript2.default.forEachChild(node, visit);
|
|
1556
|
+
};
|
|
1557
|
+
visit(expression);
|
|
1558
|
+
return found;
|
|
1559
|
+
}
|
|
1560
|
+
function parseStaticOpenApi(expression) {
|
|
1561
|
+
const value = unwrapExpression(expression);
|
|
1562
|
+
const boolean = staticBoolean(value);
|
|
1563
|
+
if (boolean !== void 0) {
|
|
1564
|
+
return boolean;
|
|
1565
|
+
}
|
|
1566
|
+
if (!import_typescript2.default.isObjectLiteralExpression(value)) {
|
|
1567
|
+
return void 0;
|
|
1568
|
+
}
|
|
1569
|
+
const openapi = {};
|
|
1570
|
+
for (const property of value.properties) {
|
|
1571
|
+
if (!import_typescript2.default.isPropertyAssignment(property)) {
|
|
1572
|
+
return void 0;
|
|
1573
|
+
}
|
|
1574
|
+
const name = propertyName2(property.name);
|
|
1575
|
+
if (!name) {
|
|
1576
|
+
return void 0;
|
|
1577
|
+
}
|
|
1578
|
+
if (name === "hidden") {
|
|
1579
|
+
const hidden = staticBoolean(property.initializer);
|
|
1580
|
+
if (hidden === void 0) {
|
|
1581
|
+
return void 0;
|
|
1582
|
+
}
|
|
1583
|
+
openapi.hidden = hidden;
|
|
1584
|
+
} else if (name === "tags") {
|
|
1585
|
+
const tags = staticStringArray(property.initializer);
|
|
1586
|
+
if (!tags) {
|
|
1587
|
+
return void 0;
|
|
1588
|
+
}
|
|
1589
|
+
openapi.tags = tags;
|
|
1590
|
+
} else if (name === "summary" || name === "description" || name === "operationId") {
|
|
1591
|
+
const string = staticString(property.initializer);
|
|
1592
|
+
if (string === void 0) {
|
|
1593
|
+
return void 0;
|
|
1594
|
+
}
|
|
1595
|
+
openapi[name] = string;
|
|
1596
|
+
} else if (name === "deprecated") {
|
|
1597
|
+
const deprecated = staticBoolean(property.initializer);
|
|
1598
|
+
if (deprecated === void 0) {
|
|
1599
|
+
return void 0;
|
|
1600
|
+
}
|
|
1601
|
+
openapi.deprecated = deprecated;
|
|
1602
|
+
} else {
|
|
1603
|
+
return void 0;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
return openapi;
|
|
1607
|
+
}
|
|
1608
|
+
function readStaticModuleMeta(file) {
|
|
1609
|
+
let source;
|
|
1610
|
+
try {
|
|
1611
|
+
source = import_typescript2.default.createSourceFile(file, (0, import_node_fs6.readFileSync)(file, "utf8"), import_typescript2.default.ScriptTarget.Latest, true);
|
|
1612
|
+
} catch {
|
|
1613
|
+
return void 0;
|
|
1614
|
+
}
|
|
1615
|
+
const importedNames = collectImportedNames(source);
|
|
1616
|
+
const sourceText = source.getFullText();
|
|
1617
|
+
const canSkipMiddlewareRuntime = !sourceText.includes("defineMiddleware") && !sourceText.includes(".openapi");
|
|
1618
|
+
const meta = { middlewareSecurity: false };
|
|
1619
|
+
for (const statement of source.statements) {
|
|
1620
|
+
if (import_typescript2.default.isImportDeclaration(statement) || import_typescript2.default.isInterfaceDeclaration(statement) || import_typescript2.default.isTypeAliasDeclaration(statement) || import_typescript2.default.isEmptyStatement(statement) || !hasExportModifier2(statement)) {
|
|
1621
|
+
continue;
|
|
1622
|
+
}
|
|
1623
|
+
if (import_typescript2.default.isFunctionDeclaration(statement) && statement.name?.text === "handle") {
|
|
1624
|
+
continue;
|
|
1625
|
+
}
|
|
1626
|
+
if (import_typescript2.default.isVariableStatement(statement)) {
|
|
1627
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
1628
|
+
if (!import_typescript2.default.isIdentifier(declaration.name)) {
|
|
1629
|
+
return void 0;
|
|
1630
|
+
}
|
|
1631
|
+
const name = declaration.name.text;
|
|
1632
|
+
if (name === "openapi") {
|
|
1633
|
+
if (!declaration.initializer) {
|
|
1634
|
+
return void 0;
|
|
1635
|
+
}
|
|
1636
|
+
const openapi = parseStaticOpenApi(declaration.initializer);
|
|
1637
|
+
if (openapi === void 0) {
|
|
1638
|
+
return void 0;
|
|
1639
|
+
}
|
|
1640
|
+
meta.openapi = openapi;
|
|
1641
|
+
} else if (name === "handle") {
|
|
1642
|
+
if (!declaration.initializer || !import_typescript2.default.isArrowFunction(declaration.initializer) && !import_typescript2.default.isFunctionExpression(declaration.initializer)) {
|
|
1643
|
+
return void 0;
|
|
1644
|
+
}
|
|
1645
|
+
continue;
|
|
1646
|
+
} else if (name === "middleware") {
|
|
1647
|
+
if (!declaration.initializer || !canSkipMiddlewareRuntime || expressionReferencesImportedMiddleware(declaration.initializer, importedNames)) {
|
|
1648
|
+
return void 0;
|
|
1649
|
+
}
|
|
1650
|
+
meta.middlewareSecurity = false;
|
|
1651
|
+
continue;
|
|
1652
|
+
} else {
|
|
1653
|
+
return void 0;
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
continue;
|
|
1657
|
+
}
|
|
1658
|
+
return void 0;
|
|
1659
|
+
}
|
|
1660
|
+
return meta;
|
|
1661
|
+
}
|
|
1662
|
+
function resolveStaticOpenApi(route, routeModule, loadShared) {
|
|
1261
1663
|
let hidden = false;
|
|
1664
|
+
const tags = [];
|
|
1665
|
+
const meta = {};
|
|
1666
|
+
const apply = (value, isVerb) => {
|
|
1667
|
+
if (value === false) {
|
|
1668
|
+
hidden = true;
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
if (value === true) {
|
|
1672
|
+
hidden = false;
|
|
1673
|
+
return;
|
|
1674
|
+
}
|
|
1675
|
+
if (!value) {
|
|
1676
|
+
return;
|
|
1677
|
+
}
|
|
1678
|
+
if (typeof value.hidden === "boolean") {
|
|
1679
|
+
hidden = value.hidden;
|
|
1680
|
+
}
|
|
1681
|
+
if (value.tags) {
|
|
1682
|
+
tags.push(...value.tags);
|
|
1683
|
+
}
|
|
1684
|
+
if (typeof value.summary === "string") {
|
|
1685
|
+
meta.summary = value.summary;
|
|
1686
|
+
}
|
|
1687
|
+
if (typeof value.description === "string") {
|
|
1688
|
+
meta.description = value.description;
|
|
1689
|
+
}
|
|
1690
|
+
if (typeof value.deprecated === "boolean") {
|
|
1691
|
+
meta.deprecated = value.deprecated;
|
|
1692
|
+
}
|
|
1693
|
+
if (isVerb && typeof value.operationId === "string") {
|
|
1694
|
+
meta.operationId = value.operationId;
|
|
1695
|
+
}
|
|
1696
|
+
};
|
|
1262
1697
|
for (const file of route.sharedFiles) {
|
|
1263
|
-
const
|
|
1264
|
-
if (
|
|
1265
|
-
|
|
1698
|
+
const shared = loadShared(file);
|
|
1699
|
+
if (!shared) {
|
|
1700
|
+
return void 0;
|
|
1701
|
+
}
|
|
1702
|
+
apply(shared.openapi, false);
|
|
1703
|
+
}
|
|
1704
|
+
apply(routeModule.openapi, true);
|
|
1705
|
+
if (tags.length > 0) {
|
|
1706
|
+
meta.tags = [...new Set(tags)];
|
|
1707
|
+
}
|
|
1708
|
+
return { hidden, meta };
|
|
1709
|
+
}
|
|
1710
|
+
function extractStaticMeta(route, routeModule, loadShared) {
|
|
1711
|
+
const openapi = resolveStaticOpenApi(route, routeModule, loadShared);
|
|
1712
|
+
if (!openapi) {
|
|
1713
|
+
return void 0;
|
|
1714
|
+
}
|
|
1715
|
+
const meta = {};
|
|
1716
|
+
if (openapi.hidden) {
|
|
1717
|
+
meta.hidden = true;
|
|
1718
|
+
}
|
|
1719
|
+
if (Object.keys(openapi.meta).length > 0) {
|
|
1720
|
+
meta.openapi = openapi.meta;
|
|
1721
|
+
}
|
|
1722
|
+
return meta;
|
|
1723
|
+
}
|
|
1724
|
+
function extractRuntimeSharedMeta(route, routeModule, loadShared) {
|
|
1725
|
+
const meta = {};
|
|
1726
|
+
const security = collectSecurity(route, {}, loadShared);
|
|
1727
|
+
const { hidden, meta: openapi } = resolveOpenApi(route, { openapi: routeModule.openapi }, loadShared);
|
|
1728
|
+
if (security) {
|
|
1729
|
+
meta.security = security;
|
|
1730
|
+
}
|
|
1731
|
+
if (hidden) {
|
|
1732
|
+
meta.hidden = true;
|
|
1733
|
+
}
|
|
1734
|
+
if (Object.keys(openapi).length > 0) {
|
|
1735
|
+
meta.openapi = openapi;
|
|
1736
|
+
}
|
|
1737
|
+
return meta;
|
|
1738
|
+
}
|
|
1739
|
+
function resolveOpenApi(route, routeModule, loadShared) {
|
|
1740
|
+
let hidden = false;
|
|
1741
|
+
const tags = [];
|
|
1742
|
+
const meta = {};
|
|
1743
|
+
const apply = (value, isVerb) => {
|
|
1744
|
+
if (value === false) {
|
|
1745
|
+
hidden = true;
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1748
|
+
if (value === true) {
|
|
1749
|
+
hidden = false;
|
|
1750
|
+
return;
|
|
1751
|
+
}
|
|
1752
|
+
if (!value || typeof value !== "object") {
|
|
1753
|
+
return;
|
|
1266
1754
|
}
|
|
1755
|
+
const o = value;
|
|
1756
|
+
if ("hidden" in o) {
|
|
1757
|
+
hidden = Boolean(o.hidden);
|
|
1758
|
+
}
|
|
1759
|
+
if (Array.isArray(o.tags)) {
|
|
1760
|
+
tags.push(...o.tags.filter((tag2) => typeof tag2 === "string"));
|
|
1761
|
+
}
|
|
1762
|
+
if (typeof o.summary === "string") {
|
|
1763
|
+
meta.summary = o.summary;
|
|
1764
|
+
}
|
|
1765
|
+
if (typeof o.description === "string") {
|
|
1766
|
+
meta.description = o.description;
|
|
1767
|
+
}
|
|
1768
|
+
if (typeof o.deprecated === "boolean") {
|
|
1769
|
+
meta.deprecated = o.deprecated;
|
|
1770
|
+
}
|
|
1771
|
+
if (isVerb && typeof o.operationId === "string") {
|
|
1772
|
+
meta.operationId = o.operationId;
|
|
1773
|
+
}
|
|
1774
|
+
};
|
|
1775
|
+
for (const file of route.sharedFiles) {
|
|
1776
|
+
apply(loadShared(file).openapi, false);
|
|
1777
|
+
}
|
|
1778
|
+
apply(routeModule.openapi, true);
|
|
1779
|
+
if (tags.length > 0) {
|
|
1780
|
+
meta.tags = [...new Set(tags)];
|
|
1267
1781
|
}
|
|
1268
|
-
|
|
1269
|
-
return verb ?? hidden;
|
|
1782
|
+
return { hidden, meta };
|
|
1270
1783
|
}
|
|
1271
1784
|
function collectSecurity(route, routeModule, loadShared) {
|
|
1272
1785
|
const skipInherited = Boolean(
|
|
@@ -1298,6 +1811,33 @@ function collectSecurity(route, routeModule, loadShared) {
|
|
|
1298
1811
|
}
|
|
1299
1812
|
async function extractRouteMeta(config, paths, routes) {
|
|
1300
1813
|
const byFile = /* @__PURE__ */ new Map();
|
|
1814
|
+
const remainingRoutes = [];
|
|
1815
|
+
const runtimeSharedRoutes = [];
|
|
1816
|
+
const staticCache = /* @__PURE__ */ new Map();
|
|
1817
|
+
const loadStatic = (file) => {
|
|
1818
|
+
if (!staticCache.has(file)) {
|
|
1819
|
+
staticCache.set(file, readStaticModuleMeta(file));
|
|
1820
|
+
}
|
|
1821
|
+
return staticCache.get(file);
|
|
1822
|
+
};
|
|
1823
|
+
for (const route of routes) {
|
|
1824
|
+
const routeModule = loadStatic(route.file);
|
|
1825
|
+
if (!routeModule) {
|
|
1826
|
+
remainingRoutes.push(route);
|
|
1827
|
+
continue;
|
|
1828
|
+
}
|
|
1829
|
+
const meta = extractStaticMeta(route, routeModule, loadStatic);
|
|
1830
|
+
if (meta) {
|
|
1831
|
+
if (meta.hidden || meta.openapi) {
|
|
1832
|
+
byFile.set(route.file, meta);
|
|
1833
|
+
}
|
|
1834
|
+
continue;
|
|
1835
|
+
}
|
|
1836
|
+
runtimeSharedRoutes.push({ route, routeModule });
|
|
1837
|
+
}
|
|
1838
|
+
if (remainingRoutes.length === 0 && runtimeSharedRoutes.length === 0) {
|
|
1839
|
+
return byFile;
|
|
1840
|
+
}
|
|
1301
1841
|
const { unregister } = await safeRegister();
|
|
1302
1842
|
const unregisterAlias = registerAliasResolver(config.alias, paths.cwd);
|
|
1303
1843
|
const sharedCache = /* @__PURE__ */ new Map();
|
|
@@ -1312,13 +1852,19 @@ async function extractRouteMeta(config, paths, routes) {
|
|
|
1312
1852
|
return sharedCache.get(file);
|
|
1313
1853
|
};
|
|
1314
1854
|
try {
|
|
1315
|
-
for (const route of
|
|
1855
|
+
for (const { route, routeModule } of runtimeSharedRoutes) {
|
|
1856
|
+
const meta = extractRuntimeSharedMeta(route, routeModule, loadShared);
|
|
1857
|
+
if (meta.input || meta.security || meta.hidden || meta.openapi) {
|
|
1858
|
+
byFile.set(route.file, meta);
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
for (const route of remainingRoutes) {
|
|
1316
1862
|
try {
|
|
1317
1863
|
const routeModule = loadModule2(route.file);
|
|
1318
1864
|
const meta = {};
|
|
1319
1865
|
const input = readInput(routeModule);
|
|
1320
1866
|
const security = collectSecurity(route, routeModule, loadShared);
|
|
1321
|
-
const hidden =
|
|
1867
|
+
const { hidden, meta: openapi } = resolveOpenApi(route, routeModule, loadShared);
|
|
1322
1868
|
if (input) {
|
|
1323
1869
|
meta.input = input;
|
|
1324
1870
|
}
|
|
@@ -1328,7 +1874,10 @@ async function extractRouteMeta(config, paths, routes) {
|
|
|
1328
1874
|
if (hidden) {
|
|
1329
1875
|
meta.hidden = true;
|
|
1330
1876
|
}
|
|
1331
|
-
if (
|
|
1877
|
+
if (Object.keys(openapi).length > 0) {
|
|
1878
|
+
meta.openapi = openapi;
|
|
1879
|
+
}
|
|
1880
|
+
if (meta.input || meta.security || meta.hidden || meta.openapi) {
|
|
1332
1881
|
byFile.set(route.file, meta);
|
|
1333
1882
|
}
|
|
1334
1883
|
} catch {
|
|
@@ -1400,6 +1949,103 @@ async function writeTsConfig(paths, config) {
|
|
|
1400
1949
|
});
|
|
1401
1950
|
}
|
|
1402
1951
|
|
|
1952
|
+
// src/generator/cache.ts
|
|
1953
|
+
var import_node_crypto = require("crypto");
|
|
1954
|
+
var import_node_fs7 = require("fs");
|
|
1955
|
+
var import_promises3 = require("fs/promises");
|
|
1956
|
+
var import_node_path11 = require("path");
|
|
1957
|
+
var import_tinyglobby2 = require("tinyglobby");
|
|
1958
|
+
var CACHE_VERSION = 1;
|
|
1959
|
+
var SYNC_CACHE_NAME = ".sync-cache.json";
|
|
1960
|
+
function stableConfig(config) {
|
|
1961
|
+
const alias = Object.entries(config.alias ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => [key, Array.isArray(value) ? [...value] : value]);
|
|
1962
|
+
return { alias, outDir: config.outDir ?? ".giri" };
|
|
1963
|
+
}
|
|
1964
|
+
async function syncFingerprint(config, paths) {
|
|
1965
|
+
const outRelative = slash((0, import_node_path11.relative)(paths.cwd, paths.outDir));
|
|
1966
|
+
const ignore = ["**/node_modules/**", "**/.git/**"];
|
|
1967
|
+
if (outRelative && !outRelative.startsWith("..")) {
|
|
1968
|
+
ignore.push(`${outRelative}/**`);
|
|
1969
|
+
}
|
|
1970
|
+
const files = await (0, import_tinyglobby2.glob)([
|
|
1971
|
+
"src/**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs,json}",
|
|
1972
|
+
"giri.config.{ts,js,mts,cts,mjs,cjs}",
|
|
1973
|
+
"tsconfig*.json",
|
|
1974
|
+
"package.json",
|
|
1975
|
+
"package-lock.json",
|
|
1976
|
+
"npm-shrinkwrap.json",
|
|
1977
|
+
"yarn.lock",
|
|
1978
|
+
"pnpm-lock.yaml",
|
|
1979
|
+
"bun.lock",
|
|
1980
|
+
"bun.lockb"
|
|
1981
|
+
], {
|
|
1982
|
+
cwd: paths.cwd,
|
|
1983
|
+
absolute: false,
|
|
1984
|
+
onlyFiles: true,
|
|
1985
|
+
dot: true,
|
|
1986
|
+
ignore
|
|
1987
|
+
});
|
|
1988
|
+
const hash = (0, import_node_crypto.createHash)("sha256");
|
|
1989
|
+
hash.update(JSON.stringify(stableConfig(config)));
|
|
1990
|
+
for (const file of files.sort()) {
|
|
1991
|
+
hash.update("\0");
|
|
1992
|
+
hash.update(slash(file));
|
|
1993
|
+
hash.update("\0");
|
|
1994
|
+
hash.update(await (0, import_promises3.readFile)((0, import_node_path11.resolve)(paths.cwd, file)));
|
|
1995
|
+
}
|
|
1996
|
+
return hash.digest("hex");
|
|
1997
|
+
}
|
|
1998
|
+
function cachePath(paths) {
|
|
1999
|
+
return (0, import_node_path11.join)(paths.outDir, SYNC_CACHE_NAME);
|
|
2000
|
+
}
|
|
2001
|
+
function serializePath(paths, file) {
|
|
2002
|
+
return slash((0, import_node_path11.relative)(paths.cwd, file));
|
|
2003
|
+
}
|
|
2004
|
+
function deserializePath(paths, file) {
|
|
2005
|
+
return slash((0, import_node_path11.resolve)(paths.cwd, file.split("/").join(import_node_path11.sep)));
|
|
2006
|
+
}
|
|
2007
|
+
function serializeMap(paths, values) {
|
|
2008
|
+
return [...values].map(([file, value]) => [serializePath(paths, file), value]);
|
|
2009
|
+
}
|
|
2010
|
+
function deserializeMap(paths, values) {
|
|
2011
|
+
return new Map(values.map(([file, value]) => [deserializePath(paths, file), value]));
|
|
2012
|
+
}
|
|
2013
|
+
async function readSyncCache(paths, fingerprint) {
|
|
2014
|
+
const file = cachePath(paths);
|
|
2015
|
+
if (!(0, import_node_fs7.existsSync)(file)) {
|
|
2016
|
+
return void 0;
|
|
2017
|
+
}
|
|
2018
|
+
try {
|
|
2019
|
+
const cache = JSON.parse(await (0, import_promises3.readFile)(file, "utf8"));
|
|
2020
|
+
if (cache.version !== CACHE_VERSION || cache.fingerprint !== fingerprint) {
|
|
2021
|
+
return void 0;
|
|
2022
|
+
}
|
|
2023
|
+
return {
|
|
2024
|
+
responsesByFile: deserializeMap(paths, cache.data.responsesByFile),
|
|
2025
|
+
inputsByFile: deserializeMap(paths, cache.data.inputsByFile),
|
|
2026
|
+
securityByFile: deserializeMap(paths, cache.data.securityByFile),
|
|
2027
|
+
hiddenFiles: new Set(cache.data.hiddenFiles.map((entry) => deserializePath(paths, entry))),
|
|
2028
|
+
openapiByFile: deserializeMap(paths, cache.data.openapiByFile)
|
|
2029
|
+
};
|
|
2030
|
+
} catch {
|
|
2031
|
+
return void 0;
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
async function writeSyncCache(paths, fingerprint, data) {
|
|
2035
|
+
const cache = {
|
|
2036
|
+
version: CACHE_VERSION,
|
|
2037
|
+
fingerprint,
|
|
2038
|
+
data: {
|
|
2039
|
+
responsesByFile: serializeMap(paths, data.responsesByFile),
|
|
2040
|
+
inputsByFile: serializeMap(paths, data.inputsByFile),
|
|
2041
|
+
securityByFile: serializeMap(paths, data.securityByFile),
|
|
2042
|
+
hiddenFiles: [...data.hiddenFiles].map((file) => serializePath(paths, file)),
|
|
2043
|
+
openapiByFile: serializeMap(paths, data.openapiByFile)
|
|
2044
|
+
}
|
|
2045
|
+
};
|
|
2046
|
+
await writeJson(cachePath(paths), cache);
|
|
2047
|
+
}
|
|
2048
|
+
|
|
1403
2049
|
// src/generator/sync.ts
|
|
1404
2050
|
async function typeFolders(paths, routes) {
|
|
1405
2051
|
const verbsByDir = /* @__PURE__ */ new Map();
|
|
@@ -1410,10 +2056,11 @@ async function typeFolders(paths, routes) {
|
|
|
1410
2056
|
verbsByDir.set(key, list);
|
|
1411
2057
|
}
|
|
1412
2058
|
const dirs = await scanRouteFolders(paths.routesDir);
|
|
2059
|
+
const sharedCache = /* @__PURE__ */ new Map();
|
|
1413
2060
|
return dirs.map((dir) => ({
|
|
1414
2061
|
dir,
|
|
1415
2062
|
params: routeParamsForDir(paths.routesDir, dir),
|
|
1416
|
-
sharedFiles: sharedFilesForDir(paths.routesDir, dir),
|
|
2063
|
+
sharedFiles: sharedFilesForDir(paths.routesDir, dir, sharedCache),
|
|
1417
2064
|
verbs: verbsByDir.get(slash(dir)) ?? []
|
|
1418
2065
|
}));
|
|
1419
2066
|
}
|
|
@@ -1425,11 +2072,9 @@ async function extractResponses(paths, routes) {
|
|
|
1425
2072
|
try {
|
|
1426
2073
|
const { createSchemaProgram: createSchemaProgram2, extractRouteResponses: extractRouteResponses2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
|
|
1427
2074
|
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
|
-
);
|
|
2075
|
+
const appTypes = (0, import_node_path12.join)(paths.outDir, "types", "app.d.ts");
|
|
2076
|
+
const roots = (0, import_node_fs8.existsSync)(appTypes) ? [...files, appTypes] : files;
|
|
2077
|
+
const program = createSchemaProgram2(paths, roots);
|
|
1433
2078
|
for (const file of files) {
|
|
1434
2079
|
byFile.set(file, extractRouteResponses2(program, file));
|
|
1435
2080
|
}
|
|
@@ -1442,8 +2087,9 @@ async function extractMeta(config, paths, routes) {
|
|
|
1442
2087
|
const inputsByFile = /* @__PURE__ */ new Map();
|
|
1443
2088
|
const securityByFile = /* @__PURE__ */ new Map();
|
|
1444
2089
|
const hiddenFiles = /* @__PURE__ */ new Set();
|
|
2090
|
+
const openapiByFile = /* @__PURE__ */ new Map();
|
|
1445
2091
|
if (routes.length === 0) {
|
|
1446
|
-
return { inputsByFile, securityByFile, hiddenFiles };
|
|
2092
|
+
return { inputsByFile, securityByFile, hiddenFiles, openapiByFile };
|
|
1447
2093
|
}
|
|
1448
2094
|
try {
|
|
1449
2095
|
const meta = await extractRouteMeta(config, paths, routes);
|
|
@@ -1457,79 +2103,180 @@ async function extractMeta(config, paths, routes) {
|
|
|
1457
2103
|
if (entry.hidden) {
|
|
1458
2104
|
hiddenFiles.add(file);
|
|
1459
2105
|
}
|
|
2106
|
+
if (entry.openapi) {
|
|
2107
|
+
openapiByFile.set(file, entry.openapi);
|
|
2108
|
+
}
|
|
1460
2109
|
}
|
|
1461
2110
|
} catch (error) {
|
|
1462
2111
|
console.warn(`giri: skipped input/security generation (${error.message}).`);
|
|
1463
2112
|
}
|
|
1464
|
-
return { inputsByFile, securityByFile, hiddenFiles };
|
|
2113
|
+
return { inputsByFile, securityByFile, hiddenFiles, openapiByFile };
|
|
1465
2114
|
}
|
|
1466
2115
|
async function syncProject(config, options = {}) {
|
|
1467
2116
|
const paths = resolveGiriPaths(config, options.cwd);
|
|
1468
2117
|
assertSafeOutDir(paths);
|
|
2118
|
+
const hadOutDir = (0, import_node_fs8.existsSync)(paths.outDir);
|
|
1469
2119
|
const routes = await scanRoutes(paths.routesDir);
|
|
1470
2120
|
const folders = await typeFolders(paths, routes);
|
|
1471
|
-
await (
|
|
2121
|
+
const fingerprint = await syncFingerprint(config, paths);
|
|
2122
|
+
const cached = await readSyncCache(paths, fingerprint);
|
|
2123
|
+
const generatedFiles = [
|
|
2124
|
+
(0, import_node_path12.join)(paths.outDir, "tsconfig.json"),
|
|
2125
|
+
(0, import_node_path12.join)(paths.outDir, "manifest.json"),
|
|
2126
|
+
(0, import_node_path12.join)(paths.outDir, "openapi.json"),
|
|
2127
|
+
(0, import_node_path12.join)(paths.outDir, "routes.d.ts"),
|
|
2128
|
+
(0, import_node_path12.join)(paths.outDir, "types", "app.d.ts"),
|
|
2129
|
+
...folders.map((folder) => typeFilePath(paths, folder.dir))
|
|
2130
|
+
];
|
|
2131
|
+
if (cached && generatedFiles.every(import_node_fs8.existsSync)) {
|
|
2132
|
+
return { paths, routes, folders, data: cached };
|
|
2133
|
+
}
|
|
2134
|
+
await (0, import_promises4.mkdir)(paths.outDir, { recursive: true });
|
|
1472
2135
|
await writeParamTypes(paths, folders);
|
|
1473
2136
|
await writeRouteTypes(paths, routes);
|
|
1474
2137
|
await writeAppTypes(paths);
|
|
1475
2138
|
await writeTsConfig(paths, config);
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1478
|
-
|
|
2139
|
+
const data = cached ?? {
|
|
2140
|
+
responsesByFile: await extractResponses(paths, routes),
|
|
2141
|
+
...await extractMeta(config, paths, routes)
|
|
2142
|
+
};
|
|
1479
2143
|
await writeManifest(paths, routes, data);
|
|
1480
2144
|
await writeOpenApi(paths, routes, data);
|
|
1481
|
-
await
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
2145
|
+
await writeSyncCache(paths, fingerprint, data);
|
|
2146
|
+
if (hadOutDir) {
|
|
2147
|
+
await pruneDir(
|
|
2148
|
+
paths.outDir,
|
|
2149
|
+
/* @__PURE__ */ new Set([
|
|
2150
|
+
(0, import_node_path12.join)(paths.outDir, "tsconfig.json"),
|
|
2151
|
+
(0, import_node_path12.join)(paths.outDir, "manifest.json"),
|
|
2152
|
+
(0, import_node_path12.join)(paths.outDir, "openapi.json"),
|
|
2153
|
+
(0, import_node_path12.join)(paths.outDir, "routes.d.ts"),
|
|
2154
|
+
(0, import_node_path12.join)(paths.outDir, SYNC_CACHE_NAME),
|
|
2155
|
+
(0, import_node_path12.join)(paths.outDir, "types", "app.d.ts"),
|
|
2156
|
+
...folders.map((folder) => typeFilePath(paths, folder.dir))
|
|
2157
|
+
])
|
|
2158
|
+
);
|
|
2159
|
+
}
|
|
1492
2160
|
return { paths, routes, folders, data };
|
|
1493
2161
|
}
|
|
1494
2162
|
|
|
1495
2163
|
// src/generator/watch.ts
|
|
1496
|
-
var
|
|
1497
|
-
var
|
|
2164
|
+
var import_node_fs10 = require("fs");
|
|
2165
|
+
var import_node_path15 = require("path");
|
|
1498
2166
|
|
|
1499
|
-
// src/loader/
|
|
1500
|
-
var
|
|
1501
|
-
var
|
|
1502
|
-
var
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
var
|
|
1506
|
-
|
|
2167
|
+
// src/loader/import-graph.ts
|
|
2168
|
+
var import_node_fs9 = require("fs");
|
|
2169
|
+
var import_node_path13 = require("path");
|
|
2170
|
+
var import_typescript6 = __toESM(require("typescript"));
|
|
2171
|
+
var import_tinyglobby3 = require("tinyglobby");
|
|
2172
|
+
var RESOLVE_EXTS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
2173
|
+
var JS_EXT = /\.(?:c|m)?jsx?$/;
|
|
2174
|
+
var toSlash = (path) => path.split(import_node_path13.sep).join("/");
|
|
2175
|
+
function probeFile(base) {
|
|
2176
|
+
if ((0, import_node_fs9.existsSync)(base) && (0, import_node_fs9.statSync)(base).isFile()) {
|
|
2177
|
+
return base;
|
|
2178
|
+
}
|
|
2179
|
+
for (const ext of RESOLVE_EXTS) {
|
|
2180
|
+
const candidate = base + ext;
|
|
2181
|
+
if ((0, import_node_fs9.existsSync)(candidate)) {
|
|
2182
|
+
return candidate;
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
for (const ext of RESOLVE_EXTS) {
|
|
2186
|
+
const candidate = (0, import_node_path13.join)(base, `index${ext}`);
|
|
2187
|
+
if ((0, import_node_fs9.existsSync)(candidate)) {
|
|
2188
|
+
return candidate;
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
return void 0;
|
|
2192
|
+
}
|
|
2193
|
+
function resolveSpecifier(specifier, fromFile, alias, cwd) {
|
|
2194
|
+
let target;
|
|
2195
|
+
if (specifier.startsWith(".")) {
|
|
2196
|
+
target = (0, import_node_path13.resolve)((0, import_node_path13.dirname)(fromFile), specifier);
|
|
2197
|
+
} else {
|
|
2198
|
+
const aliased = resolveAliasRequest(specifier, alias, cwd);
|
|
2199
|
+
if (aliased === void 0) {
|
|
2200
|
+
return void 0;
|
|
2201
|
+
}
|
|
2202
|
+
target = aliased;
|
|
2203
|
+
}
|
|
2204
|
+
const resolved = probeFile(target);
|
|
2205
|
+
if (resolved) {
|
|
2206
|
+
return resolved;
|
|
2207
|
+
}
|
|
2208
|
+
if (JS_EXT.test(target)) {
|
|
2209
|
+
return probeFile(target.replace(JS_EXT, ""));
|
|
2210
|
+
}
|
|
2211
|
+
return void 0;
|
|
2212
|
+
}
|
|
2213
|
+
function importSpecifiers(file) {
|
|
2214
|
+
let source;
|
|
2215
|
+
try {
|
|
2216
|
+
source = import_typescript6.default.createSourceFile(file, (0, import_node_fs9.readFileSync)(file, "utf8"), import_typescript6.default.ScriptTarget.Latest, false);
|
|
2217
|
+
} catch {
|
|
2218
|
+
return [];
|
|
2219
|
+
}
|
|
2220
|
+
const specifiers = [];
|
|
2221
|
+
const visit = (node) => {
|
|
2222
|
+
if ((import_typescript6.default.isImportDeclaration(node) || import_typescript6.default.isExportDeclaration(node)) && node.moduleSpecifier && import_typescript6.default.isStringLiteral(node.moduleSpecifier)) {
|
|
2223
|
+
specifiers.push(node.moduleSpecifier.text);
|
|
2224
|
+
} else if (import_typescript6.default.isImportEqualsDeclaration(node) && import_typescript6.default.isExternalModuleReference(node.moduleReference) && import_typescript6.default.isStringLiteralLike(node.moduleReference.expression)) {
|
|
2225
|
+
specifiers.push(node.moduleReference.expression.text);
|
|
2226
|
+
} else if (import_typescript6.default.isCallExpression(node)) {
|
|
2227
|
+
const isRequire = import_typescript6.default.isIdentifier(node.expression) && node.expression.text === "require";
|
|
2228
|
+
const isDynamicImport = node.expression.kind === import_typescript6.default.SyntaxKind.ImportKeyword;
|
|
2229
|
+
const [first] = node.arguments;
|
|
2230
|
+
if ((isRequire || isDynamicImport) && first && import_typescript6.default.isStringLiteralLike(first)) {
|
|
2231
|
+
specifiers.push(first.text);
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
import_typescript6.default.forEachChild(node, visit);
|
|
2235
|
+
};
|
|
2236
|
+
visit(source);
|
|
2237
|
+
return specifiers;
|
|
2238
|
+
}
|
|
2239
|
+
async function buildImportGraph(config, cwd) {
|
|
2240
|
+
const root = (0, import_node_path13.resolve)(cwd);
|
|
2241
|
+
const outDir = (0, import_node_path13.resolve)(root, config.outDir ?? ".giri") + import_node_path13.sep;
|
|
2242
|
+
const outRel = (0, import_node_path13.relative)(root, outDir).split(import_node_path13.sep).join("/").replace(/\/$/, "");
|
|
2243
|
+
const files = await (0, import_tinyglobby3.glob)("**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}", {
|
|
2244
|
+
cwd: root,
|
|
2245
|
+
absolute: true,
|
|
2246
|
+
onlyFiles: true,
|
|
2247
|
+
ignore: ["**/node_modules/**", "**/.git/**", outRel ? `${outRel}/**` : ".giri/**"]
|
|
2248
|
+
});
|
|
1507
2249
|
const importers = /* @__PURE__ */ new Map();
|
|
1508
2250
|
const nodes = /* @__PURE__ */ new Set();
|
|
1509
|
-
for (const
|
|
1510
|
-
if (
|
|
1511
|
-
continue;
|
|
1512
|
-
}
|
|
1513
|
-
const mod = require.cache[id];
|
|
1514
|
-
if (!mod) {
|
|
2251
|
+
for (const file of files) {
|
|
2252
|
+
if (file.startsWith(outDir)) {
|
|
1515
2253
|
continue;
|
|
1516
2254
|
}
|
|
1517
|
-
nodes.add(toSlash(
|
|
1518
|
-
for (const
|
|
1519
|
-
|
|
2255
|
+
nodes.add(toSlash(file));
|
|
2256
|
+
for (const specifier of importSpecifiers(file)) {
|
|
2257
|
+
const dep = resolveSpecifier(specifier, file, config.alias, root);
|
|
2258
|
+
if (!dep || dep.startsWith(outDir)) {
|
|
1520
2259
|
continue;
|
|
1521
2260
|
}
|
|
1522
|
-
|
|
1523
|
-
const
|
|
1524
|
-
|
|
2261
|
+
const from = toSlash(file);
|
|
2262
|
+
const to = toSlash(dep);
|
|
2263
|
+
nodes.add(to);
|
|
2264
|
+
let set = importers.get(to);
|
|
1525
2265
|
if (!set) {
|
|
1526
2266
|
set = /* @__PURE__ */ new Set();
|
|
1527
|
-
importers.set(
|
|
2267
|
+
importers.set(to, set);
|
|
1528
2268
|
}
|
|
1529
|
-
set.add(
|
|
2269
|
+
set.add(from);
|
|
1530
2270
|
}
|
|
1531
2271
|
}
|
|
1532
2272
|
return { importers, nodes };
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
// src/loader/module-loader.ts
|
|
2276
|
+
var import_node_path14 = require("path");
|
|
2277
|
+
var toSlash2 = (path) => path.split(import_node_path14.sep).join("/");
|
|
2278
|
+
var isProjectModule = (id, root) => {
|
|
2279
|
+
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
2280
|
};
|
|
1534
2281
|
var collectDependents = (graph, start) => {
|
|
1535
2282
|
const out = /* @__PURE__ */ new Set([start]);
|
|
@@ -1547,26 +2294,36 @@ var collectDependents = (graph, start) => {
|
|
|
1547
2294
|
};
|
|
1548
2295
|
var purgeModules = (files) => {
|
|
1549
2296
|
for (const id of Object.keys(require.cache)) {
|
|
1550
|
-
if (files.has(
|
|
2297
|
+
if (files.has(toSlash2(id))) {
|
|
1551
2298
|
delete require.cache[id];
|
|
1552
2299
|
}
|
|
1553
2300
|
}
|
|
1554
2301
|
};
|
|
1555
2302
|
var purgeProjectModules = (cwd) => {
|
|
1556
|
-
const root = (0,
|
|
2303
|
+
const root = (0, import_node_path14.resolve)(cwd) + import_node_path14.sep;
|
|
1557
2304
|
for (const id of Object.keys(require.cache)) {
|
|
1558
2305
|
if (isProjectModule(id, root)) {
|
|
1559
2306
|
delete require.cache[id];
|
|
1560
2307
|
}
|
|
1561
2308
|
}
|
|
1562
2309
|
};
|
|
2310
|
+
var purgeGeneratedModules = (outDir) => {
|
|
2311
|
+
const root = (0, import_node_path14.resolve)(outDir) + import_node_path14.sep;
|
|
2312
|
+
for (const id of Object.keys(require.cache)) {
|
|
2313
|
+
if ((0, import_node_path14.resolve)(id).startsWith(root)) {
|
|
2314
|
+
delete require.cache[id];
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
};
|
|
1563
2318
|
|
|
1564
2319
|
// src/generator/watch.ts
|
|
1565
2320
|
function createWatchUpdater(config, initial) {
|
|
1566
2321
|
const paths = initial.paths;
|
|
1567
2322
|
let routes = initial.routes;
|
|
1568
2323
|
const data = initial.data;
|
|
2324
|
+
let metadataQueue = Promise.resolve();
|
|
1569
2325
|
const fullResync = async () => {
|
|
2326
|
+
await metadataQueue;
|
|
1570
2327
|
purgeProjectModules(paths.cwd);
|
|
1571
2328
|
const result = await syncProject(config, { cwd: paths.cwd });
|
|
1572
2329
|
routes = result.routes;
|
|
@@ -1574,48 +2331,84 @@ function createWatchUpdater(config, initial) {
|
|
|
1574
2331
|
data.inputsByFile = result.data.inputsByFile;
|
|
1575
2332
|
data.securityByFile = result.data.securityByFile;
|
|
1576
2333
|
data.hiddenFiles = result.data.hiddenFiles;
|
|
2334
|
+
data.openapiByFile = result.data.openapiByFile;
|
|
2335
|
+
purgeGeneratedModules(paths.outDir);
|
|
1577
2336
|
return "full";
|
|
1578
2337
|
};
|
|
1579
|
-
const
|
|
1580
|
-
|
|
2338
|
+
const reextractRoutes = async (affected) => {
|
|
2339
|
+
if (affected.length === 0) {
|
|
2340
|
+
return;
|
|
2341
|
+
}
|
|
1581
2342
|
try {
|
|
1582
2343
|
const { createSchemaProgram: createSchemaProgram2, extractRouteResponses: extractRouteResponses2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
|
|
1583
|
-
const appTypes = (0,
|
|
1584
|
-
const
|
|
1585
|
-
|
|
2344
|
+
const appTypes = (0, import_node_path15.join)(paths.outDir, "types", "app.d.ts");
|
|
2345
|
+
const files = affected.map((route) => route.file);
|
|
2346
|
+
const program = createSchemaProgram2(
|
|
2347
|
+
paths,
|
|
2348
|
+
(0, import_node_fs10.existsSync)(appTypes) ? [...files, appTypes] : files
|
|
2349
|
+
);
|
|
2350
|
+
for (const route of affected) {
|
|
2351
|
+
data.responsesByFile.set(
|
|
2352
|
+
route.file,
|
|
2353
|
+
extractRouteResponses2(program, route.file)
|
|
2354
|
+
);
|
|
2355
|
+
}
|
|
1586
2356
|
} catch {
|
|
1587
2357
|
}
|
|
1588
2358
|
try {
|
|
1589
|
-
const meta = await extractRouteMeta(config, paths,
|
|
1590
|
-
const
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
data.
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
2359
|
+
const meta = await extractRouteMeta(config, paths, affected);
|
|
2360
|
+
for (const route of affected) {
|
|
2361
|
+
const key = route.file;
|
|
2362
|
+
const entry = meta.get(key);
|
|
2363
|
+
data.inputsByFile.delete(key);
|
|
2364
|
+
data.securityByFile.delete(key);
|
|
2365
|
+
data.hiddenFiles.delete(key);
|
|
2366
|
+
data.openapiByFile.delete(key);
|
|
2367
|
+
if (entry?.input) {
|
|
2368
|
+
data.inputsByFile.set(key, entry.input);
|
|
2369
|
+
}
|
|
2370
|
+
if (entry?.security) {
|
|
2371
|
+
data.securityByFile.set(key, entry.security);
|
|
2372
|
+
}
|
|
2373
|
+
if (entry?.hidden) {
|
|
2374
|
+
data.hiddenFiles.add(key);
|
|
2375
|
+
}
|
|
2376
|
+
if (entry?.openapi) {
|
|
2377
|
+
data.openapiByFile.set(key, entry.openapi);
|
|
2378
|
+
}
|
|
1602
2379
|
}
|
|
1603
2380
|
} catch {
|
|
1604
2381
|
}
|
|
1605
2382
|
};
|
|
1606
2383
|
return {
|
|
1607
|
-
|
|
2384
|
+
settled: () => metadataQueue,
|
|
2385
|
+
async apply(filename, options = {}) {
|
|
1608
2386
|
if (!filename) {
|
|
1609
2387
|
return fullResync();
|
|
1610
2388
|
}
|
|
1611
|
-
const abs = (0,
|
|
2389
|
+
const abs = (0, import_node_path15.resolve)((0, import_node_path15.dirname)(paths.routesDir), filename);
|
|
1612
2390
|
const file = slash(abs);
|
|
1613
|
-
if (!(0,
|
|
2391
|
+
if (!(0, import_node_fs10.existsSync)(abs)) {
|
|
1614
2392
|
return fullResync();
|
|
1615
2393
|
}
|
|
1616
|
-
|
|
2394
|
+
if ((0, import_node_fs10.statSync)(abs).isDirectory()) {
|
|
2395
|
+
return "skip";
|
|
2396
|
+
}
|
|
2397
|
+
assertSourceSyntax(abs);
|
|
1617
2398
|
const isRoute = routes.some((candidate) => slash(candidate.file) === file);
|
|
1618
|
-
|
|
2399
|
+
const isShared = routes.some(
|
|
2400
|
+
(route) => route.sharedFiles.some((shared) => slash(shared) === file)
|
|
2401
|
+
);
|
|
2402
|
+
const isMethodFile = /^\+(?:get|post|put|patch|delete|options|head)\.(?:[cm]?[jt]s|[jt]sx)$/i.test((0, import_node_path15.basename)(file));
|
|
2403
|
+
const isNewRouteStructure = file.startsWith(`${slash(paths.routesDir)}/`) && /^\+(?:get|post|put|patch|delete|options|head|shared)\.(?:[cm]?[jt]s|[jt]sx)$/i.test((0, import_node_path15.basename)(file)) && !isRoute && !isShared;
|
|
2404
|
+
if (isRoute || isNewRouteStructure && isMethodFile) {
|
|
2405
|
+
assertRouteHandleExport(abs);
|
|
2406
|
+
}
|
|
2407
|
+
if (isNewRouteStructure) {
|
|
2408
|
+
return fullResync();
|
|
2409
|
+
}
|
|
2410
|
+
const graph = await buildImportGraph(config, paths.cwd);
|
|
2411
|
+
if (!graph.nodes.has(file) && !isRoute && !isShared) {
|
|
1619
2412
|
return fullResync();
|
|
1620
2413
|
}
|
|
1621
2414
|
const dependents = collectDependents(graph, file);
|
|
@@ -1623,31 +2416,40 @@ function createWatchUpdater(config, initial) {
|
|
|
1623
2416
|
(route) => dependents.has(slash(route.file)) || route.sharedFiles.some((shared) => dependents.has(slash(shared)))
|
|
1624
2417
|
);
|
|
1625
2418
|
purgeModules(dependents);
|
|
1626
|
-
|
|
1627
|
-
await
|
|
2419
|
+
const refreshMetadata = async () => {
|
|
2420
|
+
await reextractRoutes(affected);
|
|
2421
|
+
await writeManifest(paths, routes, data);
|
|
2422
|
+
await writeOpenApi(paths, routes, data);
|
|
2423
|
+
await writeSyncCache(paths, await syncFingerprint(config, paths), data);
|
|
2424
|
+
purgeGeneratedModules(paths.outDir);
|
|
2425
|
+
};
|
|
2426
|
+
const task = metadataQueue.then(refreshMetadata);
|
|
2427
|
+
metadataQueue = task.catch((error) => {
|
|
2428
|
+
console.error("giri: deferred metadata update failed", error);
|
|
2429
|
+
});
|
|
2430
|
+
if (!options.deferMetadata) {
|
|
2431
|
+
await task;
|
|
1628
2432
|
}
|
|
1629
|
-
await writeManifest(paths, routes, data);
|
|
1630
|
-
await writeOpenApi(paths, routes, data);
|
|
1631
2433
|
return "incremental";
|
|
1632
2434
|
}
|
|
1633
2435
|
};
|
|
1634
2436
|
}
|
|
1635
2437
|
|
|
1636
2438
|
// src/lifecycle.ts
|
|
1637
|
-
var
|
|
1638
|
-
var
|
|
2439
|
+
var import_node_fs11 = require("fs");
|
|
2440
|
+
var import_node_path16 = require("path");
|
|
1639
2441
|
var MAIN_EXTENSIONS2 = ["ts", "tsx", "mts", "cts", "js", "jsx", "mjs", "cjs"];
|
|
1640
2442
|
function resolveMainFile(cwd) {
|
|
1641
2443
|
for (const ext of MAIN_EXTENSIONS2) {
|
|
1642
|
-
const file = (0,
|
|
1643
|
-
if ((0,
|
|
2444
|
+
const file = (0, import_node_path16.join)(cwd, "src", `main.${ext}`);
|
|
2445
|
+
if ((0, import_node_fs11.existsSync)(file)) {
|
|
1644
2446
|
return file;
|
|
1645
2447
|
}
|
|
1646
2448
|
}
|
|
1647
2449
|
return void 0;
|
|
1648
2450
|
}
|
|
1649
2451
|
async function loadLifecycle(cwd = process.cwd()) {
|
|
1650
|
-
const file = resolveMainFile((0,
|
|
2452
|
+
const file = resolveMainFile((0, import_node_path16.resolve)(cwd));
|
|
1651
2453
|
if (!file) {
|
|
1652
2454
|
return {};
|
|
1653
2455
|
}
|
|
@@ -1719,6 +2521,12 @@ var tag = {
|
|
|
1719
2521
|
warn: color.bold(color.yellow(`[${TAG}]`)),
|
|
1720
2522
|
error: color.bold(color.red(`[${TAG}]`))
|
|
1721
2523
|
};
|
|
2524
|
+
function formatError(error) {
|
|
2525
|
+
if (error instanceof Error) {
|
|
2526
|
+
return error.stack ?? `${error.name}: ${error.message}`;
|
|
2527
|
+
}
|
|
2528
|
+
return String(error);
|
|
2529
|
+
}
|
|
1722
2530
|
var log2 = {
|
|
1723
2531
|
info(message, scope) {
|
|
1724
2532
|
console.log(line(tag.info, message, scope));
|
|
@@ -1805,25 +2613,25 @@ function parseFlags(args) {
|
|
|
1805
2613
|
return flags;
|
|
1806
2614
|
}
|
|
1807
2615
|
async function ensureGitignore(cwd) {
|
|
1808
|
-
const file = (0,
|
|
2616
|
+
const file = (0, import_node_path17.join)(cwd, ".gitignore");
|
|
1809
2617
|
const entry = ".giri";
|
|
1810
|
-
if (!(0,
|
|
1811
|
-
await (0,
|
|
2618
|
+
if (!(0, import_node_fs12.existsSync)(file)) {
|
|
2619
|
+
await (0, import_promises5.writeFile)(file, `${entry}
|
|
1812
2620
|
`);
|
|
1813
2621
|
return;
|
|
1814
2622
|
}
|
|
1815
|
-
const content = await (0,
|
|
2623
|
+
const content = await (0, import_promises5.readFile)(file, "utf8");
|
|
1816
2624
|
if (!content.split(/\r?\n/).includes(entry)) {
|
|
1817
|
-
await (0,
|
|
2625
|
+
await (0, import_promises5.appendFile)(file, `${content.endsWith("\n") ? "" : "\n"}${entry}
|
|
1818
2626
|
`);
|
|
1819
2627
|
}
|
|
1820
2628
|
}
|
|
1821
2629
|
async function ensureTsConfig(cwd) {
|
|
1822
|
-
const file = (0,
|
|
1823
|
-
if ((0,
|
|
2630
|
+
const file = (0, import_node_path17.join)(cwd, "tsconfig.json");
|
|
2631
|
+
if ((0, import_node_fs12.existsSync)(file)) {
|
|
1824
2632
|
return;
|
|
1825
2633
|
}
|
|
1826
|
-
await (0,
|
|
2634
|
+
await (0, import_promises5.writeFile)(
|
|
1827
2635
|
file,
|
|
1828
2636
|
`${JSON.stringify(
|
|
1829
2637
|
{
|
|
@@ -1849,7 +2657,7 @@ async function ensureTsConfig(cwd) {
|
|
|
1849
2657
|
async function missingDeps(cwd, candidates) {
|
|
1850
2658
|
let pkg = {};
|
|
1851
2659
|
try {
|
|
1852
|
-
pkg = JSON.parse(await (0,
|
|
2660
|
+
pkg = JSON.parse(await (0, import_promises5.readFile)((0, import_node_path17.join)(cwd, "package.json"), "utf8"));
|
|
1853
2661
|
} catch {
|
|
1854
2662
|
}
|
|
1855
2663
|
const present = /* @__PURE__ */ new Set();
|
|
@@ -1918,7 +2726,7 @@ async function selectAdapter(interactive) {
|
|
|
1918
2726
|
return ADAPTERS.find((adapter) => adapter.value === picked) ?? null;
|
|
1919
2727
|
}
|
|
1920
2728
|
async function initProject(cwd, flags) {
|
|
1921
|
-
if (!(0,
|
|
2729
|
+
if (!(0, import_node_fs12.existsSync)((0, import_node_path17.join)(cwd, "package.json"))) {
|
|
1922
2730
|
throw new Error(
|
|
1923
2731
|
"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
2732
|
);
|
|
@@ -1943,14 +2751,14 @@ async function initProject(cwd, flags) {
|
|
|
1943
2751
|
prompts.cancel(`The ${adapter.label} adapter isn't available yet - only Hono ships today.`);
|
|
1944
2752
|
return;
|
|
1945
2753
|
}
|
|
1946
|
-
const configPath = (0,
|
|
1947
|
-
if (!(0,
|
|
1948
|
-
await (0,
|
|
2754
|
+
const configPath = (0, import_node_path17.join)(cwd, "giri.config.ts");
|
|
2755
|
+
if (!(0, import_node_fs12.existsSync)(configPath)) {
|
|
2756
|
+
await (0, import_promises5.writeFile)(configPath, configSource(adapter));
|
|
1949
2757
|
}
|
|
1950
|
-
const routePath = (0,
|
|
1951
|
-
if (!(0,
|
|
1952
|
-
await (0,
|
|
1953
|
-
await (0,
|
|
2758
|
+
const routePath = (0, import_node_path17.join)(cwd, "src", "routes", "+get.ts");
|
|
2759
|
+
if (!(0, import_node_fs12.existsSync)(routePath)) {
|
|
2760
|
+
await (0, import_promises5.mkdir)((0, import_node_path17.join)(cwd, "src", "routes"), { recursive: true });
|
|
2761
|
+
await (0, import_promises5.writeFile)(
|
|
1954
2762
|
routePath,
|
|
1955
2763
|
[
|
|
1956
2764
|
'import type { Handle } from "@boon4681/giri";',
|
|
@@ -2017,69 +2825,158 @@ function displayHost(address) {
|
|
|
2017
2825
|
return address.includes(":") ? `[${address}]` : address;
|
|
2018
2826
|
}
|
|
2019
2827
|
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
|
-
|
|
2828
|
+
Error.stackTraceLimit = 30;
|
|
2829
|
+
const { watch } = await import("chokidar");
|
|
2830
|
+
let stop;
|
|
2831
|
+
const boot = async (cfg) => {
|
|
2832
|
+
const closers = [];
|
|
2833
|
+
closers.push(registerAliasResolver(cfg.alias, (0, import_node_path17.resolve)(process.cwd())));
|
|
2834
|
+
const lifecycle = await loadLifecycle();
|
|
2835
|
+
const sync = syncProject(cfg).then((initial2) => {
|
|
2836
|
+
log2.success(
|
|
2837
|
+
`synced ${initial2.routes.length} route${initial2.routes.length === 1 ? "" : "s"} ${muted(`at ${initial2.paths.outDir}`)}`,
|
|
2838
|
+
"sync"
|
|
2839
|
+
);
|
|
2840
|
+
return initial2;
|
|
2841
|
+
});
|
|
2842
|
+
const [initial, services] = await Promise.all([sync, runInit(lifecycle)]);
|
|
2843
|
+
const loader = await safeRegister();
|
|
2844
|
+
closers.push(loader.unregister);
|
|
2845
|
+
let current = await buildGiriApp(cfg, {
|
|
2846
|
+
services,
|
|
2847
|
+
lazy: true,
|
|
2848
|
+
loaderRegistered: true,
|
|
2849
|
+
aliasResolverRegistered: true
|
|
2850
|
+
});
|
|
2851
|
+
const port = flags.port ?? cfg.server?.port ?? 3e3;
|
|
2852
|
+
const hostname = flags.hostname ?? cfg.server?.hostname;
|
|
2853
|
+
if (flags.watch) {
|
|
2854
|
+
const srcDir = (0, import_node_path17.resolve)(current.paths.routesDir, "..");
|
|
2855
|
+
if ((0, import_node_fs12.existsSync)(srcDir)) {
|
|
2856
|
+
let timer;
|
|
2857
|
+
let syncing = false;
|
|
2858
|
+
const changed = /* @__PURE__ */ new Set();
|
|
2859
|
+
const updater = createWatchUpdater(cfg, initial);
|
|
2860
|
+
const hmrCount = /* @__PURE__ */ new Map();
|
|
2861
|
+
const bump = (key) => {
|
|
2862
|
+
const next = (hmrCount.get(key) ?? 0) + 1;
|
|
2863
|
+
hmrCount.set(key, next);
|
|
2864
|
+
return next;
|
|
2865
|
+
};
|
|
2866
|
+
const flush = async () => {
|
|
2867
|
+
if (syncing) {
|
|
2868
|
+
return;
|
|
2869
|
+
}
|
|
2870
|
+
syncing = true;
|
|
2871
|
+
try {
|
|
2872
|
+
while (changed.size > 0) {
|
|
2873
|
+
const batch = [...changed];
|
|
2874
|
+
changed.clear();
|
|
2875
|
+
let rebuild = false;
|
|
2876
|
+
let fullSync = false;
|
|
2877
|
+
const dirtySet = /* @__PURE__ */ new Set();
|
|
2878
|
+
for (const name of batch) {
|
|
2879
|
+
const outcome = await updater.apply(name || null, {
|
|
2880
|
+
deferMetadata: true
|
|
2881
|
+
});
|
|
2882
|
+
if (outcome === "skip") {
|
|
2883
|
+
continue;
|
|
2884
|
+
}
|
|
2885
|
+
rebuild = true;
|
|
2886
|
+
if (name) {
|
|
2887
|
+
dirtySet.add((0, import_node_path17.resolve)(srcDir, name));
|
|
2888
|
+
}
|
|
2889
|
+
const rel = name ? `src/${name.replace(/\\/g, "/")}` : "src";
|
|
2890
|
+
log2.change(outcome === "full" ? "sync" : "update", rel, bump(rel));
|
|
2891
|
+
if (outcome === "full") {
|
|
2892
|
+
fullSync = true;
|
|
2893
|
+
break;
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
if (rebuild) {
|
|
2897
|
+
current = await buildGiriApp(cfg, {
|
|
2898
|
+
services,
|
|
2899
|
+
dirty: fullSync ? void 0 : dirtySet,
|
|
2900
|
+
lazy: true,
|
|
2901
|
+
loaderRegistered: true,
|
|
2902
|
+
aliasResolverRegistered: true
|
|
2903
|
+
});
|
|
2904
|
+
}
|
|
2057
2905
|
}
|
|
2058
|
-
|
|
2906
|
+
} catch (error) {
|
|
2907
|
+
log2.error(formatError(error), "watch");
|
|
2908
|
+
} finally {
|
|
2909
|
+
syncing = false;
|
|
2059
2910
|
}
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
}
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2911
|
+
if (changed.size > 0) {
|
|
2912
|
+
void flush();
|
|
2913
|
+
}
|
|
2914
|
+
};
|
|
2915
|
+
const watcher = watch(srcDir, { persistent: true, ignoreInitial: true });
|
|
2916
|
+
const onFileChange = (filename) => {
|
|
2917
|
+
changed.add((0, import_node_path17.isAbsolute)(filename) ? (0, import_node_path17.relative)(srcDir, filename) : filename);
|
|
2918
|
+
clearTimeout(timer);
|
|
2919
|
+
timer = setTimeout(() => void flush(), 150);
|
|
2920
|
+
};
|
|
2921
|
+
watcher.on("change", onFileChange);
|
|
2922
|
+
watcher.on("add", onFileChange);
|
|
2923
|
+
watcher.on("unlink", onFileChange);
|
|
2924
|
+
closers.push(() => {
|
|
2925
|
+
clearTimeout(timer);
|
|
2926
|
+
watcher.close();
|
|
2927
|
+
});
|
|
2928
|
+
}
|
|
2074
2929
|
}
|
|
2930
|
+
const handler = (request) => cfg.adapter.fetch(current.app, request);
|
|
2931
|
+
const server = cfg.adapter.serve(handler, { port, hostname }, (info) => {
|
|
2932
|
+
log2.ready(`http://${displayHost(info.address)}:${info.port}`);
|
|
2933
|
+
});
|
|
2934
|
+
stop = async () => {
|
|
2935
|
+
for (const close of closers) {
|
|
2936
|
+
await close();
|
|
2937
|
+
}
|
|
2938
|
+
try {
|
|
2939
|
+
await server.close();
|
|
2940
|
+
} catch {
|
|
2941
|
+
}
|
|
2942
|
+
if (lifecycle.teardown) {
|
|
2943
|
+
await lifecycle.teardown(services);
|
|
2944
|
+
}
|
|
2945
|
+
};
|
|
2946
|
+
};
|
|
2947
|
+
await boot(config);
|
|
2948
|
+
const configPath = flags.watch ? findConfigPath((0, import_node_path17.resolve)(process.cwd())) : void 0;
|
|
2949
|
+
if (configPath) {
|
|
2950
|
+
let timer;
|
|
2951
|
+
let restarting = false;
|
|
2952
|
+
const configName = (0, import_node_path17.basename)(configPath);
|
|
2953
|
+
const restart = async () => {
|
|
2954
|
+
if (restarting) {
|
|
2955
|
+
return;
|
|
2956
|
+
}
|
|
2957
|
+
restarting = true;
|
|
2958
|
+
try {
|
|
2959
|
+
log2.info(`${color.green("restart")} ${configName} changed`, "config");
|
|
2960
|
+
delete require.cache[configPath];
|
|
2961
|
+
const next = await load({ throwOnError: true });
|
|
2962
|
+
await stop?.();
|
|
2963
|
+
await boot(next);
|
|
2964
|
+
} catch (error) {
|
|
2965
|
+
log2.error(error instanceof Error ? error.message : String(error), "config");
|
|
2966
|
+
log2.info("kept the previous server running \u2014 fix the config and save again", "config");
|
|
2967
|
+
} finally {
|
|
2968
|
+
restarting = false;
|
|
2969
|
+
}
|
|
2970
|
+
};
|
|
2971
|
+
const configWatcher = watch(configPath, { persistent: true, ignoreInitial: true });
|
|
2972
|
+
configWatcher.on("all", () => {
|
|
2973
|
+
clearTimeout(timer);
|
|
2974
|
+
timer = setTimeout(() => void restart(), 150);
|
|
2975
|
+
});
|
|
2075
2976
|
}
|
|
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);
|
|
2977
|
+
registerShutdown(() => stop?.());
|
|
2081
2978
|
}
|
|
2082
|
-
function registerShutdown(
|
|
2979
|
+
function registerShutdown(cleanup) {
|
|
2083
2980
|
let shuttingDown = false;
|
|
2084
2981
|
const shutdown = async () => {
|
|
2085
2982
|
if (shuttingDown) {
|
|
@@ -2087,10 +2984,7 @@ function registerShutdown(server, lifecycle, services) {
|
|
|
2087
2984
|
}
|
|
2088
2985
|
shuttingDown = true;
|
|
2089
2986
|
try {
|
|
2090
|
-
await
|
|
2091
|
-
if (lifecycle.teardown) {
|
|
2092
|
-
await lifecycle.teardown(services);
|
|
2093
|
-
}
|
|
2987
|
+
await cleanup();
|
|
2094
2988
|
} catch (error) {
|
|
2095
2989
|
log2.error(error instanceof Error ? error.message : String(error));
|
|
2096
2990
|
process.exitCode = 1;
|
|
@@ -2103,7 +2997,7 @@ function registerShutdown(server, lifecycle, services) {
|
|
|
2103
2997
|
}
|
|
2104
2998
|
async function main() {
|
|
2105
2999
|
const [command = "help", ...args] = process.argv.slice(2);
|
|
2106
|
-
const cwd = (0,
|
|
3000
|
+
const cwd = (0, import_node_path17.resolve)(process.cwd());
|
|
2107
3001
|
if (command === "help" || command === "--help" || command === "-h") {
|
|
2108
3002
|
help();
|
|
2109
3003
|
return;
|