@marko/run 0.6.6 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,9 +16,12 @@ import createDebug from "debug";
16
16
  import { resolveToEsbuildTarget } from "esbuild-plugin-browserslist";
17
17
  import fs3 from "fs";
18
18
  import { glob } from "glob";
19
- import path4 from "path";
19
+ import path6 from "path";
20
20
  import { fileURLToPath } from "url";
21
- import { buildErrorMessage, mergeConfig } from "vite";
21
+ import {
22
+ buildErrorMessage,
23
+ mergeConfig
24
+ } from "vite";
22
25
 
23
26
  // src/adapter/utils.ts
24
27
  import kleur from "kleur";
@@ -46,7 +49,7 @@ function prepareError(err) {
46
49
  }
47
50
 
48
51
  // src/vite/codegen/index.ts
49
- import path from "path";
52
+ import path2 from "path";
50
53
 
51
54
  // src/vite/constants.ts
52
55
  var markoRunFilePrefix = "__marko-run__";
@@ -71,6 +74,12 @@ var RoutableFileTypes = {
71
74
  Error: "500"
72
75
  };
73
76
 
77
+ // src/vite/utils/fs.ts
78
+ import path from "path";
79
+ var POSIX_SEP = "/";
80
+ var WINDOWS_SEP = "\\";
81
+ var normalizePath = path.sep === WINDOWS_SEP ? (id) => id.replace(/\\/g, POSIX_SEP) : (id) => id;
82
+
74
83
  // src/vite/utils/route.ts
75
84
  var httpVerbOrder = httpVerbs.reduce(
76
85
  (order, verb, index) => {
@@ -94,6 +103,9 @@ function hasVerb(route, verb) {
94
103
  var _a, _b;
95
104
  return verb === "get" && !!route.page || ((_b = (_a = route.handler) == null ? void 0 : _a.verbs) == null ? void 0 : _b.includes(verb)) || verb === "head" && hasVerb(route, "get");
96
105
  }
106
+ function getRouteVirtualFileName(route) {
107
+ return `${markoRunFilePrefix}${route.key.replace(/\//g, ".")}.js`;
108
+ }
97
109
 
98
110
  // src/vite/codegen/writer.ts
99
111
  function createWriter(sink, options) {
@@ -236,27 +248,34 @@ function createStringWriter(opts) {
236
248
  }
237
249
 
238
250
  // src/vite/codegen/index.ts
239
- function renderRouteTemplate(route, getRelativePath) {
251
+ function normalizedRelativePath(from, to) {
252
+ const relativePath = normalizePath(path2.relative(from, to));
253
+ return relativePath.startsWith(".") ? relativePath : "./" + relativePath;
254
+ }
255
+ function renderRouteTemplate(route, rootDir) {
240
256
  if (!route.page) {
241
257
  throw new Error(`Route ${route.key} has no page to render`);
242
258
  }
259
+ if (!route.templateFilePath) {
260
+ throw new Error(`Route ${route.key} has no template file path`);
261
+ }
243
262
  return renderEntryTemplate(
244
- route.entryName,
263
+ normalizedRelativePath(rootDir, route.templateFilePath),
245
264
  [...route.layouts, route.page].map(
246
- (file) => getRelativePath(file.importPath)
265
+ (file) => normalizedRelativePath(
266
+ path2.dirname(route.templateFilePath),
267
+ file.filePath
268
+ )
247
269
  ),
248
270
  route.key === RoutableFileTypes.Error ? ["error"] : []
249
271
  );
250
272
  }
251
273
  function renderEntryTemplate(name, files, pageInputs = []) {
252
- if (!name) {
253
- throw new Error(`Invalid argument - 'name' cannot be empty`);
254
- }
255
274
  if (!files.length) {
256
275
  throw new Error(`Invalid argument - 'files' cannot be empty`);
257
276
  }
258
277
  const writer = createStringWriter();
259
- writer.writeLines(`// ${name}.marko`);
278
+ writer.writeLines(`// ${name}`);
260
279
  writer.branch("imports");
261
280
  writer.writeLines("");
262
281
  writeEntryTemplateTag(writer, files, pageInputs);
@@ -266,7 +285,7 @@ function writeEntryTemplateTag(writer, [file, ...rest], pageInputs, index = 1) {
266
285
  if (file) {
267
286
  const isLast = !rest.length;
268
287
  const tag = isLast ? "Page" : `Layout${index}`;
269
- writer.branch("imports").writeLines(`import ${tag} from '${file}';`);
288
+ writer.branch("imports").writeLines(`import ${tag} from "${file}";`);
270
289
  if (isLast) {
271
290
  const attributes = pageInputs.length ? " " + pageInputs.map((name) => `${name}=input.${name}`).join(" ") : "";
272
291
  writer.writeLines(`<${tag}${attributes}/>`);
@@ -277,9 +296,9 @@ function writeEntryTemplateTag(writer, [file, ...rest], pageInputs, index = 1) {
277
296
  }
278
297
  }
279
298
  }
280
- function renderRouteEntry(route, entriesDir) {
299
+ function renderRouteEntry(route, rootDir) {
281
300
  var _a;
282
- const { key, index, handler, page, middleware, meta, entryName } = route;
301
+ const { key, index, handler, page, middleware, meta } = route;
283
302
  const verbs = getVerbs(route);
284
303
  if (!verbs) {
285
304
  throw new Error(
@@ -287,7 +306,7 @@ function renderRouteEntry(route, entriesDir) {
287
306
  );
288
307
  }
289
308
  const writer = createStringWriter();
290
- writer.writeLines(`// ${virtualFilePrefix}/${entryName}.js`);
309
+ writer.writeLines(`// ${virtualFilePrefix}${getRouteVirtualFileName(route)}`);
291
310
  const imports = writer.branch("imports");
292
311
  const runtimeImports = [];
293
312
  if (handler) {
@@ -299,9 +318,6 @@ function renderRouteEntry(route, entriesDir) {
299
318
  if (!page || verbs.some((verb) => verb !== "get" && verb !== "head")) {
300
319
  runtimeImports.push("noContent");
301
320
  }
302
- if (page) {
303
- runtimeImports.push("pageResponse");
304
- }
305
321
  if (verbs.includes("head")) {
306
322
  runtimeImports.push("stripResponseBody");
307
323
  }
@@ -309,7 +325,7 @@ function renderRouteEntry(route, entriesDir) {
309
325
  imports.writeLines(
310
326
  `import { ${runtimeImports.join(
311
327
  ", "
312
- )} } from '${virtualFilePrefix}/runtime/internal';`
328
+ )} } from "${virtualFilePrefix}/runtime/internal";`
313
329
  );
314
330
  }
315
331
  if (middleware.length) {
@@ -317,7 +333,7 @@ function renderRouteEntry(route, entriesDir) {
317
333
  imports.writeLines(
318
334
  `import { ${names.join(
319
335
  ", "
320
- )} } from '${virtualFilePrefix}/${markoRunFilePrefix}middleware.js';`
336
+ )} } from "${virtualFilePrefix}/${markoRunFilePrefix}middleware.js";`
321
337
  );
322
338
  }
323
339
  if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.length) {
@@ -329,18 +345,17 @@ function renderRouteEntry(route, entriesDir) {
329
345
  writer.writeLines(`const ${verb}Handler = normalize(${importName});`);
330
346
  }
331
347
  imports.writeLines(
332
- `import { ${names.join(", ")} } from './${handler.importPath}';`
348
+ `import { ${names.join(", ")} } from "${normalizedRelativePath(rootDir, handler.filePath)}";`
333
349
  );
334
350
  }
335
351
  if (page) {
336
- const pageNameIndex = page.name.indexOf("+page");
337
- const pageNamePrefix = pageNameIndex > 0 ? `${page.name.slice(0, pageNameIndex)}.` : "";
338
- const importPath = route.layouts.length ? `./${path.posix.join(entriesDir, page.relativePath, "..", pageNamePrefix + "route.marko")}` : `./${page.importPath}`;
339
- imports.writeLines(`import page from '${importPath}${serverEntryQuery}';`);
352
+ imports.writeLines(
353
+ `import page from "${normalizedRelativePath(rootDir, route.templateFilePath || page.filePath)}${serverEntryQuery}";`
354
+ );
340
355
  }
341
356
  if (meta) {
342
357
  imports.writeLines(
343
- `export { default as meta${index} } from './${meta.importPath}';`
358
+ `export { default as meta${index} } from "${normalizedRelativePath(rootDir, meta.filePath)}";`
344
359
  );
345
360
  }
346
361
  for (const verb of verbs) {
@@ -357,9 +372,7 @@ function writeRouteEntryHandler(writer, route, verb) {
357
372
  let hasBody = false;
358
373
  writer.writeLines("");
359
374
  if (page && (verb === "get" || verb === "head")) {
360
- writer.writeBlockStart(
361
- `export function ${verb}${index}(context, buildInput) {`
362
- );
375
+ writer.writeBlockStart(`export function ${verb}${index}(context) {`);
363
376
  } else {
364
377
  writer.writeBlockStart(`export function ${verb}${index}(context) {`);
365
378
  }
@@ -369,7 +382,7 @@ function writeRouteEntryHandler(writer, route, verb) {
369
382
  if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes(verb)) {
370
383
  const name = `${verb}Handler`;
371
384
  continuations.writeLines(
372
- `const ${currentName} = () => pageResponse(page, buildInput());`
385
+ `const ${currentName} = () => context.render(page, {});`
373
386
  );
374
387
  if (len) {
375
388
  nextName = currentName;
@@ -388,17 +401,15 @@ function writeRouteEntryHandler(writer, route, verb) {
388
401
  hasBody = true;
389
402
  }
390
403
  } else if (verb === "head") {
391
- writer.writeLines(
392
- `return stripResponseBody(get${index}(context, buildInput));`
393
- );
404
+ writer.writeLines(`return stripResponseBody(get${index}(context));`);
394
405
  hasBody = true;
395
406
  } else if (len) {
396
407
  continuations.writeLines(
397
- `const ${currentName} = () => pageResponse(page, buildInput());`
408
+ `const ${currentName} = () => context.render(page, {});`
398
409
  );
399
410
  nextName = currentName;
400
411
  } else {
401
- writer.writeLines(`return pageResponse(page, buildInput());`);
412
+ writer.writeLines(`return context.render(page, {});`);
402
413
  hasBody = true;
403
414
  }
404
415
  } else if ((_b = handler == null ? void 0 : handler.verbs) == null ? void 0 : _b.includes(verb)) {
@@ -448,7 +459,7 @@ function writeRouteEntryHandler(writer, route, verb) {
448
459
  continuations.join();
449
460
  writer.writeBlockEnd("}");
450
461
  }
451
- function renderRouter(routes, entriesDir, options = {
462
+ function renderRouter(routes, rootDir, options = {
452
463
  trailingSlashes: "RedirectWithout"
453
464
  }) {
454
465
  const writer = createStringWriter();
@@ -457,20 +468,19 @@ function renderRouter(routes, entriesDir, options = {
457
468
  writer.writeLines(`// @marko/run/router`);
458
469
  const imports = writer.branch("imports");
459
470
  imports.writeLines(
460
- `import { NotHandled, NotMatched, createContext${hasErrorPage || hasNotFoundPage ? ", pageResponse" : ""} } from '${virtualFilePrefix}/runtime/internal';`
471
+ `import { NotHandled, NotMatched, createContext } from "${virtualFilePrefix}/runtime/internal";`
461
472
  );
462
473
  for (const route of routes.list) {
463
474
  const verbs = getVerbs(route);
464
475
  const names = verbs.map((verb) => `${verb}${route.index}`);
465
476
  route.meta && names.push(`meta${route.index}`);
466
477
  imports.writeLines(
467
- `import { ${names.join(", ")} } from '${virtualFilePrefix}/${route.entryName}.js';`
478
+ `import { ${names.join(", ")} } from "${virtualFilePrefix}/${getRouteVirtualFileName(route)}";`
468
479
  );
469
480
  }
470
481
  for (const route of Object.values(routes.special)) {
471
- const importPath = route.layouts.length ? `./${path.posix.join(entriesDir, route.page.relativePath, "..", `route.${route.key}.marko`)}` : `./${route.page.importPath}`;
472
482
  imports.writeLines(
473
- `import page${route.key} from '${importPath}${serverEntryQuery}';`
483
+ `import page${route.key} from "${normalizedRelativePath(rootDir, route.templateFilePath || route.page.filePath)}${serverEntryQuery}";`
474
484
  );
475
485
  }
476
486
  writer.writeLines(
@@ -478,11 +488,12 @@ function renderRouter(routes, entriesDir, options = {
478
488
  globalThis.__marko_run__ = { match, fetch, invoke };
479
489
  `
480
490
  ).writeBlockStart(`export function match(method, pathname) {`).writeLines(
481
- `if (!pathname) {
482
- pathname = '/';
483
- } else if (pathname.charAt(0) !== '/') {
484
- pathname = '/' + pathname;
485
- }`
491
+ `const last = pathname.length - 1;
492
+ return match_internal(method, last && pathname.charAt(last) === '/' ? pathname.slice(0, last) : pathname)
493
+ };
494
+
495
+ function match_internal(method, pathname) {
496
+ const len = pathname.length;`
486
497
  ).writeBlockStart(`switch (method) {`);
487
498
  for (const verb of httpVerbs) {
488
499
  const filteredRoutes = routes.list.filter((route) => hasVerb(route, verb));
@@ -498,13 +509,13 @@ globalThis.__marko_run__ = { match, fetch, invoke };
498
509
  writer.writeLines("").writeBlockStart(
499
510
  "export async function invoke(route, request, platform, url) {"
500
511
  ).writeLines(
501
- "const [context, buildInput] = createContext(route, request, platform, url);"
512
+ "const context = createContext(route, request, platform, url);"
502
513
  );
503
514
  if (hasErrorPage) {
504
515
  writer.writeBlockStart("try {");
505
516
  }
506
517
  writer.writeBlockStart("if (route) {").writeBlockStart("try {").writeLines(
507
- "const response = await route.handler(context, buildInput);",
518
+ "const response = await route.handler(context);",
508
519
  "if (response) return response;"
509
520
  ).indent--;
510
521
  writer.writeBlockStart("} catch (error) {").writeLines(
@@ -521,14 +532,16 @@ const page404ResponseInit = {
521
532
  );
522
533
  writer.write(`
523
534
  if (context.request.headers.get('Accept')?.includes('text/html')) {
524
- return pageResponse(page404, buildInput(), page404ResponseInit);
535
+ return context.render(page404, {}, page404ResponseInit);
525
536
  }`);
526
537
  }
527
538
  writer.indent--;
528
- writer.writeLines(`
539
+ if (routes.list.length) {
540
+ writer.writeLines(`
529
541
  return new Response(null, {
530
542
  status: 404,
531
543
  });`);
544
+ }
532
545
  if (hasErrorPage) {
533
546
  imports.writeLines(`
534
547
  const page500ResponseInit = {
@@ -538,7 +551,7 @@ const page500ResponseInit = {
538
551
  writer.writeBlockStart(`} catch (error) {`).writeBlockStart(
539
552
  `if (context.request.headers.get('Accept')?.includes('text/html')) {`
540
553
  ).writeLines(
541
- `return pageResponse(page500, buildInput({ error }), page500ResponseInit);`
554
+ `return context.render(page500, { error }, page500ResponseInit);`
542
555
  ).writeBlockEnd("}").writeLines("throw error;").writeBlockEnd("}");
543
556
  }
544
557
  writer.writeBlockEnd("}");
@@ -550,38 +563,40 @@ function renderFetch(writer, options) {
550
563
  export async function fetch(request, platform) {
551
564
  try {
552
565
  const url = new URL(request.url);
553
- let { pathname } = url;`);
566
+ const { pathname } = url;
567
+ const last = pathname.length - 1;
568
+ const hasTrailingSlash = last && pathname.charAt(last) === '/';
569
+ const normalizedPathname = hasTrailingSlash ? pathname.slice(0, last) : pathname;
570
+ const route = match_internal(request.method, normalizedPathname);`);
554
571
  switch (options.trailingSlashes) {
555
572
  case "RedirectWithout":
556
573
  writer.write(`
557
- if (pathname !== '/' && pathname.endsWith('/')) {
558
- url.pathname = pathname.slice(0, -1);
574
+ if (route && hasTrailingSlash) {
575
+ url.pathname = normalizedPathname
559
576
  return Response.redirect(url);
560
577
  }`);
561
578
  break;
562
579
  case "RedirectWith":
563
580
  writer.write(`
564
- if (pathname !== '/' && !pathname.endsWith('/')) {
565
- url.pathname = pathname + '/';
581
+ if (route && pathname !== '/' && !hasTrailingSlash) {
582
+ url.pathname += '/';
566
583
  return Response.redirect(url);
567
584
  }`);
568
585
  break;
569
586
  case "RewriteWithout":
570
587
  writer.write(`
571
- if (pathname !== '/' && pathname.endsWith('/')) {
572
- url.pathname = pathname = pathname.slice(0, -1);
588
+ if (route && hasTrailingSlash) {
589
+ url.pathname = normalizedPathname;
573
590
  }`);
574
591
  break;
575
592
  case "RewriteWith":
576
593
  writer.write(`
577
- if (pathname !== '/' && !pathname.endsWith('/')) {
578
- url.pathname = pathname = pathname + '/';
594
+ if (route && pathname !== '/' && !hasTrailingSlash) {
595
+ url.pathname += '/';
579
596
  }`);
580
597
  break;
581
598
  }
582
599
  writer.write(`
583
-
584
- const route = match(request.method, pathname);
585
600
  return await invoke(route, request, platform, url);
586
601
  } catch (error) {
587
602
  if (import.meta.env.DEV) {
@@ -597,10 +612,9 @@ function writeRouterVerb(writer, trie, verb, level = 0, offset = 1) {
597
612
  const { route, dynamic, catchAll } = trie;
598
613
  let closeCount = 0;
599
614
  if (level === 0) {
600
- writer.writeLines(`const len = pathname.length;`);
601
615
  if (route) {
602
616
  writer.writeLines(
603
- `if (len === 1) return ${renderMatch(verb, route, trie.path)}; // ${trie.path.path}`
617
+ `if (len === 1) return ${renderMatch(verb, route, trie.path)};`
604
618
  );
605
619
  } else if (trie.static || dynamic) {
606
620
  writer.writeBlockStart(`if (len > 1) {`);
@@ -641,17 +655,15 @@ function writeRouterVerb(writer, trie, verb, level = 0, offset = 1) {
641
655
  if (useSwitch) {
642
656
  writer.writeBlockStart(`switch (${value}) {`);
643
657
  }
644
- for (const { key, path: path5, route: route2 } of terminal) {
658
+ for (const { key, path: path7, route: route2 } of terminal) {
645
659
  const decodedKey = decodeURIComponent(key);
646
660
  if (useSwitch) {
647
661
  writer.write(`case '${decodedKey}': `, true);
648
662
  } else {
649
663
  writer.write(`if (${value} === '${decodedKey}') `, true);
650
664
  }
651
- writer.write(
652
- `return ${renderMatch(verb, route2, path5)}; // ${path5.path}
653
- `
654
- );
665
+ writer.write(`return ${renderMatch(verb, route2, path7)};
666
+ `);
655
667
  }
656
668
  if (useSwitch) {
657
669
  writer.writeBlockEnd("}");
@@ -663,7 +675,7 @@ function writeRouterVerb(writer, trie, verb, level = 0, offset = 1) {
663
675
  verb,
664
676
  dynamic.route,
665
677
  dynamic.path
666
- )}; // ${dynamic.path.path}`
678
+ )};`
667
679
  );
668
680
  }
669
681
  }
@@ -723,7 +735,7 @@ function writeRouterVerb(writer, trie, verb, level = 0, offset = 1) {
723
735
  catchAll.route,
724
736
  catchAll.path,
725
737
  String(offset)
726
- )}; // ${catchAll.path.path}`
738
+ )};`
727
739
  );
728
740
  } else if (level === 0) {
729
741
  writer.writeLines("return null;");
@@ -752,43 +764,47 @@ function renderParams(params, pathIndex) {
752
764
  }
753
765
  return result ? result + " }" : "{}";
754
766
  }
755
- function renderMatch(verb, route, path5, pathIndex) {
767
+ function renderMatch(verb, route, path7, pathIndex) {
756
768
  const handler = `${verb}${route.index}`;
757
- const params = path5.params ? renderParams(path5.params, pathIndex) : "{}";
769
+ const params = path7.params ? renderParams(path7.params, pathIndex) : "{}";
758
770
  const meta = route.meta ? `meta${route.index}` : "{}";
759
- const pathPattern = pathToURLPatternString(path5.path);
760
- return `{ handler: ${handler}, params: ${params}, meta: ${meta}, path: '${pathPattern}' }`;
771
+ return `{ handler: ${handler}, params: ${params}, meta: ${meta}, path: '${path7.path}' }`;
761
772
  }
762
- function renderMiddleware(middleware) {
773
+ function renderMiddleware(middleware, rootDir) {
763
774
  const writer = createStringWriter();
764
775
  writer.writeLines(
765
776
  `// ${virtualFilePrefix}/${markoRunFilePrefix}middleware.js`
766
777
  );
767
778
  const imports = writer.branch("imports");
768
779
  imports.writeLines(
769
- `import { normalize } from '${virtualFilePrefix}/runtime/internal';`
780
+ `import { normalize } from "${virtualFilePrefix}/runtime/internal";`
770
781
  );
771
782
  writer.writeLines("");
772
- for (const { id, importPath } of middleware) {
783
+ for (const { id, filePath } of middleware) {
773
784
  const importName = `middleware${id}`;
774
- imports.writeLines(`import ${importName} from './${importPath}';`);
785
+ imports.writeLines(
786
+ `import ${importName} from "${normalizedRelativePath(rootDir, filePath)}";`
787
+ );
775
788
  writer.writeLines(`export const mware${id} = normalize(${importName});`);
776
789
  }
777
790
  imports.join();
778
791
  return writer.end();
779
792
  }
780
- function stripTsExtension(path5) {
781
- const index = path5.lastIndexOf(".");
793
+ function stripTsExtension(path7) {
794
+ const index = path7.lastIndexOf(".");
782
795
  if (index !== -1) {
783
- const ext = path5.slice(index + 1);
796
+ const ext = path7.slice(index + 1);
784
797
  if (ext.toLowerCase() === "ts") {
785
- return path5.slice(0, index);
798
+ return path7.slice(0, index);
786
799
  }
787
800
  }
788
- return path5;
801
+ return path7;
789
802
  }
790
- async function renderRouteTypeInfo(routes, pathPrefix = ".", adapter) {
791
- var _a, _b;
803
+ function decodePath(path7) {
804
+ return path7;
805
+ }
806
+ async function renderRouteTypeInfo(routes, outDir, adapter) {
807
+ var _a, _b, _c, _d;
792
808
  const writer = createStringWriter();
793
809
  writer.writeLines(
794
810
  `/*
@@ -817,11 +833,31 @@ async function renderRouteTypeInfo(routes, pathPrefix = ".", adapter) {
817
833
  const routeTypes = /* @__PURE__ */ new Map();
818
834
  for (const route of routes.list) {
819
835
  let routeType = "";
820
- for (const path5 of route.paths) {
821
- const pathType = `"${pathToURLPatternString(path5.path)}"`;
822
- routeType += routeType ? " | " + pathType : pathType;
823
- routesWriter.writeLines(`${pathType}: Routes["${route.key}"];`);
836
+ let routeDefinition = "";
837
+ if (route.page || route.handler) {
838
+ const verbs = [];
839
+ if (route.page || ((_b = (_a = route.handler) == null ? void 0 : _a.verbs) == null ? void 0 : _b.includes("get"))) {
840
+ verbs.push(`"get"`);
841
+ }
842
+ if ((_d = (_c = route.handler) == null ? void 0 : _c.verbs) == null ? void 0 : _d.includes("post")) {
843
+ verbs.push(`"post"`);
844
+ }
845
+ routeDefinition = `{ verb: ${verbs.join(" | ")};`;
846
+ if (route.meta) {
847
+ const metaPath = stripTsExtension(
848
+ normalizedRelativePath(outDir, route.meta.filePath)
849
+ );
850
+ let metaType = `typeof import("${metaPath}")`;
851
+ if (/\.(ts|js|mjs)$/.test(route.meta.name)) {
852
+ metaType += `["default"]`;
853
+ }
854
+ routeDefinition += ` meta: ${metaType};`;
855
+ }
856
+ routeDefinition += " }";
824
857
  }
858
+ const pathType = `"${decodePath(route.path.path)}"`;
859
+ routeType += routeType ? " | " + pathType : pathType;
860
+ routesWriter.writeLines(`${pathType}: ${routeDefinition};`);
825
861
  for (const file of [route.handler, route.page]) {
826
862
  if (file) {
827
863
  const existing = routeTypes.get(file);
@@ -854,31 +890,33 @@ async function renderRouteTypeInfo(routes, pathPrefix = ".", adapter) {
854
890
  const pageWriter = writer.branch("page");
855
891
  const layoutWriter = writer.branch("layout");
856
892
  for (const [file, types] of routeTypes) {
857
- const path5 = `${pathPrefix}/${file.relativePath}`;
893
+ const modulePath = stripTsExtension(
894
+ normalizedRelativePath(outDir, file.filePath)
895
+ );
858
896
  const routeType = `Run.Routes[${types.join(" | ")}]`;
859
897
  switch (file.type) {
860
898
  case RoutableFileTypes.Handler:
861
- writeModuleDeclaration(handlerWriter, path5, routeType);
899
+ writeModuleDeclaration(handlerWriter, modulePath, routeType);
862
900
  break;
863
901
  case RoutableFileTypes.Middleware:
864
- writeModuleDeclaration(middlewareWriter, path5, routeType);
902
+ writeModuleDeclaration(middlewareWriter, modulePath, routeType);
865
903
  break;
866
904
  case RoutableFileTypes.Page:
867
- writeModuleDeclaration(pageWriter, path5, routeType);
905
+ writeModuleDeclaration(pageWriter, modulePath, routeType);
868
906
  break;
869
907
  case RoutableFileTypes.Layout:
870
908
  writeModuleDeclaration(
871
909
  layoutWriter,
872
- path5,
910
+ modulePath,
873
911
  routeType,
874
912
  `
875
- export interface Input extends Run.LayoutInput<typeof import('${path5}')> {}`
913
+ export interface Input extends Run.LayoutInput<typeof import("${modulePath}")> {}`
876
914
  );
877
915
  break;
878
916
  case RoutableFileTypes.Error:
879
917
  writeModuleDeclaration(
880
918
  writer,
881
- path5,
919
+ modulePath,
882
920
  "globalThis.MarkoRun.Route",
883
921
  `
884
922
  export interface Input {
@@ -887,7 +925,7 @@ async function renderRouteTypeInfo(routes, pathPrefix = ".", adapter) {
887
925
  );
888
926
  break;
889
927
  case RoutableFileTypes.NotFound:
890
- writeModuleDeclaration(writer, path5, "Run.Route");
928
+ writeModuleDeclaration(writer, modulePath, "Run.Route");
891
929
  break;
892
930
  }
893
931
  }
@@ -895,40 +933,15 @@ async function renderRouteTypeInfo(routes, pathPrefix = ".", adapter) {
895
933
  middlewareWriter.join();
896
934
  pageWriter.join();
897
935
  layoutWriter.join();
898
- writer.writeBlockStart(`
899
- type Routes = {`);
900
- for (const route of routes.list) {
901
- const { meta, handler, page } = route;
902
- if (page || handler) {
903
- const verbs = [];
904
- if (page || ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes("get"))) {
905
- verbs.push(`"get"`);
906
- }
907
- if ((_b = handler == null ? void 0 : handler.verbs) == null ? void 0 : _b.includes("post")) {
908
- verbs.push(`"post"`);
909
- }
910
- let routeType = `{ verb: ${verbs.join(" | ")};`;
911
- if (meta) {
912
- const metaPath = stripTsExtension(`${pathPrefix}/${meta.relativePath}`);
913
- let metaType = `typeof import("${metaPath}")`;
914
- if (/\.(ts|js|mjs)$/.test(meta.name)) {
915
- metaType += `["default"]`;
916
- }
917
- routeType += ` meta: ${metaType};`;
918
- }
919
- writer.writeLines(`"${route.key}": ${routeType} };`);
920
- }
921
- }
922
- writer.writeBlockEnd("}");
923
936
  return writer.end();
924
937
  }
925
- function writeModuleDeclaration(writer, path5, routeType, moduleTypes) {
926
- writer.writeLines("").write(`declare module "${stripTsExtension(path5)}" {`);
938
+ function writeModuleDeclaration(writer, name, routeType, moduleTypes) {
939
+ writer.writeLines("").write(`declare module "${name}" {`);
927
940
  if (moduleTypes) {
928
941
  writer.write(moduleTypes);
929
942
  }
930
943
  if (routeType) {
931
- const isMarko = path5.endsWith(".marko");
944
+ const isMarko = name.endsWith(".marko");
932
945
  writer.write(`
933
946
  namespace MarkoRun {
934
947
  export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
@@ -942,21 +955,15 @@ function writeModuleDeclaration(writer, path5, routeType, moduleTypes) {
942
955
  writer.writeLines(`
943
956
  }`);
944
957
  }
945
- function pathToURLPatternString(path5) {
946
- return path5.replace(/\/\$(\$?)([^/]*)/g, (_, catchAll, name) => {
947
- name = decodeURIComponent(name);
948
- return catchAll ? `/:${name || "rest"}*` : `/:${name}`;
949
- });
950
- }
951
958
  function createRouteTrie(routes) {
952
959
  const root = {
953
960
  key: ""
954
961
  };
955
- function insert(path5, route) {
962
+ function insert(path7, route) {
956
963
  let node = root;
957
- for (const segment of path5.segments) {
964
+ for (const segment of path7.segments) {
958
965
  if (segment === "$$") {
959
- node.catchAll ?? (node.catchAll = { route, path: path5 });
966
+ node.catchAll ?? (node.catchAll = { route, path: path7 });
960
967
  return;
961
968
  } else if (segment === "$") {
962
969
  node = node.dynamic ?? (node.dynamic = {
@@ -974,17 +981,18 @@ function createRouteTrie(routes) {
974
981
  node = next;
975
982
  }
976
983
  }
977
- node.path ?? (node.path = path5);
984
+ node.path ?? (node.path = path7);
978
985
  node.route ?? (node.route = route);
979
986
  }
980
987
  for (const route of routes) {
981
- for (const path5 of route.paths) {
982
- insert(path5, route);
983
- }
988
+ insert(route.path, route);
984
989
  }
985
990
  return root;
986
991
  }
987
992
 
993
+ // src/vite/routes/builder.ts
994
+ import path3 from "path";
995
+
988
996
  // src/vite/routes/parse.ts
989
997
  function parseFlatRoute(pattern) {
990
998
  if (!pattern) throw new Error("Empty pattern");
@@ -999,53 +1007,72 @@ function parseFlatRoute(pattern) {
999
1007
  ]);
1000
1008
  function parse2(basePaths, group) {
1001
1009
  const pathMap = /* @__PURE__ */ new Map();
1002
- const delimiters = group ? ").," : ".,";
1010
+ const delimiters = group ? "`).," : "`.,";
1003
1011
  let charCode;
1004
1012
  let segmentStart = i;
1005
1013
  let type;
1006
1014
  let current;
1015
+ let escaped = "";
1016
+ let escapeStart = 0;
1007
1017
  do {
1008
1018
  charCode = pattern.charCodeAt(i);
1009
- if (charCode === 41 && group) {
1019
+ if (charCode === 96 /* Escape */) {
1020
+ if (escapeStart) {
1021
+ escaped += pattern.slice(segmentStart, escapeStart - 1) + pattern.slice(escapeStart, i);
1022
+ escapeStart = 0;
1023
+ segmentStart = ++i;
1024
+ } else {
1025
+ escapeStart = i + 1;
1026
+ i = pattern.indexOf("`", escapeStart);
1027
+ if (i < 0) break;
1028
+ }
1029
+ } else if (charCode === 41 /* GroupEnd */ && group) {
1010
1030
  break;
1011
- } else if (charCode === 44) {
1031
+ } else if (charCode === 44 /* Alternate */) {
1012
1032
  if (!current) {
1013
1033
  segmentEnd(
1014
- basePaths.map((path5) => ({
1015
- ...path5,
1016
- segments: path5.segments.slice()
1034
+ basePaths.map((path7) => ({
1035
+ ...path7,
1036
+ segments: path7.segments.slice()
1017
1037
  })),
1018
- "",
1038
+ escaped,
1019
1039
  "_",
1020
1040
  pathMap
1021
1041
  );
1022
1042
  } else {
1023
- segmentEnd(current, pattern.slice(segmentStart, i), type, pathMap);
1043
+ segmentEnd(
1044
+ current,
1045
+ escaped + pattern.slice(segmentStart, i),
1046
+ type,
1047
+ pathMap
1048
+ );
1024
1049
  }
1025
1050
  current = void 0;
1026
1051
  type = void 0;
1052
+ escaped = "";
1027
1053
  segmentStart = ++i;
1028
- } else if (charCode === 46) {
1054
+ } else if (charCode === 46 /* Directory */) {
1029
1055
  if (current) {
1030
- segmentEnd(current, pattern.slice(segmentStart, i), type);
1056
+ segmentEnd(current, escaped + pattern.slice(segmentStart, i), type);
1031
1057
  }
1032
1058
  type = void 0;
1059
+ escaped = "";
1033
1060
  segmentStart = ++i;
1034
- } else if (charCode === 40) {
1061
+ } else if (charCode === 40 /* GroupStart */) {
1035
1062
  const groupPaths = parse2(current || basePaths, ++i);
1036
1063
  if (groupPaths.length) {
1037
1064
  current = groupPaths;
1038
1065
  }
1039
1066
  segmentStart = ++i;
1040
1067
  } else {
1041
- if (charCode === 95) {
1068
+ if (charCode === 95 /* Pathless */) {
1042
1069
  type = "_";
1043
- } else if (charCode === 36) {
1070
+ } else if (charCode === 36 /* Dynamic */) {
1044
1071
  type = pattern.charCodeAt(i + 1) === 36 ? "$$" : "$";
1045
1072
  }
1046
- current ?? (current = basePaths.map((path5) => ({
1047
- ...path5,
1048
- segments: path5.segments.slice()
1073
+ current ?? (current = basePaths.map((path7) => ({
1074
+ ...path7,
1075
+ segments: path7.segments.slice()
1049
1076
  })));
1050
1077
  i = len;
1051
1078
  for (const char of delimiters) {
@@ -1056,7 +1083,12 @@ function parseFlatRoute(pattern) {
1056
1083
  }
1057
1084
  }
1058
1085
  } while (i < len);
1059
- if (group && charCode !== 41) {
1086
+ if (escapeStart) {
1087
+ throw new Error(
1088
+ `Invalid route pattern: unclosed escape '${pattern.slice(escapeStart)}' in '${pattern}'`
1089
+ );
1090
+ }
1091
+ if (group && charCode !== 41 /* GroupEnd */) {
1060
1092
  throw new Error(
1061
1093
  `Invalid route pattern: group was not closed '${pattern.slice(
1062
1094
  group
@@ -1065,16 +1097,21 @@ function parseFlatRoute(pattern) {
1065
1097
  }
1066
1098
  if (!current) {
1067
1099
  segmentEnd(
1068
- basePaths.map((path5) => ({
1069
- ...path5,
1070
- segments: path5.segments.slice()
1100
+ basePaths.map((path7) => ({
1101
+ ...path7,
1102
+ segments: path7.segments.slice()
1071
1103
  })),
1072
- "",
1073
- "_",
1104
+ escaped,
1105
+ void 0,
1074
1106
  pathMap
1075
1107
  );
1076
1108
  } else {
1077
- segmentEnd(current, pattern.slice(segmentStart, i), type, pathMap);
1109
+ segmentEnd(
1110
+ current,
1111
+ escaped + pattern.slice(segmentStart, i),
1112
+ type,
1113
+ pathMap
1114
+ );
1078
1115
  }
1079
1116
  return [...pathMap.values()];
1080
1117
  }
@@ -1083,17 +1120,17 @@ function parseFlatRoute(pattern) {
1083
1120
  if (raw) {
1084
1121
  segment = {
1085
1122
  raw,
1086
- name: raw,
1123
+ name: normalizeSegment(raw),
1087
1124
  type
1088
1125
  };
1089
1126
  if (type === "$" || type === "$$") {
1090
1127
  segment.name = type;
1091
- segment.param = raw.slice(type.length);
1128
+ segment.param = normalizeParam(raw.slice(type.length));
1092
1129
  }
1093
1130
  }
1094
- for (const path5 of paths) {
1131
+ for (const path7 of paths) {
1095
1132
  if (segment) {
1096
- if (path5.isCatchall) {
1133
+ if (path7.isCatchall) {
1097
1134
  throw new Error(
1098
1135
  `Invalid route pattern: nested segments are not allowed after a catch-all parameter. Found '.' following '${pattern.slice(
1099
1136
  0,
@@ -1101,26 +1138,36 @@ function parseFlatRoute(pattern) {
1101
1138
  )}' in '${pattern}'.`
1102
1139
  );
1103
1140
  }
1104
- path5.segments.push(segment);
1105
- path5.id += path5.id === "/" ? segment.name : `/${segment.name}`;
1141
+ path7.segments.push(segment);
1142
+ path7.id += path7.id === "/" ? segment.name : `/${segment.name}`;
1106
1143
  if (type === "$$") {
1107
- path5.isCatchall = true;
1144
+ path7.isCatchall = true;
1108
1145
  }
1109
1146
  }
1110
1147
  if (map) {
1111
- if (map.has(path5.id)) {
1112
- const existing = map.get(path5.id);
1148
+ if (map.has(path7.id)) {
1149
+ const existing = map.get(path7.id);
1113
1150
  const existingExpansion = existing.segments.map((s) => s.raw).join(".");
1114
- const currentExpansion = path5.segments.map((s) => s.raw).join(".");
1151
+ const currentExpansion = path7.segments.map((s) => s.raw).join(".");
1115
1152
  throw new Error(
1116
- `Invalid route pattern: route '${path5.id}' is ambiguous. Expansion '${currentExpansion}' collides with '${existingExpansion}' in '${pattern}'.`
1153
+ `Invalid route pattern: route '${path7.id}' is ambiguous. Expansion '${currentExpansion}' collides with '${existingExpansion}' in '${pattern}'.`
1117
1154
  );
1118
1155
  }
1119
- map.set(path5.id, path5);
1156
+ map.set(path7.id, path7);
1120
1157
  }
1121
1158
  }
1122
1159
  }
1123
1160
  }
1161
+ function normalizeParam(segment) {
1162
+ const normalized = normalizeSegment(segment);
1163
+ return /^\$/.test(normalized) ? `\`${normalized}\`` : normalized;
1164
+ }
1165
+ function normalizeSegment(segment) {
1166
+ return decodeURIComponent(segment).replace(
1167
+ /[/?#]/g,
1168
+ (char) => "%" + char.charCodeAt(0).toString(16)
1169
+ );
1170
+ }
1124
1171
 
1125
1172
  // src/vite/routes/vdir.ts
1126
1173
  var _dirs, _pathlessDirs;
@@ -1131,14 +1178,12 @@ var _VDir = class _VDir {
1131
1178
  __publicField(this, "parent");
1132
1179
  __publicField(this, "source");
1133
1180
  __publicField(this, "path");
1134
- __publicField(this, "fullPath");
1135
1181
  __publicField(this, "segment");
1136
1182
  __publicField(this, "files");
1137
1183
  if (!parent || !segment) {
1138
1184
  this.parent = null;
1139
1185
  this.source = null;
1140
1186
  this.path = "/";
1141
- this.fullPath = "/";
1142
1187
  this.segment = {
1143
1188
  raw: "",
1144
1189
  name: ""
@@ -1146,12 +1191,8 @@ var _VDir = class _VDir {
1146
1191
  } else {
1147
1192
  this.parent = parent;
1148
1193
  this.source = source;
1149
- this.path = parent.path + (parent.path === "/" ? segment.name : `/${segment.name}`);
1150
- this.fullPath = parent.fullPath + (parent.fullPath === "/" ? segment.name : `/${segment.name}`);
1151
- if (segment.param) {
1152
- this.fullPath += segment.param;
1153
- }
1154
1194
  this.segment = segment;
1195
+ this.path = parent.path + (parent.path === "/" ? "" : "/") + segment.name;
1155
1196
  }
1156
1197
  }
1157
1198
  get pathInfo() {
@@ -1164,17 +1205,18 @@ var _VDir = class _VDir {
1164
1205
  for (const { segment } of this) {
1165
1206
  const { type, name, param } = segment;
1166
1207
  if (name && type !== "_") {
1167
- value.id += sep + (type || name);
1208
+ value.id += sep + name;
1168
1209
  value.path += sep + name;
1169
1210
  value.isEnd = type === "$$";
1170
1211
  if (param) {
1171
- value.path += param;
1212
+ const unescapedParam = param.charAt(0) === "`" ? param.slice(1, -1) : param;
1172
1213
  const index = type === "$$" ? null : value.segments.length;
1173
1214
  if (!value.params) {
1174
- value.params = { [param]: index };
1215
+ value.params = { [unescapedParam]: index };
1175
1216
  } else if (!(param in value.params)) {
1176
- value.params[param] = index;
1217
+ value.params[unescapedParam] = index;
1177
1218
  }
1219
+ value.path += param;
1178
1220
  }
1179
1221
  value.segments.push(name);
1180
1222
  sep = "/";
@@ -1186,11 +1228,11 @@ var _VDir = class _VDir {
1186
1228
  });
1187
1229
  return value;
1188
1230
  }
1189
- addDir(path5, segment) {
1231
+ addDir(path7, segment) {
1190
1232
  const map = segment.type === "_" ? __privateGet(this, _pathlessDirs) ?? __privateSet(this, _pathlessDirs, /* @__PURE__ */ new Map()) : __privateGet(this, _dirs) ?? __privateSet(this, _dirs, /* @__PURE__ */ new Map());
1191
1233
  const key = segment.type === "$" ? segment.raw : segment.name;
1192
1234
  if (!map.has(key)) {
1193
- const dir = new _VDir(this, segment, path5);
1235
+ const dir = new _VDir(this, segment, path7);
1194
1236
  map.set(key, dir);
1195
1237
  return dir;
1196
1238
  }
@@ -1206,15 +1248,15 @@ var _VDir = class _VDir {
1206
1248
  const existing = this.files.get(file.type);
1207
1249
  if (existing !== file) {
1208
1250
  throw new Error(
1209
- `Duplicate file type '${file.type}' added at path '${this.path}'. File '${file.importPath}' collides with '${existing.importPath}'.`
1251
+ `Duplicate file type ${file.type} added at path ${this.path}. File ${file.filePath} collides with ${existing.filePath}.`
1210
1252
  );
1211
1253
  } else if (file.type === RoutableFileTypes.Page || file.type === RoutableFileTypes.Handler) {
1212
1254
  throw new Error(
1213
- `Ambiguous path definition: route '${this.path}' is defined multiple times by ${file.importPath}`
1255
+ `Ambiguous path definition: route ${this.path} is defined multiple times by ${file.filePath}`
1214
1256
  );
1215
1257
  }
1216
1258
  throw new Error(
1217
- `Ambiguous path definition: file '${this.path}' is included multiple times by ${file.importPath}`
1259
+ `Ambiguous path definition: file ${this.path} is included multiple times by ${file.filePath}`
1218
1260
  );
1219
1261
  }
1220
1262
  }
@@ -1236,10 +1278,10 @@ var _VDir = class _VDir {
1236
1278
  const dirs = [];
1237
1279
  const unique = /* @__PURE__ */ new Map();
1238
1280
  for (const root of roots) {
1239
- for (const path5 of paths) {
1281
+ for (const path7 of paths) {
1240
1282
  let dir = root;
1241
- for (const segment of path5.segments) {
1242
- dir = dir.addDir(path5, segment);
1283
+ for (const segment of path7.segments) {
1284
+ dir = dir.addDir(path7, segment);
1243
1285
  }
1244
1286
  const existing = unique.get(dir.path);
1245
1287
  if (existing) {
@@ -1252,7 +1294,7 @@ var _VDir = class _VDir {
1252
1294
  }
1253
1295
  }
1254
1296
  throw new Error(
1255
- `Ambiguous directory structure: '${sourcePath}${path5.source}' defines '${dir.path}' multiple times.`
1297
+ `Ambiguous directory structure: ${sourcePath}${path7.source} defines ${dir.path} multiple times.`
1256
1298
  );
1257
1299
  } else {
1258
1300
  unique.set(dir.path, dir);
@@ -1278,13 +1320,11 @@ function matchRoutableFile(filename) {
1278
1320
  const match = filename.match(routeableFileRegex);
1279
1321
  return match && (match[1] || match[3]).toLowerCase();
1280
1322
  }
1281
- function isSpecialType(type) {
1282
- return type === RoutableFileTypes.NotFound || type === RoutableFileTypes.Error;
1283
- }
1284
- async function buildRoutes(sources) {
1323
+ async function buildRoutes(sources, outDir) {
1285
1324
  const uniqueRoutes = /* @__PURE__ */ new Map();
1286
1325
  const routes = [];
1287
1326
  const special = {};
1327
+ const seenKeys = /* @__PURE__ */ new Map();
1288
1328
  const middlewares = /* @__PURE__ */ new Set();
1289
1329
  const unusedFiles = /* @__PURE__ */ new Set();
1290
1330
  const currentLayouts = /* @__PURE__ */ new Set();
@@ -1292,13 +1332,13 @@ async function buildRoutes(sources) {
1292
1332
  const root = new VDir();
1293
1333
  const dirStack = [];
1294
1334
  let basePath;
1295
- let importPrefix;
1296
1335
  let activeDirs;
1297
1336
  let isBaseDir;
1298
1337
  let nextFileId = 1;
1299
1338
  let nextRouteIndex = 1;
1300
1339
  const walkOptions = {
1301
- onEnter({ name }) {
1340
+ onEnter(dir) {
1341
+ let { name } = dir;
1302
1342
  const prevDirStackLength = dirStack.length;
1303
1343
  if (isBaseDir) {
1304
1344
  isBaseDir = false;
@@ -1317,15 +1357,16 @@ async function buildRoutes(sources) {
1317
1357
  dirStack.length = prevDirStackLength;
1318
1358
  };
1319
1359
  },
1320
- onFile({ name, path: path5 }) {
1360
+ onFile(file) {
1361
+ const { name } = file;
1321
1362
  const match = name.match(routeableFileRegex);
1322
1363
  if (!match) {
1323
1364
  return;
1324
1365
  }
1325
1366
  const type = (match[1] || match[3]).toLowerCase();
1326
- if (dirStack.length && isSpecialType(type)) {
1367
+ if (dirStack.length && (type === RoutableFileTypes.NotFound || type === RoutableFileTypes.Error)) {
1327
1368
  console.warn(
1328
- `Special pages '${RoutableFileTypes.NotFound}' and '${RoutableFileTypes.Error}' are only considered in the root directory - ignoring ${path5}`
1369
+ `Special pages '${RoutableFileTypes.NotFound}' and '${RoutableFileTypes.Error}' are only considered in the root directory - ignoring ${file.path}`
1329
1370
  );
1330
1371
  return;
1331
1372
  }
@@ -1334,19 +1375,15 @@ async function buildRoutes(sources) {
1334
1375
  const paths = parseFlatRoute(name.slice(0, match.index));
1335
1376
  dirs = VDir.addPaths(activeDirs, paths);
1336
1377
  }
1337
- const dirPath = dirStack.join("/");
1338
- const relativePath = dirPath ? `${dirPath}/${name}` : name;
1339
- const file = {
1378
+ const routableFile = {
1340
1379
  id: String(nextFileId++),
1341
1380
  name,
1342
1381
  type,
1343
- filePath: path5,
1344
- relativePath,
1345
- importPath: `${importPrefix}/${relativePath}`,
1382
+ filePath: file.path,
1346
1383
  verbs: type === RoutableFileTypes.Page ? ["get", "head"] : void 0
1347
1384
  };
1348
1385
  for (const dir of dirs) {
1349
- dir.addFile(file);
1386
+ dir.addFile(routableFile);
1350
1387
  }
1351
1388
  }
1352
1389
  };
@@ -1354,7 +1391,6 @@ async function buildRoutes(sources) {
1354
1391
  sources = [sources];
1355
1392
  }
1356
1393
  for (const source of sources) {
1357
- importPrefix = source.importPrefix ? source.importPrefix.replace(/^\/+|\/+$/g, "") : "";
1358
1394
  basePath = source.basePath || "";
1359
1395
  activeDirs = [root];
1360
1396
  isBaseDir = true;
@@ -1374,7 +1410,8 @@ async function buildRoutes(sources) {
1374
1410
  layout = dir.files.get(RoutableFileTypes.Layout);
1375
1411
  const handler = dir.files.get(RoutableFileTypes.Handler);
1376
1412
  const page = dir.files.get(RoutableFileTypes.Page);
1377
- let hasSpecial = false;
1413
+ const pathInfo = dir.pathInfo;
1414
+ let layoutsUsed = false;
1378
1415
  if (middleware) {
1379
1416
  if (currentMiddleware.has(middleware)) {
1380
1417
  middleware = void 0;
@@ -1391,64 +1428,64 @@ async function buildRoutes(sources) {
1391
1428
  unusedFiles.add(layout);
1392
1429
  }
1393
1430
  }
1431
+ if (dir === root) {
1432
+ for (const [type, file] of dir.files) {
1433
+ if (type === RoutableFileTypes.NotFound || type === RoutableFileTypes.Error) {
1434
+ special[type] = {
1435
+ index: nextRouteIndex++,
1436
+ key: type,
1437
+ path: dir.pathInfo,
1438
+ middleware: [],
1439
+ layouts: [...currentLayouts],
1440
+ page: file,
1441
+ templateFilePath: currentLayouts.size ? path3.join(outDir, `${type}.marko`) : void 0
1442
+ };
1443
+ layoutsUsed = true;
1444
+ }
1445
+ }
1446
+ }
1394
1447
  if (page || handler) {
1395
- const path5 = dir.pathInfo;
1396
- if (uniqueRoutes.has(path5.id)) {
1397
- const existing = uniqueRoutes.get(path5.id);
1448
+ if (uniqueRoutes.has(pathInfo.id)) {
1449
+ const existing = uniqueRoutes.get(pathInfo.id);
1398
1450
  const route = routes[existing.index];
1399
1451
  const existingFiles = [route.handler, route.page].filter(Boolean).map((f) => f.filePath);
1400
1452
  const currentFiles = [handler, page].filter(Boolean).map((f) => f.filePath);
1401
- throw new Error(`Duplicate routes for path '${path5.id}' were defined. A route established by:
1402
- ${existingFiles.join(" and ")} via '${existing.dir.fullPath}'
1403
- collides with
1404
- ${currentFiles.join(" and ")} via '${dir.fullPath}'
1405
- `);
1453
+ throw new Error(
1454
+ `Duplicate routes for path ${pathInfo.id} were defined. A route established by: "${existingFiles.join(" and ")}" collides with "${currentFiles.join(" and ")}"`
1455
+ );
1456
+ }
1457
+ uniqueRoutes.set(pathInfo.id, { dir, index: routes.length });
1458
+ let key = pathInfo.segments.map(replaceInvalidFilenameChars).concat("route").join("/");
1459
+ const keyCount = (seenKeys.get(key) || 0) + 1;
1460
+ seenKeys.set(key, keyCount);
1461
+ if (keyCount > 1) {
1462
+ key += keyCount;
1406
1463
  }
1407
- uniqueRoutes.set(path5.id, { dir, index: routes.length });
1408
1464
  routes.push({
1409
1465
  index: nextRouteIndex++,
1410
- key: dir.fullPath,
1411
- paths: [path5],
1466
+ key,
1467
+ path: pathInfo,
1412
1468
  middleware: [...currentMiddleware],
1413
1469
  layouts: page ? [...currentLayouts] : [],
1414
1470
  meta: dir.files.get(RoutableFileTypes.Meta),
1415
1471
  page,
1416
1472
  handler,
1417
- entryName: `${markoRunFilePrefix}route` + (dir.path !== "/" ? dir.fullPath.replace(/\//g, ".").replace(/(%[A-Fa-f0-9]{2})+/g, "_") : "")
1473
+ templateFilePath: page && currentLayouts.size ? path3.join(outDir, key + ".marko") : void 0
1418
1474
  });
1419
- }
1420
- if (dir === root) {
1421
- for (const [type, file] of dir.files) {
1422
- if (isSpecialType(type)) {
1423
- hasSpecial = true;
1424
- special[type] = {
1425
- index: 0,
1426
- key: type,
1427
- paths: [],
1428
- middleware: [],
1429
- layouts: [...currentLayouts],
1430
- page: file,
1431
- entryName: `${markoRunFilePrefix}special.${type}`
1432
- };
1433
- }
1434
- }
1435
- }
1436
- if (handler || page) {
1475
+ layoutsUsed = !!page;
1437
1476
  for (const middleware2 of currentMiddleware) {
1438
1477
  middlewares.add(middleware2);
1439
1478
  unusedFiles.delete(middleware2);
1440
1479
  }
1441
1480
  }
1442
- if (page || hasSpecial) {
1481
+ if (layoutsUsed) {
1443
1482
  for (const layout2 of currentLayouts) {
1444
1483
  unusedFiles.delete(layout2);
1445
1484
  }
1446
1485
  }
1447
1486
  }
1448
- if (dir.dirs) {
1449
- for (const child of dir.dirs()) {
1450
- traverse(child);
1451
- }
1487
+ for (const childDir of dir.dirs()) {
1488
+ traverse(childDir);
1452
1489
  }
1453
1490
  if (middleware) {
1454
1491
  currentMiddleware.delete(middleware);
@@ -1458,10 +1495,13 @@ async function buildRoutes(sources) {
1458
1495
  }
1459
1496
  }
1460
1497
  }
1498
+ function replaceInvalidFilenameChars(str) {
1499
+ return str.replace(/[<>:"/\\|?*]+/g, "_");
1500
+ }
1461
1501
 
1462
1502
  // src/vite/routes/walk.ts
1463
1503
  import fs from "fs";
1464
- import path2 from "path";
1504
+ import path4 from "path";
1465
1505
  function createFSWalker(dir) {
1466
1506
  return async function walkFS({
1467
1507
  onEnter,
@@ -1476,7 +1516,7 @@ function createFSWalker(dir) {
1476
1516
  const entries = await fs.promises.readdir(dir2.path, {
1477
1517
  withFileTypes: true
1478
1518
  });
1479
- const prefix = dir2.path + path2.sep;
1519
+ const prefix = dir2.path + path4.sep;
1480
1520
  for (const entry of entries) {
1481
1521
  const walkEntry = {
1482
1522
  name: entry.name,
@@ -1499,7 +1539,7 @@ function createFSWalker(dir) {
1499
1539
  await walk(
1500
1540
  {
1501
1541
  path: dir,
1502
- name: path2.basename(dir)
1542
+ name: path4.basename(dir)
1503
1543
  },
1504
1544
  maxDepth
1505
1545
  );
@@ -1616,36 +1656,34 @@ function logRoutesTable(routes, bundle) {
1616
1656
  style: { compact: true }
1617
1657
  });
1618
1658
  for (const route of routes.list) {
1619
- for (const path5 of route.paths) {
1620
- const verbs = getVerbs(route, true);
1621
- let firstRow = true;
1622
- for (const verb of verbs) {
1623
- const entryType = [];
1624
- let size = "";
1625
- let verbCell = verbColor(verb)(verb.toUpperCase());
1626
- if (verb === "get" && !verbs.includes("head")) {
1627
- verbCell += kleur2.dim(`,${verbColor(verb)("HEAD")}`);
1628
- }
1629
- if (route.handler) {
1630
- entryType.push(kleur2.blue("handler"));
1631
- }
1632
- if (route.page && (verb === "get" || verb === "head")) {
1633
- entryType.push(kleur2.yellow("page"));
1634
- if (verb === "get") {
1635
- size = prettySize(computeRouteSize(route, bundle));
1636
- }
1637
- }
1638
- const row = [verbCell];
1639
- if (verbs.length === 1 || firstRow) {
1640
- row.push({ rowSpan: verbs.length, content: prettyPath(path5.path) });
1641
- firstRow = false;
1659
+ const verbs = getVerbs(route, true);
1660
+ let firstRow = true;
1661
+ for (const verb of verbs) {
1662
+ const entryType = [];
1663
+ let size = "";
1664
+ const verbCell = verbColor(verb)(verb.toUpperCase());
1665
+ if (route.handler) {
1666
+ entryType.push(kleur2.blue("handler"));
1667
+ }
1668
+ if (route.page && (verb === "get" || verb === "head")) {
1669
+ entryType.push(kleur2.yellow("page"));
1670
+ if (verb === "get") {
1671
+ size = prettySize(computeRouteSize(route, bundle));
1642
1672
  }
1643
- row.push(entryType.join(" -> "));
1644
- hasMiddleware && row.push(route.middleware.length || "");
1645
- hasMeta && row.push(route.meta ? "\u2713" : "");
1646
- row.push(size || "");
1647
- table.push(row);
1648
1673
  }
1674
+ const row = [verbCell];
1675
+ if (verbs.length === 1 || firstRow) {
1676
+ row.push({
1677
+ rowSpan: verbs.length,
1678
+ content: prettyPath(route.path.path)
1679
+ });
1680
+ firstRow = false;
1681
+ }
1682
+ row.push(entryType.join(" -> "));
1683
+ hasMiddleware && row.push(route.middleware.length || "");
1684
+ hasMeta && row.push(route.meta ? "\u2713" : "");
1685
+ row.push(size || "");
1686
+ table.push(row);
1649
1687
  }
1650
1688
  }
1651
1689
  for (const [key, route] of Object.entries(routes.special).sort()) {
@@ -1655,6 +1693,15 @@ function logRoutesTable(routes, bundle) {
1655
1693
  row.push(prettySize(computeRouteSize(route, bundle)));
1656
1694
  table.push(row);
1657
1695
  }
1696
+ if (!table.length) {
1697
+ table.push([
1698
+ {
1699
+ colSpan: 4,
1700
+ hAlign: "center",
1701
+ content: kleur2.dim(kleur2.white("No routes found"))
1702
+ }
1703
+ ]);
1704
+ }
1658
1705
  console.log(table.toString());
1659
1706
  }
1660
1707
  function computeRouteSize(route, bundle) {
@@ -1701,17 +1748,20 @@ function prettySize([bytes, compBytes]) {
1701
1748
  else str += kleur2.bold(kleur2.red(compSize));
1702
1749
  return str;
1703
1750
  }
1704
- function prettyPath(path5) {
1705
- return path5.replace(/\/\$\$(.*)$/, (_, p) => "/" + kleur2.bold(kleur2.dim(`*${p}`))).replace(/\/\$([^/]+)/g, (_, p) => "/" + kleur2.bold(kleur2.dim(`:${p}`)));
1751
+ function prettyPath(path7) {
1752
+ return path7.replace(
1753
+ /\/(\$\$?)(`?)([^/`]+)\2/g,
1754
+ (_, type, tick, key) => "/" + type + tick + kleur2.bold(kleur2.dim(key)) + tick
1755
+ );
1706
1756
  }
1707
1757
 
1708
1758
  // src/vite/utils/read-once-persisted-store.ts
1709
1759
  import { promises as fs2 } from "fs";
1710
1760
  import os from "os";
1711
- import path3 from "path";
1761
+ import path5 from "path";
1712
1762
  var noop = () => {
1713
1763
  };
1714
- var tmpFile = path3.join(os.tmpdir(), "marko-run-storage.json");
1764
+ var tmpFile = path5.join(os.tmpdir(), "marko-run-storage.json");
1715
1765
  var values = /* @__PURE__ */ new Map();
1716
1766
  var loadedFromDisk;
1717
1767
  var ReadOncePersistedStore = class {
@@ -1753,17 +1803,17 @@ process.once("beforeExit", (code) => {
1753
1803
 
1754
1804
  // src/vite/plugin.ts
1755
1805
  var debug = createDebug("@marko/run");
1756
- var __dirname = path4.dirname(fileURLToPath(import.meta.url));
1806
+ var __dirname = path6.dirname(fileURLToPath(import.meta.url));
1757
1807
  var PLUGIN_NAME_PREFIX = "marko-run-vite";
1758
- var POSIX_SEP = "/";
1759
- var WINDOWS_SEP = "\\";
1760
1808
  var CLIENT_OUT_DIR = "public";
1761
1809
  var MIDDLEWARE_FILENAME = `${markoRunFilePrefix}middleware.js`;
1762
1810
  var ROUTER_FILENAME = `${markoRunFilePrefix}router.js`;
1763
1811
  var defaultPort = Number(process.env.PORT || 3e3);
1764
- var normalizePath = path4.sep === WINDOWS_SEP ? (id) => id.replace(/\\/g, POSIX_SEP) : (id) => id;
1765
1812
  function markoRun(opts = {}) {
1766
- let { routesDir, adapter, ...markoVitePluginOptions } = opts;
1813
+ let routesDir;
1814
+ let adapter;
1815
+ let trailingSlashes;
1816
+ const { ...markoVitePluginOptions } = opts;
1767
1817
  let store;
1768
1818
  let root;
1769
1819
  let shouldEmptyOutDir = false;
@@ -1796,12 +1846,8 @@ function markoRun(opts = {}) {
1796
1846
  root,
1797
1847
  "{.tsconfig*,tsconfig*.json}"
1798
1848
  )))) {
1799
- const filepath = path4.join(typesDir, "routes.d.ts");
1800
- const data = await renderRouteTypeInfo(
1801
- routes2,
1802
- normalizePath(path4.relative(typesDir, resolvedRoutesDir)),
1803
- adapter
1804
- );
1849
+ const filepath = path6.join(typesDir, "routes.d.ts");
1850
+ const data = await renderRouteTypeInfo(routes2, typesDir, adapter);
1805
1851
  if (data !== typesFile || !fs3.existsSync(filepath)) {
1806
1852
  await ensureDir(typesDir);
1807
1853
  await fs3.promises.writeFile(filepath, typesFile = data);
@@ -1812,20 +1858,36 @@ function markoRun(opts = {}) {
1812
1858
  function buildVirtualFiles() {
1813
1859
  return buildVirtualFilesResult ?? (buildVirtualFilesResult = (async () => {
1814
1860
  virtualFiles.clear();
1815
- routes = await buildRoutes({
1816
- walker: createFSWalker(resolvedRoutesDir),
1817
- importPrefix: routesDir
1818
- });
1819
- if (!routes.list.length) {
1820
- throw new Error("No routes generated");
1861
+ if (fs3.existsSync(resolvedRoutesDir)) {
1862
+ routes = await buildRoutes(
1863
+ {
1864
+ walker: createFSWalker(resolvedRoutesDir)
1865
+ },
1866
+ entryFilesDir
1867
+ );
1868
+ if (!isBuild && !routes.list.length && !Object.keys(routes.special).length) {
1869
+ console.warn(`No routes found in ${resolvedRoutesDir}`);
1870
+ }
1871
+ } else {
1872
+ routes = {
1873
+ list: [],
1874
+ special: {},
1875
+ middleware: []
1876
+ };
1877
+ if (!isBuild) {
1878
+ console.warn(`Routes directory ${resolvedRoutesDir} does not exist`);
1879
+ }
1821
1880
  }
1822
1881
  for (const route of routes.list) {
1823
- virtualFiles.set(path4.posix.join(root, `${route.entryName}.js`), "");
1882
+ virtualFiles.set(
1883
+ path6.posix.join(root, getRouteVirtualFileName(route)),
1884
+ ""
1885
+ );
1824
1886
  }
1825
1887
  if (routes.middleware.length) {
1826
- virtualFiles.set(path4.posix.join(root, MIDDLEWARE_FILENAME), "");
1888
+ virtualFiles.set(path6.posix.join(root, MIDDLEWARE_FILENAME), "");
1827
1889
  }
1828
- virtualFiles.set(path4.posix.join(root, ROUTER_FILENAME), "");
1890
+ virtualFiles.set(path6.posix.join(root, ROUTER_FILENAME), "");
1829
1891
  return routes;
1830
1892
  })());
1831
1893
  }
@@ -1839,111 +1901,78 @@ function markoRun(opts = {}) {
1839
1901
  fs3.rmSync(entryFilesDir, { recursive: true });
1840
1902
  }
1841
1903
  for (const route of routes2.list) {
1842
- const { handler, page, layouts } = route;
1843
- if (handler) {
1844
- const exports = await getExportsFromFile(context, handler.filePath);
1845
- handler.verbs = [];
1904
+ if (route.handler) {
1905
+ const exports = await getExportsFromFile(
1906
+ context,
1907
+ route.handler.filePath
1908
+ );
1909
+ route.handler.verbs = [];
1846
1910
  for (const name of exports) {
1847
1911
  const verb = name.toLowerCase();
1848
1912
  if (name === verb.toUpperCase() && httpVerbs.includes(verb)) {
1849
- handler.verbs.push(verb);
1913
+ route.handler.verbs.push(verb);
1850
1914
  }
1851
1915
  }
1852
- if (!handler.verbs.length) {
1916
+ if (!route.handler.verbs.length) {
1853
1917
  context.warn(
1854
- `Did not find any http verb exports in handler '${path4.relative(root, handler.filePath)}' - expected ${httpVerbs.map((v) => v.toUpperCase()).join(", ")}`
1918
+ `Did not find any http verb exports in ${path6.relative(root, route.handler.filePath)} - expected ${httpVerbs.map((v) => v.toUpperCase()).join(", ")}`
1855
1919
  );
1856
1920
  }
1857
1921
  }
1858
- if (page) {
1859
- if (layouts.length) {
1860
- const relativePath = path4.relative(
1861
- resolvedRoutesDir,
1862
- page.filePath
1863
- );
1864
- const routeFileDir = path4.join(entryFilesDir, relativePath, "..");
1865
- const routeFileRelativePathPosix = normalizePath(
1866
- path4.relative(routeFileDir, root)
1867
- );
1868
- fs3.mkdirSync(routeFileDir, { recursive: true });
1869
- const pageNameIndex = page.name.indexOf("+page");
1870
- const pageNamePrefix = pageNameIndex > 0 ? `${page.name.slice(0, pageNameIndex)}.` : "";
1871
- fs3.writeFileSync(
1872
- route.templateFilePath = path4.join(
1873
- routeFileDir,
1874
- pageNamePrefix + "route.marko"
1875
- ),
1876
- renderRouteTemplate(
1877
- route,
1878
- (to) => path4.posix.join(routeFileRelativePathPosix, to)
1879
- )
1880
- );
1881
- } else {
1882
- route.templateFilePath = page.filePath;
1883
- }
1922
+ if (route.templateFilePath) {
1923
+ fs3.mkdirSync(path6.dirname(route.templateFilePath), {
1924
+ recursive: true
1925
+ });
1926
+ fs3.writeFileSync(
1927
+ route.templateFilePath,
1928
+ renderRouteTemplate(route, root)
1929
+ );
1884
1930
  }
1885
1931
  virtualFiles.set(
1886
- path4.posix.join(root, `${route.entryName}.js`),
1887
- renderRouteEntry(route, relativeEntryFilesDirPosix)
1932
+ path6.posix.join(root, getRouteVirtualFileName(route)),
1933
+ renderRouteEntry(route, root)
1888
1934
  );
1889
1935
  }
1890
1936
  for (const route of Object.values(routes2.special)) {
1891
- const { page, layouts, key } = route;
1892
- if (page) {
1893
- if (layouts.length) {
1894
- const relativePath = path4.relative(
1895
- resolvedRoutesDir,
1896
- page.filePath
1897
- );
1898
- const routeFileDir = path4.join(entryFilesDir, relativePath, "..");
1899
- const routeFileRelativePathPosix = normalizePath(
1900
- path4.relative(routeFileDir, root)
1901
- );
1902
- fs3.mkdirSync(routeFileDir, { recursive: true });
1903
- fs3.writeFileSync(
1904
- route.templateFilePath = path4.join(
1905
- routeFileDir,
1906
- `route.${key}.marko`
1907
- ),
1908
- renderRouteTemplate(
1909
- route,
1910
- (to) => path4.posix.join(routeFileRelativePathPosix, to)
1911
- )
1912
- );
1913
- } else {
1914
- route.templateFilePath = page.filePath;
1915
- }
1937
+ if (route.templateFilePath) {
1938
+ fs3.mkdirSync(path6.dirname(route.templateFilePath), {
1939
+ recursive: true
1940
+ });
1941
+ fs3.writeFileSync(
1942
+ route.templateFilePath,
1943
+ renderRouteTemplate(route, root)
1944
+ );
1916
1945
  }
1917
1946
  }
1918
1947
  if (routes2.middleware.length) {
1919
1948
  for (const middleware of routes2.middleware) {
1920
1949
  if (!(await getExportsFromFile(context, middleware.filePath)).includes("default")) {
1921
1950
  context.warn(
1922
- `Did not find a default export in middleware '${path4.relative(root, middleware.filePath)}'`
1951
+ `Did not find a default export in middleware '${path6.relative(root, middleware.filePath)}'`
1923
1952
  );
1924
1953
  }
1925
1954
  }
1926
1955
  virtualFiles.set(
1927
- path4.posix.join(root, MIDDLEWARE_FILENAME),
1928
- renderMiddleware(routes2.middleware)
1956
+ path6.posix.join(root, MIDDLEWARE_FILENAME),
1957
+ renderMiddleware(routes2.middleware, root)
1929
1958
  );
1930
1959
  }
1931
1960
  virtualFiles.set(
1932
- path4.posix.join(root, ROUTER_FILENAME),
1933
- renderRouter(routes2, relativeEntryFilesDirPosix, {
1934
- trailingSlashes: opts.trailingSlashes || "RedirectWithout"
1961
+ path6.posix.join(root, ROUTER_FILENAME),
1962
+ renderRouter(routes2, root, {
1963
+ trailingSlashes
1935
1964
  })
1936
1965
  );
1937
1966
  await writeTypesFile(routes2);
1938
1967
  if (adapter == null ? void 0 : adapter.routesGenerated) {
1939
- await adapter.routesGenerated(
1940
- routes2,
1941
- new Map(virtualFiles.entries()),
1942
- {
1968
+ await adapter.routesGenerated({
1969
+ routes: routes2,
1970
+ virtualFiles: new Map(virtualFiles.entries()),
1971
+ meta: {
1943
1972
  buildTime: times.routesBuild,
1944
1973
  renderTime: times.routesRender
1945
1974
  }
1946
- );
1975
+ });
1947
1976
  if (!isBuild) {
1948
1977
  await ((_a = opts == null ? void 0 : opts.emitRoutes) == null ? void 0 : _a.call(opts, routes2.list));
1949
1978
  }
@@ -1953,7 +1982,7 @@ function markoRun(opts = {}) {
1953
1982
  throw err;
1954
1983
  }
1955
1984
  virtualFiles.set(
1956
- path4.posix.join(root, ROUTER_FILENAME),
1985
+ path6.posix.join(root, ROUTER_FILENAME),
1957
1986
  `throw ${JSON.stringify(prepareError(err))}`
1958
1987
  );
1959
1988
  }
@@ -1990,27 +2019,28 @@ function markoRun(opts = {}) {
1990
2019
  }
1991
2020
  }
1992
2021
  routesDir = opts.routesDir || "src/routes";
2022
+ trailingSlashes = opts.trailingSlashes || "RedirectWithout";
1993
2023
  store = new ReadOncePersistedStore(
1994
2024
  `vite-marko-run${opts.runtimeId ? `-${opts.runtimeId}` : ""}`
1995
2025
  );
1996
2026
  markoVitePluginOptions.runtimeId = opts.runtimeId;
1997
2027
  markoVitePluginOptions.basePathVar = opts.basePathVar;
1998
- resolvedRoutesDir = path4.resolve(root, routesDir);
1999
- outputDir = path4.join(root, ((_d = config2.build) == null ? void 0 : _d.outDir) || "dist");
2000
- entryFilesDir = path4.join(outputDir, ".marko-run");
2028
+ resolvedRoutesDir = path6.resolve(root, routesDir);
2029
+ outputDir = path6.join(root, ((_d = config2.build) == null ? void 0 : _d.outDir) || "dist");
2030
+ entryFilesDir = path6.join(outputDir, ".marko-run");
2001
2031
  entryFilesDirPosix = normalizePath(entryFilesDir);
2002
2032
  relativeEntryFilesDirPosix = normalizePath(
2003
- path4.relative(root, entryFilesDir)
2033
+ path6.relative(root, entryFilesDir)
2004
2034
  );
2005
- typesDir = path4.join(root, ".marko-run");
2006
- devEntryFile = path4.join(root, "index.html");
2035
+ typesDir = path6.join(root, ".marko-run");
2036
+ devEntryFile = path6.join(root, "index.html");
2007
2037
  devEntryFilePosix = normalizePath(devEntryFile);
2008
2038
  let outDir = ((_e = config2.build) == null ? void 0 : _e.outDir) || "dist";
2009
2039
  const assetsDir = ((_f = config2.build) == null ? void 0 : _f.assetsDir) || "assets";
2010
2040
  let rollupOutputOptions = (_h = (_g = config2.build) == null ? void 0 : _g.rollupOptions) == null ? void 0 : _h.output;
2011
2041
  if (isBuild) {
2012
2042
  if (!isSSRBuild) {
2013
- outDir = path4.join(outDir, CLIENT_OUT_DIR);
2043
+ outDir = path6.join(outDir, CLIENT_OUT_DIR);
2014
2044
  }
2015
2045
  const defaultRollupOutputOptions = {
2016
2046
  assetFileNames({ name }) {
@@ -2135,7 +2165,7 @@ function markoRun(opts = {}) {
2135
2165
  devServer.watcher.on("all", async (type, filename) => {
2136
2166
  seenErrors.clear();
2137
2167
  const routableFileType = matchRoutableFile(
2138
- path4.parse(filename).base
2168
+ path6.parse(filename).base
2139
2169
  );
2140
2170
  if (filename.startsWith(resolvedRoutesDir) && routableFileType) {
2141
2171
  if (type === "add" || type === "unlink" || type === "change" && (routableFileType === RoutableFileTypes.Handler || routableFileType === RoutableFileTypes.Middleware)) {
@@ -2185,19 +2215,19 @@ function markoRun(opts = {}) {
2185
2215
  },
2186
2216
  async resolveId(importee, importer) {
2187
2217
  if (importee === "@marko/run/router") {
2188
- return normalizePath(path4.resolve(root, ROUTER_FILENAME));
2218
+ return normalizePath(path6.resolve(root, ROUTER_FILENAME));
2189
2219
  } else if (importee.endsWith(".marko") && importee.includes(relativeEntryFilesDirPosix)) {
2190
2220
  if (!importee.startsWith(root)) {
2191
- importee = path4.resolve(root, "." + importee);
2221
+ importee = path6.resolve(root, "." + importee);
2192
2222
  }
2193
2223
  return normalizePath(importee);
2194
2224
  }
2195
2225
  let virtualFilePath;
2196
2226
  if (importee.startsWith(virtualFilePrefix)) {
2197
2227
  virtualFilePath = importee.slice(virtualFilePrefix.length + 1);
2198
- importee = path4.resolve(root, virtualFilePath);
2228
+ importee = path6.resolve(root, virtualFilePath);
2199
2229
  } else if (!isBuild && importer && (importer === devEntryFile || normalizePath(importer) === devEntryFilePosix) && importee.startsWith(`/${markoRunFilePrefix}`)) {
2200
- importee = path4.resolve(root, "." + importee);
2230
+ importee = path6.resolve(root, "." + importee);
2201
2231
  }
2202
2232
  importee = normalizePath(importee);
2203
2233
  if (!buildVirtualFilesResult) {
@@ -2206,7 +2236,7 @@ function markoRun(opts = {}) {
2206
2236
  if (virtualFiles.has(importee)) {
2207
2237
  return importee;
2208
2238
  } else if (virtualFilePath) {
2209
- const filePath = path4.resolve(__dirname, "..", virtualFilePath);
2239
+ const filePath = path6.resolve(__dirname, "..", virtualFilePath);
2210
2240
  return await this.resolve(filePath, importer, {
2211
2241
  skipSelf: true
2212
2242
  });
@@ -2220,7 +2250,8 @@ function markoRun(opts = {}) {
2220
2250
  await renderVirtualFiles(this);
2221
2251
  }
2222
2252
  if (virtualFiles.has(id)) {
2223
- return virtualFiles.get(id);
2253
+ const file = virtualFiles.get(id);
2254
+ return file;
2224
2255
  } else if (!id.startsWith(entryFilesDirPosix) && /[/\\]__marko-run__[^?/\\]+\.(js|marko)$/.exec(id)) {
2225
2256
  return "";
2226
2257
  }
@@ -2245,7 +2276,7 @@ function markoRun(opts = {}) {
2245
2276
  const builtEntries = Object.values(bundle).reduce(
2246
2277
  (acc, item) => {
2247
2278
  if (item.type === "chunk" && item.isEntry) {
2248
- acc.push(path4.join(options.dir, item.fileName));
2279
+ acc.push(path6.join(options.dir, item.fileName));
2249
2280
  }
2250
2281
  return acc;
2251
2282
  },
@@ -2273,12 +2304,12 @@ function markoRun(opts = {}) {
2273
2304
  fs3.rmSync(entryFilesDir, { recursive: true });
2274
2305
  }
2275
2306
  if ((adapter == null ? void 0 : adapter.buildEnd) && routes) {
2276
- await adapter.buildEnd(
2277
- resolvedConfig,
2278
- routes.list,
2279
- routeData.builtEntries,
2280
- routeData.sourceEntries
2281
- );
2307
+ await adapter.buildEnd({
2308
+ routes,
2309
+ config: resolvedConfig,
2310
+ builtEntries: routeData.builtEntries,
2311
+ sourceEntries: routeData.sourceEntries
2312
+ });
2282
2313
  }
2283
2314
  }
2284
2315
  }
@@ -2310,11 +2341,11 @@ async function ensureDir(dir) {
2310
2341
  }
2311
2342
  async function getPackageData(dir) {
2312
2343
  do {
2313
- const pkgPath = path4.join(dir, "package.json");
2344
+ const pkgPath = path6.join(dir, "package.json");
2314
2345
  if (fs3.existsSync(pkgPath)) {
2315
2346
  return JSON.parse(await fs3.promises.readFile(pkgPath, "utf-8"));
2316
2347
  }
2317
- } while (dir !== (dir = path4.dirname(dir)));
2348
+ } while (dir !== (dir = path6.dirname(dir)));
2318
2349
  return null;
2319
2350
  }
2320
2351
  async function resolveAdapter(root, options, log) {