@marko/run 0.6.5 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,7 +15,7 @@ import sade from "sade";
15
15
 
16
16
  // src/cli/commands.ts
17
17
  import fs5 from "fs";
18
- import path5 from "path";
18
+ import path7 from "path";
19
19
  import { fileURLToPath as fileURLToPath2 } from "url";
20
20
  import {
21
21
  build as viteBuild,
@@ -29,9 +29,12 @@ import createDebug from "debug";
29
29
  import { resolveToEsbuildTarget } from "esbuild-plugin-browserslist";
30
30
  import fs3 from "fs";
31
31
  import { glob } from "glob";
32
- import path4 from "path";
32
+ import path6 from "path";
33
33
  import { fileURLToPath } from "url";
34
- import { buildErrorMessage, mergeConfig } from "vite";
34
+ import {
35
+ buildErrorMessage,
36
+ mergeConfig
37
+ } from "vite";
35
38
 
36
39
  // src/adapter/utils.ts
37
40
  import kleur from "kleur";
@@ -59,7 +62,7 @@ function prepareError(err) {
59
62
  }
60
63
 
61
64
  // src/vite/codegen/index.ts
62
- import path from "path";
65
+ import path2 from "path";
63
66
 
64
67
  // src/vite/constants.ts
65
68
  var markoRunFilePrefix = "__marko-run__";
@@ -84,6 +87,12 @@ var RoutableFileTypes = {
84
87
  Error: "500"
85
88
  };
86
89
 
90
+ // src/vite/utils/fs.ts
91
+ import path from "path";
92
+ var POSIX_SEP = "/";
93
+ var WINDOWS_SEP = "\\";
94
+ var normalizePath = path.sep === WINDOWS_SEP ? (id) => id.replace(/\\/g, POSIX_SEP) : (id) => id;
95
+
87
96
  // src/vite/utils/route.ts
88
97
  var httpVerbOrder = httpVerbs.reduce(
89
98
  (order, verb, index) => {
@@ -107,6 +116,9 @@ function hasVerb(route, verb) {
107
116
  var _a, _b;
108
117
  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");
109
118
  }
119
+ function getRouteVirtualFileName(route) {
120
+ return `${markoRunFilePrefix}${route.key.replace(/\//g, ".")}.js`;
121
+ }
110
122
 
111
123
  // src/vite/codegen/writer.ts
112
124
  function createWriter(sink, options) {
@@ -249,27 +261,34 @@ function createStringWriter(opts) {
249
261
  }
250
262
 
251
263
  // src/vite/codegen/index.ts
252
- function renderRouteTemplate(route, getRelativePath) {
264
+ function normalizedRelativePath(from, to) {
265
+ const relativePath = normalizePath(path2.relative(from, to));
266
+ return relativePath.startsWith(".") ? relativePath : "./" + relativePath;
267
+ }
268
+ function renderRouteTemplate(route, rootDir) {
253
269
  if (!route.page) {
254
270
  throw new Error(`Route ${route.key} has no page to render`);
255
271
  }
272
+ if (!route.templateFilePath) {
273
+ throw new Error(`Route ${route.key} has no template file path`);
274
+ }
256
275
  return renderEntryTemplate(
257
- route.entryName,
276
+ normalizedRelativePath(rootDir, route.templateFilePath),
258
277
  [...route.layouts, route.page].map(
259
- (file) => getRelativePath(file.importPath)
278
+ (file) => normalizedRelativePath(
279
+ path2.dirname(route.templateFilePath),
280
+ file.filePath
281
+ )
260
282
  ),
261
283
  route.key === RoutableFileTypes.Error ? ["error"] : []
262
284
  );
263
285
  }
264
286
  function renderEntryTemplate(name, files, pageInputs = []) {
265
- if (!name) {
266
- throw new Error(`Invalid argument - 'name' cannot be empty`);
267
- }
268
287
  if (!files.length) {
269
288
  throw new Error(`Invalid argument - 'files' cannot be empty`);
270
289
  }
271
290
  const writer = createStringWriter();
272
- writer.writeLines(`// ${name}.marko`);
291
+ writer.writeLines(`// ${name}`);
273
292
  writer.branch("imports");
274
293
  writer.writeLines("");
275
294
  writeEntryTemplateTag(writer, files, pageInputs);
@@ -279,7 +298,7 @@ function writeEntryTemplateTag(writer, [file, ...rest], pageInputs, index = 1) {
279
298
  if (file) {
280
299
  const isLast = !rest.length;
281
300
  const tag = isLast ? "Page" : `Layout${index}`;
282
- writer.branch("imports").writeLines(`import ${tag} from '${file}';`);
301
+ writer.branch("imports").writeLines(`import ${tag} from "${file}";`);
283
302
  if (isLast) {
284
303
  const attributes = pageInputs.length ? " " + pageInputs.map((name) => `${name}=input.${name}`).join(" ") : "";
285
304
  writer.writeLines(`<${tag}${attributes}/>`);
@@ -290,9 +309,9 @@ function writeEntryTemplateTag(writer, [file, ...rest], pageInputs, index = 1) {
290
309
  }
291
310
  }
292
311
  }
293
- function renderRouteEntry(route, entriesDir) {
312
+ function renderRouteEntry(route, rootDir) {
294
313
  var _a;
295
- const { key, index, handler, page, middleware, meta, entryName } = route;
314
+ const { key, index, handler, page, middleware, meta } = route;
296
315
  const verbs = getVerbs(route);
297
316
  if (!verbs) {
298
317
  throw new Error(
@@ -300,7 +319,7 @@ function renderRouteEntry(route, entriesDir) {
300
319
  );
301
320
  }
302
321
  const writer = createStringWriter();
303
- writer.writeLines(`// ${virtualFilePrefix}/${entryName}.js`);
322
+ writer.writeLines(`// ${virtualFilePrefix}${getRouteVirtualFileName(route)}`);
304
323
  const imports = writer.branch("imports");
305
324
  const runtimeImports = [];
306
325
  if (handler) {
@@ -312,9 +331,6 @@ function renderRouteEntry(route, entriesDir) {
312
331
  if (!page || verbs.some((verb) => verb !== "get" && verb !== "head")) {
313
332
  runtimeImports.push("noContent");
314
333
  }
315
- if (page) {
316
- runtimeImports.push("pageResponse");
317
- }
318
334
  if (verbs.includes("head")) {
319
335
  runtimeImports.push("stripResponseBody");
320
336
  }
@@ -322,7 +338,7 @@ function renderRouteEntry(route, entriesDir) {
322
338
  imports.writeLines(
323
339
  `import { ${runtimeImports.join(
324
340
  ", "
325
- )} } from '${virtualFilePrefix}/runtime/internal';`
341
+ )} } from "${virtualFilePrefix}/runtime/internal";`
326
342
  );
327
343
  }
328
344
  if (middleware.length) {
@@ -330,7 +346,7 @@ function renderRouteEntry(route, entriesDir) {
330
346
  imports.writeLines(
331
347
  `import { ${names.join(
332
348
  ", "
333
- )} } from '${virtualFilePrefix}/${markoRunFilePrefix}middleware.js';`
349
+ )} } from "${virtualFilePrefix}/${markoRunFilePrefix}middleware.js";`
334
350
  );
335
351
  }
336
352
  if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.length) {
@@ -342,18 +358,17 @@ function renderRouteEntry(route, entriesDir) {
342
358
  writer.writeLines(`const ${verb}Handler = normalize(${importName});`);
343
359
  }
344
360
  imports.writeLines(
345
- `import { ${names.join(", ")} } from './${handler.importPath}';`
361
+ `import { ${names.join(", ")} } from "${normalizedRelativePath(rootDir, handler.filePath)}";`
346
362
  );
347
363
  }
348
364
  if (page) {
349
- const pageNameIndex = page.name.indexOf("+page");
350
- const pageNamePrefix = pageNameIndex > 0 ? `${page.name.slice(0, pageNameIndex)}.` : "";
351
- const importPath = route.layouts.length ? `./${path.posix.join(entriesDir, page.relativePath, "..", pageNamePrefix + "route.marko")}` : `./${page.importPath}`;
352
- imports.writeLines(`import page from '${importPath}${serverEntryQuery}';`);
365
+ imports.writeLines(
366
+ `import page from "${normalizedRelativePath(rootDir, route.templateFilePath || page.filePath)}${serverEntryQuery}";`
367
+ );
353
368
  }
354
369
  if (meta) {
355
370
  imports.writeLines(
356
- `export { default as meta${index} } from './${meta.importPath}';`
371
+ `export { default as meta${index} } from "${normalizedRelativePath(rootDir, meta.filePath)}";`
357
372
  );
358
373
  }
359
374
  for (const verb of verbs) {
@@ -370,9 +385,7 @@ function writeRouteEntryHandler(writer, route, verb) {
370
385
  let hasBody = false;
371
386
  writer.writeLines("");
372
387
  if (page && (verb === "get" || verb === "head")) {
373
- writer.writeBlockStart(
374
- `export function ${verb}${index}(context, buildInput) {`
375
- );
388
+ writer.writeBlockStart(`export function ${verb}${index}(context) {`);
376
389
  } else {
377
390
  writer.writeBlockStart(`export function ${verb}${index}(context) {`);
378
391
  }
@@ -382,7 +395,7 @@ function writeRouteEntryHandler(writer, route, verb) {
382
395
  if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes(verb)) {
383
396
  const name = `${verb}Handler`;
384
397
  continuations.writeLines(
385
- `const ${currentName} = () => pageResponse(page, buildInput());`
398
+ `const ${currentName} = () => context.render(page, {});`
386
399
  );
387
400
  if (len) {
388
401
  nextName = currentName;
@@ -401,17 +414,15 @@ function writeRouteEntryHandler(writer, route, verb) {
401
414
  hasBody = true;
402
415
  }
403
416
  } else if (verb === "head") {
404
- writer.writeLines(
405
- `return stripResponseBody(get${index}(context, buildInput));`
406
- );
417
+ writer.writeLines(`return stripResponseBody(get${index}(context));`);
407
418
  hasBody = true;
408
419
  } else if (len) {
409
420
  continuations.writeLines(
410
- `const ${currentName} = () => pageResponse(page, buildInput());`
421
+ `const ${currentName} = () => context.render(page, {});`
411
422
  );
412
423
  nextName = currentName;
413
424
  } else {
414
- writer.writeLines(`return pageResponse(page, buildInput());`);
425
+ writer.writeLines(`return context.render(page, {});`);
415
426
  hasBody = true;
416
427
  }
417
428
  } else if ((_b = handler == null ? void 0 : handler.verbs) == null ? void 0 : _b.includes(verb)) {
@@ -461,7 +472,7 @@ function writeRouteEntryHandler(writer, route, verb) {
461
472
  continuations.join();
462
473
  writer.writeBlockEnd("}");
463
474
  }
464
- function renderRouter(routes, entriesDir, options = {
475
+ function renderRouter(routes, rootDir, options = {
465
476
  trailingSlashes: "RedirectWithout"
466
477
  }) {
467
478
  const writer = createStringWriter();
@@ -470,20 +481,19 @@ function renderRouter(routes, entriesDir, options = {
470
481
  writer.writeLines(`// @marko/run/router`);
471
482
  const imports = writer.branch("imports");
472
483
  imports.writeLines(
473
- `import { NotHandled, NotMatched, createContext${hasErrorPage || hasNotFoundPage ? ", pageResponse" : ""} } from '${virtualFilePrefix}/runtime/internal';`
484
+ `import { NotHandled, NotMatched, createContext } from "${virtualFilePrefix}/runtime/internal";`
474
485
  );
475
486
  for (const route of routes.list) {
476
487
  const verbs = getVerbs(route);
477
488
  const names = verbs.map((verb) => `${verb}${route.index}`);
478
489
  route.meta && names.push(`meta${route.index}`);
479
490
  imports.writeLines(
480
- `import { ${names.join(", ")} } from '${virtualFilePrefix}/${route.entryName}.js';`
491
+ `import { ${names.join(", ")} } from "${virtualFilePrefix}/${getRouteVirtualFileName(route)}";`
481
492
  );
482
493
  }
483
494
  for (const route of Object.values(routes.special)) {
484
- const importPath = route.layouts.length ? `./${path.posix.join(entriesDir, route.page.relativePath, "..", `route.${route.key}.marko`)}` : `./${route.page.importPath}`;
485
495
  imports.writeLines(
486
- `import page${route.key} from '${importPath}${serverEntryQuery}';`
496
+ `import page${route.key} from "${normalizedRelativePath(rootDir, route.templateFilePath || route.page.filePath)}${serverEntryQuery}";`
487
497
  );
488
498
  }
489
499
  writer.writeLines(
@@ -491,11 +501,12 @@ function renderRouter(routes, entriesDir, options = {
491
501
  globalThis.__marko_run__ = { match, fetch, invoke };
492
502
  `
493
503
  ).writeBlockStart(`export function match(method, pathname) {`).writeLines(
494
- `if (!pathname) {
495
- pathname = '/';
496
- } else if (pathname.charAt(0) !== '/') {
497
- pathname = '/' + pathname;
498
- }`
504
+ `const last = pathname.length - 1;
505
+ return match_internal(method, last && pathname.charAt(last) === '/' ? pathname.slice(0, last) : pathname)
506
+ };
507
+
508
+ function match_internal(method, pathname) {
509
+ const len = pathname.length;`
499
510
  ).writeBlockStart(`switch (method) {`);
500
511
  for (const verb of httpVerbs) {
501
512
  const filteredRoutes = routes.list.filter((route) => hasVerb(route, verb));
@@ -511,13 +522,13 @@ globalThis.__marko_run__ = { match, fetch, invoke };
511
522
  writer.writeLines("").writeBlockStart(
512
523
  "export async function invoke(route, request, platform, url) {"
513
524
  ).writeLines(
514
- "const [context, buildInput] = createContext(route, request, platform, url);"
525
+ "const context = createContext(route, request, platform, url);"
515
526
  );
516
527
  if (hasErrorPage) {
517
528
  writer.writeBlockStart("try {");
518
529
  }
519
530
  writer.writeBlockStart("if (route) {").writeBlockStart("try {").writeLines(
520
- "const response = await route.handler(context, buildInput);",
531
+ "const response = await route.handler(context);",
521
532
  "if (response) return response;"
522
533
  ).indent--;
523
534
  writer.writeBlockStart("} catch (error) {").writeLines(
@@ -534,7 +545,7 @@ const page404ResponseInit = {
534
545
  );
535
546
  writer.write(`
536
547
  if (context.request.headers.get('Accept')?.includes('text/html')) {
537
- return pageResponse(page404, buildInput(), page404ResponseInit);
548
+ return context.render(page404, {}, page404ResponseInit);
538
549
  }`);
539
550
  }
540
551
  writer.indent--;
@@ -551,7 +562,7 @@ const page500ResponseInit = {
551
562
  writer.writeBlockStart(`} catch (error) {`).writeBlockStart(
552
563
  `if (context.request.headers.get('Accept')?.includes('text/html')) {`
553
564
  ).writeLines(
554
- `return pageResponse(page500, buildInput({ error }), page500ResponseInit);`
565
+ `return context.render(page500, { error }, page500ResponseInit);`
555
566
  ).writeBlockEnd("}").writeLines("throw error;").writeBlockEnd("}");
556
567
  }
557
568
  writer.writeBlockEnd("}");
@@ -563,38 +574,40 @@ function renderFetch(writer, options) {
563
574
  export async function fetch(request, platform) {
564
575
  try {
565
576
  const url = new URL(request.url);
566
- let { pathname } = url;`);
577
+ const { pathname } = url;
578
+ const last = pathname.length - 1;
579
+ const hasTrailingSlash = last && pathname.charAt(last) === '/';
580
+ const normalizedPathname = hasTrailingSlash ? pathname.slice(0, last) : pathname;
581
+ const route = match_internal(request.method, normalizedPathname);`);
567
582
  switch (options.trailingSlashes) {
568
583
  case "RedirectWithout":
569
584
  writer.write(`
570
- if (pathname !== '/' && pathname.endsWith('/')) {
571
- url.pathname = pathname.slice(0, -1);
585
+ if (route && hasTrailingSlash) {
586
+ url.pathname = normalizedPathname
572
587
  return Response.redirect(url);
573
588
  }`);
574
589
  break;
575
590
  case "RedirectWith":
576
591
  writer.write(`
577
- if (pathname !== '/' && !pathname.endsWith('/')) {
578
- url.pathname = pathname + '/';
592
+ if (route && pathname !== '/' && !hasTrailingSlash) {
593
+ url.pathname += '/';
579
594
  return Response.redirect(url);
580
595
  }`);
581
596
  break;
582
597
  case "RewriteWithout":
583
598
  writer.write(`
584
- if (pathname !== '/' && pathname.endsWith('/')) {
585
- url.pathname = pathname = pathname.slice(0, -1);
599
+ if (route && hasTrailingSlash) {
600
+ url.pathname = normalizedPathname;
586
601
  }`);
587
602
  break;
588
603
  case "RewriteWith":
589
604
  writer.write(`
590
- if (pathname !== '/' && !pathname.endsWith('/')) {
591
- url.pathname = pathname = pathname + '/';
605
+ if (route && pathname !== '/' && !hasTrailingSlash) {
606
+ url.pathname += '/';
592
607
  }`);
593
608
  break;
594
609
  }
595
610
  writer.write(`
596
-
597
- const route = match(request.method, pathname);
598
611
  return await invoke(route, request, platform, url);
599
612
  } catch (error) {
600
613
  if (import.meta.env.DEV) {
@@ -610,10 +623,9 @@ function writeRouterVerb(writer, trie, verb, level = 0, offset = 1) {
610
623
  const { route, dynamic, catchAll } = trie;
611
624
  let closeCount = 0;
612
625
  if (level === 0) {
613
- writer.writeLines(`const len = pathname.length;`);
614
626
  if (route) {
615
627
  writer.writeLines(
616
- `if (len === 1) return ${renderMatch(verb, route, trie.path)}; // ${trie.path.path}`
628
+ `if (len === 1) return ${renderMatch(verb, route, trie.path)};`
617
629
  );
618
630
  } else if (trie.static || dynamic) {
619
631
  writer.writeBlockStart(`if (len > 1) {`);
@@ -654,17 +666,15 @@ function writeRouterVerb(writer, trie, verb, level = 0, offset = 1) {
654
666
  if (useSwitch) {
655
667
  writer.writeBlockStart(`switch (${value}) {`);
656
668
  }
657
- for (const { key, path: path6, route: route2 } of terminal) {
669
+ for (const { key, path: path8, route: route2 } of terminal) {
658
670
  const decodedKey = decodeURIComponent(key);
659
671
  if (useSwitch) {
660
672
  writer.write(`case '${decodedKey}': `, true);
661
673
  } else {
662
674
  writer.write(`if (${value} === '${decodedKey}') `, true);
663
675
  }
664
- writer.write(
665
- `return ${renderMatch(verb, route2, path6)}; // ${path6.path}
666
- `
667
- );
676
+ writer.write(`return ${renderMatch(verb, route2, path8)};
677
+ `);
668
678
  }
669
679
  if (useSwitch) {
670
680
  writer.writeBlockEnd("}");
@@ -676,7 +686,7 @@ function writeRouterVerb(writer, trie, verb, level = 0, offset = 1) {
676
686
  verb,
677
687
  dynamic.route,
678
688
  dynamic.path
679
- )}; // ${dynamic.path.path}`
689
+ )};`
680
690
  );
681
691
  }
682
692
  }
@@ -736,7 +746,7 @@ function writeRouterVerb(writer, trie, verb, level = 0, offset = 1) {
736
746
  catchAll.route,
737
747
  catchAll.path,
738
748
  String(offset)
739
- )}; // ${catchAll.path.path}`
749
+ )};`
740
750
  );
741
751
  } else if (level === 0) {
742
752
  writer.writeLines("return null;");
@@ -765,43 +775,47 @@ function renderParams(params, pathIndex) {
765
775
  }
766
776
  return result ? result + " }" : "{}";
767
777
  }
768
- function renderMatch(verb, route, path6, pathIndex) {
778
+ function renderMatch(verb, route, path8, pathIndex) {
769
779
  const handler = `${verb}${route.index}`;
770
- const params = path6.params ? renderParams(path6.params, pathIndex) : "{}";
780
+ const params = path8.params ? renderParams(path8.params, pathIndex) : "{}";
771
781
  const meta = route.meta ? `meta${route.index}` : "{}";
772
- const pathPattern = pathToURLPatternString(path6.path);
773
- return `{ handler: ${handler}, params: ${params}, meta: ${meta}, path: '${pathPattern}' }`;
782
+ return `{ handler: ${handler}, params: ${params}, meta: ${meta}, path: '${path8.path}' }`;
774
783
  }
775
- function renderMiddleware(middleware) {
784
+ function renderMiddleware(middleware, rootDir) {
776
785
  const writer = createStringWriter();
777
786
  writer.writeLines(
778
787
  `// ${virtualFilePrefix}/${markoRunFilePrefix}middleware.js`
779
788
  );
780
789
  const imports = writer.branch("imports");
781
790
  imports.writeLines(
782
- `import { normalize } from '${virtualFilePrefix}/runtime/internal';`
791
+ `import { normalize } from "${virtualFilePrefix}/runtime/internal";`
783
792
  );
784
793
  writer.writeLines("");
785
- for (const { id, importPath } of middleware) {
794
+ for (const { id, filePath } of middleware) {
786
795
  const importName = `middleware${id}`;
787
- imports.writeLines(`import ${importName} from './${importPath}';`);
796
+ imports.writeLines(
797
+ `import ${importName} from "${normalizedRelativePath(rootDir, filePath)}";`
798
+ );
788
799
  writer.writeLines(`export const mware${id} = normalize(${importName});`);
789
800
  }
790
801
  imports.join();
791
802
  return writer.end();
792
803
  }
793
- function stripTsExtension(path6) {
794
- const index = path6.lastIndexOf(".");
804
+ function stripTsExtension(path8) {
805
+ const index = path8.lastIndexOf(".");
795
806
  if (index !== -1) {
796
- const ext = path6.slice(index + 1);
807
+ const ext = path8.slice(index + 1);
797
808
  if (ext.toLowerCase() === "ts") {
798
- return path6.slice(0, index);
809
+ return path8.slice(0, index);
799
810
  }
800
811
  }
801
- return path6;
812
+ return path8;
802
813
  }
803
- async function renderRouteTypeInfo(routes, pathPrefix = ".", adapter) {
804
- var _a, _b;
814
+ function decodePath(path8) {
815
+ return path8;
816
+ }
817
+ async function renderRouteTypeInfo(routes, outDir, adapter) {
818
+ var _a, _b, _c, _d;
805
819
  const writer = createStringWriter();
806
820
  writer.writeLines(
807
821
  `/*
@@ -830,11 +844,31 @@ async function renderRouteTypeInfo(routes, pathPrefix = ".", adapter) {
830
844
  const routeTypes = /* @__PURE__ */ new Map();
831
845
  for (const route of routes.list) {
832
846
  let routeType = "";
833
- for (const path6 of route.paths) {
834
- const pathType = `"${pathToURLPatternString(path6.path)}"`;
835
- routeType += routeType ? " | " + pathType : pathType;
836
- routesWriter.writeLines(`${pathType}: Routes["${route.key}"];`);
847
+ let routeDefinition = "";
848
+ if (route.page || route.handler) {
849
+ const verbs = [];
850
+ if (route.page || ((_b = (_a = route.handler) == null ? void 0 : _a.verbs) == null ? void 0 : _b.includes("get"))) {
851
+ verbs.push(`"get"`);
852
+ }
853
+ if ((_d = (_c = route.handler) == null ? void 0 : _c.verbs) == null ? void 0 : _d.includes("post")) {
854
+ verbs.push(`"post"`);
855
+ }
856
+ routeDefinition = `{ verb: ${verbs.join(" | ")};`;
857
+ if (route.meta) {
858
+ const metaPath = stripTsExtension(
859
+ normalizedRelativePath(outDir, route.meta.filePath)
860
+ );
861
+ let metaType = `typeof import("${metaPath}")`;
862
+ if (/\.(ts|js|mjs)$/.test(route.meta.name)) {
863
+ metaType += `["default"]`;
864
+ }
865
+ routeDefinition += ` meta: ${metaType};`;
866
+ }
867
+ routeDefinition += " }";
837
868
  }
869
+ const pathType = `"${decodePath(route.path.path)}"`;
870
+ routeType += routeType ? " | " + pathType : pathType;
871
+ routesWriter.writeLines(`${pathType}: ${routeDefinition};`);
838
872
  for (const file of [route.handler, route.page]) {
839
873
  if (file) {
840
874
  const existing = routeTypes.get(file);
@@ -867,33 +901,33 @@ async function renderRouteTypeInfo(routes, pathPrefix = ".", adapter) {
867
901
  const pageWriter = writer.branch("page");
868
902
  const layoutWriter = writer.branch("layout");
869
903
  for (const [file, types] of routeTypes) {
870
- const path6 = `${pathPrefix}/${file.relativePath}`;
904
+ const modulePath = stripTsExtension(
905
+ normalizedRelativePath(outDir, file.filePath)
906
+ );
871
907
  const routeType = `Run.Routes[${types.join(" | ")}]`;
872
908
  switch (file.type) {
873
909
  case RoutableFileTypes.Handler:
874
- writeModuleDeclaration(handlerWriter, path6, routeType);
910
+ writeModuleDeclaration(handlerWriter, modulePath, routeType);
875
911
  break;
876
912
  case RoutableFileTypes.Middleware:
877
- writeModuleDeclaration(middlewareWriter, path6, routeType);
913
+ writeModuleDeclaration(middlewareWriter, modulePath, routeType);
878
914
  break;
879
915
  case RoutableFileTypes.Page:
880
- writeModuleDeclaration(pageWriter, path6, routeType);
916
+ writeModuleDeclaration(pageWriter, modulePath, routeType);
881
917
  break;
882
918
  case RoutableFileTypes.Layout:
883
919
  writeModuleDeclaration(
884
920
  layoutWriter,
885
- path6,
921
+ modulePath,
886
922
  routeType,
887
923
  `
888
- export interface Input {
889
- [Run.ContentKeyFor<typeof import('${path6}')>]: Marko.Body;
890
- }`
924
+ export interface Input extends Run.LayoutInput<typeof import("${modulePath}")> {}`
891
925
  );
892
926
  break;
893
927
  case RoutableFileTypes.Error:
894
928
  writeModuleDeclaration(
895
929
  writer,
896
- path6,
930
+ modulePath,
897
931
  "globalThis.MarkoRun.Route",
898
932
  `
899
933
  export interface Input {
@@ -902,7 +936,7 @@ async function renderRouteTypeInfo(routes, pathPrefix = ".", adapter) {
902
936
  );
903
937
  break;
904
938
  case RoutableFileTypes.NotFound:
905
- writeModuleDeclaration(writer, path6, "Run.Route");
939
+ writeModuleDeclaration(writer, modulePath, "Run.Route");
906
940
  break;
907
941
  }
908
942
  }
@@ -910,40 +944,15 @@ async function renderRouteTypeInfo(routes, pathPrefix = ".", adapter) {
910
944
  middlewareWriter.join();
911
945
  pageWriter.join();
912
946
  layoutWriter.join();
913
- writer.writeBlockStart(`
914
- type Routes = {`);
915
- for (const route of routes.list) {
916
- const { meta, handler, page } = route;
917
- if (page || handler) {
918
- const verbs = [];
919
- if (page || ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes("get"))) {
920
- verbs.push(`"get"`);
921
- }
922
- if ((_b = handler == null ? void 0 : handler.verbs) == null ? void 0 : _b.includes("post")) {
923
- verbs.push(`"post"`);
924
- }
925
- let routeType = `{ verb: ${verbs.join(" | ")};`;
926
- if (meta) {
927
- const metaPath = stripTsExtension(`${pathPrefix}/${meta.relativePath}`);
928
- let metaType = `typeof import("${metaPath}")`;
929
- if (/\.(ts|js|mjs)$/.test(meta.name)) {
930
- metaType += `["default"]`;
931
- }
932
- routeType += ` meta: ${metaType};`;
933
- }
934
- writer.writeLines(`"${route.key}": ${routeType} };`);
935
- }
936
- }
937
- writer.writeBlockEnd("}");
938
947
  return writer.end();
939
948
  }
940
- function writeModuleDeclaration(writer, path6, routeType, moduleTypes) {
941
- writer.writeLines("").write(`declare module "${stripTsExtension(path6)}" {`);
949
+ function writeModuleDeclaration(writer, name, routeType, moduleTypes) {
950
+ writer.writeLines("").write(`declare module "${name}" {`);
942
951
  if (moduleTypes) {
943
952
  writer.write(moduleTypes);
944
953
  }
945
954
  if (routeType) {
946
- const isMarko = path6.endsWith(".marko");
955
+ const isMarko = name.endsWith(".marko");
947
956
  writer.write(`
948
957
  namespace MarkoRun {
949
958
  export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
@@ -957,21 +966,15 @@ function writeModuleDeclaration(writer, path6, routeType, moduleTypes) {
957
966
  writer.writeLines(`
958
967
  }`);
959
968
  }
960
- function pathToURLPatternString(path6) {
961
- return path6.replace(/\/\$(\$?)([^/]*)/g, (_, catchAll, name) => {
962
- name = decodeURIComponent(name);
963
- return catchAll ? `/:${name || "rest"}*` : `/:${name}`;
964
- });
965
- }
966
969
  function createRouteTrie(routes) {
967
970
  const root = {
968
971
  key: ""
969
972
  };
970
- function insert(path6, route) {
973
+ function insert(path8, route) {
971
974
  let node = root;
972
- for (const segment of path6.segments) {
975
+ for (const segment of path8.segments) {
973
976
  if (segment === "$$") {
974
- node.catchAll ?? (node.catchAll = { route, path: path6 });
977
+ node.catchAll ?? (node.catchAll = { route, path: path8 });
975
978
  return;
976
979
  } else if (segment === "$") {
977
980
  node = node.dynamic ?? (node.dynamic = {
@@ -989,17 +992,18 @@ function createRouteTrie(routes) {
989
992
  node = next;
990
993
  }
991
994
  }
992
- node.path ?? (node.path = path6);
995
+ node.path ?? (node.path = path8);
993
996
  node.route ?? (node.route = route);
994
997
  }
995
998
  for (const route of routes) {
996
- for (const path6 of route.paths) {
997
- insert(path6, route);
998
- }
999
+ insert(route.path, route);
999
1000
  }
1000
1001
  return root;
1001
1002
  }
1002
1003
 
1004
+ // src/vite/routes/builder.ts
1005
+ import path3 from "path";
1006
+
1003
1007
  // src/vite/routes/parse.ts
1004
1008
  function parseFlatRoute(pattern) {
1005
1009
  if (!pattern) throw new Error("Empty pattern");
@@ -1014,53 +1018,72 @@ function parseFlatRoute(pattern) {
1014
1018
  ]);
1015
1019
  function parse2(basePaths, group) {
1016
1020
  const pathMap = /* @__PURE__ */ new Map();
1017
- const delimiters = group ? ").," : ".,";
1021
+ const delimiters = group ? "`).," : "`.,";
1018
1022
  let charCode;
1019
1023
  let segmentStart = i;
1020
1024
  let type;
1021
1025
  let current;
1026
+ let escaped = "";
1027
+ let escapeStart = 0;
1022
1028
  do {
1023
1029
  charCode = pattern.charCodeAt(i);
1024
- if (charCode === 41 && group) {
1030
+ if (charCode === 96 /* Escape */) {
1031
+ if (escapeStart) {
1032
+ escaped += pattern.slice(segmentStart, escapeStart - 1) + pattern.slice(escapeStart, i);
1033
+ escapeStart = 0;
1034
+ segmentStart = ++i;
1035
+ } else {
1036
+ escapeStart = i + 1;
1037
+ i = pattern.indexOf("`", escapeStart);
1038
+ if (i < 0) break;
1039
+ }
1040
+ } else if (charCode === 41 /* GroupEnd */ && group) {
1025
1041
  break;
1026
- } else if (charCode === 44) {
1042
+ } else if (charCode === 44 /* Alternate */) {
1027
1043
  if (!current) {
1028
1044
  segmentEnd(
1029
- basePaths.map((path6) => ({
1030
- ...path6,
1031
- segments: path6.segments.slice()
1045
+ basePaths.map((path8) => ({
1046
+ ...path8,
1047
+ segments: path8.segments.slice()
1032
1048
  })),
1033
- "",
1049
+ escaped,
1034
1050
  "_",
1035
1051
  pathMap
1036
1052
  );
1037
1053
  } else {
1038
- segmentEnd(current, pattern.slice(segmentStart, i), type, pathMap);
1054
+ segmentEnd(
1055
+ current,
1056
+ escaped + pattern.slice(segmentStart, i),
1057
+ type,
1058
+ pathMap
1059
+ );
1039
1060
  }
1040
1061
  current = void 0;
1041
1062
  type = void 0;
1063
+ escaped = "";
1042
1064
  segmentStart = ++i;
1043
- } else if (charCode === 46) {
1065
+ } else if (charCode === 46 /* Directory */) {
1044
1066
  if (current) {
1045
- segmentEnd(current, pattern.slice(segmentStart, i), type);
1067
+ segmentEnd(current, escaped + pattern.slice(segmentStart, i), type);
1046
1068
  }
1047
1069
  type = void 0;
1070
+ escaped = "";
1048
1071
  segmentStart = ++i;
1049
- } else if (charCode === 40) {
1072
+ } else if (charCode === 40 /* GroupStart */) {
1050
1073
  const groupPaths = parse2(current || basePaths, ++i);
1051
1074
  if (groupPaths.length) {
1052
1075
  current = groupPaths;
1053
1076
  }
1054
1077
  segmentStart = ++i;
1055
1078
  } else {
1056
- if (charCode === 95) {
1079
+ if (charCode === 95 /* Pathless */) {
1057
1080
  type = "_";
1058
- } else if (charCode === 36) {
1081
+ } else if (charCode === 36 /* Dynamic */) {
1059
1082
  type = pattern.charCodeAt(i + 1) === 36 ? "$$" : "$";
1060
1083
  }
1061
- current ?? (current = basePaths.map((path6) => ({
1062
- ...path6,
1063
- segments: path6.segments.slice()
1084
+ current ?? (current = basePaths.map((path8) => ({
1085
+ ...path8,
1086
+ segments: path8.segments.slice()
1064
1087
  })));
1065
1088
  i = len;
1066
1089
  for (const char of delimiters) {
@@ -1071,7 +1094,12 @@ function parseFlatRoute(pattern) {
1071
1094
  }
1072
1095
  }
1073
1096
  } while (i < len);
1074
- if (group && charCode !== 41) {
1097
+ if (escapeStart) {
1098
+ throw new Error(
1099
+ `Invalid route pattern: unclosed escape '${pattern.slice(escapeStart)}' in '${pattern}'`
1100
+ );
1101
+ }
1102
+ if (group && charCode !== 41 /* GroupEnd */) {
1075
1103
  throw new Error(
1076
1104
  `Invalid route pattern: group was not closed '${pattern.slice(
1077
1105
  group
@@ -1080,16 +1108,21 @@ function parseFlatRoute(pattern) {
1080
1108
  }
1081
1109
  if (!current) {
1082
1110
  segmentEnd(
1083
- basePaths.map((path6) => ({
1084
- ...path6,
1085
- segments: path6.segments.slice()
1111
+ basePaths.map((path8) => ({
1112
+ ...path8,
1113
+ segments: path8.segments.slice()
1086
1114
  })),
1087
- "",
1088
- "_",
1115
+ escaped,
1116
+ void 0,
1089
1117
  pathMap
1090
1118
  );
1091
1119
  } else {
1092
- segmentEnd(current, pattern.slice(segmentStart, i), type, pathMap);
1120
+ segmentEnd(
1121
+ current,
1122
+ escaped + pattern.slice(segmentStart, i),
1123
+ type,
1124
+ pathMap
1125
+ );
1093
1126
  }
1094
1127
  return [...pathMap.values()];
1095
1128
  }
@@ -1098,17 +1131,17 @@ function parseFlatRoute(pattern) {
1098
1131
  if (raw) {
1099
1132
  segment = {
1100
1133
  raw,
1101
- name: raw,
1134
+ name: normalizeSegment(raw),
1102
1135
  type
1103
1136
  };
1104
1137
  if (type === "$" || type === "$$") {
1105
1138
  segment.name = type;
1106
- segment.param = raw.slice(type.length);
1139
+ segment.param = normalizeParam(raw.slice(type.length));
1107
1140
  }
1108
1141
  }
1109
- for (const path6 of paths) {
1142
+ for (const path8 of paths) {
1110
1143
  if (segment) {
1111
- if (path6.isCatchall) {
1144
+ if (path8.isCatchall) {
1112
1145
  throw new Error(
1113
1146
  `Invalid route pattern: nested segments are not allowed after a catch-all parameter. Found '.' following '${pattern.slice(
1114
1147
  0,
@@ -1116,26 +1149,36 @@ function parseFlatRoute(pattern) {
1116
1149
  )}' in '${pattern}'.`
1117
1150
  );
1118
1151
  }
1119
- path6.segments.push(segment);
1120
- path6.id += path6.id === "/" ? segment.name : `/${segment.name}`;
1152
+ path8.segments.push(segment);
1153
+ path8.id += path8.id === "/" ? segment.name : `/${segment.name}`;
1121
1154
  if (type === "$$") {
1122
- path6.isCatchall = true;
1155
+ path8.isCatchall = true;
1123
1156
  }
1124
1157
  }
1125
1158
  if (map) {
1126
- if (map.has(path6.id)) {
1127
- const existing = map.get(path6.id);
1159
+ if (map.has(path8.id)) {
1160
+ const existing = map.get(path8.id);
1128
1161
  const existingExpansion = existing.segments.map((s) => s.raw).join(".");
1129
- const currentExpansion = path6.segments.map((s) => s.raw).join(".");
1162
+ const currentExpansion = path8.segments.map((s) => s.raw).join(".");
1130
1163
  throw new Error(
1131
- `Invalid route pattern: route '${path6.id}' is ambiguous. Expansion '${currentExpansion}' collides with '${existingExpansion}' in '${pattern}'.`
1164
+ `Invalid route pattern: route '${path8.id}' is ambiguous. Expansion '${currentExpansion}' collides with '${existingExpansion}' in '${pattern}'.`
1132
1165
  );
1133
1166
  }
1134
- map.set(path6.id, path6);
1167
+ map.set(path8.id, path8);
1135
1168
  }
1136
1169
  }
1137
1170
  }
1138
1171
  }
1172
+ function normalizeParam(segment) {
1173
+ const normalized = normalizeSegment(segment);
1174
+ return /^\$/.test(normalized) ? `\`${normalized}\`` : normalized;
1175
+ }
1176
+ function normalizeSegment(segment) {
1177
+ return decodeURIComponent(segment).replace(
1178
+ /[/?#]/g,
1179
+ (char) => "%" + char.charCodeAt(0).toString(16)
1180
+ );
1181
+ }
1139
1182
 
1140
1183
  // src/vite/routes/vdir.ts
1141
1184
  var _dirs, _pathlessDirs;
@@ -1146,14 +1189,12 @@ var _VDir = class _VDir {
1146
1189
  __publicField(this, "parent");
1147
1190
  __publicField(this, "source");
1148
1191
  __publicField(this, "path");
1149
- __publicField(this, "fullPath");
1150
1192
  __publicField(this, "segment");
1151
1193
  __publicField(this, "files");
1152
1194
  if (!parent || !segment) {
1153
1195
  this.parent = null;
1154
1196
  this.source = null;
1155
1197
  this.path = "/";
1156
- this.fullPath = "/";
1157
1198
  this.segment = {
1158
1199
  raw: "",
1159
1200
  name: ""
@@ -1161,12 +1202,8 @@ var _VDir = class _VDir {
1161
1202
  } else {
1162
1203
  this.parent = parent;
1163
1204
  this.source = source;
1164
- this.path = parent.path + (parent.path === "/" ? segment.name : `/${segment.name}`);
1165
- this.fullPath = parent.fullPath + (parent.fullPath === "/" ? segment.name : `/${segment.name}`);
1166
- if (segment.param) {
1167
- this.fullPath += segment.param;
1168
- }
1169
1205
  this.segment = segment;
1206
+ this.path = parent.path + (parent.path === "/" ? "" : "/") + segment.name;
1170
1207
  }
1171
1208
  }
1172
1209
  get pathInfo() {
@@ -1179,17 +1216,18 @@ var _VDir = class _VDir {
1179
1216
  for (const { segment } of this) {
1180
1217
  const { type, name, param } = segment;
1181
1218
  if (name && type !== "_") {
1182
- value.id += sep + (type || name);
1219
+ value.id += sep + name;
1183
1220
  value.path += sep + name;
1184
1221
  value.isEnd = type === "$$";
1185
1222
  if (param) {
1186
- value.path += param;
1223
+ const unescapedParam = param.charAt(0) === "`" ? param.slice(1, -1) : param;
1187
1224
  const index = type === "$$" ? null : value.segments.length;
1188
1225
  if (!value.params) {
1189
- value.params = { [param]: index };
1226
+ value.params = { [unescapedParam]: index };
1190
1227
  } else if (!(param in value.params)) {
1191
- value.params[param] = index;
1228
+ value.params[unescapedParam] = index;
1192
1229
  }
1230
+ value.path += param;
1193
1231
  }
1194
1232
  value.segments.push(name);
1195
1233
  sep = "/";
@@ -1201,11 +1239,11 @@ var _VDir = class _VDir {
1201
1239
  });
1202
1240
  return value;
1203
1241
  }
1204
- addDir(path6, segment) {
1242
+ addDir(path8, segment) {
1205
1243
  const map = segment.type === "_" ? __privateGet(this, _pathlessDirs) ?? __privateSet(this, _pathlessDirs, /* @__PURE__ */ new Map()) : __privateGet(this, _dirs) ?? __privateSet(this, _dirs, /* @__PURE__ */ new Map());
1206
1244
  const key = segment.type === "$" ? segment.raw : segment.name;
1207
1245
  if (!map.has(key)) {
1208
- const dir = new _VDir(this, segment, path6);
1246
+ const dir = new _VDir(this, segment, path8);
1209
1247
  map.set(key, dir);
1210
1248
  return dir;
1211
1249
  }
@@ -1221,15 +1259,15 @@ var _VDir = class _VDir {
1221
1259
  const existing = this.files.get(file.type);
1222
1260
  if (existing !== file) {
1223
1261
  throw new Error(
1224
- `Duplicate file type '${file.type}' added at path '${this.path}'. File '${file.importPath}' collides with '${existing.importPath}'.`
1262
+ `Duplicate file type ${file.type} added at path ${this.path}. File ${file.filePath} collides with ${existing.filePath}.`
1225
1263
  );
1226
1264
  } else if (file.type === RoutableFileTypes.Page || file.type === RoutableFileTypes.Handler) {
1227
1265
  throw new Error(
1228
- `Ambiguous path definition: route '${this.path}' is defined multiple times by ${file.importPath}`
1266
+ `Ambiguous path definition: route ${this.path} is defined multiple times by ${file.filePath}`
1229
1267
  );
1230
1268
  }
1231
1269
  throw new Error(
1232
- `Ambiguous path definition: file '${this.path}' is included multiple times by ${file.importPath}`
1270
+ `Ambiguous path definition: file ${this.path} is included multiple times by ${file.filePath}`
1233
1271
  );
1234
1272
  }
1235
1273
  }
@@ -1251,10 +1289,10 @@ var _VDir = class _VDir {
1251
1289
  const dirs = [];
1252
1290
  const unique = /* @__PURE__ */ new Map();
1253
1291
  for (const root of roots) {
1254
- for (const path6 of paths) {
1292
+ for (const path8 of paths) {
1255
1293
  let dir = root;
1256
- for (const segment of path6.segments) {
1257
- dir = dir.addDir(path6, segment);
1294
+ for (const segment of path8.segments) {
1295
+ dir = dir.addDir(path8, segment);
1258
1296
  }
1259
1297
  const existing = unique.get(dir.path);
1260
1298
  if (existing) {
@@ -1267,7 +1305,7 @@ var _VDir = class _VDir {
1267
1305
  }
1268
1306
  }
1269
1307
  throw new Error(
1270
- `Ambiguous directory structure: '${sourcePath}${path6.source}' defines '${dir.path}' multiple times.`
1308
+ `Ambiguous directory structure: ${sourcePath}${path8.source} defines ${dir.path} multiple times.`
1271
1309
  );
1272
1310
  } else {
1273
1311
  unique.set(dir.path, dir);
@@ -1293,13 +1331,11 @@ function matchRoutableFile(filename) {
1293
1331
  const match = filename.match(routeableFileRegex);
1294
1332
  return match && (match[1] || match[3]).toLowerCase();
1295
1333
  }
1296
- function isSpecialType(type) {
1297
- return type === RoutableFileTypes.NotFound || type === RoutableFileTypes.Error;
1298
- }
1299
- async function buildRoutes(sources) {
1334
+ async function buildRoutes(sources, outDir) {
1300
1335
  const uniqueRoutes = /* @__PURE__ */ new Map();
1301
1336
  const routes = [];
1302
1337
  const special = {};
1338
+ const seenKeys = /* @__PURE__ */ new Map();
1303
1339
  const middlewares = /* @__PURE__ */ new Set();
1304
1340
  const unusedFiles = /* @__PURE__ */ new Set();
1305
1341
  const currentLayouts = /* @__PURE__ */ new Set();
@@ -1307,13 +1343,13 @@ async function buildRoutes(sources) {
1307
1343
  const root = new VDir();
1308
1344
  const dirStack = [];
1309
1345
  let basePath;
1310
- let importPrefix;
1311
1346
  let activeDirs;
1312
1347
  let isBaseDir;
1313
1348
  let nextFileId = 1;
1314
1349
  let nextRouteIndex = 1;
1315
1350
  const walkOptions = {
1316
- onEnter({ name }) {
1351
+ onEnter(dir) {
1352
+ let { name } = dir;
1317
1353
  const prevDirStackLength = dirStack.length;
1318
1354
  if (isBaseDir) {
1319
1355
  isBaseDir = false;
@@ -1332,15 +1368,16 @@ async function buildRoutes(sources) {
1332
1368
  dirStack.length = prevDirStackLength;
1333
1369
  };
1334
1370
  },
1335
- onFile({ name, path: path6 }) {
1371
+ onFile(file) {
1372
+ const { name } = file;
1336
1373
  const match = name.match(routeableFileRegex);
1337
1374
  if (!match) {
1338
1375
  return;
1339
1376
  }
1340
1377
  const type = (match[1] || match[3]).toLowerCase();
1341
- if (dirStack.length && isSpecialType(type)) {
1378
+ if (dirStack.length && (type === RoutableFileTypes.NotFound || type === RoutableFileTypes.Error)) {
1342
1379
  console.warn(
1343
- `Special pages '${RoutableFileTypes.NotFound}' and '${RoutableFileTypes.Error}' are only considered in the root directory - ignoring ${path6}`
1380
+ `Special pages '${RoutableFileTypes.NotFound}' and '${RoutableFileTypes.Error}' are only considered in the root directory - ignoring ${file.path}`
1344
1381
  );
1345
1382
  return;
1346
1383
  }
@@ -1349,19 +1386,15 @@ async function buildRoutes(sources) {
1349
1386
  const paths = parseFlatRoute(name.slice(0, match.index));
1350
1387
  dirs = VDir.addPaths(activeDirs, paths);
1351
1388
  }
1352
- const dirPath = dirStack.join("/");
1353
- const relativePath = dirPath ? `${dirPath}/${name}` : name;
1354
- const file = {
1389
+ const routableFile = {
1355
1390
  id: String(nextFileId++),
1356
1391
  name,
1357
1392
  type,
1358
- filePath: path6,
1359
- relativePath,
1360
- importPath: `${importPrefix}/${relativePath}`,
1393
+ filePath: file.path,
1361
1394
  verbs: type === RoutableFileTypes.Page ? ["get", "head"] : void 0
1362
1395
  };
1363
1396
  for (const dir of dirs) {
1364
- dir.addFile(file);
1397
+ dir.addFile(routableFile);
1365
1398
  }
1366
1399
  }
1367
1400
  };
@@ -1369,7 +1402,6 @@ async function buildRoutes(sources) {
1369
1402
  sources = [sources];
1370
1403
  }
1371
1404
  for (const source of sources) {
1372
- importPrefix = source.importPrefix ? source.importPrefix.replace(/^\/+|\/+$/g, "") : "";
1373
1405
  basePath = source.basePath || "";
1374
1406
  activeDirs = [root];
1375
1407
  isBaseDir = true;
@@ -1389,7 +1421,8 @@ async function buildRoutes(sources) {
1389
1421
  layout = dir.files.get(RoutableFileTypes.Layout);
1390
1422
  const handler = dir.files.get(RoutableFileTypes.Handler);
1391
1423
  const page = dir.files.get(RoutableFileTypes.Page);
1392
- let hasSpecial = false;
1424
+ const pathInfo = dir.pathInfo;
1425
+ let layoutsUsed = false;
1393
1426
  if (middleware) {
1394
1427
  if (currentMiddleware.has(middleware)) {
1395
1428
  middleware = void 0;
@@ -1406,64 +1439,64 @@ async function buildRoutes(sources) {
1406
1439
  unusedFiles.add(layout);
1407
1440
  }
1408
1441
  }
1442
+ if (dir === root) {
1443
+ for (const [type, file] of dir.files) {
1444
+ if (type === RoutableFileTypes.NotFound || type === RoutableFileTypes.Error) {
1445
+ special[type] = {
1446
+ index: nextRouteIndex++,
1447
+ key: type,
1448
+ path: dir.pathInfo,
1449
+ middleware: [],
1450
+ layouts: [...currentLayouts],
1451
+ page: file,
1452
+ templateFilePath: currentLayouts.size ? path3.join(outDir, `${type}.marko`) : void 0
1453
+ };
1454
+ layoutsUsed = true;
1455
+ }
1456
+ }
1457
+ }
1409
1458
  if (page || handler) {
1410
- const path6 = dir.pathInfo;
1411
- if (uniqueRoutes.has(path6.id)) {
1412
- const existing = uniqueRoutes.get(path6.id);
1459
+ if (uniqueRoutes.has(pathInfo.id)) {
1460
+ const existing = uniqueRoutes.get(pathInfo.id);
1413
1461
  const route = routes[existing.index];
1414
1462
  const existingFiles = [route.handler, route.page].filter(Boolean).map((f) => f.filePath);
1415
1463
  const currentFiles = [handler, page].filter(Boolean).map((f) => f.filePath);
1416
- throw new Error(`Duplicate routes for path '${path6.id}' were defined. A route established by:
1417
- ${existingFiles.join(" and ")} via '${existing.dir.fullPath}'
1418
- collides with
1419
- ${currentFiles.join(" and ")} via '${dir.fullPath}'
1420
- `);
1464
+ throw new Error(
1465
+ `Duplicate routes for path ${pathInfo.id} were defined. A route established by: "${existingFiles.join(" and ")}" collides with "${currentFiles.join(" and ")}"`
1466
+ );
1467
+ }
1468
+ uniqueRoutes.set(pathInfo.id, { dir, index: routes.length });
1469
+ let key = pathInfo.segments.map(replaceInvalidFilenameChars).concat("route").join("/");
1470
+ const keyCount = (seenKeys.get(key) || 0) + 1;
1471
+ seenKeys.set(key, keyCount);
1472
+ if (keyCount > 1) {
1473
+ key += keyCount;
1421
1474
  }
1422
- uniqueRoutes.set(path6.id, { dir, index: routes.length });
1423
1475
  routes.push({
1424
1476
  index: nextRouteIndex++,
1425
- key: dir.fullPath,
1426
- paths: [path6],
1477
+ key,
1478
+ path: pathInfo,
1427
1479
  middleware: [...currentMiddleware],
1428
1480
  layouts: page ? [...currentLayouts] : [],
1429
1481
  meta: dir.files.get(RoutableFileTypes.Meta),
1430
1482
  page,
1431
1483
  handler,
1432
- entryName: `${markoRunFilePrefix}route` + (dir.path !== "/" ? dir.fullPath.replace(/\//g, ".").replace(/(%[A-Fa-f0-9]{2})+/g, "_") : "")
1484
+ templateFilePath: page && currentLayouts.size ? path3.join(outDir, key + ".marko") : void 0
1433
1485
  });
1434
- }
1435
- if (dir === root) {
1436
- for (const [type, file] of dir.files) {
1437
- if (isSpecialType(type)) {
1438
- hasSpecial = true;
1439
- special[type] = {
1440
- index: 0,
1441
- key: type,
1442
- paths: [],
1443
- middleware: [],
1444
- layouts: [...currentLayouts],
1445
- page: file,
1446
- entryName: `${markoRunFilePrefix}special.${type}`
1447
- };
1448
- }
1449
- }
1450
- }
1451
- if (handler || page) {
1486
+ layoutsUsed = !!page;
1452
1487
  for (const middleware2 of currentMiddleware) {
1453
1488
  middlewares.add(middleware2);
1454
1489
  unusedFiles.delete(middleware2);
1455
1490
  }
1456
1491
  }
1457
- if (page || hasSpecial) {
1492
+ if (layoutsUsed) {
1458
1493
  for (const layout2 of currentLayouts) {
1459
1494
  unusedFiles.delete(layout2);
1460
1495
  }
1461
1496
  }
1462
1497
  }
1463
- if (dir.dirs) {
1464
- for (const child of dir.dirs()) {
1465
- traverse(child);
1466
- }
1498
+ for (const childDir of dir.dirs()) {
1499
+ traverse(childDir);
1467
1500
  }
1468
1501
  if (middleware) {
1469
1502
  currentMiddleware.delete(middleware);
@@ -1473,10 +1506,13 @@ async function buildRoutes(sources) {
1473
1506
  }
1474
1507
  }
1475
1508
  }
1509
+ function replaceInvalidFilenameChars(str) {
1510
+ return str.replace(/[<>:"/\\|?*]+/g, "_");
1511
+ }
1476
1512
 
1477
1513
  // src/vite/routes/walk.ts
1478
1514
  import fs from "fs";
1479
- import path2 from "path";
1515
+ import path4 from "path";
1480
1516
  function createFSWalker(dir) {
1481
1517
  return async function walkFS({
1482
1518
  onEnter,
@@ -1491,7 +1527,7 @@ function createFSWalker(dir) {
1491
1527
  const entries = await fs.promises.readdir(dir2.path, {
1492
1528
  withFileTypes: true
1493
1529
  });
1494
- const prefix = dir2.path + path2.sep;
1530
+ const prefix = dir2.path + path4.sep;
1495
1531
  for (const entry of entries) {
1496
1532
  const walkEntry = {
1497
1533
  name: entry.name,
@@ -1514,7 +1550,7 @@ function createFSWalker(dir) {
1514
1550
  await walk(
1515
1551
  {
1516
1552
  path: dir,
1517
- name: path2.basename(dir)
1553
+ name: path4.basename(dir)
1518
1554
  },
1519
1555
  maxDepth
1520
1556
  );
@@ -1632,36 +1668,34 @@ function logRoutesTable(routes, bundle) {
1632
1668
  style: { compact: true }
1633
1669
  });
1634
1670
  for (const route of routes.list) {
1635
- for (const path6 of route.paths) {
1636
- const verbs = getVerbs(route, true);
1637
- let firstRow = true;
1638
- for (const verb of verbs) {
1639
- const entryType = [];
1640
- let size = "";
1641
- let verbCell = verbColor(verb)(verb.toUpperCase());
1642
- if (verb === "get" && !verbs.includes("head")) {
1643
- verbCell += kleur2.dim(`,${verbColor(verb)("HEAD")}`);
1644
- }
1645
- if (route.handler) {
1646
- entryType.push(kleur2.blue("handler"));
1647
- }
1648
- if (route.page && (verb === "get" || verb === "head")) {
1649
- entryType.push(kleur2.yellow("page"));
1650
- if (verb === "get") {
1651
- size = prettySize(computeRouteSize(route, bundle));
1652
- }
1653
- }
1654
- const row = [verbCell];
1655
- if (verbs.length === 1 || firstRow) {
1656
- row.push({ rowSpan: verbs.length, content: prettyPath(path6.path) });
1657
- firstRow = false;
1671
+ const verbs = getVerbs(route, true);
1672
+ let firstRow = true;
1673
+ for (const verb of verbs) {
1674
+ const entryType = [];
1675
+ let size = "";
1676
+ const verbCell = verbColor(verb)(verb.toUpperCase());
1677
+ if (route.handler) {
1678
+ entryType.push(kleur2.blue("handler"));
1679
+ }
1680
+ if (route.page && (verb === "get" || verb === "head")) {
1681
+ entryType.push(kleur2.yellow("page"));
1682
+ if (verb === "get") {
1683
+ size = prettySize(computeRouteSize(route, bundle));
1658
1684
  }
1659
- row.push(entryType.join(" -> "));
1660
- hasMiddleware && row.push(route.middleware.length || "");
1661
- hasMeta && row.push(route.meta ? "\u2713" : "");
1662
- row.push(size || "");
1663
- table.push(row);
1664
1685
  }
1686
+ const row = [verbCell];
1687
+ if (verbs.length === 1 || firstRow) {
1688
+ row.push({
1689
+ rowSpan: verbs.length,
1690
+ content: prettyPath(route.path.path)
1691
+ });
1692
+ firstRow = false;
1693
+ }
1694
+ row.push(entryType.join(" -> "));
1695
+ hasMiddleware && row.push(route.middleware.length || "");
1696
+ hasMeta && row.push(route.meta ? "\u2713" : "");
1697
+ row.push(size || "");
1698
+ table.push(row);
1665
1699
  }
1666
1700
  }
1667
1701
  for (const [key, route] of Object.entries(routes.special).sort()) {
@@ -1717,17 +1751,20 @@ function prettySize([bytes, compBytes]) {
1717
1751
  else str += kleur2.bold(kleur2.red(compSize));
1718
1752
  return str;
1719
1753
  }
1720
- function prettyPath(path6) {
1721
- return path6.replace(/\/\$\$(.*)$/, (_, p) => "/" + kleur2.bold(kleur2.dim(`*${p}`))).replace(/\/\$([^/]+)/g, (_, p) => "/" + kleur2.bold(kleur2.dim(`:${p}`)));
1754
+ function prettyPath(path8) {
1755
+ return path8.replace(
1756
+ /\/(\$\$?)(`?)([^/`]+)\2/g,
1757
+ (_, type, tick, key) => "/" + type + tick + kleur2.bold(kleur2.dim(key)) + tick
1758
+ );
1722
1759
  }
1723
1760
 
1724
1761
  // src/vite/utils/read-once-persisted-store.ts
1725
1762
  import { promises as fs2 } from "fs";
1726
1763
  import os from "os";
1727
- import path3 from "path";
1764
+ import path5 from "path";
1728
1765
  var noop = () => {
1729
1766
  };
1730
- var tmpFile = path3.join(os.tmpdir(), "marko-run-storage.json");
1767
+ var tmpFile = path5.join(os.tmpdir(), "marko-run-storage.json");
1731
1768
  var values = /* @__PURE__ */ new Map();
1732
1769
  var loadedFromDisk;
1733
1770
  var ReadOncePersistedStore = class {
@@ -1769,17 +1806,17 @@ process.once("beforeExit", (code) => {
1769
1806
 
1770
1807
  // src/vite/plugin.ts
1771
1808
  var debug = createDebug("@marko/run");
1772
- var __dirname = path4.dirname(fileURLToPath(import.meta.url));
1809
+ var __dirname = path6.dirname(fileURLToPath(import.meta.url));
1773
1810
  var PLUGIN_NAME_PREFIX = "marko-run-vite";
1774
- var POSIX_SEP = "/";
1775
- var WINDOWS_SEP = "\\";
1776
1811
  var CLIENT_OUT_DIR = "public";
1777
1812
  var MIDDLEWARE_FILENAME = `${markoRunFilePrefix}middleware.js`;
1778
1813
  var ROUTER_FILENAME = `${markoRunFilePrefix}router.js`;
1779
1814
  var defaultPort = Number(process.env.PORT || 3e3);
1780
- var normalizePath = path4.sep === WINDOWS_SEP ? (id) => id.replace(/\\/g, POSIX_SEP) : (id) => id;
1781
1815
  function markoRun(opts = {}) {
1782
- let { routesDir, adapter, ...markoVitePluginOptions } = opts;
1816
+ let routesDir;
1817
+ let adapter;
1818
+ let trailingSlashes;
1819
+ const { ...markoVitePluginOptions } = opts;
1783
1820
  let store;
1784
1821
  let root;
1785
1822
  let shouldEmptyOutDir = false;
@@ -1812,12 +1849,8 @@ function markoRun(opts = {}) {
1812
1849
  root,
1813
1850
  "{.tsconfig*,tsconfig*.json}"
1814
1851
  )))) {
1815
- const filepath = path4.join(typesDir, "routes.d.ts");
1816
- const data = await renderRouteTypeInfo(
1817
- routes2,
1818
- normalizePath(path4.relative(typesDir, resolvedRoutesDir)),
1819
- adapter
1820
- );
1852
+ const filepath = path6.join(typesDir, "routes.d.ts");
1853
+ const data = await renderRouteTypeInfo(routes2, typesDir, adapter);
1821
1854
  if (data !== typesFile || !fs3.existsSync(filepath)) {
1822
1855
  await ensureDir(typesDir);
1823
1856
  await fs3.promises.writeFile(filepath, typesFile = data);
@@ -1828,20 +1861,25 @@ function markoRun(opts = {}) {
1828
1861
  function buildVirtualFiles() {
1829
1862
  return buildVirtualFilesResult ?? (buildVirtualFilesResult = (async () => {
1830
1863
  virtualFiles.clear();
1831
- routes = await buildRoutes({
1832
- walker: createFSWalker(resolvedRoutesDir),
1833
- importPrefix: routesDir
1834
- });
1864
+ routes = await buildRoutes(
1865
+ {
1866
+ walker: createFSWalker(resolvedRoutesDir)
1867
+ },
1868
+ entryFilesDir
1869
+ );
1835
1870
  if (!routes.list.length) {
1836
1871
  throw new Error("No routes generated");
1837
1872
  }
1838
1873
  for (const route of routes.list) {
1839
- virtualFiles.set(path4.posix.join(root, `${route.entryName}.js`), "");
1874
+ virtualFiles.set(
1875
+ path6.posix.join(root, getRouteVirtualFileName(route)),
1876
+ ""
1877
+ );
1840
1878
  }
1841
1879
  if (routes.middleware.length) {
1842
- virtualFiles.set(path4.posix.join(root, MIDDLEWARE_FILENAME), "");
1880
+ virtualFiles.set(path6.posix.join(root, MIDDLEWARE_FILENAME), "");
1843
1881
  }
1844
- virtualFiles.set(path4.posix.join(root, ROUTER_FILENAME), "");
1882
+ virtualFiles.set(path6.posix.join(root, ROUTER_FILENAME), "");
1845
1883
  return routes;
1846
1884
  })());
1847
1885
  }
@@ -1855,111 +1893,78 @@ function markoRun(opts = {}) {
1855
1893
  fs3.rmSync(entryFilesDir, { recursive: true });
1856
1894
  }
1857
1895
  for (const route of routes2.list) {
1858
- const { handler, page, layouts } = route;
1859
- if (handler) {
1860
- const exports = await getExportsFromFile(context, handler.filePath);
1861
- handler.verbs = [];
1896
+ if (route.handler) {
1897
+ const exports = await getExportsFromFile(
1898
+ context,
1899
+ route.handler.filePath
1900
+ );
1901
+ route.handler.verbs = [];
1862
1902
  for (const name of exports) {
1863
1903
  const verb = name.toLowerCase();
1864
1904
  if (name === verb.toUpperCase() && httpVerbs.includes(verb)) {
1865
- handler.verbs.push(verb);
1905
+ route.handler.verbs.push(verb);
1866
1906
  }
1867
1907
  }
1868
- if (!handler.verbs.length) {
1908
+ if (!route.handler.verbs.length) {
1869
1909
  context.warn(
1870
- `Did not find any http verb exports in handler '${path4.relative(root, handler.filePath)}' - expected ${httpVerbs.map((v) => v.toUpperCase()).join(", ")}`
1910
+ `Did not find any http verb exports in ${path6.relative(root, route.handler.filePath)} - expected ${httpVerbs.map((v) => v.toUpperCase()).join(", ")}`
1871
1911
  );
1872
1912
  }
1873
1913
  }
1874
- if (page) {
1875
- if (layouts.length) {
1876
- const relativePath = path4.relative(
1877
- resolvedRoutesDir,
1878
- page.filePath
1879
- );
1880
- const routeFileDir = path4.join(entryFilesDir, relativePath, "..");
1881
- const routeFileRelativePathPosix = normalizePath(
1882
- path4.relative(routeFileDir, root)
1883
- );
1884
- fs3.mkdirSync(routeFileDir, { recursive: true });
1885
- const pageNameIndex = page.name.indexOf("+page");
1886
- const pageNamePrefix = pageNameIndex > 0 ? `${page.name.slice(0, pageNameIndex)}.` : "";
1887
- fs3.writeFileSync(
1888
- route.templateFilePath = path4.join(
1889
- routeFileDir,
1890
- pageNamePrefix + "route.marko"
1891
- ),
1892
- renderRouteTemplate(
1893
- route,
1894
- (to) => path4.posix.join(routeFileRelativePathPosix, to)
1895
- )
1896
- );
1897
- } else {
1898
- route.templateFilePath = page.filePath;
1899
- }
1914
+ if (route.templateFilePath) {
1915
+ fs3.mkdirSync(path6.dirname(route.templateFilePath), {
1916
+ recursive: true
1917
+ });
1918
+ fs3.writeFileSync(
1919
+ route.templateFilePath,
1920
+ renderRouteTemplate(route, root)
1921
+ );
1900
1922
  }
1901
1923
  virtualFiles.set(
1902
- path4.posix.join(root, `${route.entryName}.js`),
1903
- renderRouteEntry(route, relativeEntryFilesDirPosix)
1924
+ path6.posix.join(root, getRouteVirtualFileName(route)),
1925
+ renderRouteEntry(route, root)
1904
1926
  );
1905
1927
  }
1906
1928
  for (const route of Object.values(routes2.special)) {
1907
- const { page, layouts, key } = route;
1908
- if (page) {
1909
- if (layouts.length) {
1910
- const relativePath = path4.relative(
1911
- resolvedRoutesDir,
1912
- page.filePath
1913
- );
1914
- const routeFileDir = path4.join(entryFilesDir, relativePath, "..");
1915
- const routeFileRelativePathPosix = normalizePath(
1916
- path4.relative(routeFileDir, root)
1917
- );
1918
- fs3.mkdirSync(routeFileDir, { recursive: true });
1919
- fs3.writeFileSync(
1920
- route.templateFilePath = path4.join(
1921
- routeFileDir,
1922
- `route.${key}.marko`
1923
- ),
1924
- renderRouteTemplate(
1925
- route,
1926
- (to) => path4.posix.join(routeFileRelativePathPosix, to)
1927
- )
1928
- );
1929
- } else {
1930
- route.templateFilePath = page.filePath;
1931
- }
1929
+ if (route.templateFilePath) {
1930
+ fs3.mkdirSync(path6.dirname(route.templateFilePath), {
1931
+ recursive: true
1932
+ });
1933
+ fs3.writeFileSync(
1934
+ route.templateFilePath,
1935
+ renderRouteTemplate(route, root)
1936
+ );
1932
1937
  }
1933
1938
  }
1934
1939
  if (routes2.middleware.length) {
1935
1940
  for (const middleware of routes2.middleware) {
1936
1941
  if (!(await getExportsFromFile(context, middleware.filePath)).includes("default")) {
1937
1942
  context.warn(
1938
- `Did not find a default export in middleware '${path4.relative(root, middleware.filePath)}'`
1943
+ `Did not find a default export in middleware '${path6.relative(root, middleware.filePath)}'`
1939
1944
  );
1940
1945
  }
1941
1946
  }
1942
1947
  virtualFiles.set(
1943
- path4.posix.join(root, MIDDLEWARE_FILENAME),
1944
- renderMiddleware(routes2.middleware)
1948
+ path6.posix.join(root, MIDDLEWARE_FILENAME),
1949
+ renderMiddleware(routes2.middleware, root)
1945
1950
  );
1946
1951
  }
1947
1952
  virtualFiles.set(
1948
- path4.posix.join(root, ROUTER_FILENAME),
1949
- renderRouter(routes2, relativeEntryFilesDirPosix, {
1950
- trailingSlashes: opts.trailingSlashes || "RedirectWithout"
1953
+ path6.posix.join(root, ROUTER_FILENAME),
1954
+ renderRouter(routes2, root, {
1955
+ trailingSlashes
1951
1956
  })
1952
1957
  );
1953
1958
  await writeTypesFile(routes2);
1954
1959
  if (adapter == null ? void 0 : adapter.routesGenerated) {
1955
- await adapter.routesGenerated(
1956
- routes2,
1957
- new Map(virtualFiles.entries()),
1958
- {
1960
+ await adapter.routesGenerated({
1961
+ routes: routes2,
1962
+ virtualFiles: new Map(virtualFiles.entries()),
1963
+ meta: {
1959
1964
  buildTime: times.routesBuild,
1960
1965
  renderTime: times.routesRender
1961
1966
  }
1962
- );
1967
+ });
1963
1968
  if (!isBuild) {
1964
1969
  await ((_a = opts == null ? void 0 : opts.emitRoutes) == null ? void 0 : _a.call(opts, routes2.list));
1965
1970
  }
@@ -1969,7 +1974,7 @@ function markoRun(opts = {}) {
1969
1974
  throw err;
1970
1975
  }
1971
1976
  virtualFiles.set(
1972
- path4.posix.join(root, ROUTER_FILENAME),
1977
+ path6.posix.join(root, ROUTER_FILENAME),
1973
1978
  `throw ${JSON.stringify(prepareError(err))}`
1974
1979
  );
1975
1980
  }
@@ -2006,27 +2011,28 @@ function markoRun(opts = {}) {
2006
2011
  }
2007
2012
  }
2008
2013
  routesDir = opts.routesDir || "src/routes";
2014
+ trailingSlashes = opts.trailingSlashes || "RedirectWithout";
2009
2015
  store = new ReadOncePersistedStore(
2010
2016
  `vite-marko-run${opts.runtimeId ? `-${opts.runtimeId}` : ""}`
2011
2017
  );
2012
2018
  markoVitePluginOptions.runtimeId = opts.runtimeId;
2013
2019
  markoVitePluginOptions.basePathVar = opts.basePathVar;
2014
- resolvedRoutesDir = path4.resolve(root, routesDir);
2015
- outputDir = path4.join(root, ((_d = config2.build) == null ? void 0 : _d.outDir) || "dist");
2016
- entryFilesDir = path4.join(outputDir, ".marko-run");
2020
+ resolvedRoutesDir = path6.resolve(root, routesDir);
2021
+ outputDir = path6.join(root, ((_d = config2.build) == null ? void 0 : _d.outDir) || "dist");
2022
+ entryFilesDir = path6.join(outputDir, ".marko-run");
2017
2023
  entryFilesDirPosix = normalizePath(entryFilesDir);
2018
2024
  relativeEntryFilesDirPosix = normalizePath(
2019
- path4.relative(root, entryFilesDir)
2025
+ path6.relative(root, entryFilesDir)
2020
2026
  );
2021
- typesDir = path4.join(root, ".marko-run");
2022
- devEntryFile = path4.join(root, "index.html");
2027
+ typesDir = path6.join(root, ".marko-run");
2028
+ devEntryFile = path6.join(root, "index.html");
2023
2029
  devEntryFilePosix = normalizePath(devEntryFile);
2024
2030
  let outDir = ((_e = config2.build) == null ? void 0 : _e.outDir) || "dist";
2025
2031
  const assetsDir = ((_f = config2.build) == null ? void 0 : _f.assetsDir) || "assets";
2026
2032
  let rollupOutputOptions = (_h = (_g = config2.build) == null ? void 0 : _g.rollupOptions) == null ? void 0 : _h.output;
2027
2033
  if (isBuild) {
2028
2034
  if (!isSSRBuild) {
2029
- outDir = path4.join(outDir, CLIENT_OUT_DIR);
2035
+ outDir = path6.join(outDir, CLIENT_OUT_DIR);
2030
2036
  }
2031
2037
  const defaultRollupOutputOptions = {
2032
2038
  assetFileNames({ name }) {
@@ -2151,7 +2157,7 @@ function markoRun(opts = {}) {
2151
2157
  devServer.watcher.on("all", async (type, filename) => {
2152
2158
  seenErrors.clear();
2153
2159
  const routableFileType = matchRoutableFile(
2154
- path4.parse(filename).base
2160
+ path6.parse(filename).base
2155
2161
  );
2156
2162
  if (filename.startsWith(resolvedRoutesDir) && routableFileType) {
2157
2163
  if (type === "add" || type === "unlink" || type === "change" && (routableFileType === RoutableFileTypes.Handler || routableFileType === RoutableFileTypes.Middleware)) {
@@ -2201,19 +2207,19 @@ function markoRun(opts = {}) {
2201
2207
  },
2202
2208
  async resolveId(importee, importer) {
2203
2209
  if (importee === "@marko/run/router") {
2204
- return normalizePath(path4.resolve(root, ROUTER_FILENAME));
2210
+ return normalizePath(path6.resolve(root, ROUTER_FILENAME));
2205
2211
  } else if (importee.endsWith(".marko") && importee.includes(relativeEntryFilesDirPosix)) {
2206
2212
  if (!importee.startsWith(root)) {
2207
- importee = path4.resolve(root, "." + importee);
2213
+ importee = path6.resolve(root, "." + importee);
2208
2214
  }
2209
2215
  return normalizePath(importee);
2210
2216
  }
2211
2217
  let virtualFilePath;
2212
2218
  if (importee.startsWith(virtualFilePrefix)) {
2213
2219
  virtualFilePath = importee.slice(virtualFilePrefix.length + 1);
2214
- importee = path4.resolve(root, virtualFilePath);
2220
+ importee = path6.resolve(root, virtualFilePath);
2215
2221
  } else if (!isBuild && importer && (importer === devEntryFile || normalizePath(importer) === devEntryFilePosix) && importee.startsWith(`/${markoRunFilePrefix}`)) {
2216
- importee = path4.resolve(root, "." + importee);
2222
+ importee = path6.resolve(root, "." + importee);
2217
2223
  }
2218
2224
  importee = normalizePath(importee);
2219
2225
  if (!buildVirtualFilesResult) {
@@ -2222,7 +2228,7 @@ function markoRun(opts = {}) {
2222
2228
  if (virtualFiles.has(importee)) {
2223
2229
  return importee;
2224
2230
  } else if (virtualFilePath) {
2225
- const filePath = path4.resolve(__dirname, "..", virtualFilePath);
2231
+ const filePath = path6.resolve(__dirname, "..", virtualFilePath);
2226
2232
  return await this.resolve(filePath, importer, {
2227
2233
  skipSelf: true
2228
2234
  });
@@ -2261,7 +2267,7 @@ function markoRun(opts = {}) {
2261
2267
  const builtEntries = Object.values(bundle).reduce(
2262
2268
  (acc, item) => {
2263
2269
  if (item.type === "chunk" && item.isEntry) {
2264
- acc.push(path4.join(options.dir, item.fileName));
2270
+ acc.push(path6.join(options.dir, item.fileName));
2265
2271
  }
2266
2272
  return acc;
2267
2273
  },
@@ -2289,12 +2295,12 @@ function markoRun(opts = {}) {
2289
2295
  fs3.rmSync(entryFilesDir, { recursive: true });
2290
2296
  }
2291
2297
  if ((adapter == null ? void 0 : adapter.buildEnd) && routes) {
2292
- await adapter.buildEnd(
2293
- resolvedConfig,
2294
- routes.list,
2295
- routeData.builtEntries,
2296
- routeData.sourceEntries
2297
- );
2298
+ await adapter.buildEnd({
2299
+ routes,
2300
+ config: resolvedConfig,
2301
+ builtEntries: routeData.builtEntries,
2302
+ sourceEntries: routeData.sourceEntries
2303
+ });
2298
2304
  }
2299
2305
  }
2300
2306
  }
@@ -2326,11 +2332,11 @@ async function ensureDir(dir) {
2326
2332
  }
2327
2333
  async function getPackageData(dir) {
2328
2334
  do {
2329
- const pkgPath = path4.join(dir, "package.json");
2335
+ const pkgPath = path6.join(dir, "package.json");
2330
2336
  if (fs3.existsSync(pkgPath)) {
2331
2337
  return JSON.parse(await fs3.promises.readFile(pkgPath, "utf-8"));
2332
2338
  }
2333
- } while (dir !== (dir = path4.dirname(dir)));
2339
+ } while (dir !== (dir = path6.dirname(dir)));
2334
2340
  return null;
2335
2341
  }
2336
2342
  async function resolveAdapter(root, options, log) {
@@ -2435,7 +2441,7 @@ async function getAvailablePort(port) {
2435
2441
  }
2436
2442
 
2437
2443
  // src/cli/commands.ts
2438
- var __dirname2 = path5.dirname(fileURLToPath2(import.meta.url));
2444
+ var __dirname2 = path7.dirname(fileURLToPath2(import.meta.url));
2439
2445
  var defaultConfigFileBases = ["vite.config"];
2440
2446
  var defaultConfigFileExts = [".js", ".cjs", ".mjs", ".ts", ".mts"];
2441
2447
  async function preview(entry, distEntry, cwd, configFile, port, outDir, envFile, args = []) {
@@ -2464,10 +2470,10 @@ async function preview(entry, distEntry, cwd, configFile, port, outDir, envFile,
2464
2470
  if (!entry) {
2465
2471
  entry = await ((_a = adapter.getEntryFile) == null ? void 0 : _a.call(adapter));
2466
2472
  }
2467
- const dir = path5.resolve(cwd, resolvedConfig.build.outDir);
2468
- const entryFile = distEntry ? path5.join(dir, distEntry) : await findFileWithExt(dir, "index", [".mjs", ".js"]);
2473
+ const dir = path7.resolve(cwd, resolvedConfig.build.outDir);
2474
+ const entryFile = distEntry ? path7.join(dir, distEntry) : findFileWithExt(dir, "index", [".mjs", ".js"]);
2469
2475
  if (envFile) {
2470
- envFile = path5.resolve(cwd, envFile);
2476
+ envFile = path7.resolve(cwd, envFile);
2471
2477
  }
2472
2478
  const options = {
2473
2479
  cwd,
@@ -2477,7 +2483,10 @@ async function preview(entry, distEntry, cwd, configFile, port, outDir, envFile,
2477
2483
  envFile,
2478
2484
  entry
2479
2485
  };
2480
- return await adapter.startPreview(entryFile, options);
2486
+ return await adapter.startPreview({
2487
+ entry: entryFile,
2488
+ options
2489
+ });
2481
2490
  }
2482
2491
  async function dev(entry, cwd, configFile, port, envFile, args = []) {
2483
2492
  var _a;
@@ -2491,7 +2500,7 @@ async function dev(entry, cwd, configFile, port, envFile, args = []) {
2491
2500
  "serve"
2492
2501
  );
2493
2502
  if (envFile) {
2494
- envFile = path5.resolve(cwd, envFile);
2503
+ envFile = path7.resolve(cwd, envFile);
2495
2504
  }
2496
2505
  const [availablePort, adapter] = await Promise.all([
2497
2506
  getAvailablePort(
@@ -2521,7 +2530,7 @@ async function dev(entry, cwd, configFile, port, envFile, args = []) {
2521
2530
  port: availablePort,
2522
2531
  envFile
2523
2532
  };
2524
- return await adapter.startDev(entry, config2, options);
2533
+ return await adapter.startDev({ entry, config: config2, options });
2525
2534
  }
2526
2535
  async function build(entry, cwd, configFile, outDir, envFile) {
2527
2536
  var _a;
@@ -2545,7 +2554,7 @@ async function build(entry, cwd, configFile, outDir, envFile) {
2545
2554
  }
2546
2555
  }
2547
2556
  if (envFile) {
2548
- envFile = path5.resolve(cwd, envFile);
2557
+ envFile = path7.resolve(cwd, envFile);
2549
2558
  }
2550
2559
  const buildConfig = setExternalAdapterOptions(
2551
2560
  {
@@ -2583,7 +2592,7 @@ async function build(entry, cwd, configFile, outDir, envFile) {
2583
2592
  }
2584
2593
  function findFileWithExt(dir, base, extensions = defaultConfigFileExts) {
2585
2594
  for (const ext of extensions) {
2586
- const filePath = path5.join(dir, base + ext);
2595
+ const filePath = path7.join(dir, base + ext);
2587
2596
  if (fs5.existsSync(filePath)) {
2588
2597
  return filePath;
2589
2598
  }
@@ -2592,7 +2601,7 @@ function findFileWithExt(dir, base, extensions = defaultConfigFileExts) {
2592
2601
  }
2593
2602
  async function getViteConfig(dir, configFile, bases = defaultConfigFileBases) {
2594
2603
  if (configFile) {
2595
- const configFilePath = path5.join(dir, configFile);
2604
+ const configFilePath = path7.join(dir, configFile);
2596
2605
  if (!fs5.existsSync(configFilePath)) {
2597
2606
  throw new Error(`No config file found at '${configFilePath}'`);
2598
2607
  }
@@ -2604,7 +2613,7 @@ async function getViteConfig(dir, configFile, bases = defaultConfigFileBases) {
2604
2613
  return configFile;
2605
2614
  }
2606
2615
  }
2607
- return path5.join(__dirname2, "default.config.mjs");
2616
+ return path7.join(__dirname2, "default.config.mjs");
2608
2617
  }
2609
2618
  async function resolveAdapter2(config2) {
2610
2619
  const options = getExternalPluginOptions(config2);