@marko/run 0.5.13 → 0.5.14

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.
@@ -14,69 +14,53 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
14
14
  import sade from "sade";
15
15
 
16
16
  // src/cli/commands.ts
17
- import path5 from "path";
18
17
  import fs5 from "fs";
18
+ import path5 from "path";
19
19
  import { fileURLToPath as fileURLToPath2 } from "url";
20
20
  import {
21
21
  build as viteBuild,
22
22
  resolveConfig
23
23
  } from "vite";
24
24
 
25
- // src/vite/utils/config.ts
26
- var PluginConfigKey = "__MARKO_RUN_PLUGIN_CONFIG__";
27
- var AdapterConfigKey = "__MARKO_RUN_ADAPTER_CONFIG__";
28
- function getConfig(obj, key) {
29
- return obj[key];
30
- }
31
- function setConfig(obj, key, value) {
32
- obj[key] = value;
33
- return obj;
34
- }
35
- var getExternalPluginOptions = (viteConfig) => getConfig(viteConfig, PluginConfigKey);
36
- var setExternalPluginOptions = (viteConfig, value) => setConfig(viteConfig, PluginConfigKey, value);
37
- var getExternalAdapterOptions = (viteConfig) => getConfig(viteConfig, AdapterConfigKey);
38
- var setExternalAdapterOptions = (viteConfig, value) => setConfig(viteConfig, AdapterConfigKey, value);
25
+ // src/vite/plugin.ts
26
+ import markoVitePlugin from "@marko/vite";
27
+ import browserslist from "browserslist";
28
+ import { createHash } from "crypto";
29
+ import createDebug from "debug";
30
+ import { resolveToEsbuildTarget } from "esbuild-plugin-browserslist";
31
+ import fs3 from "fs";
32
+ import { glob } from "glob";
33
+ import path4 from "path";
34
+ import { fileURLToPath } from "url";
35
+ import { buildErrorMessage, mergeConfig } from "vite";
39
36
 
40
- // src/vite/utils/server.ts
41
- import net from "net";
42
- import cp from "child_process";
43
- import { parse, config } from "dotenv";
44
- import fs from "fs";
45
- import cluster from "cluster";
46
- async function getConnection(port) {
47
- return new Promise((resolve) => {
48
- const connection = net.connect(port).setNoDelay(true).setKeepAlive(false).on("error", () => {
49
- connection.end();
50
- resolve(null);
51
- }).on("connect", () => {
52
- resolve(connection);
53
- });
54
- });
37
+ // src/adapter/utils.ts
38
+ import kleur from "kleur";
39
+ import supporsColor from "supports-color";
40
+ function stripAnsi(string) {
41
+ return string.replace(
42
+ /([\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><])/g,
43
+ ""
44
+ );
55
45
  }
56
- async function isPortInUse(port) {
57
- return Boolean(await getConnection(port));
46
+ function cleanStack(stack) {
47
+ return stack.split(/\n/).filter((l) => /^\s*at/.test(l)).join("\n");
58
48
  }
59
- async function getAvailablePort(port) {
60
- if (port && !await isPortInUse(port)) {
61
- return port;
62
- }
63
- return new Promise((resolve) => {
64
- const server = net.createServer().listen(0, () => {
65
- const { port: port2 } = server.address();
66
- server.close(() => resolve(port2));
67
- });
68
- });
49
+ function prepareError(err) {
50
+ var _a;
51
+ return {
52
+ message: stripAnsi(err.message),
53
+ stack: stripAnsi(cleanStack(err.stack || "")),
54
+ id: err.id,
55
+ frame: stripAnsi(err.frame || ""),
56
+ plugin: err.plugin,
57
+ pluginCode: (_a = err.pluginCode) == null ? void 0 : _a.toString(),
58
+ loc: err.loc
59
+ };
69
60
  }
70
61
 
71
- // src/vite/plugin.ts
72
- import path4 from "path";
73
- import fs4 from "fs";
74
- import { glob } from "glob";
75
- import { fileURLToPath } from "url";
76
- import browserslist from "browserslist";
77
- import { resolveToEsbuildTarget } from "esbuild-plugin-browserslist";
78
- import { buildErrorMessage, mergeConfig } from "vite";
79
- import markoVitePlugin from "@marko/vite";
62
+ // src/vite/codegen/index.ts
63
+ import path from "path";
80
64
 
81
65
  // src/vite/constants.ts
82
66
  var markoRunFilePrefix = "__marko-run__";
@@ -93,557 +77,52 @@ var RoutableFileTypes = {
93
77
  Error: "500"
94
78
  };
95
79
 
96
- // src/vite/routes/vdir.ts
97
- var _dirs, _pathlessDirs;
98
- var _VDir = class _VDir {
99
- constructor(parent, segment, source) {
100
- __privateAdd(this, _dirs);
101
- __privateAdd(this, _pathlessDirs);
102
- __publicField(this, "parent");
103
- __publicField(this, "source");
104
- __publicField(this, "path");
105
- __publicField(this, "fullPath");
106
- __publicField(this, "segment");
107
- __publicField(this, "files");
108
- if (!parent || !segment) {
109
- this.parent = null;
110
- this.source = null;
111
- this.path = "/";
112
- this.fullPath = "/";
113
- this.segment = {
114
- raw: "",
115
- name: ""
116
- };
117
- } else {
118
- this.parent = parent;
119
- this.source = source;
120
- this.path = parent.path + (parent.path === "/" ? segment.name : `/${segment.name}`);
121
- this.fullPath = parent.fullPath + (parent.fullPath === "/" ? segment.name : `/${segment.name}`);
122
- if (segment.param) {
123
- this.fullPath += segment.param;
124
- }
125
- this.segment = segment;
126
- }
127
- }
128
- get pathInfo() {
129
- const value = {
130
- id: "/",
131
- path: "/",
132
- segments: []
133
- };
134
- let sep = "";
135
- for (const { segment } of this) {
136
- const { type, name, param } = segment;
137
- if (name && type !== "_") {
138
- value.id += sep + (type || name);
139
- value.path += sep + name;
140
- value.isEnd = type === "$$";
141
- if (param) {
142
- value.path += param;
143
- let index = type === "$$" ? null : value.segments.length;
144
- if (!value.params) {
145
- value.params = { [param]: index };
146
- } else if (!(param in value.params)) {
147
- value.params[param] = index;
148
- }
149
- }
150
- value.segments.push(name);
151
- sep = "/";
152
- }
153
- }
154
- Object.defineProperty(this, "pathInfo", {
155
- value,
156
- enumerable: true
157
- });
158
- return value;
80
+ // src/vite/utils/route.ts
81
+ function getVerbs(route) {
82
+ var _a, _b;
83
+ const verbs = ((_b = (_a = route.handler) == null ? void 0 : _a.verbs) == null ? void 0 : _b.slice()) || [];
84
+ if (route.page && !verbs.includes("get")) {
85
+ verbs.unshift("get");
159
86
  }
160
- addDir(path6, segment) {
161
- const map = segment.type === "_" ? __privateGet(this, _pathlessDirs) ?? __privateSet(this, _pathlessDirs, /* @__PURE__ */ new Map()) : __privateGet(this, _dirs) ?? __privateSet(this, _dirs, /* @__PURE__ */ new Map());
162
- if (!map.has(segment.name)) {
163
- const dir = new _VDir(this, segment, path6);
164
- map.set(segment.name, dir);
165
- return dir;
87
+ return verbs;
88
+ }
89
+ function hasVerb(route, verb) {
90
+ var _a, _b;
91
+ return verb === "get" && route.page || ((_b = (_a = route.handler) == null ? void 0 : _a.verbs) == null ? void 0 : _b.includes(verb));
92
+ }
93
+
94
+ // src/vite/codegen/writer.ts
95
+ function createWriter(sink, options) {
96
+ let buffer = "";
97
+ let indentLevel = 0;
98
+ let indentString = "";
99
+ let firstOpenIndex = 0;
100
+ const branches = [];
101
+ const openWriters = /* @__PURE__ */ new Map();
102
+ function write(data) {
103
+ if (!writer.__isActive) {
104
+ throw new Error("Cannot write to branch that has been joined");
166
105
  }
167
- return map.get(segment.name);
168
- }
169
- addFile(file) {
170
- if (!this.files) {
171
- this.files = /* @__PURE__ */ new Map();
172
- this.files.set(file.type, file);
173
- } else if (!this.files.has(file.type)) {
174
- this.files.set(file.type, file);
106
+ if (openWriters.size) {
107
+ buffer += data;
175
108
  } else {
176
- const existing = this.files.get(file.type);
177
- if (existing !== file) {
178
- throw new Error(
179
- `Duplicate file type '${file.type}' added at path '${this.path}'. File '${file.importPath}' collides with '${existing.importPath}'.`
180
- );
181
- } else if (file.type === RoutableFileTypes.Page || file.type === RoutableFileTypes.Handler) {
182
- throw new Error(
183
- `Ambiguous path definition: route '${this.path}' is defined multiple times by ${file.importPath}`
184
- );
185
- }
186
- throw new Error(
187
- `Ambiguous path definition: file '${this.path}' is included multiple times by ${file.importPath}`
188
- );
189
- }
190
- }
191
- *dirs() {
192
- if (__privateGet(this, _pathlessDirs)) {
193
- yield* __privateGet(this, _pathlessDirs).values();
194
- }
195
- if (__privateGet(this, _dirs)) {
196
- yield* __privateGet(this, _dirs).values();
197
- }
198
- }
199
- *[Symbol.iterator]() {
200
- if (this.parent) {
201
- yield* this.parent;
109
+ sink(data);
202
110
  }
203
- yield this;
111
+ return writer;
204
112
  }
205
- static addPaths(roots, paths) {
206
- const dirs = [];
207
- const unique = /* @__PURE__ */ new Set();
208
- for (const root of roots) {
209
- for (const path6 of paths) {
210
- let dir = root;
211
- for (const segment of path6.segments) {
212
- dir = dir.addDir(path6, segment);
113
+ const writer = {
114
+ __isActive: true,
115
+ get indent() {
116
+ return indentLevel;
117
+ },
118
+ set indent(value) {
119
+ if (options == null ? void 0 : options.indentWith) {
120
+ if (value < 0) {
121
+ value = 0;
213
122
  }
214
- if (unique.has(dir.path)) {
215
- const sources = /* @__PURE__ */ new Set();
216
- let sourcePath = "";
217
- for (const { source } of dir) {
218
- if (source && !sources.has(source.source)) {
219
- sources.add(source.source);
220
- sourcePath += source.source + "/";
221
- }
222
- }
223
- throw new Error(
224
- `Ambiguous directory structure: '${sourcePath}${path6.source}' defines '${dir.path}' multiple times.`
225
- );
226
- } else {
227
- unique.add(dir.path);
228
- dirs.push(dir);
229
- }
230
- }
231
- }
232
- return dirs;
233
- }
234
- };
235
- _dirs = new WeakMap();
236
- _pathlessDirs = new WeakMap();
237
- var VDir = _VDir;
238
-
239
- // src/vite/routes/parse.ts
240
- function parseFlatRoute(pattern) {
241
- if (!pattern) throw new Error("Empty pattern");
242
- const len = pattern.length;
243
- let i = 0;
244
- return parse2([
245
- {
246
- id: "/",
247
- segments: [],
248
- source: pattern
249
- }
250
- ]);
251
- function parse2(basePaths, group) {
252
- const pathMap = /* @__PURE__ */ new Map();
253
- const delimiters = group ? ").," : ".,";
254
- let charCode;
255
- let segmentStart = i;
256
- let type;
257
- let current;
258
- do {
259
- charCode = pattern.charCodeAt(i);
260
- if (charCode === 41 && group) {
261
- break;
262
- } else if (charCode === 44) {
263
- if (!current) {
264
- segmentEnd(
265
- basePaths.map((path6) => ({
266
- ...path6,
267
- segments: path6.segments.slice()
268
- })),
269
- "",
270
- "_",
271
- pathMap
272
- );
273
- } else {
274
- segmentEnd(current, pattern.slice(segmentStart, i), type, pathMap);
275
- }
276
- current = void 0;
277
- type = void 0;
278
- segmentStart = ++i;
279
- } else if (charCode === 46) {
280
- if (current) {
281
- segmentEnd(current, pattern.slice(segmentStart, i), type);
282
- }
283
- type = void 0;
284
- segmentStart = ++i;
285
- } else if (charCode === 40) {
286
- const groupPaths = parse2(current || basePaths, ++i);
287
- if (groupPaths.length) {
288
- current = groupPaths;
289
- }
290
- segmentStart = ++i;
291
- } else {
292
- if (charCode === 95) {
293
- type = "_";
294
- } else if (charCode === 36) {
295
- type = pattern.charCodeAt(i + 1) === 36 ? "$$" : "$";
296
- }
297
- current ?? (current = basePaths.map((path6) => ({
298
- ...path6,
299
- segments: path6.segments.slice()
300
- })));
301
- i = len;
302
- for (const char of delimiters) {
303
- const index = pattern.indexOf(char, segmentStart);
304
- if (index >= 0 && index < i) {
305
- i = index;
306
- }
307
- }
308
- }
309
- } while (i < len);
310
- if (group && charCode !== 41) {
311
- throw new Error(
312
- `Invalid route pattern: group was not closed '${pattern.slice(
313
- group
314
- )}' in '${pattern}'`
315
- );
316
- }
317
- if (!current) {
318
- segmentEnd(
319
- basePaths.map((path6) => ({
320
- ...path6,
321
- segments: path6.segments.slice()
322
- })),
323
- "",
324
- "_",
325
- pathMap
326
- );
327
- } else {
328
- segmentEnd(current, pattern.slice(segmentStart, i), type, pathMap);
329
- }
330
- return [...pathMap.values()];
331
- }
332
- function segmentEnd(paths, raw, type, map) {
333
- let segment;
334
- if (raw) {
335
- segment = {
336
- raw,
337
- name: raw,
338
- type
339
- };
340
- if (type === "$" || type === "$$") {
341
- segment.name = type;
342
- segment.param = raw.slice(type.length);
343
- }
344
- }
345
- for (const path6 of paths) {
346
- if (segment) {
347
- if (path6.isCatchall) {
348
- throw new Error(
349
- `Invalid route pattern: nested segments are not allowed after a catch-all parameter. Found '.' following '${pattern.slice(
350
- 0,
351
- i
352
- )}' in '${pattern}'.`
353
- );
354
- }
355
- path6.segments.push(segment);
356
- path6.id += path6.id === "/" ? segment.name : `/${segment.name}`;
357
- if (type === "$$") {
358
- path6.isCatchall = true;
359
- }
360
- }
361
- if (map) {
362
- if (map.has(path6.id)) {
363
- const existing = map.get(path6.id);
364
- const existingExpansion = existing.segments.map((s) => s.raw).join(".");
365
- const currentExpansion = path6.segments.map((s) => s.raw).join(".");
366
- throw new Error(
367
- `Invalid route pattern: route '${path6.id}' is ambiguous. Expansion '${currentExpansion}' collides with '${existingExpansion}' in '${pattern}'.`
368
- );
369
- }
370
- map.set(path6.id, path6);
371
- }
372
- }
373
- }
374
- }
375
-
376
- // src/vite/routes/builder.ts
377
- var markoFiles = `(${RoutableFileTypes.Layout}|${RoutableFileTypes.Page}|${RoutableFileTypes.NotFound}|${RoutableFileTypes.Error})\\.(?:.*\\.)?(marko)`;
378
- var nonMarkoFiles = `(${RoutableFileTypes.Middleware}|${RoutableFileTypes.Handler}|${RoutableFileTypes.Meta})\\.(?:.*\\.)?(.+)`;
379
- var routeableFileRegex = new RegExp(
380
- `[+](?:${markoFiles}|${nonMarkoFiles})$`,
381
- "i"
382
- );
383
- function matchRoutableFile(filename) {
384
- const match = filename.match(routeableFileRegex);
385
- return match && (match[1] || match[3]).toLowerCase();
386
- }
387
- function isSpecialType(type) {
388
- return type === RoutableFileTypes.NotFound || type === RoutableFileTypes.Error;
389
- }
390
- async function buildRoutes(sources) {
391
- const uniqueRoutes = /* @__PURE__ */ new Map();
392
- const routes = [];
393
- const special = {};
394
- const middlewares = /* @__PURE__ */ new Set();
395
- const unusedFiles = /* @__PURE__ */ new Set();
396
- const currentLayouts = /* @__PURE__ */ new Set();
397
- const currentMiddleware = /* @__PURE__ */ new Set();
398
- const root = new VDir();
399
- const dirStack = [];
400
- let basePath;
401
- let importPrefix;
402
- let activeDirs;
403
- let isBaseDir;
404
- let nextFileId = 1;
405
- let nextRouteIndex = 1;
406
- const walkOptions = {
407
- onEnter({ name }) {
408
- const prevDirStackLength = dirStack.length;
409
- if (isBaseDir) {
410
- isBaseDir = false;
411
- if (!basePath) {
412
- return;
413
- }
414
- name = basePath;
415
- } else {
416
- dirStack.push(name);
417
- }
418
- const previousDirs = activeDirs;
419
- const paths = parseFlatRoute(name);
420
- activeDirs = VDir.addPaths(previousDirs, paths);
421
- return () => {
422
- activeDirs = previousDirs;
423
- dirStack.length = prevDirStackLength;
424
- };
425
- },
426
- onFile({ name, path: path6 }) {
427
- const match = name.match(routeableFileRegex);
428
- if (!match) {
429
- return;
430
- }
431
- const type = (match[1] || match[3]).toLowerCase();
432
- if (dirStack.length && isSpecialType(type)) {
433
- console.warn(
434
- `Special pages '${RoutableFileTypes.NotFound}' and '${RoutableFileTypes.Error}' are only considered in the root directory - ignoring ${path6}`
435
- );
436
- return;
437
- }
438
- let dirs = activeDirs;
439
- if (match.index) {
440
- const paths = parseFlatRoute(name.slice(0, match.index));
441
- dirs = VDir.addPaths(activeDirs, paths);
442
- }
443
- const dirPath = dirStack.join("/");
444
- const relativePath = dirPath ? `${dirPath}/${name}` : name;
445
- const file = {
446
- id: String(nextFileId++),
447
- name,
448
- type,
449
- filePath: path6,
450
- relativePath,
451
- importPath: `${importPrefix}/${relativePath}`,
452
- verbs: type === RoutableFileTypes.Page ? ["get"] : void 0
453
- };
454
- for (const dir of dirs) {
455
- dir.addFile(file);
456
- }
457
- }
458
- };
459
- if (!Array.isArray(sources)) {
460
- sources = [sources];
461
- }
462
- for (const source of sources) {
463
- importPrefix = source.importPrefix ? source.importPrefix.replace(/^\/+|\/+$/g, "") : "";
464
- basePath = source.basePath || "";
465
- activeDirs = [root];
466
- isBaseDir = true;
467
- await source.walker(walkOptions);
468
- }
469
- traverse(root);
470
- return {
471
- list: routes,
472
- middleware: [...middlewares],
473
- special
474
- };
475
- function traverse(dir) {
476
- let middleware;
477
- let layout;
478
- if (dir.files) {
479
- middleware = dir.files.get(RoutableFileTypes.Middleware);
480
- layout = dir.files.get(RoutableFileTypes.Layout);
481
- const handler = dir.files.get(RoutableFileTypes.Handler);
482
- const page = dir.files.get(RoutableFileTypes.Page);
483
- let hasSpecial = false;
484
- if (middleware) {
485
- if (currentMiddleware.has(middleware)) {
486
- middleware = void 0;
487
- } else {
488
- currentMiddleware.add(middleware);
489
- unusedFiles.add(middleware);
490
- }
491
- }
492
- if (layout) {
493
- if (currentLayouts.has(layout)) {
494
- layout = void 0;
495
- } else {
496
- currentLayouts.add(layout);
497
- unusedFiles.add(layout);
498
- }
499
- }
500
- if (page || handler) {
501
- const path6 = dir.pathInfo;
502
- if (uniqueRoutes.has(path6.id)) {
503
- const existing = uniqueRoutes.get(path6.id);
504
- const route = routes[existing.index];
505
- const existingFiles = [route.handler, route.page].filter(Boolean).map((f) => f.filePath);
506
- const currentFiles = [handler, page].filter(Boolean).map((f) => f.filePath);
507
- throw new Error(`Duplicate routes for path '${path6.path}' were defined. A route established by:
508
- ${existingFiles.join(" and ")} via '${existing.dir.path}'
509
- collides with
510
- ${currentFiles.join(" and ")} via '${dir.path}'
511
- `);
512
- }
513
- uniqueRoutes.set(path6.id, { dir, index: routes.length });
514
- routes.push({
515
- index: nextRouteIndex++,
516
- key: dir.fullPath,
517
- paths: [path6],
518
- middleware: [...currentMiddleware],
519
- layouts: page ? [...currentLayouts] : [],
520
- meta: dir.files.get(RoutableFileTypes.Meta),
521
- page,
522
- handler,
523
- entryName: `${markoRunFilePrefix}route` + (dir.path !== "/" ? dir.fullPath.replace(/\//g, ".").replace(/(%[A-Fa-f0-9]{2})+/g, "_") : "")
524
- });
525
- }
526
- if (dir === root) {
527
- for (const [type, file] of dir.files) {
528
- if (isSpecialType(type)) {
529
- hasSpecial = true;
530
- special[type] = {
531
- index: 0,
532
- key: type,
533
- paths: [],
534
- middleware: [],
535
- layouts: [...currentLayouts],
536
- page: file,
537
- entryName: `${markoRunFilePrefix}special.${type}`
538
- };
539
- }
540
- }
541
- }
542
- if (handler || page) {
543
- for (const middleware2 of currentMiddleware) {
544
- middlewares.add(middleware2);
545
- unusedFiles.delete(middleware2);
546
- }
547
- }
548
- if (page || hasSpecial) {
549
- for (const layout2 of currentLayouts) {
550
- unusedFiles.delete(layout2);
551
- }
552
- }
553
- }
554
- if (dir.dirs) {
555
- for (const child of dir.dirs()) {
556
- traverse(child);
557
- }
558
- }
559
- if (middleware) {
560
- currentMiddleware.delete(middleware);
561
- }
562
- if (layout) {
563
- currentLayouts.delete(layout);
564
- }
565
- }
566
- }
567
-
568
- // src/vite/routes/walk.ts
569
- import fs2 from "fs";
570
- import path from "path";
571
- function createFSWalker(dir) {
572
- return async function walkFS({
573
- onEnter,
574
- onFile,
575
- onDir,
576
- maxDepth = 50
577
- }) {
578
- async function walk(dir2, depth) {
579
- const onExit = onEnter == null ? void 0 : onEnter(dir2);
580
- if (onExit !== false) {
581
- const dirs = [];
582
- const entries = await fs2.promises.readdir(dir2.path, {
583
- withFileTypes: true
584
- });
585
- const prefix = dir2.path + path.sep;
586
- for (const entry of entries) {
587
- const walkEntry = {
588
- name: entry.name,
589
- path: prefix + entry.name
590
- };
591
- if (entry.isDirectory()) {
592
- dirs.push(walkEntry);
593
- } else {
594
- onFile == null ? void 0 : onFile(walkEntry);
595
- }
596
- }
597
- if ((onDir == null ? void 0 : onDir()) !== false && --depth > 0) {
598
- for (const entry of dirs) {
599
- await walk(entry, depth);
600
- }
601
- }
602
- onExit == null ? void 0 : onExit();
603
- }
604
- }
605
- await walk(
606
- {
607
- path: dir,
608
- name: path.basename(dir)
609
- },
610
- maxDepth
611
- );
612
- };
613
- }
614
-
615
- // src/vite/codegen/writer.ts
616
- function createWriter(sink, options) {
617
- let buffer = "";
618
- let indentLevel = 0;
619
- let indentString = "";
620
- let firstOpenIndex = 0;
621
- const branches = [];
622
- const openWriters = /* @__PURE__ */ new Map();
623
- function write(data) {
624
- if (!writer.__isActive) {
625
- throw new Error("Cannot write to branch that has been joined");
626
- }
627
- if (openWriters.size) {
628
- buffer += data;
629
- } else {
630
- sink(data);
631
- }
632
- return writer;
633
- }
634
- const writer = {
635
- __isActive: true,
636
- get indent() {
637
- return indentLevel;
638
- },
639
- set indent(value) {
640
- if (options == null ? void 0 : options.indentWith) {
641
- if (value < 0) {
642
- value = 0;
643
- }
644
- if (value !== indentLevel) {
645
- indentLevel = value;
646
- indentString = options.indentWith.repeat(indentLevel);
123
+ if (value !== indentLevel) {
124
+ indentLevel = value;
125
+ indentString = options.indentWith.repeat(indentLevel);
647
126
  }
648
127
  }
649
128
  },
@@ -675,7 +154,7 @@ function createWriter(sink, options) {
675
154
  return writer.writeBlockStart(start).writeLines(...lines).writeBlockEnd(end);
676
155
  },
677
156
  branch(name) {
678
- let existing = openWriters.get(name);
157
+ const existing = openWriters.get(name);
679
158
  if (existing) {
680
159
  return existing;
681
160
  }
@@ -738,9 +217,12 @@ function createWriter(sink, options) {
738
217
  }
739
218
  function createStringWriter(opts) {
740
219
  let code = "";
741
- const writer = createWriter((data) => {
742
- code += data;
743
- }, { indentWith: " ", ...opts });
220
+ const writer = createWriter(
221
+ (data) => {
222
+ code += data;
223
+ },
224
+ { indentWith: " ", ...opts }
225
+ );
744
226
  return Object.assign(writer, {
745
227
  end() {
746
228
  writer.join(true);
@@ -749,22 +231,7 @@ function createStringWriter(opts) {
749
231
  });
750
232
  }
751
233
 
752
- // src/vite/utils/route.ts
753
- function getVerbs(route) {
754
- var _a, _b;
755
- const verbs = ((_b = (_a = route.handler) == null ? void 0 : _a.verbs) == null ? void 0 : _b.slice()) || [];
756
- if (route.page && !verbs.includes("get")) {
757
- verbs.unshift("get");
758
- }
759
- return verbs;
760
- }
761
- function hasVerb(route, verb) {
762
- var _a, _b;
763
- return verb === "get" && route.page || ((_b = (_a = route.handler) == null ? void 0 : _a.verbs) == null ? void 0 : _b.includes(verb));
764
- }
765
-
766
234
  // src/vite/codegen/index.ts
767
- import path2 from "path";
768
235
  function renderRouteTemplate(route, getRelativePath) {
769
236
  if (!route.page) {
770
237
  throw new Error(`Route ${route.key} has no page to render`);
@@ -859,628 +326,1149 @@ function renderRouteEntry(route, entriesDir) {
859
326
  );
860
327
  }
861
328
  if (page) {
862
- const importPath = route.layouts.length ? `./${path2.posix.join(entriesDir, page.relativePath, "..", "route.marko")}` : `./${page.importPath}`;
329
+ const pageNameIndex = page.name.indexOf("+page");
330
+ const pageNamePrefix = pageNameIndex > 0 ? `${page.name.slice(0, pageNameIndex)}.` : "";
331
+ const importPath = route.layouts.length ? `./${path.posix.join(entriesDir, page.relativePath, "..", pageNamePrefix + "route.marko")}` : `./${page.importPath}`;
863
332
  imports.writeLines(`import page from '${importPath}${serverEntryQuery}';`);
864
333
  }
865
334
  if (meta) {
866
335
  imports.writeLines(
867
- `export { default as meta${index} } from './${meta.importPath}';`
336
+ `export { default as meta${index} } from './${meta.importPath}';`
337
+ );
338
+ }
339
+ for (const verb of verbs) {
340
+ writeRouteEntryHandler(writer, route, verb);
341
+ }
342
+ return writer.end();
343
+ }
344
+ function writePageResponse(writer, wrapFn) {
345
+ writer.writeLines(
346
+ `${wrapFn ? `const ${wrapFn} = () =>` : `return`} pageResponse(page, buildInput());`
347
+ );
348
+ }
349
+ function writeMiddleware(writer, middleware, next, wrapFn) {
350
+ if (wrapFn) {
351
+ writer.writeLines(
352
+ `const ${wrapFn} = () => call(${middleware}, ${next}, context);`
353
+ );
354
+ } else {
355
+ writer.writeLines(`return call(${middleware}, ${next}, context);`);
356
+ }
357
+ }
358
+ function writeRouteEntryHandler(writer, route, verb) {
359
+ var _a;
360
+ const { key, index, page, handler, middleware } = route;
361
+ const len = middleware.length;
362
+ let nextName;
363
+ let currentName;
364
+ let hasBody = false;
365
+ writer.writeLines("");
366
+ if (page) {
367
+ writer.writeBlockStart(
368
+ `export async function ${verb}${index}(context, buildInput) {`
369
+ );
370
+ } else {
371
+ writer.writeBlockStart(`export async function ${verb}${index}(context) {`);
372
+ }
373
+ const continuations = writer.branch("cont");
374
+ if (page && verb === "get") {
375
+ currentName = "__page";
376
+ if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes(verb)) {
377
+ const name = `${verb}Handler`;
378
+ writePageResponse(continuations, currentName);
379
+ if (len) {
380
+ nextName = currentName;
381
+ currentName = `__${name}`;
382
+ writeMiddleware(continuations, name, nextName, currentName);
383
+ } else {
384
+ writeMiddleware(writer, name, currentName);
385
+ hasBody = true;
386
+ }
387
+ } else if (len) {
388
+ writePageResponse(continuations, currentName);
389
+ nextName = currentName;
390
+ } else {
391
+ writePageResponse(continuations);
392
+ hasBody = true;
393
+ }
394
+ } else if (handler) {
395
+ const name = `${verb}Handler`;
396
+ currentName = `__${name}`;
397
+ nextName = "noContent";
398
+ if (len) {
399
+ writeMiddleware(continuations, name, nextName, currentName);
400
+ } else {
401
+ writeMiddleware(writer, name, nextName);
402
+ hasBody = true;
403
+ }
404
+ } else {
405
+ throw new Error(`Route ${key} has no handler for ${verb} requests`);
406
+ }
407
+ if (!hasBody) {
408
+ let i = len;
409
+ while (i--) {
410
+ const { id } = middleware[i];
411
+ const name = `mware${id}`;
412
+ nextName = currentName;
413
+ currentName = i ? `__${name}` : "";
414
+ writeMiddleware(continuations, name, nextName, currentName);
415
+ }
416
+ }
417
+ continuations.join();
418
+ writer.writeBlockEnd("}");
419
+ }
420
+ function renderRouter(routes, entriesDir, options = {
421
+ trailingSlashes: "RedirectWithout"
422
+ }) {
423
+ const writer = createStringWriter();
424
+ writer.writeLines(`// @marko/run/router`);
425
+ const imports = writer.branch("imports");
426
+ imports.writeLines(
427
+ `import { NotHandled, NotMatched, createContext } from '${virtualFilePrefix}/runtime/internal';`
428
+ );
429
+ for (const route of routes.list) {
430
+ const verbs = getVerbs(route);
431
+ const names = verbs.map((verb) => `${verb}${route.index}`);
432
+ route.meta && names.push(`meta${route.index}`);
433
+ imports.writeLines(
434
+ `import { ${names.join(", ")} } from '${virtualFilePrefix}/${route.entryName}.js';`
435
+ );
436
+ }
437
+ for (const route of Object.values(routes.special)) {
438
+ const importPath = route.layouts.length ? `./${path.posix.join(entriesDir, route.page.relativePath, "..", `route.${route.key}.marko`)}` : `./${route.page.importPath}`;
439
+ imports.writeLines(
440
+ `import page${route.key} from '${importPath}${serverEntryQuery}';`
441
+ );
442
+ }
443
+ writer.writeLines(
444
+ `
445
+ globalThis.__marko_run__ = { match, fetch, invoke };
446
+ `
447
+ ).writeBlockStart(`export function match(method, pathname) {`).writeLines(
448
+ `if (!pathname) {
449
+ pathname = '/';
450
+ } else if (pathname.charAt(0) !== '/') {
451
+ pathname = '/' + pathname;
452
+ }`
453
+ ).writeBlockStart(`switch (method) {`);
454
+ for (const verb of httpVerbs) {
455
+ const filteredRoutes = routes.list.filter((route) => hasVerb(route, verb));
456
+ if (filteredRoutes.length) {
457
+ const trie = createRouteTrie(filteredRoutes);
458
+ writer.writeLines(`case '${verb.toUpperCase()}':`);
459
+ writer.writeBlockStart(`case '${verb.toLowerCase()}': {`);
460
+ writeRouterVerb(writer, trie, verb);
461
+ writer.writeBlockEnd("}");
462
+ }
463
+ }
464
+ writer.writeBlockEnd("}").writeLines("return null;").writeBlockEnd("}");
465
+ writer.writeLines("").writeBlockStart(
466
+ "export async function invoke(route, request, platform, url) {"
467
+ ).writeLines(
468
+ "const [context, buildInput] = createContext(route, request, platform, url);"
469
+ );
470
+ const hasErrorPage = Boolean(routes.special[RoutableFileTypes.Error]);
471
+ if (hasErrorPage) {
472
+ writer.writeBlockStart("try {");
473
+ }
474
+ writer.writeBlockStart("if (route) {").writeBlockStart("try {").writeLines(
475
+ "const response = await route.handler(context, buildInput);",
476
+ "if (response) return response;"
477
+ ).indent--;
478
+ writer.writeBlockStart("} catch (error) {").writeLines(
479
+ "if (error === NotHandled) return;",
480
+ "if (error !== NotMatched) throw error;"
481
+ ).writeBlockEnd("}").writeBlockEnd("}");
482
+ if (routes.special[RoutableFileTypes.NotFound]) {
483
+ imports.writeLines(
484
+ `
485
+ const page404ResponseInit = {
486
+ status: 404,
487
+ headers: { "content-type": "text/html;charset=UTF-8" },
488
+ };`
868
489
  );
490
+ writer.write(`
491
+ if (context.request.headers.get('Accept')?.includes('text/html')) {
492
+ return new Response(page404.stream(buildInput()), page404ResponseInit);
493
+ }
494
+ `);
869
495
  }
870
- for (const verb of verbs) {
871
- writeRouteEntryHandler(writer, route, verb);
496
+ writer.indent--;
497
+ if (hasErrorPage) {
498
+ imports.writeLines(`
499
+ const page500ResponseInit = {
500
+ status: 500,
501
+ headers: { "content-type": "text/html;charset=UTF-8" },
502
+ };`);
503
+ writer.writeBlockStart(`} catch (error) {`).writeBlockStart(
504
+ `if (context.request.headers.get('Accept')?.includes('text/html')) {`
505
+ ).writeLines(
506
+ `return new Response(page500.stream(buildInput({ error })), page500ResponseInit);`
507
+ ).writeBlockEnd("}").writeLines("throw error;").writeBlockEnd("}");
872
508
  }
509
+ writer.writeBlockEnd("}");
510
+ renderFetch(writer, options);
873
511
  return writer.end();
874
512
  }
875
- function writePageResponse(writer, wrapFn) {
876
- writer.writeLines(
877
- `${wrapFn ? `const ${wrapFn} = () =>` : `return`} pageResponse(page, buildInput());`
878
- );
879
- }
880
- function writeMiddleware(writer, middleware, next, wrapFn) {
881
- if (wrapFn) {
882
- writer.writeLines(
883
- `const ${wrapFn} = () => call(${middleware}, ${next}, context);`
884
- );
885
- } else {
886
- writer.writeLines(`return call(${middleware}, ${next}, context);`);
513
+ function renderFetch(writer, options) {
514
+ writer.write(`
515
+ export async function fetch(request, platform) {
516
+ try {
517
+ const url = new URL(request.url);
518
+ let { pathname } = url;`);
519
+ switch (options.trailingSlashes) {
520
+ case "RedirectWithout":
521
+ writer.write(`
522
+ if (pathname !== '/' && pathname.endsWith('/')) {
523
+ url.pathname = pathname.slice(0, -1);
524
+ return Response.redirect(url);
525
+ }`);
526
+ break;
527
+ case "RedirectWith":
528
+ writer.write(`
529
+ if (pathname !== '/' && !pathname.endsWith('/')) {
530
+ url.pathname = pathname + '/';
531
+ return Response.redirect(url);
532
+ }`);
533
+ break;
534
+ case "RewriteWithout":
535
+ writer.write(`
536
+ if (pathname !== '/' && pathname.endsWith('/')) {
537
+ url.pathname = pathname = pathname.slice(0, -1);
538
+ }`);
539
+ break;
540
+ case "RewriteWith":
541
+ writer.write(`
542
+ if (pathname !== '/' && !pathname.endsWith('/')) {
543
+ url.pathname = pathname = pathname + '/';
544
+ }`);
545
+ break;
546
+ }
547
+ writer.write(`
548
+
549
+ const route = match(request.method, pathname);
550
+ return await invoke(route, request, platform, url);
551
+ } catch (error) {
552
+ if (import.meta.env.DEV) {
553
+ throw error;
554
+ }
555
+ return new Response(null, {
556
+ status: 500
557
+ });
887
558
  }
559
+ }`);
888
560
  }
889
- function writeRouteEntryHandler(writer, route, verb) {
890
- var _a;
891
- const { key, index, page, handler, middleware } = route;
892
- const len = middleware.length;
893
- let nextName;
894
- let currentName;
895
- let hasBody = false;
896
- writer.writeLines("");
897
- if (page) {
898
- writer.writeBlockStart(
899
- `export async function ${verb}${index}(context, buildInput) {`
900
- );
901
- } else {
902
- writer.writeBlockStart(`export async function ${verb}${index}(context) {`);
561
+ function writeRouterVerb(writer, trie, verb, level = 0, offset = 1) {
562
+ const { route, dynamic, catchAll } = trie;
563
+ let closeCount = 0;
564
+ if (level === 0) {
565
+ writer.writeLines(`const len = pathname.length;`);
566
+ if (route) {
567
+ writer.writeLines(
568
+ `if (len === 1) return ${renderMatch(verb, route, trie.path)}; // ${trie.path.path}`
569
+ );
570
+ } else if (trie.static || dynamic) {
571
+ writer.writeBlockStart(`if (len > 1) {`);
572
+ closeCount++;
573
+ }
903
574
  }
904
- const continuations = writer.branch("cont");
905
- if (page && verb === "get") {
906
- currentName = "__page";
907
- if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes(verb)) {
908
- const name = `${verb}Handler`;
909
- writePageResponse(continuations, currentName);
910
- if (len) {
911
- nextName = currentName;
912
- currentName = `__${name}`;
913
- writeMiddleware(continuations, name, nextName, currentName);
914
- } else {
915
- writeMiddleware(writer, name, currentName);
916
- hasBody = true;
575
+ if (trie.static || dynamic) {
576
+ const next = level + 1;
577
+ const index = `i${next}`;
578
+ let terminal;
579
+ let children;
580
+ writer.writeLines(`const ${index} = pathname.indexOf('/', ${offset}) + 1;`);
581
+ if (trie.static) {
582
+ for (const child of trie.static.values()) {
583
+ if (child.route) {
584
+ (terminal ?? (terminal = [])).push(child);
585
+ }
586
+ if (child.static || child.dynamic || child.catchAll) {
587
+ (children ?? (children = [])).push(child);
588
+ }
917
589
  }
918
- } else if (len) {
919
- writePageResponse(continuations, currentName);
920
- nextName = currentName;
921
- } else {
922
- writePageResponse(continuations);
923
- hasBody = true;
924
590
  }
925
- } else if (handler) {
926
- const name = `${verb}Handler`;
927
- currentName = `__${name}`;
928
- nextName = "noContent";
929
- if (len) {
930
- writeMiddleware(continuations, name, nextName, currentName);
931
- } else {
932
- writeMiddleware(writer, name, nextName);
933
- hasBody = true;
591
+ if (terminal || (dynamic == null ? void 0 : dynamic.route)) {
592
+ closeCount++;
593
+ writer.writeBlockStart(`if (!${index} || ${index} === len) {`);
594
+ let value = `pathname.slice(${offset}, ${index} ? -1 : len)`;
595
+ if (dynamic == null ? void 0 : dynamic.route) {
596
+ const segment = `s${next}`;
597
+ writer.writeLines(`const ${segment} = decodeURIComponent(${value});`);
598
+ value = segment;
599
+ } else if (terminal == null ? void 0 : terminal.some(
600
+ (terminal2) => decodeURIComponent(terminal2.key) !== terminal2.key
601
+ )) {
602
+ value = `decodeURIComponent(${value})`;
603
+ }
604
+ if (terminal) {
605
+ const useSwitch = terminal.length > 1;
606
+ if (useSwitch) {
607
+ writer.writeBlockStart(`switch (${value}) {`);
608
+ }
609
+ for (const { key, path: path6, route: route2 } of terminal) {
610
+ const decodedKey = decodeURIComponent(key);
611
+ if (useSwitch) {
612
+ writer.write(`case '${decodedKey}': `, true);
613
+ } else {
614
+ writer.write(`if (${value} === '${decodedKey}') `, true);
615
+ }
616
+ writer.write(
617
+ `return ${renderMatch(verb, route2, path6)}; // ${path6.path}
618
+ `
619
+ );
620
+ }
621
+ if (useSwitch) {
622
+ writer.writeBlockEnd("}");
623
+ }
624
+ }
625
+ if (dynamic == null ? void 0 : dynamic.route) {
626
+ writer.writeLines(
627
+ `if (${value}) return ${renderMatch(
628
+ verb,
629
+ dynamic.route,
630
+ dynamic.path
631
+ )}; // ${dynamic.path.path}`
632
+ );
633
+ }
934
634
  }
935
- } else {
936
- throw new Error(`Route ${key} has no handler for ${verb} requests`);
635
+ if (children || (dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic) || (dynamic == null ? void 0 : dynamic.catchAll)) {
636
+ if (terminal || (dynamic == null ? void 0 : dynamic.route)) {
637
+ writer.writeBlockEnd("} else {").indent++;
638
+ } else {
639
+ writer.writeBlockStart(`if (${index} && ${index} !== len) {`);
640
+ closeCount++;
641
+ }
642
+ let value = `pathname.slice(${offset}, ${index} - 1)`;
643
+ if ((dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic) || (dynamic == null ? void 0 : dynamic.catchAll)) {
644
+ const segment = `s${next}`;
645
+ writer.writeLines(`const ${segment} = decodeURIComponent(${value});`);
646
+ value = segment;
647
+ } else if (children == null ? void 0 : children.some((child) => decodeURIComponent(child.key) !== child.key)) {
648
+ value = `decodeURIComponent(${value})`;
649
+ }
650
+ if (children) {
651
+ const useSwitch = children.length > 1;
652
+ if (useSwitch) {
653
+ writer.writeBlockStart(`switch (${value}) {`);
654
+ }
655
+ for (const child of children) {
656
+ const decodedKey = decodeURIComponent(child.key);
657
+ if (useSwitch) {
658
+ writer.writeBlockStart(`case '${decodedKey}': {`);
659
+ } else {
660
+ writer.writeBlockStart(`if (${value} === '${decodedKey}') {`);
661
+ }
662
+ const nextOffset = typeof offset === "string" ? index : offset + child.key.length + 1;
663
+ writeRouterVerb(writer, child, verb, next, nextOffset);
664
+ if (useSwitch) {
665
+ writer.writeBlockEnd("} break;");
666
+ } else {
667
+ writer.writeBlockEnd("}");
668
+ }
669
+ }
670
+ if (useSwitch) {
671
+ writer.writeBlockEnd("}");
672
+ }
673
+ }
674
+ if ((dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic) || (dynamic == null ? void 0 : dynamic.catchAll)) {
675
+ writer.writeBlockStart(`if (${value}) {`);
676
+ writeRouterVerb(writer, dynamic, verb, next, index);
677
+ writer.writeBlockEnd(`}`);
678
+ }
679
+ }
680
+ }
681
+ while (closeCount--) {
682
+ writer.writeBlockEnd("}");
683
+ }
684
+ if (catchAll) {
685
+ writer.writeLines(
686
+ `return ${renderMatch(
687
+ verb,
688
+ catchAll.route,
689
+ catchAll.path,
690
+ String(offset)
691
+ )}; // ${catchAll.path.path}`
692
+ );
693
+ } else if (level === 0) {
694
+ writer.writeLines("return null;");
937
695
  }
938
- if (!hasBody) {
939
- let i = len;
940
- while (i--) {
941
- const { id } = middleware[i];
942
- const name = `mware${id}`;
943
- nextName = currentName;
944
- currentName = i ? `__${name}` : "";
945
- writeMiddleware(continuations, name, nextName, currentName);
696
+ }
697
+ function wrapPropertyName(name) {
698
+ name = decodeURIComponent(name);
699
+ return /^[^A-Za-z_$]|[^A-Za-z0-9$_]/.test(name) ? `'${name}'` : name;
700
+ }
701
+ function renderParams(params, pathIndex) {
702
+ let result = "";
703
+ let catchAll = "";
704
+ let sep = "{";
705
+ for (const [name, index] of Object.entries(params)) {
706
+ if (typeof index === "number") {
707
+ result += `${sep} ${wrapPropertyName(name)}: s${index + 1}`;
708
+ sep = ",";
709
+ } else if (pathIndex) {
710
+ catchAll = name;
946
711
  }
947
712
  }
948
- continuations.join();
949
- writer.writeBlockEnd("}");
713
+ if (catchAll) {
714
+ result += `${sep} ${wrapPropertyName(
715
+ catchAll
716
+ )}: pathname.slice(${pathIndex})`;
717
+ }
718
+ return result ? result + " }" : "{}";
950
719
  }
951
- function renderRouter(routes, entriesDir, options = {
952
- trailingSlashes: "RedirectWithout"
953
- }) {
720
+ function renderMatch(verb, route, path6, pathIndex) {
721
+ const handler = `${verb}${route.index}`;
722
+ const params = path6.params ? renderParams(path6.params, pathIndex) : "{}";
723
+ const meta = route.meta ? `meta${route.index}` : "{}";
724
+ const pathPattern = pathToURLPatternString(path6.path);
725
+ return `{ handler: ${handler}, params: ${params}, meta: ${meta}, path: '${pathPattern}' }`;
726
+ }
727
+ function renderMiddleware(middleware) {
954
728
  const writer = createStringWriter();
955
- writer.writeLines(`// @marko/run/router`);
729
+ writer.writeLines(
730
+ `// ${virtualFilePrefix}/${markoRunFilePrefix}middleware.js`
731
+ );
956
732
  const imports = writer.branch("imports");
957
733
  imports.writeLines(
958
- `import { NotHandled, NotMatched, createContext } from '${virtualFilePrefix}/runtime/internal';`
734
+ `import { normalize } from '${virtualFilePrefix}/runtime/internal';`
959
735
  );
960
- for (const route of routes.list) {
961
- const verbs = getVerbs(route);
962
- const names = verbs.map((verb) => `${verb}${route.index}`);
963
- route.meta && names.push(`meta${route.index}`);
964
- imports.writeLines(
965
- `import { ${names.join(", ")} } from '${virtualFilePrefix}/${route.entryName}.js';`
966
- );
736
+ writer.writeLines("");
737
+ for (const { id, importPath } of middleware) {
738
+ const importName = `middleware${id}`;
739
+ imports.writeLines(`import ${importName} from './${importPath}';`);
740
+ writer.writeLines(`export const mware${id} = normalize(${importName});`);
967
741
  }
968
- for (const route of Object.values(routes.special)) {
969
- const importPath = route.layouts.length ? `./${path2.posix.join(entriesDir, route.page.relativePath, "..", `route.${route.key}.marko`)}` : `./${route.page.importPath}`;
970
- imports.writeLines(
971
- `import page${route.key} from '${importPath}${serverEntryQuery}';`
972
- );
742
+ imports.join();
743
+ return writer.end();
744
+ }
745
+ function stripTsExtension(path6) {
746
+ const index = path6.lastIndexOf(".");
747
+ if (index !== -1) {
748
+ const ext = path6.slice(index + 1);
749
+ if (ext.toLowerCase() === "ts") {
750
+ return path6.slice(0, index);
751
+ }
973
752
  }
753
+ return path6;
754
+ }
755
+ async function renderRouteTypeInfo(routes, pathPrefix = ".", adapter) {
756
+ var _a, _b;
757
+ const writer = createStringWriter();
974
758
  writer.writeLines(
975
- `
976
- globalThis.__marko_run__ = { match, fetch, invoke };
977
- `
978
- ).writeBlockStart(`export function match(method, pathname) {`).writeLines(
979
- `if (!pathname) {
980
- pathname = '/';
981
- } else if (pathname.charAt(0) !== '/') {
982
- pathname = '/' + pathname;
983
- }`
984
- ).writeBlockStart(`switch (method) {`);
985
- for (const verb of httpVerbs) {
986
- const filteredRoutes = routes.list.filter((route) => hasVerb(route, verb));
987
- if (filteredRoutes.length) {
988
- const trie = createRouteTrie(filteredRoutes);
989
- writer.writeLines(`case '${verb.toUpperCase()}':`);
990
- writer.writeBlockStart(`case '${verb.toLowerCase()}': {`);
991
- writeRouterVerb(writer, trie, verb);
992
- writer.writeBlockEnd("}");
759
+ `/*
760
+ WARNING: This file is automatically generated and any changes made to it will be overwritten without warning.
761
+ Do NOT manually edit this file or your changes will be lost.
762
+ */
763
+ `,
764
+ `import { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform } from "@marko/run/namespace";`,
765
+ `import type * as Run from "@marko/run";`
766
+ );
767
+ const headWriter = writer.branch("head");
768
+ writer.writeLines("\n").writeBlockStart(`declare module "@marko/run" {`);
769
+ if (adapter && adapter.typeInfo) {
770
+ const platformType = await adapter.typeInfo(
771
+ (data) => headWriter.write(data)
772
+ );
773
+ if (platformType) {
774
+ writer.writeLines(`interface Platform extends ${platformType} {}
775
+ `);
993
776
  }
994
777
  }
995
- writer.writeBlockEnd("}").writeLines("return null;").writeBlockEnd("}");
996
- writer.writeLines("").writeBlockStart(
997
- "export async function invoke(route, request, platform, url) {"
998
- ).writeLines(
999
- "const [context, buildInput] = createContext(route, request, platform, url);"
1000
- );
1001
- const hasErrorPage = Boolean(routes.special[RoutableFileTypes.Error]);
1002
- if (hasErrorPage) {
1003
- writer.writeBlockStart("try {");
778
+ headWriter.join();
779
+ writer.writeBlockStart(`interface AppData extends Run.DefineApp<{`).writeBlockStart("routes: {");
780
+ const routesWriter = writer.branch("routes");
781
+ writer.writeBlockEnd("}").writeBlockEnd(`}> {}`).writeBlockEnd(`}`);
782
+ const routeTypes = /* @__PURE__ */ new Map();
783
+ for (const route of routes.list) {
784
+ let routeType = "";
785
+ for (const path6 of route.paths) {
786
+ const pathType = `"${pathToURLPatternString(path6.path)}"`;
787
+ routeType += routeType ? " | " + pathType : pathType;
788
+ routesWriter.writeLines(`${pathType}: Routes["${route.key}"];`);
789
+ }
790
+ for (const file of [route.handler, route.page]) {
791
+ if (file) {
792
+ const existing = routeTypes.get(file);
793
+ if (!existing) {
794
+ routeTypes.set(file, [routeType]);
795
+ } else {
796
+ existing.push(routeType);
797
+ }
798
+ }
799
+ }
800
+ for (const files of [route.middleware, route.layouts]) {
801
+ if (files) {
802
+ for (const file of files) {
803
+ const existing = routeTypes.get(file);
804
+ if (!existing) {
805
+ routeTypes.set(file, [routeType]);
806
+ } else {
807
+ existing.push(routeType);
808
+ }
809
+ }
810
+ }
811
+ }
1004
812
  }
1005
- writer.writeBlockStart("if (route) {").writeBlockStart("try {").writeLines(
1006
- "const response = await route.handler(context, buildInput);",
1007
- "if (response) return response;"
1008
- ).indent--;
1009
- writer.writeBlockStart("} catch (error) {").writeLines(
1010
- "if (error === NotHandled) return;",
1011
- "if (error !== NotMatched) throw error;"
1012
- ).writeBlockEnd("}").writeBlockEnd("}");
1013
- if (routes.special[RoutableFileTypes.NotFound]) {
1014
- imports.writeLines(
1015
- `
1016
- const page404ResponseInit = {
1017
- status: 404,
1018
- headers: { "content-type": "text/html;charset=UTF-8" },
1019
- };`
1020
- );
1021
- writer.write(`
1022
- if (context.request.headers.get('Accept')?.includes('text/html')) {
1023
- return new Response(page404.stream(buildInput()), page404ResponseInit);
813
+ for (const special of Object.values(routes.special)) {
814
+ routeTypes.set(special.page, []);
815
+ }
816
+ routesWriter.join();
817
+ const handlerWriter = writer.branch("handler");
818
+ const middlewareWriter = writer.branch("middleware");
819
+ const pageWriter = writer.branch("page");
820
+ const layoutWriter = writer.branch("layout");
821
+ for (const [file, types] of routeTypes) {
822
+ const path6 = `${pathPrefix}/${file.relativePath}`;
823
+ const routeType = `Run.Routes[${types.join(" | ")}]`;
824
+ switch (file.type) {
825
+ case RoutableFileTypes.Handler:
826
+ writeModuleDeclaration(handlerWriter, path6, routeType);
827
+ break;
828
+ case RoutableFileTypes.Middleware:
829
+ writeModuleDeclaration(middlewareWriter, path6, routeType);
830
+ break;
831
+ case RoutableFileTypes.Page:
832
+ writeModuleDeclaration(pageWriter, path6, routeType);
833
+ break;
834
+ case RoutableFileTypes.Layout:
835
+ writeModuleDeclaration(
836
+ layoutWriter,
837
+ path6,
838
+ routeType,
839
+ `
840
+ export interface Input {
841
+ renderBody: Marko.Body;
842
+ }`
843
+ );
844
+ break;
845
+ case RoutableFileTypes.Error:
846
+ writeModuleDeclaration(
847
+ writer,
848
+ path6,
849
+ "globalThis.MarkoRun.Route",
850
+ `
851
+ export interface Input {
852
+ error: unknown;
853
+ }`
854
+ );
855
+ break;
856
+ case RoutableFileTypes.NotFound:
857
+ writeModuleDeclaration(writer, path6, "Run.Route");
858
+ break;
859
+ }
860
+ }
861
+ handlerWriter.join();
862
+ middlewareWriter.join();
863
+ pageWriter.join();
864
+ layoutWriter.join();
865
+ writer.writeBlockStart(`
866
+ type Routes = {`);
867
+ for (const route of routes.list) {
868
+ const { meta, handler, page } = route;
869
+ if (page || handler) {
870
+ const verbs = [];
871
+ if (page || ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes("get"))) {
872
+ verbs.push(`"get"`);
873
+ }
874
+ if ((_b = handler == null ? void 0 : handler.verbs) == null ? void 0 : _b.includes("post")) {
875
+ verbs.push(`"post"`);
876
+ }
877
+ let routeType = `{ verb: ${verbs.join(" | ")};`;
878
+ if (meta) {
879
+ const metaPath = stripTsExtension(`${pathPrefix}/${meta.relativePath}`);
880
+ let metaType = `typeof import("${metaPath}")`;
881
+ if (/\.(ts|js|mjs)$/.test(meta.name)) {
882
+ metaType += `["default"]`;
883
+ }
884
+ routeType += ` meta: ${metaType};`;
885
+ }
886
+ writer.writeLines(`"${route.key}": ${routeType} };`);
1024
887
  }
1025
- `);
1026
- }
1027
- writer.indent--;
1028
- if (hasErrorPage) {
1029
- imports.writeLines(`
1030
- const page500ResponseInit = {
1031
- status: 500,
1032
- headers: { "content-type": "text/html;charset=UTF-8" },
1033
- };`);
1034
- writer.writeBlockStart(`} catch (error) {`).writeBlockStart(
1035
- `if (context.request.headers.get('Accept')?.includes('text/html')) {`
1036
- ).writeLines(
1037
- `return new Response(page500.stream(buildInput({ error })), page500ResponseInit);`
1038
- ).writeBlockEnd("}").writeLines("throw error;").writeBlockEnd("}");
1039
888
  }
1040
889
  writer.writeBlockEnd("}");
1041
- renderFetch(writer, options);
1042
890
  return writer.end();
1043
891
  }
1044
- function renderFetch(writer, options) {
1045
- writer.write(`
1046
- export async function fetch(request, platform) {
1047
- try {
1048
- const url = new URL(request.url);
1049
- let { pathname } = url;`);
1050
- switch (options.trailingSlashes) {
1051
- case "RedirectWithout":
1052
- writer.write(`
1053
- if (pathname !== '/' && pathname.endsWith('/')) {
1054
- url.pathname = pathname.slice(0, -1);
1055
- return Response.redirect(url);
1056
- }`);
1057
- break;
1058
- case "RedirectWith":
1059
- writer.write(`
1060
- if (pathname !== '/' && !pathname.endsWith('/')) {
1061
- url.pathname = pathname + '/';
1062
- return Response.redirect(url);
1063
- }`);
1064
- break;
1065
- case "RewriteWithout":
1066
- writer.write(`
1067
- if (pathname !== '/' && pathname.endsWith('/')) {
1068
- url.pathname = pathname = pathname.slice(0, -1);
1069
- }`);
1070
- break;
1071
- case "RewriteWith":
1072
- writer.write(`
1073
- if (pathname !== '/' && !pathname.endsWith('/')) {
1074
- url.pathname = pathname = pathname + '/';
1075
- }`);
1076
- break;
892
+ function writeModuleDeclaration(writer, path6, routeType, moduleTypes) {
893
+ writer.writeLines("").write(`declare module "${stripTsExtension(path6)}" {`);
894
+ if (moduleTypes) {
895
+ writer.write(moduleTypes);
1077
896
  }
1078
- writer.write(`
1079
-
1080
- const route = match(request.method, pathname);
1081
- return await invoke(route, request, platform, url);
1082
- } catch (error) {
1083
- if (import.meta.env.DEV) {
1084
- throw error;
1085
- }
1086
- return new Response(null, {
1087
- status: 500
1088
- });
897
+ if (routeType) {
898
+ const isMarko = path6.endsWith(".marko");
899
+ writer.write(`
900
+ namespace MarkoRun {
901
+ export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
902
+ export type Route = ${routeType};
903
+ export type Context = Run.MultiRouteContext<Route>${isMarko ? " & Marko.Global" : ""};
904
+ export type Handler = Run.HandlerLike<Route>;
905
+ /** @deprecated use \`((context, next) => { ... }) satisfies MarkoRun.Handler\` instead */
906
+ export const route: Run.HandlerTypeFn<Route>;
907
+ }`);
1089
908
  }
909
+ writer.writeLines(`
1090
910
  }`);
1091
911
  }
1092
- function writeRouterVerb(writer, trie, verb, level = 0, offset = 1) {
1093
- const { route, dynamic, catchAll } = trie;
1094
- let closeCount = 0;
1095
- if (level === 0) {
1096
- writer.writeLines(`const len = pathname.length;`);
1097
- if (route) {
1098
- writer.writeLines(
1099
- `if (len === 1) return ${renderMatch(verb, route, trie.path)}; // ${trie.path.path}`
1100
- );
1101
- } else if (trie.static || dynamic) {
1102
- writer.writeBlockStart(`if (len > 1) {`);
1103
- closeCount++;
912
+ function pathToURLPatternString(path6) {
913
+ return path6.replace(/\/\$(\$?)([^/]*)/g, (_, catchAll, name) => {
914
+ name = decodeURIComponent(name);
915
+ return catchAll ? `/:${name || "rest"}*` : `/:${name}`;
916
+ });
917
+ }
918
+ function createRouteTrie(routes) {
919
+ const root = {
920
+ key: ""
921
+ };
922
+ function insert(path6, route) {
923
+ let node = root;
924
+ for (const segment of path6.segments) {
925
+ if (segment === "$$") {
926
+ node.catchAll ?? (node.catchAll = { route, path: path6 });
927
+ return;
928
+ } else if (segment === "$") {
929
+ node = node.dynamic ?? (node.dynamic = {
930
+ key: ""
931
+ });
932
+ } else {
933
+ node.static ?? (node.static = /* @__PURE__ */ new Map());
934
+ let next = node.static.get(segment);
935
+ if (!next) {
936
+ next = {
937
+ key: segment
938
+ };
939
+ node.static.set(segment, next);
940
+ }
941
+ node = next;
942
+ }
1104
943
  }
944
+ node.path ?? (node.path = path6);
945
+ node.route ?? (node.route = route);
1105
946
  }
1106
- if (trie.static || dynamic) {
1107
- const next = level + 1;
1108
- const index = `i${next}`;
1109
- let terminal;
1110
- let children;
1111
- writer.writeLines(`const ${index} = pathname.indexOf('/', ${offset}) + 1;`);
1112
- if (trie.static) {
1113
- for (const child of trie.static.values()) {
1114
- if (child.route) {
1115
- (terminal ?? (terminal = [])).push(child);
947
+ for (const route of routes) {
948
+ for (const path6 of route.paths) {
949
+ insert(path6, route);
950
+ }
951
+ }
952
+ return root;
953
+ }
954
+
955
+ // src/vite/routes/parse.ts
956
+ function parseFlatRoute(pattern) {
957
+ if (!pattern) throw new Error("Empty pattern");
958
+ const len = pattern.length;
959
+ let i = 0;
960
+ return parse2([
961
+ {
962
+ id: "/",
963
+ segments: [],
964
+ source: pattern
965
+ }
966
+ ]);
967
+ function parse2(basePaths, group) {
968
+ const pathMap = /* @__PURE__ */ new Map();
969
+ const delimiters = group ? ").," : ".,";
970
+ let charCode;
971
+ let segmentStart = i;
972
+ let type;
973
+ let current;
974
+ do {
975
+ charCode = pattern.charCodeAt(i);
976
+ if (charCode === 41 && group) {
977
+ break;
978
+ } else if (charCode === 44) {
979
+ if (!current) {
980
+ segmentEnd(
981
+ basePaths.map((path6) => ({
982
+ ...path6,
983
+ segments: path6.segments.slice()
984
+ })),
985
+ "",
986
+ "_",
987
+ pathMap
988
+ );
989
+ } else {
990
+ segmentEnd(current, pattern.slice(segmentStart, i), type, pathMap);
1116
991
  }
1117
- if (child.static || child.dynamic || child.catchAll) {
1118
- (children ?? (children = [])).push(child);
992
+ current = void 0;
993
+ type = void 0;
994
+ segmentStart = ++i;
995
+ } else if (charCode === 46) {
996
+ if (current) {
997
+ segmentEnd(current, pattern.slice(segmentStart, i), type);
1119
998
  }
1120
- }
1121
- }
1122
- if (terminal || (dynamic == null ? void 0 : dynamic.route)) {
1123
- closeCount++;
1124
- writer.writeBlockStart(`if (!${index} || ${index} === len) {`);
1125
- let value = `pathname.slice(${offset}, ${index} ? -1 : len)`;
1126
- if (dynamic == null ? void 0 : dynamic.route) {
1127
- const segment = `s${next}`;
1128
- writer.writeLines(`const ${segment} = decodeURIComponent(${value});`);
1129
- value = segment;
1130
- } else if (terminal == null ? void 0 : terminal.some(
1131
- (terminal2) => decodeURIComponent(terminal2.key) !== terminal2.key
1132
- )) {
1133
- value = `decodeURIComponent(${value})`;
1134
- }
1135
- if (terminal) {
1136
- const useSwitch = terminal.length > 1;
1137
- if (useSwitch) {
1138
- writer.writeBlockStart(`switch (${value}) {`);
999
+ type = void 0;
1000
+ segmentStart = ++i;
1001
+ } else if (charCode === 40) {
1002
+ const groupPaths = parse2(current || basePaths, ++i);
1003
+ if (groupPaths.length) {
1004
+ current = groupPaths;
1139
1005
  }
1140
- for (const { key, path: path6, route: route2 } of terminal) {
1141
- const decodedKey = decodeURIComponent(key);
1142
- if (useSwitch) {
1143
- writer.write(`case '${decodedKey}': `, true);
1144
- } else {
1145
- writer.write(`if (${value} === '${decodedKey}') `, true);
1146
- }
1147
- writer.write(
1148
- `return ${renderMatch(verb, route2, path6)}; // ${path6.path}
1149
- `
1150
- );
1006
+ segmentStart = ++i;
1007
+ } else {
1008
+ if (charCode === 95) {
1009
+ type = "_";
1010
+ } else if (charCode === 36) {
1011
+ type = pattern.charCodeAt(i + 1) === 36 ? "$$" : "$";
1151
1012
  }
1152
- if (useSwitch) {
1153
- writer.writeBlockEnd("}");
1013
+ current ?? (current = basePaths.map((path6) => ({
1014
+ ...path6,
1015
+ segments: path6.segments.slice()
1016
+ })));
1017
+ i = len;
1018
+ for (const char of delimiters) {
1019
+ const index = pattern.indexOf(char, segmentStart);
1020
+ if (index >= 0 && index < i) {
1021
+ i = index;
1022
+ }
1154
1023
  }
1155
1024
  }
1156
- if (dynamic == null ? void 0 : dynamic.route) {
1157
- writer.writeLines(
1158
- `if (${value}) return ${renderMatch(
1159
- verb,
1160
- dynamic.route,
1161
- dynamic.path
1162
- )}; // ${dynamic.path.path}`
1163
- );
1164
- }
1025
+ } while (i < len);
1026
+ if (group && charCode !== 41) {
1027
+ throw new Error(
1028
+ `Invalid route pattern: group was not closed '${pattern.slice(
1029
+ group
1030
+ )}' in '${pattern}'`
1031
+ );
1032
+ }
1033
+ if (!current) {
1034
+ segmentEnd(
1035
+ basePaths.map((path6) => ({
1036
+ ...path6,
1037
+ segments: path6.segments.slice()
1038
+ })),
1039
+ "",
1040
+ "_",
1041
+ pathMap
1042
+ );
1043
+ } else {
1044
+ segmentEnd(current, pattern.slice(segmentStart, i), type, pathMap);
1165
1045
  }
1166
- if (children || (dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic) || (dynamic == null ? void 0 : dynamic.catchAll)) {
1167
- if (terminal || (dynamic == null ? void 0 : dynamic.route)) {
1168
- writer.writeBlockEnd("} else {").indent++;
1169
- } else {
1170
- writer.writeBlockStart(`if (${index} && ${index} !== len) {`);
1171
- closeCount++;
1172
- }
1173
- let value = `pathname.slice(${offset}, ${index} - 1)`;
1174
- if ((dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic) || (dynamic == null ? void 0 : dynamic.catchAll)) {
1175
- const segment = `s${next}`;
1176
- writer.writeLines(`const ${segment} = decodeURIComponent(${value});`);
1177
- value = segment;
1178
- } else if (children == null ? void 0 : children.some((child) => decodeURIComponent(child.key) !== child.key)) {
1179
- value = `decodeURIComponent(${value})`;
1046
+ return [...pathMap.values()];
1047
+ }
1048
+ function segmentEnd(paths, raw, type, map) {
1049
+ let segment;
1050
+ if (raw) {
1051
+ segment = {
1052
+ raw,
1053
+ name: raw,
1054
+ type
1055
+ };
1056
+ if (type === "$" || type === "$$") {
1057
+ segment.name = type;
1058
+ segment.param = raw.slice(type.length);
1180
1059
  }
1181
- if (children) {
1182
- const useSwitch = children.length > 1;
1183
- if (useSwitch) {
1184
- writer.writeBlockStart(`switch (${value}) {`);
1185
- }
1186
- for (const child of children) {
1187
- const decodedKey = decodeURIComponent(child.key);
1188
- if (useSwitch) {
1189
- writer.writeBlockStart(`case '${decodedKey}': {`);
1190
- } else {
1191
- writer.writeBlockStart(`if (${value} === '${decodedKey}') {`);
1192
- }
1193
- const nextOffset = typeof offset === "string" ? index : offset + child.key.length + 1;
1194
- writeRouterVerb(writer, child, verb, next, nextOffset);
1195
- if (useSwitch) {
1196
- writer.writeBlockEnd("} break;");
1197
- } else {
1198
- writer.writeBlockEnd("}");
1199
- }
1060
+ }
1061
+ for (const path6 of paths) {
1062
+ if (segment) {
1063
+ if (path6.isCatchall) {
1064
+ throw new Error(
1065
+ `Invalid route pattern: nested segments are not allowed after a catch-all parameter. Found '.' following '${pattern.slice(
1066
+ 0,
1067
+ i
1068
+ )}' in '${pattern}'.`
1069
+ );
1200
1070
  }
1201
- if (useSwitch) {
1202
- writer.writeBlockEnd("}");
1071
+ path6.segments.push(segment);
1072
+ path6.id += path6.id === "/" ? segment.name : `/${segment.name}`;
1073
+ if (type === "$$") {
1074
+ path6.isCatchall = true;
1203
1075
  }
1204
1076
  }
1205
- if ((dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic) || (dynamic == null ? void 0 : dynamic.catchAll)) {
1206
- writer.writeBlockStart(`if (${value}) {`);
1207
- writeRouterVerb(writer, dynamic, verb, next, index);
1208
- writer.writeBlockEnd(`}`);
1077
+ if (map) {
1078
+ if (map.has(path6.id)) {
1079
+ const existing = map.get(path6.id);
1080
+ const existingExpansion = existing.segments.map((s) => s.raw).join(".");
1081
+ const currentExpansion = path6.segments.map((s) => s.raw).join(".");
1082
+ throw new Error(
1083
+ `Invalid route pattern: route '${path6.id}' is ambiguous. Expansion '${currentExpansion}' collides with '${existingExpansion}' in '${pattern}'.`
1084
+ );
1085
+ }
1086
+ map.set(path6.id, path6);
1209
1087
  }
1210
1088
  }
1211
1089
  }
1212
- while (closeCount--) {
1213
- writer.writeBlockEnd("}");
1214
- }
1215
- if (catchAll) {
1216
- writer.writeLines(
1217
- `return ${renderMatch(
1218
- verb,
1219
- catchAll.route,
1220
- catchAll.path,
1221
- String(offset)
1222
- )}; // ${catchAll.path.path}`
1223
- );
1224
- } else if (level === 0) {
1225
- writer.writeLines("return null;");
1226
- }
1227
- }
1228
- function wrapPropertyName(name) {
1229
- name = decodeURIComponent(name);
1230
- return /^[^A-Za-z_$]|[^A-Za-z0-9$_]/.test(name) ? `'${name}'` : name;
1231
1090
  }
1232
- function renderParams(params, pathIndex) {
1233
- let result = "";
1234
- let catchAll = "";
1235
- let sep = "{";
1236
- for (const [name, index] of Object.entries(params)) {
1237
- if (typeof index === "number") {
1238
- result += `${sep} ${wrapPropertyName(name)}: s${index + 1}`;
1239
- sep = ",";
1240
- } else if (pathIndex) {
1241
- catchAll = name;
1091
+
1092
+ // src/vite/routes/vdir.ts
1093
+ var _dirs, _pathlessDirs;
1094
+ var _VDir = class _VDir {
1095
+ constructor(parent, segment, source) {
1096
+ __privateAdd(this, _dirs);
1097
+ __privateAdd(this, _pathlessDirs);
1098
+ __publicField(this, "parent");
1099
+ __publicField(this, "source");
1100
+ __publicField(this, "path");
1101
+ __publicField(this, "fullPath");
1102
+ __publicField(this, "segment");
1103
+ __publicField(this, "files");
1104
+ if (!parent || !segment) {
1105
+ this.parent = null;
1106
+ this.source = null;
1107
+ this.path = "/";
1108
+ this.fullPath = "/";
1109
+ this.segment = {
1110
+ raw: "",
1111
+ name: ""
1112
+ };
1113
+ } else {
1114
+ this.parent = parent;
1115
+ this.source = source;
1116
+ this.path = parent.path + (parent.path === "/" ? segment.name : `/${segment.name}`);
1117
+ this.fullPath = parent.fullPath + (parent.fullPath === "/" ? segment.name : `/${segment.name}`);
1118
+ if (segment.param) {
1119
+ this.fullPath += segment.param;
1120
+ }
1121
+ this.segment = segment;
1242
1122
  }
1243
1123
  }
1244
- if (catchAll) {
1245
- result += `${sep} ${wrapPropertyName(
1246
- catchAll
1247
- )}: pathname.slice(${pathIndex})`;
1248
- }
1249
- return result ? result + " }" : "{}";
1250
- }
1251
- function renderMatch(verb, route, path6, pathIndex) {
1252
- const handler = `${verb}${route.index}`;
1253
- const params = path6.params ? renderParams(path6.params, pathIndex) : "{}";
1254
- const meta = route.meta ? `meta${route.index}` : "{}";
1255
- const pathPattern = pathToURLPatternString(path6.path);
1256
- return `{ handler: ${handler}, params: ${params}, meta: ${meta}, path: '${pathPattern}' }`;
1257
- }
1258
- function renderMiddleware(middleware) {
1259
- const writer = createStringWriter();
1260
- writer.writeLines(
1261
- `// ${virtualFilePrefix}/${markoRunFilePrefix}middleware.js`
1262
- );
1263
- const imports = writer.branch("imports");
1264
- imports.writeLines(
1265
- `import { normalize } from '${virtualFilePrefix}/runtime/internal';`
1266
- );
1267
- writer.writeLines("");
1268
- for (const { id, importPath } of middleware) {
1269
- const importName = `middleware${id}`;
1270
- imports.writeLines(`import ${importName} from './${importPath}';`);
1271
- writer.writeLines(`export const mware${id} = normalize(${importName});`);
1124
+ get pathInfo() {
1125
+ const value = {
1126
+ id: "/",
1127
+ path: "/",
1128
+ segments: []
1129
+ };
1130
+ let sep = "";
1131
+ for (const { segment } of this) {
1132
+ const { type, name, param } = segment;
1133
+ if (name && type !== "_") {
1134
+ value.id += sep + (type || name);
1135
+ value.path += sep + name;
1136
+ value.isEnd = type === "$$";
1137
+ if (param) {
1138
+ value.path += param;
1139
+ const index = type === "$$" ? null : value.segments.length;
1140
+ if (!value.params) {
1141
+ value.params = { [param]: index };
1142
+ } else if (!(param in value.params)) {
1143
+ value.params[param] = index;
1144
+ }
1145
+ }
1146
+ value.segments.push(name);
1147
+ sep = "/";
1148
+ }
1149
+ }
1150
+ Object.defineProperty(this, "pathInfo", {
1151
+ value,
1152
+ enumerable: true
1153
+ });
1154
+ return value;
1272
1155
  }
1273
- imports.join();
1274
- return writer.end();
1275
- }
1276
- function stripTsExtension(path6) {
1277
- const index = path6.lastIndexOf(".");
1278
- if (index !== -1) {
1279
- const ext = path6.slice(index + 1);
1280
- if (ext.toLowerCase() === "ts") {
1281
- return path6.slice(0, index);
1156
+ addDir(path6, segment) {
1157
+ const map = segment.type === "_" ? __privateGet(this, _pathlessDirs) ?? __privateSet(this, _pathlessDirs, /* @__PURE__ */ new Map()) : __privateGet(this, _dirs) ?? __privateSet(this, _dirs, /* @__PURE__ */ new Map());
1158
+ if (!map.has(segment.name)) {
1159
+ const dir = new _VDir(this, segment, path6);
1160
+ map.set(segment.name, dir);
1161
+ return dir;
1282
1162
  }
1163
+ return map.get(segment.name);
1283
1164
  }
1284
- return path6;
1285
- }
1286
- async function renderRouteTypeInfo(routes, pathPrefix = ".", adapter) {
1287
- var _a, _b;
1288
- const writer = createStringWriter();
1289
- writer.writeLines(
1290
- `/*
1291
- WARNING: This file is automatically generated and any changes made to it will be overwritten without warning.
1292
- Do NOT manually edit this file or your changes will be lost.
1293
- */
1294
- `,
1295
- `import { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform } from "@marko/run/namespace";`,
1296
- `import type * as Run from "@marko/run";`
1297
- );
1298
- const headWriter = writer.branch("head");
1299
- writer.writeLines("\n").writeBlockStart(`declare module "@marko/run" {`);
1300
- if (adapter && adapter.typeInfo) {
1301
- const platformType = await adapter.typeInfo(
1302
- (data) => headWriter.write(data)
1303
- );
1304
- if (platformType) {
1305
- writer.writeLines(`interface Platform extends ${platformType} {}
1306
- `);
1165
+ addFile(file) {
1166
+ if (!this.files) {
1167
+ this.files = /* @__PURE__ */ new Map();
1168
+ this.files.set(file.type, file);
1169
+ } else if (!this.files.has(file.type)) {
1170
+ this.files.set(file.type, file);
1171
+ } else {
1172
+ const existing = this.files.get(file.type);
1173
+ if (existing !== file) {
1174
+ throw new Error(
1175
+ `Duplicate file type '${file.type}' added at path '${this.path}'. File '${file.importPath}' collides with '${existing.importPath}'.`
1176
+ );
1177
+ } else if (file.type === RoutableFileTypes.Page || file.type === RoutableFileTypes.Handler) {
1178
+ throw new Error(
1179
+ `Ambiguous path definition: route '${this.path}' is defined multiple times by ${file.importPath}`
1180
+ );
1181
+ }
1182
+ throw new Error(
1183
+ `Ambiguous path definition: file '${this.path}' is included multiple times by ${file.importPath}`
1184
+ );
1307
1185
  }
1308
1186
  }
1309
- headWriter.join();
1310
- writer.writeBlockStart(`interface AppData extends Run.DefineApp<{`).writeBlockStart("routes: {");
1311
- const routesWriter = writer.branch("routes");
1312
- writer.writeBlockEnd("}").writeBlockEnd(`}> {}`).writeBlockEnd(`}`);
1313
- const routeTypes = /* @__PURE__ */ new Map();
1314
- for (const route of routes.list) {
1315
- let routeType = "";
1316
- for (const path6 of route.paths) {
1317
- const pathType = `"${pathToURLPatternString(path6.path)}"`;
1318
- routeType += routeType ? " | " + pathType : pathType;
1319
- routesWriter.writeLines(`${pathType}: Routes["${route.key}"];`);
1187
+ *dirs() {
1188
+ if (__privateGet(this, _pathlessDirs)) {
1189
+ yield* __privateGet(this, _pathlessDirs).values();
1320
1190
  }
1321
- for (const file of [route.handler, route.page]) {
1322
- if (file) {
1323
- const existing = routeTypes.get(file);
1324
- if (!existing) {
1325
- routeTypes.set(file, [routeType]);
1326
- } else {
1327
- existing.push(routeType);
1328
- }
1329
- }
1191
+ if (__privateGet(this, _dirs)) {
1192
+ yield* __privateGet(this, _dirs).values();
1330
1193
  }
1331
- for (const files of [route.middleware, route.layouts]) {
1332
- if (files) {
1333
- for (const file of files) {
1334
- const existing = routeTypes.get(file);
1335
- if (!existing) {
1336
- routeTypes.set(file, [routeType]);
1337
- } else {
1338
- existing.push(routeType);
1194
+ }
1195
+ *[Symbol.iterator]() {
1196
+ if (this.parent) {
1197
+ yield* this.parent;
1198
+ }
1199
+ yield this;
1200
+ }
1201
+ static addPaths(roots, paths) {
1202
+ const dirs = [];
1203
+ const unique = /* @__PURE__ */ new Set();
1204
+ for (const root of roots) {
1205
+ for (const path6 of paths) {
1206
+ let dir = root;
1207
+ for (const segment of path6.segments) {
1208
+ dir = dir.addDir(path6, segment);
1209
+ }
1210
+ if (unique.has(dir.path)) {
1211
+ const sources = /* @__PURE__ */ new Set();
1212
+ let sourcePath = "";
1213
+ for (const { source } of dir) {
1214
+ if (source && !sources.has(source.source)) {
1215
+ sources.add(source.source);
1216
+ sourcePath += source.source + "/";
1217
+ }
1339
1218
  }
1219
+ throw new Error(
1220
+ `Ambiguous directory structure: '${sourcePath}${path6.source}' defines '${dir.path}' multiple times.`
1221
+ );
1222
+ } else {
1223
+ unique.add(dir.path);
1224
+ dirs.push(dir);
1340
1225
  }
1341
1226
  }
1342
1227
  }
1228
+ return dirs;
1343
1229
  }
1344
- for (const special of Object.values(routes.special)) {
1345
- routeTypes.set(special.page, []);
1346
- }
1347
- routesWriter.join();
1348
- const handlerWriter = writer.branch("handler");
1349
- const middlewareWriter = writer.branch("middleware");
1350
- const pageWriter = writer.branch("page");
1351
- const layoutWriter = writer.branch("layout");
1352
- for (const [file, types] of routeTypes) {
1353
- const path6 = `${pathPrefix}/${file.relativePath}`;
1354
- const routeType = `Run.Routes[${types.join(" | ")}]`;
1355
- switch (file.type) {
1356
- case RoutableFileTypes.Handler:
1357
- writeModuleDeclaration(handlerWriter, path6, routeType);
1358
- break;
1359
- case RoutableFileTypes.Middleware:
1360
- writeModuleDeclaration(middlewareWriter, path6, routeType);
1361
- break;
1362
- case RoutableFileTypes.Page:
1363
- writeModuleDeclaration(pageWriter, path6, routeType);
1364
- break;
1365
- case RoutableFileTypes.Layout:
1366
- writeModuleDeclaration(
1367
- layoutWriter,
1368
- path6,
1369
- routeType,
1370
- `
1371
- export interface Input {
1372
- renderBody: Marko.Body;
1373
- }`
1374
- );
1375
- break;
1376
- case RoutableFileTypes.Error:
1377
- writeModuleDeclaration(
1378
- writer,
1379
- path6,
1380
- "globalThis.MarkoRun.Route",
1381
- `
1382
- export interface Input {
1383
- error: unknown;
1384
- }`
1230
+ };
1231
+ _dirs = new WeakMap();
1232
+ _pathlessDirs = new WeakMap();
1233
+ var VDir = _VDir;
1234
+
1235
+ // src/vite/routes/builder.ts
1236
+ var markoFiles = `(${RoutableFileTypes.Layout}|${RoutableFileTypes.Page}|${RoutableFileTypes.NotFound}|${RoutableFileTypes.Error})\\.(?:.*\\.)?(marko)`;
1237
+ var nonMarkoFiles = `(${RoutableFileTypes.Middleware}|${RoutableFileTypes.Handler}|${RoutableFileTypes.Meta})\\.(?:.*\\.)?(.+)`;
1238
+ var routeableFileRegex = new RegExp(
1239
+ `[+](?:${markoFiles}|${nonMarkoFiles})$`,
1240
+ "i"
1241
+ );
1242
+ function matchRoutableFile(filename) {
1243
+ const match = filename.match(routeableFileRegex);
1244
+ return match && (match[1] || match[3]).toLowerCase();
1245
+ }
1246
+ function isSpecialType(type) {
1247
+ return type === RoutableFileTypes.NotFound || type === RoutableFileTypes.Error;
1248
+ }
1249
+ async function buildRoutes(sources) {
1250
+ const uniqueRoutes = /* @__PURE__ */ new Map();
1251
+ const routes = [];
1252
+ const special = {};
1253
+ const middlewares = /* @__PURE__ */ new Set();
1254
+ const unusedFiles = /* @__PURE__ */ new Set();
1255
+ const currentLayouts = /* @__PURE__ */ new Set();
1256
+ const currentMiddleware = /* @__PURE__ */ new Set();
1257
+ const root = new VDir();
1258
+ const dirStack = [];
1259
+ let basePath;
1260
+ let importPrefix;
1261
+ let activeDirs;
1262
+ let isBaseDir;
1263
+ let nextFileId = 1;
1264
+ let nextRouteIndex = 1;
1265
+ const walkOptions = {
1266
+ onEnter({ name }) {
1267
+ const prevDirStackLength = dirStack.length;
1268
+ if (isBaseDir) {
1269
+ isBaseDir = false;
1270
+ if (!basePath) {
1271
+ return;
1272
+ }
1273
+ name = basePath;
1274
+ } else {
1275
+ dirStack.push(name);
1276
+ }
1277
+ const previousDirs = activeDirs;
1278
+ const paths = parseFlatRoute(name);
1279
+ activeDirs = VDir.addPaths(previousDirs, paths);
1280
+ return () => {
1281
+ activeDirs = previousDirs;
1282
+ dirStack.length = prevDirStackLength;
1283
+ };
1284
+ },
1285
+ onFile({ name, path: path6 }) {
1286
+ const match = name.match(routeableFileRegex);
1287
+ if (!match) {
1288
+ return;
1289
+ }
1290
+ const type = (match[1] || match[3]).toLowerCase();
1291
+ if (dirStack.length && isSpecialType(type)) {
1292
+ console.warn(
1293
+ `Special pages '${RoutableFileTypes.NotFound}' and '${RoutableFileTypes.Error}' are only considered in the root directory - ignoring ${path6}`
1385
1294
  );
1386
- break;
1387
- case RoutableFileTypes.NotFound:
1388
- writeModuleDeclaration(writer, path6, "Run.Route");
1389
- break;
1295
+ return;
1296
+ }
1297
+ let dirs = activeDirs;
1298
+ if (match.index) {
1299
+ const paths = parseFlatRoute(name.slice(0, match.index));
1300
+ dirs = VDir.addPaths(activeDirs, paths);
1301
+ }
1302
+ const dirPath = dirStack.join("/");
1303
+ const relativePath = dirPath ? `${dirPath}/${name}` : name;
1304
+ const file = {
1305
+ id: String(nextFileId++),
1306
+ name,
1307
+ type,
1308
+ filePath: path6,
1309
+ relativePath,
1310
+ importPath: `${importPrefix}/${relativePath}`,
1311
+ verbs: type === RoutableFileTypes.Page ? ["get"] : void 0
1312
+ };
1313
+ for (const dir of dirs) {
1314
+ dir.addFile(file);
1315
+ }
1390
1316
  }
1317
+ };
1318
+ if (!Array.isArray(sources)) {
1319
+ sources = [sources];
1391
1320
  }
1392
- handlerWriter.join();
1393
- middlewareWriter.join();
1394
- pageWriter.join();
1395
- layoutWriter.join();
1396
- writer.writeBlockStart(`
1397
- type Routes = {`);
1398
- for (const route of routes.list) {
1399
- const { meta, handler, page } = route;
1400
- if (page || handler) {
1401
- const verbs = [];
1402
- if (page || ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes("get"))) {
1403
- verbs.push(`"get"`);
1321
+ for (const source of sources) {
1322
+ importPrefix = source.importPrefix ? source.importPrefix.replace(/^\/+|\/+$/g, "") : "";
1323
+ basePath = source.basePath || "";
1324
+ activeDirs = [root];
1325
+ isBaseDir = true;
1326
+ await source.walker(walkOptions);
1327
+ }
1328
+ traverse(root);
1329
+ return {
1330
+ list: routes,
1331
+ middleware: [...middlewares],
1332
+ special
1333
+ };
1334
+ function traverse(dir) {
1335
+ let middleware;
1336
+ let layout;
1337
+ if (dir.files) {
1338
+ middleware = dir.files.get(RoutableFileTypes.Middleware);
1339
+ layout = dir.files.get(RoutableFileTypes.Layout);
1340
+ const handler = dir.files.get(RoutableFileTypes.Handler);
1341
+ const page = dir.files.get(RoutableFileTypes.Page);
1342
+ let hasSpecial = false;
1343
+ if (middleware) {
1344
+ if (currentMiddleware.has(middleware)) {
1345
+ middleware = void 0;
1346
+ } else {
1347
+ currentMiddleware.add(middleware);
1348
+ unusedFiles.add(middleware);
1349
+ }
1350
+ }
1351
+ if (layout) {
1352
+ if (currentLayouts.has(layout)) {
1353
+ layout = void 0;
1354
+ } else {
1355
+ currentLayouts.add(layout);
1356
+ unusedFiles.add(layout);
1357
+ }
1358
+ }
1359
+ if (page || handler) {
1360
+ const path6 = dir.pathInfo;
1361
+ if (uniqueRoutes.has(path6.id)) {
1362
+ const existing = uniqueRoutes.get(path6.id);
1363
+ const route = routes[existing.index];
1364
+ const existingFiles = [route.handler, route.page].filter(Boolean).map((f) => f.filePath);
1365
+ const currentFiles = [handler, page].filter(Boolean).map((f) => f.filePath);
1366
+ throw new Error(`Duplicate routes for path '${path6.path}' were defined. A route established by:
1367
+ ${existingFiles.join(" and ")} via '${existing.dir.path}'
1368
+ collides with
1369
+ ${currentFiles.join(" and ")} via '${dir.path}'
1370
+ `);
1371
+ }
1372
+ uniqueRoutes.set(path6.id, { dir, index: routes.length });
1373
+ routes.push({
1374
+ index: nextRouteIndex++,
1375
+ key: dir.fullPath,
1376
+ paths: [path6],
1377
+ middleware: [...currentMiddleware],
1378
+ layouts: page ? [...currentLayouts] : [],
1379
+ meta: dir.files.get(RoutableFileTypes.Meta),
1380
+ page,
1381
+ handler,
1382
+ entryName: `${markoRunFilePrefix}route` + (dir.path !== "/" ? dir.fullPath.replace(/\//g, ".").replace(/(%[A-Fa-f0-9]{2})+/g, "_") : "")
1383
+ });
1404
1384
  }
1405
- if ((_b = handler == null ? void 0 : handler.verbs) == null ? void 0 : _b.includes("post")) {
1406
- verbs.push(`"post"`);
1385
+ if (dir === root) {
1386
+ for (const [type, file] of dir.files) {
1387
+ if (isSpecialType(type)) {
1388
+ hasSpecial = true;
1389
+ special[type] = {
1390
+ index: 0,
1391
+ key: type,
1392
+ paths: [],
1393
+ middleware: [],
1394
+ layouts: [...currentLayouts],
1395
+ page: file,
1396
+ entryName: `${markoRunFilePrefix}special.${type}`
1397
+ };
1398
+ }
1399
+ }
1407
1400
  }
1408
- let routeType = `{ verb: ${verbs.join(" | ")};`;
1409
- if (meta) {
1410
- const metaPath = stripTsExtension(`${pathPrefix}/${meta.relativePath}`);
1411
- let metaType = `typeof import("${metaPath}")`;
1412
- if (/\.(ts|js|mjs)$/.test(meta.name)) {
1413
- metaType += `["default"]`;
1401
+ if (handler || page) {
1402
+ for (const middleware2 of currentMiddleware) {
1403
+ middlewares.add(middleware2);
1404
+ unusedFiles.delete(middleware2);
1405
+ }
1406
+ }
1407
+ if (page || hasSpecial) {
1408
+ for (const layout2 of currentLayouts) {
1409
+ unusedFiles.delete(layout2);
1414
1410
  }
1415
- routeType += ` meta: ${metaType};`;
1416
1411
  }
1417
- writer.writeLines(`"${route.key}": ${routeType} };`);
1412
+ }
1413
+ if (dir.dirs) {
1414
+ for (const child of dir.dirs()) {
1415
+ traverse(child);
1416
+ }
1417
+ }
1418
+ if (middleware) {
1419
+ currentMiddleware.delete(middleware);
1420
+ }
1421
+ if (layout) {
1422
+ currentLayouts.delete(layout);
1418
1423
  }
1419
1424
  }
1420
- writer.writeBlockEnd("}");
1421
- return writer.end();
1422
- }
1423
- function writeModuleDeclaration(writer, path6, routeType, moduleTypes) {
1424
- writer.writeLines("").write(`declare module "${stripTsExtension(path6)}" {`);
1425
- if (moduleTypes) {
1426
- writer.write(moduleTypes);
1427
- }
1428
- if (routeType) {
1429
- const isMarko = path6.endsWith(".marko");
1430
- writer.write(`
1431
- namespace MarkoRun {
1432
- export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
1433
- export type Route = ${routeType};
1434
- export type Context = Run.MultiRouteContext<Route>${isMarko ? " & Marko.Global" : ""};
1435
- export type Handler = Run.HandlerLike<Route>;
1436
- /** @deprecated use \`((context, next) => { ... }) satisfies MarkoRun.Handler\` instead */
1437
- export const route: Run.HandlerTypeFn<Route>;
1438
- }`);
1439
- }
1440
- writer.writeLines(`
1441
- }`);
1442
- }
1443
- function pathToURLPatternString(path6) {
1444
- return path6.replace(/\/\$(\$?)([^\/]*)/g, (_, catchAll, name) => {
1445
- name = decodeURIComponent(name);
1446
- return catchAll ? `/:${name || "rest"}*` : `/:${name}`;
1447
- });
1448
1425
  }
1449
- function createRouteTrie(routes) {
1450
- const root = {
1451
- key: ""
1452
- };
1453
- function insert(path6, route) {
1454
- let node = root;
1455
- for (const segment of path6.segments) {
1456
- if (segment === "$$") {
1457
- node.catchAll ?? (node.catchAll = { route, path: path6 });
1458
- return;
1459
- } else if (segment === "$") {
1460
- node = node.dynamic ?? (node.dynamic = {
1461
- key: ""
1426
+
1427
+ // src/vite/routes/walk.ts
1428
+ import fs from "fs";
1429
+ import path2 from "path";
1430
+ function createFSWalker(dir) {
1431
+ return async function walkFS({
1432
+ onEnter,
1433
+ onFile,
1434
+ onDir,
1435
+ maxDepth = 50
1436
+ }) {
1437
+ async function walk(dir2, depth) {
1438
+ const onExit = onEnter == null ? void 0 : onEnter(dir2);
1439
+ if (onExit !== false) {
1440
+ const dirs = [];
1441
+ const entries = await fs.promises.readdir(dir2.path, {
1442
+ withFileTypes: true
1462
1443
  });
1463
- } else {
1464
- node.static ?? (node.static = /* @__PURE__ */ new Map());
1465
- let next = node.static.get(segment);
1466
- if (!next) {
1467
- next = {
1468
- key: segment
1444
+ const prefix = dir2.path + path2.sep;
1445
+ for (const entry of entries) {
1446
+ const walkEntry = {
1447
+ name: entry.name,
1448
+ path: prefix + entry.name
1469
1449
  };
1470
- node.static.set(segment, next);
1450
+ if (entry.isDirectory()) {
1451
+ dirs.push(walkEntry);
1452
+ } else {
1453
+ onFile == null ? void 0 : onFile(walkEntry);
1454
+ }
1471
1455
  }
1472
- node = next;
1456
+ if ((onDir == null ? void 0 : onDir()) !== false && --depth > 0) {
1457
+ for (const entry of dirs) {
1458
+ await walk(entry, depth);
1459
+ }
1460
+ }
1461
+ onExit == null ? void 0 : onExit();
1473
1462
  }
1474
1463
  }
1475
- node.path ?? (node.path = path6);
1476
- node.route ?? (node.route = route);
1477
- }
1478
- for (const route of routes) {
1479
- for (const path6 of route.paths) {
1480
- insert(path6, route);
1481
- }
1482
- }
1483
- return root;
1464
+ await walk(
1465
+ {
1466
+ path: dir,
1467
+ name: path2.basename(dir)
1468
+ },
1469
+ maxDepth
1470
+ );
1471
+ };
1484
1472
  }
1485
1473
 
1486
1474
  // src/vite/utils/ast.ts
@@ -1539,18 +1527,33 @@ function getViteSSRExportIdentifiers(astProgramNode, exportObjectName = "__vite_
1539
1527
  return result;
1540
1528
  }
1541
1529
 
1530
+ // src/vite/utils/config.ts
1531
+ var PluginConfigKey = "__MARKO_RUN_PLUGIN_CONFIG__";
1532
+ var AdapterConfigKey = "__MARKO_RUN_ADAPTER_CONFIG__";
1533
+ function getConfig(obj, key) {
1534
+ return obj[key];
1535
+ }
1536
+ function setConfig(obj, key, value) {
1537
+ obj[key] = value;
1538
+ return obj;
1539
+ }
1540
+ var getExternalPluginOptions = (viteConfig) => getConfig(viteConfig, PluginConfigKey);
1541
+ var setExternalPluginOptions = (viteConfig, value) => setConfig(viteConfig, PluginConfigKey, value);
1542
+ var getExternalAdapterOptions = (viteConfig) => getConfig(viteConfig, AdapterConfigKey);
1543
+ var setExternalAdapterOptions = (viteConfig, value) => setConfig(viteConfig, AdapterConfigKey, value);
1544
+
1542
1545
  // src/vite/utils/log.ts
1543
1546
  import zlib from "node:zlib";
1547
+ import { Blob } from "buffer";
1544
1548
  import Table from "cli-table3";
1545
- import kleur from "kleur";
1546
1549
  import format from "human-format";
1547
- import { Blob } from "buffer";
1550
+ import kleur2 from "kleur";
1548
1551
  var HttpVerbColors = {
1549
- get: kleur.green,
1550
- post: kleur.magenta,
1551
- put: kleur.cyan,
1552
- delete: kleur.red,
1553
- other: kleur.white
1552
+ get: kleur2.green,
1553
+ post: kleur2.magenta,
1554
+ put: kleur2.cyan,
1555
+ delete: kleur2.red,
1556
+ other: kleur2.white
1554
1557
  };
1555
1558
  var HttpVerbOrder = {
1556
1559
  get: 0,
@@ -1577,7 +1580,7 @@ function logRoutesTable(routes, bundle, options) {
1577
1580
  headings.push("Size/GZip");
1578
1581
  colAligns.push("right");
1579
1582
  const table = new Table({
1580
- head: headings.map((title) => kleur.bold(kleur.white(title.toUpperCase()))),
1583
+ head: headings.map((title) => kleur2.bold(kleur2.white(title.toUpperCase()))),
1581
1584
  wordWrap: true,
1582
1585
  colAligns,
1583
1586
  style: { compact: true }
@@ -1592,14 +1595,14 @@ function logRoutesTable(routes, bundle, options) {
1592
1595
  let size = "";
1593
1596
  const entryType = [];
1594
1597
  if (route.handler) {
1595
- entryType.push(kleur.blue("handler"));
1598
+ entryType.push(kleur2.blue("handler"));
1596
1599
  }
1597
1600
  if (verb === "get" && route.page) {
1598
- entryType.push(kleur.yellow("page"));
1601
+ entryType.push(kleur2.yellow("page"));
1599
1602
  size = prettySize(computeRouteSize(getRouteChunkName(route), bundle));
1600
1603
  }
1601
1604
  const row = [
1602
- kleur.bold(HttpVerbColors[verb](verb.toUpperCase()))
1605
+ kleur2.bold(HttpVerbColors[verb](verb.toUpperCase()))
1603
1606
  ];
1604
1607
  if (verbs.length === 1 || firstRow) {
1605
1608
  row.push({ rowSpan: verbs.length, content: prettyPath(path6.path) });
@@ -1614,7 +1617,7 @@ function logRoutesTable(routes, bundle, options) {
1614
1617
  }
1615
1618
  }
1616
1619
  for (const [key, route] of Object.entries(routes.special).sort()) {
1617
- const row = [kleur.bold(kleur.white("*")), key, kleur.yellow("page")];
1620
+ const row = [kleur2.bold(kleur2.white("*")), key, kleur2.yellow("page")];
1618
1621
  hasMiddleware && row.push("");
1619
1622
  hasMeta && row.push("");
1620
1623
  row.push(prettySize(computeRouteSize(getRouteChunkName(route), bundle)));
@@ -1653,27 +1656,24 @@ function computeChunkSize(chunk, bundle, seen = /* @__PURE__ */ new Set()) {
1653
1656
  }
1654
1657
  function prettySize([bytes, compBytes]) {
1655
1658
  if (bytes <= 0) {
1656
- return kleur.gray("0.0 kB");
1659
+ return kleur2.gray("0.0 kB");
1657
1660
  }
1658
1661
  const [size, prefix] = format(bytes, { decimals: 1 }).split(/\s+/);
1659
1662
  const compSize = format(compBytes, { decimals: 1, prefix, unit: "B" });
1660
- let str = kleur.white(size) + kleur.gray("/");
1661
- if (compBytes < 20 * 1e3) str += kleur.green(compSize);
1662
- else if (compBytes < 50 * 1e3) str += kleur.yellow(compSize);
1663
- else str += kleur.bold(kleur.red(compSize));
1663
+ let str = kleur2.white(size) + kleur2.gray("/");
1664
+ if (compBytes < 20 * 1e3) str += kleur2.green(compSize);
1665
+ else if (compBytes < 50 * 1e3) str += kleur2.yellow(compSize);
1666
+ else str += kleur2.bold(kleur2.red(compSize));
1664
1667
  return str;
1665
1668
  }
1666
1669
  function prettyPath(path6) {
1667
- return path6.replace(/\/\$\$(.*)$/, (_, p) => "/" + kleur.bold(kleur.dim(`*${p}`))).replace(/\/\$([^/]+)/g, (_, p) => "/" + kleur.bold(kleur.dim(`:${p}`)));
1670
+ return path6.replace(/\/\$\$(.*)$/, (_, p) => "/" + kleur2.bold(kleur2.dim(`*${p}`))).replace(/\/\$([^/]+)/g, (_, p) => "/" + kleur2.bold(kleur2.dim(`:${p}`)));
1668
1671
  }
1669
1672
 
1670
- // src/vite/plugin.ts
1671
- import createDebug from "debug";
1672
-
1673
1673
  // src/vite/utils/read-once-persisted-store.ts
1674
+ import { promises as fs2 } from "fs";
1674
1675
  import os from "os";
1675
1676
  import path3 from "path";
1676
- import { promises as fs3 } from "fs";
1677
1677
  var noop = () => {
1678
1678
  };
1679
1679
  var tmpFile = path3.join(os.tmpdir(), "marko-run-storage.json");
@@ -1696,13 +1696,13 @@ var ReadOncePersistedStore = class {
1696
1696
  if (loadedFromDisk === true) {
1697
1697
  throw new Error(`Value for ${uid} could not be loaded.`);
1698
1698
  }
1699
- await (loadedFromDisk || (loadedFromDisk = fs3.readFile(tmpFile, "utf-8").then(syncDataFromDisk).catch(finishLoadFromDisk)));
1699
+ await (loadedFromDisk || (loadedFromDisk = fs2.readFile(tmpFile, "utf-8").then(syncDataFromDisk).catch(finishLoadFromDisk)));
1700
1700
  return this.read();
1701
1701
  }
1702
1702
  };
1703
1703
  function syncDataFromDisk(data) {
1704
1704
  finishLoadFromDisk();
1705
- fs3.unlink(tmpFile).catch(noop);
1705
+ fs2.unlink(tmpFile).catch(noop);
1706
1706
  for (const [k, v] of JSON.parse(data)) {
1707
1707
  values.set(k, v);
1708
1708
  }
@@ -1712,37 +1712,11 @@ function finishLoadFromDisk() {
1712
1712
  }
1713
1713
  process.once("beforeExit", (code) => {
1714
1714
  if (code === 0 && values.size) {
1715
- fs3.writeFile(tmpFile, JSON.stringify([...values])).catch(noop);
1715
+ fs2.writeFile(tmpFile, JSON.stringify([...values])).catch(noop);
1716
1716
  }
1717
1717
  });
1718
1718
 
1719
- // src/adapter/utils.ts
1720
- import supporsColor from "supports-color";
1721
- import kleur2 from "kleur";
1722
- function stripAnsi(string) {
1723
- return string.replace(
1724
- /([\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><])/g,
1725
- ""
1726
- );
1727
- }
1728
- function cleanStack(stack) {
1729
- return stack.split(/\n/).filter((l) => /^\s*at/.test(l)).join("\n");
1730
- }
1731
- function prepareError(err) {
1732
- var _a;
1733
- return {
1734
- message: stripAnsi(err.message),
1735
- stack: stripAnsi(cleanStack(err.stack || "")),
1736
- id: err.id,
1737
- frame: stripAnsi(err.frame || ""),
1738
- plugin: err.plugin,
1739
- pluginCode: (_a = err.pluginCode) == null ? void 0 : _a.toString(),
1740
- loc: err.loc
1741
- };
1742
- }
1743
-
1744
1719
  // src/vite/plugin.ts
1745
- import { createHash } from "crypto";
1746
1720
  var debug = createDebug("@marko/run");
1747
1721
  var __dirname = path4.dirname(fileURLToPath(import.meta.url));
1748
1722
  var PLUGIN_NAME_PREFIX = "marko-run-vite";
@@ -1774,7 +1748,7 @@ function markoRun(opts = {}) {
1774
1748
  let getExportsFromFile;
1775
1749
  let resolvedConfig;
1776
1750
  let typesFile;
1777
- let seenErrors = /* @__PURE__ */ new Set();
1751
+ const seenErrors = /* @__PURE__ */ new Set();
1778
1752
  const virtualFiles = /* @__PURE__ */ new Map();
1779
1753
  let times = {
1780
1754
  routesBuild: 0,
@@ -1791,73 +1765,77 @@ function markoRun(opts = {}) {
1791
1765
  normalizePath(path4.relative(typesDir, resolvedRoutesDir)),
1792
1766
  adapter
1793
1767
  );
1794
- if (data !== typesFile || !fs4.existsSync(filepath)) {
1768
+ if (data !== typesFile || !fs3.existsSync(filepath)) {
1795
1769
  await ensureDir(typesDir);
1796
- await fs4.promises.writeFile(filepath, typesFile = data);
1770
+ await fs3.promises.writeFile(filepath, typesFile = data);
1797
1771
  }
1798
1772
  }
1799
1773
  }
1800
1774
  let buildVirtualFilesResult;
1801
1775
  function buildVirtualFiles() {
1802
- return buildVirtualFilesResult ?? (buildVirtualFilesResult = new Promise(async (resolve, reject) => {
1803
- try {
1804
- virtualFiles.clear();
1805
- routes = await buildRoutes({
1806
- walker: createFSWalker(resolvedRoutesDir),
1807
- importPrefix: routesDir
1808
- });
1809
- if (!routes.list.length) {
1810
- throw new Error("No routes generated");
1811
- }
1812
- for (const route of routes.list) {
1813
- virtualFiles.set(path4.posix.join(root, `${route.entryName}.js`), "");
1814
- }
1815
- if (routes.middleware.length) {
1816
- virtualFiles.set(path4.posix.join(root, MIDDLEWARE_FILENAME), "");
1817
- }
1818
- virtualFiles.set(path4.posix.join(root, ROUTER_FILENAME), "");
1819
- resolve(routes);
1820
- } catch (err) {
1821
- reject(err);
1776
+ return buildVirtualFilesResult ?? (buildVirtualFilesResult = (async () => {
1777
+ virtualFiles.clear();
1778
+ routes = await buildRoutes({
1779
+ walker: createFSWalker(resolvedRoutesDir),
1780
+ importPrefix: routesDir
1781
+ });
1782
+ if (!routes.list.length) {
1783
+ throw new Error("No routes generated");
1822
1784
  }
1823
- }));
1785
+ for (const route of routes.list) {
1786
+ virtualFiles.set(path4.posix.join(root, `${route.entryName}.js`), "");
1787
+ }
1788
+ if (routes.middleware.length) {
1789
+ virtualFiles.set(path4.posix.join(root, MIDDLEWARE_FILENAME), "");
1790
+ }
1791
+ virtualFiles.set(path4.posix.join(root, ROUTER_FILENAME), "");
1792
+ return routes;
1793
+ })());
1824
1794
  }
1825
1795
  let renderVirtualFilesResult;
1826
1796
  function renderVirtualFiles(context) {
1827
- return renderVirtualFilesResult ?? (renderVirtualFilesResult = new Promise(async (resolve) => {
1797
+ return renderVirtualFilesResult ?? (renderVirtualFilesResult = (async () => {
1828
1798
  var _a;
1829
1799
  try {
1830
1800
  const routes2 = await buildVirtualFiles();
1831
- if (fs4.existsSync(entryFilesDir)) {
1832
- fs4.rmSync(entryFilesDir, { recursive: true });
1801
+ if (fs3.existsSync(entryFilesDir)) {
1802
+ fs3.rmSync(entryFilesDir, { recursive: true });
1833
1803
  }
1834
1804
  for (const route of routes2.list) {
1835
- if (route.handler) {
1836
- const exports = await getExportsFromFile(
1837
- context,
1838
- route.handler.filePath
1839
- );
1840
- route.handler.verbs = [];
1805
+ const { handler, page, layouts } = route;
1806
+ if (handler) {
1807
+ const exports = await getExportsFromFile(context, handler.filePath);
1808
+ handler.verbs = [];
1841
1809
  for (const name of exports) {
1842
1810
  const verb = name.toLowerCase();
1843
1811
  if (name === verb.toUpperCase() && httpVerbs.includes(verb)) {
1844
- route.handler.verbs.push(verb);
1812
+ handler.verbs.push(verb);
1845
1813
  }
1846
1814
  }
1847
- if (!route.handler.verbs.length) {
1815
+ if (!handler.verbs.length) {
1848
1816
  context.warn(
1849
- `Did not find any http verb exports in handler '${path4.relative(root, route.handler.filePath)}' - expected ${httpVerbs.map((v) => v.toUpperCase()).join(", ")}`
1817
+ `Did not find any http verb exports in handler '${path4.relative(root, handler.filePath)}' - expected ${httpVerbs.map((v) => v.toUpperCase()).join(", ")}`
1850
1818
  );
1851
1819
  }
1852
1820
  }
1853
- if (route.page && route.layouts.length) {
1854
- const relativePath = path4.relative(resolvedRoutesDir, route.page.filePath);
1821
+ if (page && layouts.length) {
1822
+ const relativePath = path4.relative(
1823
+ resolvedRoutesDir,
1824
+ page.filePath
1825
+ );
1855
1826
  const routeFileDir = path4.join(entryFilesDir, relativePath, "..");
1856
- const routeFileRelativePathPosix = normalizePath(path4.relative(routeFileDir, root));
1857
- fs4.mkdirSync(routeFileDir, { recursive: true });
1858
- fs4.writeFileSync(
1859
- path4.join(routeFileDir, "route.marko"),
1860
- renderRouteTemplate(route, (to) => path4.posix.join(routeFileRelativePathPosix, to))
1827
+ const routeFileRelativePathPosix = normalizePath(
1828
+ path4.relative(routeFileDir, root)
1829
+ );
1830
+ fs3.mkdirSync(routeFileDir, { recursive: true });
1831
+ const pageNameIndex = page.name.indexOf("+page");
1832
+ const pageNamePrefix = pageNameIndex > 0 ? `${page.name.slice(0, pageNameIndex)}.` : "";
1833
+ fs3.writeFileSync(
1834
+ path4.join(routeFileDir, pageNamePrefix + "route.marko"),
1835
+ renderRouteTemplate(
1836
+ route,
1837
+ (to) => path4.posix.join(routeFileRelativePathPosix, to)
1838
+ )
1861
1839
  );
1862
1840
  }
1863
1841
  virtualFiles.set(
@@ -1866,14 +1844,23 @@ function markoRun(opts = {}) {
1866
1844
  );
1867
1845
  }
1868
1846
  for (const route of Object.values(routes2.special)) {
1869
- if (route.page && route.layouts.length) {
1870
- const relativePath = path4.relative(resolvedRoutesDir, route.page.filePath);
1847
+ const { page, layouts, key } = route;
1848
+ if (page && layouts.length) {
1849
+ const relativePath = path4.relative(
1850
+ resolvedRoutesDir,
1851
+ page.filePath
1852
+ );
1871
1853
  const routeFileDir = path4.join(entryFilesDir, relativePath, "..");
1872
- const routeFileRelativePathPosix = normalizePath(path4.relative(routeFileDir, root));
1873
- fs4.mkdirSync(routeFileDir, { recursive: true });
1874
- fs4.writeFileSync(
1875
- path4.join(routeFileDir, `route.${route.key}.marko`),
1876
- renderRouteTemplate(route, (to) => path4.posix.join(routeFileRelativePathPosix, to))
1854
+ const routeFileRelativePathPosix = normalizePath(
1855
+ path4.relative(routeFileDir, root)
1856
+ );
1857
+ fs3.mkdirSync(routeFileDir, { recursive: true });
1858
+ fs3.writeFileSync(
1859
+ path4.join(routeFileDir, `route.${key}.marko`),
1860
+ renderRouteTemplate(
1861
+ route,
1862
+ (to) => path4.posix.join(routeFileRelativePathPosix, to)
1863
+ )
1877
1864
  );
1878
1865
  }
1879
1866
  }
@@ -1919,8 +1906,7 @@ function markoRun(opts = {}) {
1919
1906
  `throw ${JSON.stringify(prepareError(err))}`
1920
1907
  );
1921
1908
  }
1922
- resolve();
1923
- }));
1909
+ })());
1924
1910
  }
1925
1911
  return [
1926
1912
  defaultConfigPlugin,
@@ -1965,7 +1951,9 @@ function markoRun(opts = {}) {
1965
1951
  createHash("shake256", { outputLength: 4 }).update(root).digest("hex")
1966
1952
  );
1967
1953
  entryFilesDirPosix = normalizePath(entryFilesDir);
1968
- relativeEntryFilesDirPosix = normalizePath(path4.relative(root, entryFilesDir));
1954
+ relativeEntryFilesDirPosix = normalizePath(
1955
+ path4.relative(root, entryFilesDir)
1956
+ );
1969
1957
  typesDir = path4.join(root, ".marko-run");
1970
1958
  devEntryFile = path4.join(root, "index.html");
1971
1959
  devEntryFilePosix = normalizePath(devEntryFile);
@@ -1986,7 +1974,7 @@ function markoRun(opts = {}) {
1986
1974
  entryFileNames(info) {
1987
1975
  let name = getEntryFileName(info.facadeModuleId);
1988
1976
  if (!name) {
1989
- for (let id of info.moduleIds) {
1977
+ for (const id of info.moduleIds) {
1990
1978
  name = getEntryFileName(id);
1991
1979
  if (name) {
1992
1980
  break;
@@ -2151,6 +2139,7 @@ function markoRun(opts = {}) {
2151
2139
  },
2152
2140
  async resolveId(importee, importer) {
2153
2141
  if (importee === "@marko/run/router") {
2142
+ console.log("plugin resolveId router");
2154
2143
  return path4.resolve(root, ROUTER_FILENAME);
2155
2144
  } else if (importee.endsWith(".marko") && importee.includes(relativeEntryFilesDirPosix)) {
2156
2145
  if (!importee.startsWith(root)) {
@@ -2265,15 +2254,15 @@ async function globFileExists(root, pattern) {
2265
2254
  return (await glob(pattern, { root })).length > 0;
2266
2255
  }
2267
2256
  async function ensureDir(dir) {
2268
- if (!fs4.existsSync(dir)) {
2269
- await fs4.promises.mkdir(dir, { recursive: true });
2257
+ if (!fs3.existsSync(dir)) {
2258
+ await fs3.promises.mkdir(dir, { recursive: true });
2270
2259
  }
2271
2260
  }
2272
2261
  async function getPackageData(dir) {
2273
2262
  do {
2274
2263
  const pkgPath = path4.join(dir, "package.json");
2275
- if (fs4.existsSync(pkgPath)) {
2276
- return JSON.parse(await fs4.promises.readFile(pkgPath, "utf-8"));
2264
+ if (fs3.existsSync(pkgPath)) {
2265
+ return JSON.parse(await fs3.promises.readFile(pkgPath, "utf-8"));
2277
2266
  }
2278
2267
  } while (dir !== (dir = path4.dirname(dir)));
2279
2268
  return null;
@@ -2291,7 +2280,10 @@ async function resolveAdapter(root, options, log) {
2291
2280
  for (const name of dependecies) {
2292
2281
  if (name.startsWith("@marko/run-adapter") || name.indexOf("marko-run-adapter") !== -1) {
2293
2282
  try {
2294
- const module2 = await import(name);
2283
+ const module2 = await import(
2284
+ /* @vite-ignore */
2285
+ name
2286
+ );
2295
2287
  log && debug(
2296
2288
  `Using adapter ${name} listed in your package.json dependecies`
2297
2289
  );
@@ -2303,7 +2295,10 @@ async function resolveAdapter(root, options, log) {
2303
2295
  }
2304
2296
  }
2305
2297
  const defaultAdapter = "@marko/run/adapter";
2306
- const module = await import(defaultAdapter);
2298
+ const module = await import(
2299
+ /* @vite-ignore */
2300
+ defaultAdapter
2301
+ );
2307
2302
  log && debug("Using default adapter");
2308
2303
  return module.default();
2309
2304
  }
@@ -2351,6 +2346,37 @@ var defaultConfigPlugin = {
2351
2346
  }
2352
2347
  };
2353
2348
 
2349
+ // src/vite/utils/server.ts
2350
+ import cp from "child_process";
2351
+ import cluster from "cluster";
2352
+ import { config, parse } from "dotenv";
2353
+ import fs4 from "fs";
2354
+ import net from "net";
2355
+ async function getConnection(port) {
2356
+ return new Promise((resolve) => {
2357
+ const connection = net.connect(port).setNoDelay(true).setKeepAlive(false).on("error", () => {
2358
+ connection.end();
2359
+ resolve(null);
2360
+ }).on("connect", () => {
2361
+ resolve(connection);
2362
+ });
2363
+ });
2364
+ }
2365
+ async function isPortInUse(port) {
2366
+ return Boolean(await getConnection(port));
2367
+ }
2368
+ async function getAvailablePort(port) {
2369
+ if (port && !await isPortInUse(port)) {
2370
+ return port;
2371
+ }
2372
+ return new Promise((resolve) => {
2373
+ const server = net.createServer().listen(0, () => {
2374
+ const { port: port2 } = server.address();
2375
+ server.close(() => resolve(port2));
2376
+ });
2377
+ });
2378
+ }
2379
+
2354
2380
  // src/cli/commands.ts
2355
2381
  var __dirname2 = path5.dirname(fileURLToPath2(import.meta.url));
2356
2382
  var defaultConfigFileBases = ["serve.config", "vite.config"];