@mokup/cli 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,717 @@
1
+ 'use strict';
2
+
3
+ const node_fs = require('node:fs');
4
+ const node_process = require('node:process');
5
+ const pathe = require('pathe');
6
+ const node_buffer = require('node:buffer');
7
+ const node_module = require('node:module');
8
+ const node_url = require('node:url');
9
+ const esbuild = require('esbuild');
10
+ const runtime = require('@mokup/runtime');
11
+ const jsoncParser = require('jsonc-parser');
12
+
13
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
14
+ async function writeBundle(outDir, hasHandlers) {
15
+ const lines = [
16
+ "import manifest from './mokup.manifest.mjs'"
17
+ ];
18
+ if (hasHandlers) {
19
+ lines.push(
20
+ "import { mokupModuleMap } from './mokup-handlers/index.mjs'",
21
+ "",
22
+ "const mokupBundle = {",
23
+ " manifest,",
24
+ " moduleMap: mokupModuleMap,",
25
+ " moduleBase: './',",
26
+ "}"
27
+ );
28
+ } else {
29
+ lines.push(
30
+ "",
31
+ "const mokupBundle = {",
32
+ " manifest,",
33
+ "}"
34
+ );
35
+ }
36
+ lines.push("", "export default mokupBundle", "export { mokupBundle }", "");
37
+ const dts = [
38
+ "import type { Manifest, ModuleMap } from '@mokup/runtime'",
39
+ "export interface MokupBundle {",
40
+ " manifest: Manifest",
41
+ " moduleMap?: ModuleMap",
42
+ " moduleBase?: string | URL",
43
+ "}",
44
+ "declare const mokupBundle: MokupBundle",
45
+ "export default mokupBundle",
46
+ "export { mokupBundle }",
47
+ ""
48
+ ];
49
+ await node_fs.promises.writeFile(pathe.join(outDir, "mokup.bundle.mjs"), lines.join("\n"), "utf8");
50
+ await node_fs.promises.writeFile(pathe.join(outDir, "mokup.bundle.d.ts"), dts.join("\n"), "utf8");
51
+ await node_fs.promises.writeFile(pathe.join(outDir, "mokup.bundle.d.mts"), dts.join("\n"), "utf8");
52
+ }
53
+ async function writeManifestModule(outDir, manifest) {
54
+ const lines = [
55
+ `const manifest = ${JSON.stringify(manifest, null, 2)}`,
56
+ "",
57
+ "export default manifest",
58
+ ""
59
+ ];
60
+ const dts = [
61
+ "import type { Manifest } from '@mokup/runtime'",
62
+ "declare const manifest: Manifest",
63
+ "export default manifest",
64
+ ""
65
+ ];
66
+ await node_fs.promises.writeFile(pathe.join(outDir, "mokup.manifest.mjs"), lines.join("\n"), "utf8");
67
+ await node_fs.promises.writeFile(pathe.join(outDir, "mokup.manifest.d.mts"), dts.join("\n"), "utf8");
68
+ }
69
+
70
+ const configExtensions = [".ts", ".js", ".mjs", ".cjs"];
71
+ async function loadModule$1(file) {
72
+ const ext = configExtensions.find((extension) => file.endsWith(extension));
73
+ if (ext === ".cjs") {
74
+ const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/cli.yAHcdIVy.cjs', document.baseURI).href)));
75
+ delete require$1.cache[file];
76
+ return require$1(file);
77
+ }
78
+ if (ext === ".js" || ext === ".mjs") {
79
+ return import(`${node_url.pathToFileURL(file).href}?t=${Date.now()}`);
80
+ }
81
+ if (ext === ".ts") {
82
+ const result = await esbuild.build({
83
+ entryPoints: [file],
84
+ bundle: true,
85
+ format: "esm",
86
+ platform: "node",
87
+ sourcemap: "inline",
88
+ target: "es2020",
89
+ write: false
90
+ });
91
+ const output = result.outputFiles[0];
92
+ const code = output?.text ?? "";
93
+ const dataUrl = `data:text/javascript;base64,${node_buffer.Buffer.from(code).toString(
94
+ "base64"
95
+ )}`;
96
+ return import(`${dataUrl}#${Date.now()}`);
97
+ }
98
+ return null;
99
+ }
100
+ function getConfigFileCandidates(dir) {
101
+ return configExtensions.map((extension) => pathe.join(dir, `index.config${extension}`));
102
+ }
103
+ async function findConfigFile(dir, cache) {
104
+ const cached = cache.get(dir);
105
+ if (cached !== void 0) {
106
+ return cached;
107
+ }
108
+ for (const candidate of getConfigFileCandidates(dir)) {
109
+ try {
110
+ await node_fs.promises.stat(candidate);
111
+ cache.set(dir, candidate);
112
+ return candidate;
113
+ } catch {
114
+ continue;
115
+ }
116
+ }
117
+ cache.set(dir, null);
118
+ return null;
119
+ }
120
+ async function loadConfig(file) {
121
+ const mod = await loadModule$1(file);
122
+ if (!mod) {
123
+ return null;
124
+ }
125
+ const value = mod?.default ?? mod;
126
+ if (!value || typeof value !== "object") {
127
+ return null;
128
+ }
129
+ return value;
130
+ }
131
+ function normalizeMiddlewares(value, source, log) {
132
+ if (!value) {
133
+ return [];
134
+ }
135
+ const list = Array.isArray(value) ? value : [value];
136
+ const middlewares = [];
137
+ list.forEach((entry, index) => {
138
+ if (typeof entry !== "function") {
139
+ log?.(`Invalid middleware in ${source}`);
140
+ return;
141
+ }
142
+ middlewares.push({ file: source, index });
143
+ });
144
+ return middlewares;
145
+ }
146
+ async function resolveDirectoryConfig(params) {
147
+ const { file, rootDir, log, configCache, fileCache } = params;
148
+ const resolvedRoot = pathe.normalize(rootDir);
149
+ const resolvedFileDir = pathe.normalize(pathe.dirname(file));
150
+ const chain = [];
151
+ let current = resolvedFileDir;
152
+ while (true) {
153
+ chain.push(current);
154
+ if (current === resolvedRoot) {
155
+ break;
156
+ }
157
+ const parent = pathe.dirname(current);
158
+ if (parent === current) {
159
+ break;
160
+ }
161
+ current = parent;
162
+ }
163
+ chain.reverse();
164
+ const merged = { middlewares: [] };
165
+ for (const dir of chain) {
166
+ const configPath = await findConfigFile(dir, fileCache);
167
+ if (!configPath) {
168
+ continue;
169
+ }
170
+ let config = configCache.get(configPath);
171
+ if (config === void 0) {
172
+ config = await loadConfig(configPath);
173
+ configCache.set(configPath, config);
174
+ }
175
+ if (!config) {
176
+ log?.(`Invalid config in ${configPath}`);
177
+ continue;
178
+ }
179
+ if (config.headers) {
180
+ merged.headers = { ...merged.headers ?? {}, ...config.headers };
181
+ }
182
+ if (typeof config.status === "number") {
183
+ merged.status = config.status;
184
+ }
185
+ if (typeof config.delay === "number") {
186
+ merged.delay = config.delay;
187
+ }
188
+ if (typeof config.enabled === "boolean") {
189
+ merged.enabled = config.enabled;
190
+ }
191
+ const normalized = normalizeMiddlewares(config.middleware, configPath, log);
192
+ if (normalized.length > 0) {
193
+ merged.middlewares.push(...normalized);
194
+ }
195
+ }
196
+ return merged;
197
+ }
198
+
199
+ function toPosix(value) {
200
+ return value.replace(/\\/g, "/");
201
+ }
202
+
203
+ const supportedExtensions = /* @__PURE__ */ new Set([
204
+ ".json",
205
+ ".jsonc",
206
+ ".ts",
207
+ ".js",
208
+ ".mjs",
209
+ ".cjs"
210
+ ]);
211
+ async function exists(path) {
212
+ try {
213
+ await node_fs.promises.stat(path);
214
+ return true;
215
+ } catch {
216
+ return false;
217
+ }
218
+ }
219
+ async function walkDir(dir, rootDir, files) {
220
+ const entries = await node_fs.promises.readdir(dir, { withFileTypes: true });
221
+ for (const entry of entries) {
222
+ if (entry.name === "node_modules" || entry.name === ".git") {
223
+ continue;
224
+ }
225
+ const fullPath = pathe.join(dir, entry.name);
226
+ if (entry.isDirectory()) {
227
+ await walkDir(fullPath, rootDir, files);
228
+ continue;
229
+ }
230
+ if (entry.isFile()) {
231
+ files.push({ file: fullPath, rootDir });
232
+ }
233
+ }
234
+ }
235
+ async function collectFiles(dirs) {
236
+ const files = [];
237
+ for (const dir of dirs) {
238
+ if (!await exists(dir)) {
239
+ continue;
240
+ }
241
+ await walkDir(dir, dir, files);
242
+ }
243
+ return files;
244
+ }
245
+ function resolveDirs(dir, root) {
246
+ const raw = dir;
247
+ const resolved = Array.isArray(raw) ? raw : raw ? [raw] : ["mock"];
248
+ const normalized = resolved.map(
249
+ (entry) => pathe.isAbsolute(entry) ? entry : pathe.resolve(root, entry)
250
+ );
251
+ return Array.from(new Set(normalized));
252
+ }
253
+ function testPatterns(patterns, value) {
254
+ const list = Array.isArray(patterns) ? patterns : [patterns];
255
+ return list.some((pattern) => pattern.test(value));
256
+ }
257
+ function matchesFilter(file, include, exclude) {
258
+ const normalized = toPosix(file);
259
+ if (exclude && testPatterns(exclude, normalized)) {
260
+ return false;
261
+ }
262
+ if (include) {
263
+ return testPatterns(include, normalized);
264
+ }
265
+ return true;
266
+ }
267
+ function isSupportedFile(file) {
268
+ if (file.endsWith(".d.ts")) {
269
+ return false;
270
+ }
271
+ if (pathe.basename(file).startsWith("index.config.")) {
272
+ return false;
273
+ }
274
+ const ext = pathe.extname(file).toLowerCase();
275
+ return supportedExtensions.has(ext);
276
+ }
277
+
278
+ function getHandlerModulePath(file, handlersDir, root) {
279
+ const relFromRoot = pathe.relative(root, file);
280
+ const ext = pathe.extname(relFromRoot);
281
+ const relNoExt = `${relFromRoot.slice(0, relFromRoot.length - ext.length)}.mjs`;
282
+ const outputPath = pathe.join(handlersDir, relNoExt);
283
+ const relFromOutDir = pathe.relative(pathe.dirname(handlersDir), outputPath);
284
+ const normalized = toPosix(relFromOutDir);
285
+ return normalized.startsWith(".") ? normalized : `./${normalized}`;
286
+ }
287
+ async function writeHandlerIndex(handlerModuleMap, handlersDir, outDir) {
288
+ const modulePaths = Array.from(new Set(handlerModuleMap.values()));
289
+ if (modulePaths.length === 0) {
290
+ return;
291
+ }
292
+ const imports = [];
293
+ const entries = [];
294
+ modulePaths.forEach((modulePath, index) => {
295
+ const absolutePath = pathe.resolve(outDir, modulePath);
296
+ const relImport = toPosix(pathe.relative(handlersDir, absolutePath));
297
+ const importPath = relImport.startsWith(".") ? relImport : `./${relImport}`;
298
+ const name = `module${index}`;
299
+ imports.push(`import * as ${name} from '${importPath}'`);
300
+ entries.push({ key: modulePath, name });
301
+ });
302
+ const lines = [
303
+ ...imports,
304
+ "",
305
+ "export const mokupModuleMap = {",
306
+ ...entries.map((entry) => ` '${entry.key}': ${entry.name},`),
307
+ "}",
308
+ ""
309
+ ];
310
+ await node_fs.promises.writeFile(pathe.join(handlersDir, "index.mjs"), lines.join("\n"), "utf8");
311
+ const dts = [
312
+ "export type MokupModuleMap = Record<string, Record<string, unknown>>",
313
+ "export declare const mokupModuleMap: MokupModuleMap",
314
+ ""
315
+ ];
316
+ await node_fs.promises.writeFile(pathe.join(handlersDir, "index.d.ts"), dts.join("\n"), "utf8");
317
+ await node_fs.promises.writeFile(pathe.join(handlersDir, "index.d.mts"), dts.join("\n"), "utf8");
318
+ }
319
+ function buildResponse(response, options) {
320
+ if (typeof response === "function") {
321
+ if (!options.handlers) {
322
+ return null;
323
+ }
324
+ const moduleRel = getHandlerModulePath(
325
+ options.file,
326
+ options.handlersDir,
327
+ options.root
328
+ );
329
+ options.handlerSources.add(options.file);
330
+ options.handlerModuleMap.set(options.file, moduleRel);
331
+ return {
332
+ type: "module",
333
+ module: moduleRel,
334
+ ruleIndex: options.ruleIndex
335
+ };
336
+ }
337
+ if (typeof response === "string") {
338
+ return {
339
+ type: "text",
340
+ body: response
341
+ };
342
+ }
343
+ if (response instanceof Uint8Array || response instanceof ArrayBuffer) {
344
+ return {
345
+ type: "binary",
346
+ body: node_buffer.Buffer.from(response).toString("base64"),
347
+ encoding: "base64"
348
+ };
349
+ }
350
+ if (node_buffer.Buffer.isBuffer(response)) {
351
+ return {
352
+ type: "binary",
353
+ body: response.toString("base64"),
354
+ encoding: "base64"
355
+ };
356
+ }
357
+ return {
358
+ type: "json",
359
+ body: response
360
+ };
361
+ }
362
+ async function bundleHandlers(files, root, handlersDir) {
363
+ await esbuild.build({
364
+ entryPoints: files,
365
+ bundle: true,
366
+ format: "esm",
367
+ platform: "neutral",
368
+ target: "es2020",
369
+ outdir: handlersDir,
370
+ outbase: root,
371
+ entryNames: "[dir]/[name]",
372
+ outExtension: { ".js": ".mjs" },
373
+ logLevel: "silent"
374
+ });
375
+ }
376
+
377
+ const methodSet = /* @__PURE__ */ new Set([
378
+ "GET",
379
+ "POST",
380
+ "PUT",
381
+ "PATCH",
382
+ "DELETE",
383
+ "OPTIONS",
384
+ "HEAD"
385
+ ]);
386
+ const methodSuffixSet = new Set(
387
+ Array.from(methodSet, (method) => method.toLowerCase())
388
+ );
389
+ const jsonExtensions = /* @__PURE__ */ new Set([".json", ".jsonc"]);
390
+ function normalizePrefix(prefix) {
391
+ if (!prefix) {
392
+ return "";
393
+ }
394
+ const normalized = prefix.startsWith("/") ? prefix : `/${prefix}`;
395
+ return normalized.endsWith("/") ? normalized.slice(0, -1) : normalized;
396
+ }
397
+ function resolveTemplate(template, prefix) {
398
+ const normalized = template.startsWith("/") ? template : `/${template}`;
399
+ if (!prefix) {
400
+ return normalized;
401
+ }
402
+ const normalizedPrefix = normalizePrefix(prefix);
403
+ if (!normalizedPrefix) {
404
+ return normalized;
405
+ }
406
+ if (normalized === normalizedPrefix || normalized.startsWith(`${normalizedPrefix}/`)) {
407
+ return normalized;
408
+ }
409
+ if (normalized === "/") {
410
+ return `${normalizedPrefix}/`;
411
+ }
412
+ return `${normalizedPrefix}${normalized}`;
413
+ }
414
+ function stripMethodSuffix(base) {
415
+ const segments = base.split(".");
416
+ const last = segments.at(-1);
417
+ if (last && methodSuffixSet.has(last.toLowerCase())) {
418
+ segments.pop();
419
+ return {
420
+ name: segments.join("."),
421
+ method: last.toUpperCase()
422
+ };
423
+ }
424
+ return {
425
+ name: base,
426
+ method: void 0
427
+ };
428
+ }
429
+ function deriveRouteFromFile(file, rootDir, log) {
430
+ const rel = toPosix(pathe.relative(rootDir, file));
431
+ const ext = pathe.extname(rel);
432
+ const withoutExt = rel.slice(0, rel.length - ext.length);
433
+ const dir = pathe.dirname(withoutExt);
434
+ const base = pathe.basename(withoutExt);
435
+ const { name, method } = stripMethodSuffix(base);
436
+ const resolvedMethod = method ?? (jsonExtensions.has(ext) ? "GET" : void 0);
437
+ if (!resolvedMethod) {
438
+ log?.(`Skip mock without method suffix: ${file}`);
439
+ return null;
440
+ }
441
+ if (!name) {
442
+ log?.(`Skip mock with empty route name: ${file}`);
443
+ return null;
444
+ }
445
+ const joined = dir === "." ? name : pathe.join(dir, name);
446
+ const segments = toPosix(joined).split("/");
447
+ if (segments.at(-1) === "index") {
448
+ segments.pop();
449
+ }
450
+ const template = segments.length === 0 ? "/" : `/${segments.join("/")}`;
451
+ const parsed = runtime.parseRouteTemplate(template);
452
+ if (parsed.errors.length > 0) {
453
+ for (const error of parsed.errors) {
454
+ log?.(`${error} in ${file}`);
455
+ }
456
+ return null;
457
+ }
458
+ for (const warning of parsed.warnings) {
459
+ log?.(`${warning} in ${file}`);
460
+ }
461
+ return {
462
+ template: parsed.template,
463
+ method: resolvedMethod,
464
+ tokens: parsed.tokens,
465
+ score: parsed.score
466
+ };
467
+ }
468
+ function resolveRule(params) {
469
+ const method = params.derivedMethod;
470
+ if (!method) {
471
+ return null;
472
+ }
473
+ const template = resolveTemplate(params.derivedTemplate, params.prefix);
474
+ const parsed = runtime.parseRouteTemplate(template);
475
+ if (parsed.errors.length > 0) {
476
+ for (const error of parsed.errors) {
477
+ params.log?.(`${error} in ${params.file}`);
478
+ }
479
+ return null;
480
+ }
481
+ for (const warning of parsed.warnings) {
482
+ params.log?.(`${warning} in ${params.file}`);
483
+ }
484
+ return {
485
+ method,
486
+ template: parsed.template,
487
+ tokens: parsed.tokens,
488
+ score: parsed.score
489
+ };
490
+ }
491
+ function sortRoutes(routes) {
492
+ return routes.sort((a, b) => {
493
+ if (a.method !== b.method) {
494
+ return a.method.localeCompare(b.method);
495
+ }
496
+ return runtime.compareRouteScore(a.score ?? [], b.score ?? []);
497
+ });
498
+ }
499
+
500
+ async function readJsonFile(file) {
501
+ try {
502
+ const content = await node_fs.promises.readFile(file, "utf8");
503
+ const errors = [];
504
+ const data = jsoncParser.parse(content, errors, {
505
+ allowTrailingComma: true,
506
+ disallowComments: false
507
+ });
508
+ if (errors.length > 0) {
509
+ return void 0;
510
+ }
511
+ return data;
512
+ } catch {
513
+ return void 0;
514
+ }
515
+ }
516
+ async function loadModule(file) {
517
+ const ext = pathe.extname(file).toLowerCase();
518
+ if (ext === ".cjs") {
519
+ const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('shared/cli.yAHcdIVy.cjs', document.baseURI).href)));
520
+ delete require$1.cache[file];
521
+ return require$1(file);
522
+ }
523
+ if (ext === ".js" || ext === ".mjs") {
524
+ return import(`${node_url.pathToFileURL(file).href}?t=${Date.now()}`);
525
+ }
526
+ if (ext === ".ts") {
527
+ const result = await esbuild.build({
528
+ entryPoints: [file],
529
+ bundle: true,
530
+ format: "esm",
531
+ platform: "node",
532
+ sourcemap: "inline",
533
+ target: "es2020",
534
+ write: false
535
+ });
536
+ const output = result.outputFiles[0];
537
+ const code = output?.text ?? "";
538
+ const dataUrl = `data:text/javascript;base64,${node_buffer.Buffer.from(code).toString(
539
+ "base64"
540
+ )}`;
541
+ return import(`${dataUrl}#${Date.now()}`);
542
+ }
543
+ return null;
544
+ }
545
+ async function loadRules(file) {
546
+ const ext = pathe.extname(file).toLowerCase();
547
+ if (ext === ".json" || ext === ".jsonc") {
548
+ const json = await readJsonFile(file);
549
+ if (typeof json === "undefined") {
550
+ return [];
551
+ }
552
+ return [
553
+ {
554
+ response: json
555
+ }
556
+ ];
557
+ }
558
+ const mod = await loadModule(file);
559
+ const value = mod?.default ?? mod;
560
+ if (!value) {
561
+ return [];
562
+ }
563
+ if (Array.isArray(value)) {
564
+ return value;
565
+ }
566
+ if (typeof value === "function") {
567
+ return [
568
+ {
569
+ response: value
570
+ }
571
+ ];
572
+ }
573
+ return [value];
574
+ }
575
+
576
+ async function buildManifest(options = {}) {
577
+ const root = options.root ?? node_process.cwd();
578
+ const outDir = pathe.resolve(root, options.outDir ?? ".mokup");
579
+ const handlersDir = pathe.join(outDir, "mokup-handlers");
580
+ const dirs = resolveDirs(options.dir, root);
581
+ const files = await collectFiles(dirs);
582
+ const routes = [];
583
+ const seen = /* @__PURE__ */ new Set();
584
+ const handlerSources = /* @__PURE__ */ new Set();
585
+ const handlerModuleMap = /* @__PURE__ */ new Map();
586
+ const configCache = /* @__PURE__ */ new Map();
587
+ const configFileCache = /* @__PURE__ */ new Map();
588
+ for (const fileInfo of files) {
589
+ if (!isSupportedFile(fileInfo.file)) {
590
+ continue;
591
+ }
592
+ if (!matchesFilter(fileInfo.file, options.include, options.exclude)) {
593
+ continue;
594
+ }
595
+ const configParams = {
596
+ file: fileInfo.file,
597
+ rootDir: fileInfo.rootDir,
598
+ configCache,
599
+ fileCache: configFileCache
600
+ };
601
+ if (options.log) {
602
+ configParams.log = options.log;
603
+ }
604
+ const config = await resolveDirectoryConfig(configParams);
605
+ if (config.enabled === false) {
606
+ continue;
607
+ }
608
+ const derived = deriveRouteFromFile(fileInfo.file, fileInfo.rootDir, options.log);
609
+ if (!derived) {
610
+ continue;
611
+ }
612
+ const rules = await loadRules(fileInfo.file);
613
+ for (const [index, rule] of rules.entries()) {
614
+ if (!rule || typeof rule !== "object") {
615
+ continue;
616
+ }
617
+ if (typeof rule.response === "undefined") {
618
+ continue;
619
+ }
620
+ const resolveParams = {
621
+ rule,
622
+ derivedTemplate: derived.template,
623
+ derivedMethod: derived.method,
624
+ prefix: options.prefix ?? "",
625
+ file: fileInfo.file
626
+ };
627
+ if (options.log) {
628
+ resolveParams.log = options.log;
629
+ }
630
+ const resolved = resolveRule(resolveParams);
631
+ if (!resolved) {
632
+ continue;
633
+ }
634
+ const key = `${resolved.method} ${resolved.template}`;
635
+ if (seen.has(key)) {
636
+ options.log?.(`Duplicate mock route ${key} from ${fileInfo.file}`);
637
+ }
638
+ seen.add(key);
639
+ const response = buildResponse(
640
+ rule.response,
641
+ {
642
+ file: fileInfo.file,
643
+ handlers: options.handlers !== false,
644
+ handlerSources,
645
+ handlerModuleMap,
646
+ handlersDir,
647
+ root,
648
+ ruleIndex: index
649
+ }
650
+ );
651
+ if (!response) {
652
+ continue;
653
+ }
654
+ const source = toPosix(pathe.relative(root, fileInfo.file));
655
+ const middlewareRefs = options.handlers === false ? [] : config.middlewares.map((entry) => {
656
+ handlerSources.add(entry.file);
657
+ const modulePath = getHandlerModulePath(entry.file, handlersDir, root);
658
+ handlerModuleMap.set(entry.file, modulePath);
659
+ return {
660
+ module: modulePath,
661
+ ruleIndex: entry.index
662
+ };
663
+ });
664
+ const route = {
665
+ method: resolved.method,
666
+ url: resolved.template,
667
+ tokens: resolved.tokens,
668
+ score: resolved.score,
669
+ source,
670
+ response
671
+ };
672
+ if (typeof rule.status === "number") {
673
+ route.status = rule.status;
674
+ }
675
+ if (config.headers) {
676
+ route.headers = { ...config.headers };
677
+ }
678
+ if (rule.headers) {
679
+ route.headers = { ...route.headers ?? {}, ...rule.headers };
680
+ }
681
+ if (typeof rule.delay === "number") {
682
+ route.delay = rule.delay;
683
+ }
684
+ if (typeof route.status === "undefined" && typeof config.status === "number") {
685
+ route.status = config.status;
686
+ }
687
+ if (typeof route.delay === "undefined" && typeof config.delay === "number") {
688
+ route.delay = config.delay;
689
+ }
690
+ if (middlewareRefs.length > 0) {
691
+ route.middleware = middlewareRefs;
692
+ }
693
+ routes.push(route);
694
+ }
695
+ }
696
+ const manifest = {
697
+ version: 1,
698
+ routes: sortRoutes(routes)
699
+ };
700
+ await node_fs.promises.mkdir(outDir, { recursive: true });
701
+ if (handlerSources.size > 0) {
702
+ await node_fs.promises.mkdir(handlersDir, { recursive: true });
703
+ await bundleHandlers(Array.from(handlerSources), root, handlersDir);
704
+ await writeHandlerIndex(handlerModuleMap, handlersDir, outDir);
705
+ }
706
+ const manifestPath = pathe.join(outDir, "mokup.manifest.json");
707
+ await node_fs.promises.writeFile(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
708
+ await writeManifestModule(outDir, manifest);
709
+ await writeBundle(outDir, handlerSources.size > 0);
710
+ options.log?.(`Manifest written to ${manifestPath}`);
711
+ return {
712
+ manifest,
713
+ manifestPath
714
+ };
715
+ }
716
+
717
+ exports.buildManifest = buildManifest;