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