@marko/run 0.6.6 → 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,31 +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 extends Run.LayoutInput<typeof import('${path6}')> {}`
924
+ export interface Input extends Run.LayoutInput<typeof import("${modulePath}")> {}`
889
925
  );
890
926
  break;
891
927
  case RoutableFileTypes.Error:
892
928
  writeModuleDeclaration(
893
929
  writer,
894
- path6,
930
+ modulePath,
895
931
  "globalThis.MarkoRun.Route",
896
932
  `
897
933
  export interface Input {
@@ -900,7 +936,7 @@ async function renderRouteTypeInfo(routes, pathPrefix = ".", adapter) {
900
936
  );
901
937
  break;
902
938
  case RoutableFileTypes.NotFound:
903
- writeModuleDeclaration(writer, path6, "Run.Route");
939
+ writeModuleDeclaration(writer, modulePath, "Run.Route");
904
940
  break;
905
941
  }
906
942
  }
@@ -908,40 +944,15 @@ async function renderRouteTypeInfo(routes, pathPrefix = ".", adapter) {
908
944
  middlewareWriter.join();
909
945
  pageWriter.join();
910
946
  layoutWriter.join();
911
- writer.writeBlockStart(`
912
- type Routes = {`);
913
- for (const route of routes.list) {
914
- const { meta, handler, page } = route;
915
- if (page || handler) {
916
- const verbs = [];
917
- if (page || ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes("get"))) {
918
- verbs.push(`"get"`);
919
- }
920
- if ((_b = handler == null ? void 0 : handler.verbs) == null ? void 0 : _b.includes("post")) {
921
- verbs.push(`"post"`);
922
- }
923
- let routeType = `{ verb: ${verbs.join(" | ")};`;
924
- if (meta) {
925
- const metaPath = stripTsExtension(`${pathPrefix}/${meta.relativePath}`);
926
- let metaType = `typeof import("${metaPath}")`;
927
- if (/\.(ts|js|mjs)$/.test(meta.name)) {
928
- metaType += `["default"]`;
929
- }
930
- routeType += ` meta: ${metaType};`;
931
- }
932
- writer.writeLines(`"${route.key}": ${routeType} };`);
933
- }
934
- }
935
- writer.writeBlockEnd("}");
936
947
  return writer.end();
937
948
  }
938
- function writeModuleDeclaration(writer, path6, routeType, moduleTypes) {
939
- writer.writeLines("").write(`declare module "${stripTsExtension(path6)}" {`);
949
+ function writeModuleDeclaration(writer, name, routeType, moduleTypes) {
950
+ writer.writeLines("").write(`declare module "${name}" {`);
940
951
  if (moduleTypes) {
941
952
  writer.write(moduleTypes);
942
953
  }
943
954
  if (routeType) {
944
- const isMarko = path6.endsWith(".marko");
955
+ const isMarko = name.endsWith(".marko");
945
956
  writer.write(`
946
957
  namespace MarkoRun {
947
958
  export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
@@ -955,21 +966,15 @@ function writeModuleDeclaration(writer, path6, routeType, moduleTypes) {
955
966
  writer.writeLines(`
956
967
  }`);
957
968
  }
958
- function pathToURLPatternString(path6) {
959
- return path6.replace(/\/\$(\$?)([^/]*)/g, (_, catchAll, name) => {
960
- name = decodeURIComponent(name);
961
- return catchAll ? `/:${name || "rest"}*` : `/:${name}`;
962
- });
963
- }
964
969
  function createRouteTrie(routes) {
965
970
  const root = {
966
971
  key: ""
967
972
  };
968
- function insert(path6, route) {
973
+ function insert(path8, route) {
969
974
  let node = root;
970
- for (const segment of path6.segments) {
975
+ for (const segment of path8.segments) {
971
976
  if (segment === "$$") {
972
- node.catchAll ?? (node.catchAll = { route, path: path6 });
977
+ node.catchAll ?? (node.catchAll = { route, path: path8 });
973
978
  return;
974
979
  } else if (segment === "$") {
975
980
  node = node.dynamic ?? (node.dynamic = {
@@ -987,17 +992,18 @@ function createRouteTrie(routes) {
987
992
  node = next;
988
993
  }
989
994
  }
990
- node.path ?? (node.path = path6);
995
+ node.path ?? (node.path = path8);
991
996
  node.route ?? (node.route = route);
992
997
  }
993
998
  for (const route of routes) {
994
- for (const path6 of route.paths) {
995
- insert(path6, route);
996
- }
999
+ insert(route.path, route);
997
1000
  }
998
1001
  return root;
999
1002
  }
1000
1003
 
1004
+ // src/vite/routes/builder.ts
1005
+ import path3 from "path";
1006
+
1001
1007
  // src/vite/routes/parse.ts
1002
1008
  function parseFlatRoute(pattern) {
1003
1009
  if (!pattern) throw new Error("Empty pattern");
@@ -1012,53 +1018,72 @@ function parseFlatRoute(pattern) {
1012
1018
  ]);
1013
1019
  function parse2(basePaths, group) {
1014
1020
  const pathMap = /* @__PURE__ */ new Map();
1015
- const delimiters = group ? ").," : ".,";
1021
+ const delimiters = group ? "`).," : "`.,";
1016
1022
  let charCode;
1017
1023
  let segmentStart = i;
1018
1024
  let type;
1019
1025
  let current;
1026
+ let escaped = "";
1027
+ let escapeStart = 0;
1020
1028
  do {
1021
1029
  charCode = pattern.charCodeAt(i);
1022
- 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) {
1023
1041
  break;
1024
- } else if (charCode === 44) {
1042
+ } else if (charCode === 44 /* Alternate */) {
1025
1043
  if (!current) {
1026
1044
  segmentEnd(
1027
- basePaths.map((path6) => ({
1028
- ...path6,
1029
- segments: path6.segments.slice()
1045
+ basePaths.map((path8) => ({
1046
+ ...path8,
1047
+ segments: path8.segments.slice()
1030
1048
  })),
1031
- "",
1049
+ escaped,
1032
1050
  "_",
1033
1051
  pathMap
1034
1052
  );
1035
1053
  } else {
1036
- segmentEnd(current, pattern.slice(segmentStart, i), type, pathMap);
1054
+ segmentEnd(
1055
+ current,
1056
+ escaped + pattern.slice(segmentStart, i),
1057
+ type,
1058
+ pathMap
1059
+ );
1037
1060
  }
1038
1061
  current = void 0;
1039
1062
  type = void 0;
1063
+ escaped = "";
1040
1064
  segmentStart = ++i;
1041
- } else if (charCode === 46) {
1065
+ } else if (charCode === 46 /* Directory */) {
1042
1066
  if (current) {
1043
- segmentEnd(current, pattern.slice(segmentStart, i), type);
1067
+ segmentEnd(current, escaped + pattern.slice(segmentStart, i), type);
1044
1068
  }
1045
1069
  type = void 0;
1070
+ escaped = "";
1046
1071
  segmentStart = ++i;
1047
- } else if (charCode === 40) {
1072
+ } else if (charCode === 40 /* GroupStart */) {
1048
1073
  const groupPaths = parse2(current || basePaths, ++i);
1049
1074
  if (groupPaths.length) {
1050
1075
  current = groupPaths;
1051
1076
  }
1052
1077
  segmentStart = ++i;
1053
1078
  } else {
1054
- if (charCode === 95) {
1079
+ if (charCode === 95 /* Pathless */) {
1055
1080
  type = "_";
1056
- } else if (charCode === 36) {
1081
+ } else if (charCode === 36 /* Dynamic */) {
1057
1082
  type = pattern.charCodeAt(i + 1) === 36 ? "$$" : "$";
1058
1083
  }
1059
- current ?? (current = basePaths.map((path6) => ({
1060
- ...path6,
1061
- segments: path6.segments.slice()
1084
+ current ?? (current = basePaths.map((path8) => ({
1085
+ ...path8,
1086
+ segments: path8.segments.slice()
1062
1087
  })));
1063
1088
  i = len;
1064
1089
  for (const char of delimiters) {
@@ -1069,7 +1094,12 @@ function parseFlatRoute(pattern) {
1069
1094
  }
1070
1095
  }
1071
1096
  } while (i < len);
1072
- 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 */) {
1073
1103
  throw new Error(
1074
1104
  `Invalid route pattern: group was not closed '${pattern.slice(
1075
1105
  group
@@ -1078,16 +1108,21 @@ function parseFlatRoute(pattern) {
1078
1108
  }
1079
1109
  if (!current) {
1080
1110
  segmentEnd(
1081
- basePaths.map((path6) => ({
1082
- ...path6,
1083
- segments: path6.segments.slice()
1111
+ basePaths.map((path8) => ({
1112
+ ...path8,
1113
+ segments: path8.segments.slice()
1084
1114
  })),
1085
- "",
1086
- "_",
1115
+ escaped,
1116
+ void 0,
1087
1117
  pathMap
1088
1118
  );
1089
1119
  } else {
1090
- segmentEnd(current, pattern.slice(segmentStart, i), type, pathMap);
1120
+ segmentEnd(
1121
+ current,
1122
+ escaped + pattern.slice(segmentStart, i),
1123
+ type,
1124
+ pathMap
1125
+ );
1091
1126
  }
1092
1127
  return [...pathMap.values()];
1093
1128
  }
@@ -1096,17 +1131,17 @@ function parseFlatRoute(pattern) {
1096
1131
  if (raw) {
1097
1132
  segment = {
1098
1133
  raw,
1099
- name: raw,
1134
+ name: normalizeSegment(raw),
1100
1135
  type
1101
1136
  };
1102
1137
  if (type === "$" || type === "$$") {
1103
1138
  segment.name = type;
1104
- segment.param = raw.slice(type.length);
1139
+ segment.param = normalizeParam(raw.slice(type.length));
1105
1140
  }
1106
1141
  }
1107
- for (const path6 of paths) {
1142
+ for (const path8 of paths) {
1108
1143
  if (segment) {
1109
- if (path6.isCatchall) {
1144
+ if (path8.isCatchall) {
1110
1145
  throw new Error(
1111
1146
  `Invalid route pattern: nested segments are not allowed after a catch-all parameter. Found '.' following '${pattern.slice(
1112
1147
  0,
@@ -1114,26 +1149,36 @@ function parseFlatRoute(pattern) {
1114
1149
  )}' in '${pattern}'.`
1115
1150
  );
1116
1151
  }
1117
- path6.segments.push(segment);
1118
- path6.id += path6.id === "/" ? segment.name : `/${segment.name}`;
1152
+ path8.segments.push(segment);
1153
+ path8.id += path8.id === "/" ? segment.name : `/${segment.name}`;
1119
1154
  if (type === "$$") {
1120
- path6.isCatchall = true;
1155
+ path8.isCatchall = true;
1121
1156
  }
1122
1157
  }
1123
1158
  if (map) {
1124
- if (map.has(path6.id)) {
1125
- const existing = map.get(path6.id);
1159
+ if (map.has(path8.id)) {
1160
+ const existing = map.get(path8.id);
1126
1161
  const existingExpansion = existing.segments.map((s) => s.raw).join(".");
1127
- const currentExpansion = path6.segments.map((s) => s.raw).join(".");
1162
+ const currentExpansion = path8.segments.map((s) => s.raw).join(".");
1128
1163
  throw new Error(
1129
- `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}'.`
1130
1165
  );
1131
1166
  }
1132
- map.set(path6.id, path6);
1167
+ map.set(path8.id, path8);
1133
1168
  }
1134
1169
  }
1135
1170
  }
1136
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
+ }
1137
1182
 
1138
1183
  // src/vite/routes/vdir.ts
1139
1184
  var _dirs, _pathlessDirs;
@@ -1144,14 +1189,12 @@ var _VDir = class _VDir {
1144
1189
  __publicField(this, "parent");
1145
1190
  __publicField(this, "source");
1146
1191
  __publicField(this, "path");
1147
- __publicField(this, "fullPath");
1148
1192
  __publicField(this, "segment");
1149
1193
  __publicField(this, "files");
1150
1194
  if (!parent || !segment) {
1151
1195
  this.parent = null;
1152
1196
  this.source = null;
1153
1197
  this.path = "/";
1154
- this.fullPath = "/";
1155
1198
  this.segment = {
1156
1199
  raw: "",
1157
1200
  name: ""
@@ -1159,12 +1202,8 @@ var _VDir = class _VDir {
1159
1202
  } else {
1160
1203
  this.parent = parent;
1161
1204
  this.source = source;
1162
- this.path = parent.path + (parent.path === "/" ? segment.name : `/${segment.name}`);
1163
- this.fullPath = parent.fullPath + (parent.fullPath === "/" ? segment.name : `/${segment.name}`);
1164
- if (segment.param) {
1165
- this.fullPath += segment.param;
1166
- }
1167
1205
  this.segment = segment;
1206
+ this.path = parent.path + (parent.path === "/" ? "" : "/") + segment.name;
1168
1207
  }
1169
1208
  }
1170
1209
  get pathInfo() {
@@ -1177,17 +1216,18 @@ var _VDir = class _VDir {
1177
1216
  for (const { segment } of this) {
1178
1217
  const { type, name, param } = segment;
1179
1218
  if (name && type !== "_") {
1180
- value.id += sep + (type || name);
1219
+ value.id += sep + name;
1181
1220
  value.path += sep + name;
1182
1221
  value.isEnd = type === "$$";
1183
1222
  if (param) {
1184
- value.path += param;
1223
+ const unescapedParam = param.charAt(0) === "`" ? param.slice(1, -1) : param;
1185
1224
  const index = type === "$$" ? null : value.segments.length;
1186
1225
  if (!value.params) {
1187
- value.params = { [param]: index };
1226
+ value.params = { [unescapedParam]: index };
1188
1227
  } else if (!(param in value.params)) {
1189
- value.params[param] = index;
1228
+ value.params[unescapedParam] = index;
1190
1229
  }
1230
+ value.path += param;
1191
1231
  }
1192
1232
  value.segments.push(name);
1193
1233
  sep = "/";
@@ -1199,11 +1239,11 @@ var _VDir = class _VDir {
1199
1239
  });
1200
1240
  return value;
1201
1241
  }
1202
- addDir(path6, segment) {
1242
+ addDir(path8, segment) {
1203
1243
  const map = segment.type === "_" ? __privateGet(this, _pathlessDirs) ?? __privateSet(this, _pathlessDirs, /* @__PURE__ */ new Map()) : __privateGet(this, _dirs) ?? __privateSet(this, _dirs, /* @__PURE__ */ new Map());
1204
1244
  const key = segment.type === "$" ? segment.raw : segment.name;
1205
1245
  if (!map.has(key)) {
1206
- const dir = new _VDir(this, segment, path6);
1246
+ const dir = new _VDir(this, segment, path8);
1207
1247
  map.set(key, dir);
1208
1248
  return dir;
1209
1249
  }
@@ -1219,15 +1259,15 @@ var _VDir = class _VDir {
1219
1259
  const existing = this.files.get(file.type);
1220
1260
  if (existing !== file) {
1221
1261
  throw new Error(
1222
- `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}.`
1223
1263
  );
1224
1264
  } else if (file.type === RoutableFileTypes.Page || file.type === RoutableFileTypes.Handler) {
1225
1265
  throw new Error(
1226
- `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}`
1227
1267
  );
1228
1268
  }
1229
1269
  throw new Error(
1230
- `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}`
1231
1271
  );
1232
1272
  }
1233
1273
  }
@@ -1249,10 +1289,10 @@ var _VDir = class _VDir {
1249
1289
  const dirs = [];
1250
1290
  const unique = /* @__PURE__ */ new Map();
1251
1291
  for (const root of roots) {
1252
- for (const path6 of paths) {
1292
+ for (const path8 of paths) {
1253
1293
  let dir = root;
1254
- for (const segment of path6.segments) {
1255
- dir = dir.addDir(path6, segment);
1294
+ for (const segment of path8.segments) {
1295
+ dir = dir.addDir(path8, segment);
1256
1296
  }
1257
1297
  const existing = unique.get(dir.path);
1258
1298
  if (existing) {
@@ -1265,7 +1305,7 @@ var _VDir = class _VDir {
1265
1305
  }
1266
1306
  }
1267
1307
  throw new Error(
1268
- `Ambiguous directory structure: '${sourcePath}${path6.source}' defines '${dir.path}' multiple times.`
1308
+ `Ambiguous directory structure: ${sourcePath}${path8.source} defines ${dir.path} multiple times.`
1269
1309
  );
1270
1310
  } else {
1271
1311
  unique.set(dir.path, dir);
@@ -1291,13 +1331,11 @@ function matchRoutableFile(filename) {
1291
1331
  const match = filename.match(routeableFileRegex);
1292
1332
  return match && (match[1] || match[3]).toLowerCase();
1293
1333
  }
1294
- function isSpecialType(type) {
1295
- return type === RoutableFileTypes.NotFound || type === RoutableFileTypes.Error;
1296
- }
1297
- async function buildRoutes(sources) {
1334
+ async function buildRoutes(sources, outDir) {
1298
1335
  const uniqueRoutes = /* @__PURE__ */ new Map();
1299
1336
  const routes = [];
1300
1337
  const special = {};
1338
+ const seenKeys = /* @__PURE__ */ new Map();
1301
1339
  const middlewares = /* @__PURE__ */ new Set();
1302
1340
  const unusedFiles = /* @__PURE__ */ new Set();
1303
1341
  const currentLayouts = /* @__PURE__ */ new Set();
@@ -1305,13 +1343,13 @@ async function buildRoutes(sources) {
1305
1343
  const root = new VDir();
1306
1344
  const dirStack = [];
1307
1345
  let basePath;
1308
- let importPrefix;
1309
1346
  let activeDirs;
1310
1347
  let isBaseDir;
1311
1348
  let nextFileId = 1;
1312
1349
  let nextRouteIndex = 1;
1313
1350
  const walkOptions = {
1314
- onEnter({ name }) {
1351
+ onEnter(dir) {
1352
+ let { name } = dir;
1315
1353
  const prevDirStackLength = dirStack.length;
1316
1354
  if (isBaseDir) {
1317
1355
  isBaseDir = false;
@@ -1330,15 +1368,16 @@ async function buildRoutes(sources) {
1330
1368
  dirStack.length = prevDirStackLength;
1331
1369
  };
1332
1370
  },
1333
- onFile({ name, path: path6 }) {
1371
+ onFile(file) {
1372
+ const { name } = file;
1334
1373
  const match = name.match(routeableFileRegex);
1335
1374
  if (!match) {
1336
1375
  return;
1337
1376
  }
1338
1377
  const type = (match[1] || match[3]).toLowerCase();
1339
- if (dirStack.length && isSpecialType(type)) {
1378
+ if (dirStack.length && (type === RoutableFileTypes.NotFound || type === RoutableFileTypes.Error)) {
1340
1379
  console.warn(
1341
- `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}`
1342
1381
  );
1343
1382
  return;
1344
1383
  }
@@ -1347,19 +1386,15 @@ async function buildRoutes(sources) {
1347
1386
  const paths = parseFlatRoute(name.slice(0, match.index));
1348
1387
  dirs = VDir.addPaths(activeDirs, paths);
1349
1388
  }
1350
- const dirPath = dirStack.join("/");
1351
- const relativePath = dirPath ? `${dirPath}/${name}` : name;
1352
- const file = {
1389
+ const routableFile = {
1353
1390
  id: String(nextFileId++),
1354
1391
  name,
1355
1392
  type,
1356
- filePath: path6,
1357
- relativePath,
1358
- importPath: `${importPrefix}/${relativePath}`,
1393
+ filePath: file.path,
1359
1394
  verbs: type === RoutableFileTypes.Page ? ["get", "head"] : void 0
1360
1395
  };
1361
1396
  for (const dir of dirs) {
1362
- dir.addFile(file);
1397
+ dir.addFile(routableFile);
1363
1398
  }
1364
1399
  }
1365
1400
  };
@@ -1367,7 +1402,6 @@ async function buildRoutes(sources) {
1367
1402
  sources = [sources];
1368
1403
  }
1369
1404
  for (const source of sources) {
1370
- importPrefix = source.importPrefix ? source.importPrefix.replace(/^\/+|\/+$/g, "") : "";
1371
1405
  basePath = source.basePath || "";
1372
1406
  activeDirs = [root];
1373
1407
  isBaseDir = true;
@@ -1387,7 +1421,8 @@ async function buildRoutes(sources) {
1387
1421
  layout = dir.files.get(RoutableFileTypes.Layout);
1388
1422
  const handler = dir.files.get(RoutableFileTypes.Handler);
1389
1423
  const page = dir.files.get(RoutableFileTypes.Page);
1390
- let hasSpecial = false;
1424
+ const pathInfo = dir.pathInfo;
1425
+ let layoutsUsed = false;
1391
1426
  if (middleware) {
1392
1427
  if (currentMiddleware.has(middleware)) {
1393
1428
  middleware = void 0;
@@ -1404,64 +1439,64 @@ async function buildRoutes(sources) {
1404
1439
  unusedFiles.add(layout);
1405
1440
  }
1406
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
+ }
1407
1458
  if (page || handler) {
1408
- const path6 = dir.pathInfo;
1409
- if (uniqueRoutes.has(path6.id)) {
1410
- const existing = uniqueRoutes.get(path6.id);
1459
+ if (uniqueRoutes.has(pathInfo.id)) {
1460
+ const existing = uniqueRoutes.get(pathInfo.id);
1411
1461
  const route = routes[existing.index];
1412
1462
  const existingFiles = [route.handler, route.page].filter(Boolean).map((f) => f.filePath);
1413
1463
  const currentFiles = [handler, page].filter(Boolean).map((f) => f.filePath);
1414
- throw new Error(`Duplicate routes for path '${path6.id}' were defined. A route established by:
1415
- ${existingFiles.join(" and ")} via '${existing.dir.fullPath}'
1416
- collides with
1417
- ${currentFiles.join(" and ")} via '${dir.fullPath}'
1418
- `);
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;
1419
1474
  }
1420
- uniqueRoutes.set(path6.id, { dir, index: routes.length });
1421
1475
  routes.push({
1422
1476
  index: nextRouteIndex++,
1423
- key: dir.fullPath,
1424
- paths: [path6],
1477
+ key,
1478
+ path: pathInfo,
1425
1479
  middleware: [...currentMiddleware],
1426
1480
  layouts: page ? [...currentLayouts] : [],
1427
1481
  meta: dir.files.get(RoutableFileTypes.Meta),
1428
1482
  page,
1429
1483
  handler,
1430
- 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
1431
1485
  });
1432
- }
1433
- if (dir === root) {
1434
- for (const [type, file] of dir.files) {
1435
- if (isSpecialType(type)) {
1436
- hasSpecial = true;
1437
- special[type] = {
1438
- index: 0,
1439
- key: type,
1440
- paths: [],
1441
- middleware: [],
1442
- layouts: [...currentLayouts],
1443
- page: file,
1444
- entryName: `${markoRunFilePrefix}special.${type}`
1445
- };
1446
- }
1447
- }
1448
- }
1449
- if (handler || page) {
1486
+ layoutsUsed = !!page;
1450
1487
  for (const middleware2 of currentMiddleware) {
1451
1488
  middlewares.add(middleware2);
1452
1489
  unusedFiles.delete(middleware2);
1453
1490
  }
1454
1491
  }
1455
- if (page || hasSpecial) {
1492
+ if (layoutsUsed) {
1456
1493
  for (const layout2 of currentLayouts) {
1457
1494
  unusedFiles.delete(layout2);
1458
1495
  }
1459
1496
  }
1460
1497
  }
1461
- if (dir.dirs) {
1462
- for (const child of dir.dirs()) {
1463
- traverse(child);
1464
- }
1498
+ for (const childDir of dir.dirs()) {
1499
+ traverse(childDir);
1465
1500
  }
1466
1501
  if (middleware) {
1467
1502
  currentMiddleware.delete(middleware);
@@ -1471,10 +1506,13 @@ async function buildRoutes(sources) {
1471
1506
  }
1472
1507
  }
1473
1508
  }
1509
+ function replaceInvalidFilenameChars(str) {
1510
+ return str.replace(/[<>:"/\\|?*]+/g, "_");
1511
+ }
1474
1512
 
1475
1513
  // src/vite/routes/walk.ts
1476
1514
  import fs from "fs";
1477
- import path2 from "path";
1515
+ import path4 from "path";
1478
1516
  function createFSWalker(dir) {
1479
1517
  return async function walkFS({
1480
1518
  onEnter,
@@ -1489,7 +1527,7 @@ function createFSWalker(dir) {
1489
1527
  const entries = await fs.promises.readdir(dir2.path, {
1490
1528
  withFileTypes: true
1491
1529
  });
1492
- const prefix = dir2.path + path2.sep;
1530
+ const prefix = dir2.path + path4.sep;
1493
1531
  for (const entry of entries) {
1494
1532
  const walkEntry = {
1495
1533
  name: entry.name,
@@ -1512,7 +1550,7 @@ function createFSWalker(dir) {
1512
1550
  await walk(
1513
1551
  {
1514
1552
  path: dir,
1515
- name: path2.basename(dir)
1553
+ name: path4.basename(dir)
1516
1554
  },
1517
1555
  maxDepth
1518
1556
  );
@@ -1630,36 +1668,34 @@ function logRoutesTable(routes, bundle) {
1630
1668
  style: { compact: true }
1631
1669
  });
1632
1670
  for (const route of routes.list) {
1633
- for (const path6 of route.paths) {
1634
- const verbs = getVerbs(route, true);
1635
- let firstRow = true;
1636
- for (const verb of verbs) {
1637
- const entryType = [];
1638
- let size = "";
1639
- let verbCell = verbColor(verb)(verb.toUpperCase());
1640
- if (verb === "get" && !verbs.includes("head")) {
1641
- verbCell += kleur2.dim(`,${verbColor(verb)("HEAD")}`);
1642
- }
1643
- if (route.handler) {
1644
- entryType.push(kleur2.blue("handler"));
1645
- }
1646
- if (route.page && (verb === "get" || verb === "head")) {
1647
- entryType.push(kleur2.yellow("page"));
1648
- if (verb === "get") {
1649
- size = prettySize(computeRouteSize(route, bundle));
1650
- }
1651
- }
1652
- const row = [verbCell];
1653
- if (verbs.length === 1 || firstRow) {
1654
- row.push({ rowSpan: verbs.length, content: prettyPath(path6.path) });
1655
- 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));
1656
1684
  }
1657
- row.push(entryType.join(" -> "));
1658
- hasMiddleware && row.push(route.middleware.length || "");
1659
- hasMeta && row.push(route.meta ? "\u2713" : "");
1660
- row.push(size || "");
1661
- table.push(row);
1662
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);
1663
1699
  }
1664
1700
  }
1665
1701
  for (const [key, route] of Object.entries(routes.special).sort()) {
@@ -1715,17 +1751,20 @@ function prettySize([bytes, compBytes]) {
1715
1751
  else str += kleur2.bold(kleur2.red(compSize));
1716
1752
  return str;
1717
1753
  }
1718
- function prettyPath(path6) {
1719
- 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
+ );
1720
1759
  }
1721
1760
 
1722
1761
  // src/vite/utils/read-once-persisted-store.ts
1723
1762
  import { promises as fs2 } from "fs";
1724
1763
  import os from "os";
1725
- import path3 from "path";
1764
+ import path5 from "path";
1726
1765
  var noop = () => {
1727
1766
  };
1728
- var tmpFile = path3.join(os.tmpdir(), "marko-run-storage.json");
1767
+ var tmpFile = path5.join(os.tmpdir(), "marko-run-storage.json");
1729
1768
  var values = /* @__PURE__ */ new Map();
1730
1769
  var loadedFromDisk;
1731
1770
  var ReadOncePersistedStore = class {
@@ -1767,17 +1806,17 @@ process.once("beforeExit", (code) => {
1767
1806
 
1768
1807
  // src/vite/plugin.ts
1769
1808
  var debug = createDebug("@marko/run");
1770
- var __dirname = path4.dirname(fileURLToPath(import.meta.url));
1809
+ var __dirname = path6.dirname(fileURLToPath(import.meta.url));
1771
1810
  var PLUGIN_NAME_PREFIX = "marko-run-vite";
1772
- var POSIX_SEP = "/";
1773
- var WINDOWS_SEP = "\\";
1774
1811
  var CLIENT_OUT_DIR = "public";
1775
1812
  var MIDDLEWARE_FILENAME = `${markoRunFilePrefix}middleware.js`;
1776
1813
  var ROUTER_FILENAME = `${markoRunFilePrefix}router.js`;
1777
1814
  var defaultPort = Number(process.env.PORT || 3e3);
1778
- var normalizePath = path4.sep === WINDOWS_SEP ? (id) => id.replace(/\\/g, POSIX_SEP) : (id) => id;
1779
1815
  function markoRun(opts = {}) {
1780
- let { routesDir, adapter, ...markoVitePluginOptions } = opts;
1816
+ let routesDir;
1817
+ let adapter;
1818
+ let trailingSlashes;
1819
+ const { ...markoVitePluginOptions } = opts;
1781
1820
  let store;
1782
1821
  let root;
1783
1822
  let shouldEmptyOutDir = false;
@@ -1810,12 +1849,8 @@ function markoRun(opts = {}) {
1810
1849
  root,
1811
1850
  "{.tsconfig*,tsconfig*.json}"
1812
1851
  )))) {
1813
- const filepath = path4.join(typesDir, "routes.d.ts");
1814
- const data = await renderRouteTypeInfo(
1815
- routes2,
1816
- normalizePath(path4.relative(typesDir, resolvedRoutesDir)),
1817
- adapter
1818
- );
1852
+ const filepath = path6.join(typesDir, "routes.d.ts");
1853
+ const data = await renderRouteTypeInfo(routes2, typesDir, adapter);
1819
1854
  if (data !== typesFile || !fs3.existsSync(filepath)) {
1820
1855
  await ensureDir(typesDir);
1821
1856
  await fs3.promises.writeFile(filepath, typesFile = data);
@@ -1826,20 +1861,25 @@ function markoRun(opts = {}) {
1826
1861
  function buildVirtualFiles() {
1827
1862
  return buildVirtualFilesResult ?? (buildVirtualFilesResult = (async () => {
1828
1863
  virtualFiles.clear();
1829
- routes = await buildRoutes({
1830
- walker: createFSWalker(resolvedRoutesDir),
1831
- importPrefix: routesDir
1832
- });
1864
+ routes = await buildRoutes(
1865
+ {
1866
+ walker: createFSWalker(resolvedRoutesDir)
1867
+ },
1868
+ entryFilesDir
1869
+ );
1833
1870
  if (!routes.list.length) {
1834
1871
  throw new Error("No routes generated");
1835
1872
  }
1836
1873
  for (const route of routes.list) {
1837
- virtualFiles.set(path4.posix.join(root, `${route.entryName}.js`), "");
1874
+ virtualFiles.set(
1875
+ path6.posix.join(root, getRouteVirtualFileName(route)),
1876
+ ""
1877
+ );
1838
1878
  }
1839
1879
  if (routes.middleware.length) {
1840
- virtualFiles.set(path4.posix.join(root, MIDDLEWARE_FILENAME), "");
1880
+ virtualFiles.set(path6.posix.join(root, MIDDLEWARE_FILENAME), "");
1841
1881
  }
1842
- virtualFiles.set(path4.posix.join(root, ROUTER_FILENAME), "");
1882
+ virtualFiles.set(path6.posix.join(root, ROUTER_FILENAME), "");
1843
1883
  return routes;
1844
1884
  })());
1845
1885
  }
@@ -1853,111 +1893,78 @@ function markoRun(opts = {}) {
1853
1893
  fs3.rmSync(entryFilesDir, { recursive: true });
1854
1894
  }
1855
1895
  for (const route of routes2.list) {
1856
- const { handler, page, layouts } = route;
1857
- if (handler) {
1858
- const exports = await getExportsFromFile(context, handler.filePath);
1859
- handler.verbs = [];
1896
+ if (route.handler) {
1897
+ const exports = await getExportsFromFile(
1898
+ context,
1899
+ route.handler.filePath
1900
+ );
1901
+ route.handler.verbs = [];
1860
1902
  for (const name of exports) {
1861
1903
  const verb = name.toLowerCase();
1862
1904
  if (name === verb.toUpperCase() && httpVerbs.includes(verb)) {
1863
- handler.verbs.push(verb);
1905
+ route.handler.verbs.push(verb);
1864
1906
  }
1865
1907
  }
1866
- if (!handler.verbs.length) {
1908
+ if (!route.handler.verbs.length) {
1867
1909
  context.warn(
1868
- `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(", ")}`
1869
1911
  );
1870
1912
  }
1871
1913
  }
1872
- if (page) {
1873
- if (layouts.length) {
1874
- const relativePath = path4.relative(
1875
- resolvedRoutesDir,
1876
- page.filePath
1877
- );
1878
- const routeFileDir = path4.join(entryFilesDir, relativePath, "..");
1879
- const routeFileRelativePathPosix = normalizePath(
1880
- path4.relative(routeFileDir, root)
1881
- );
1882
- fs3.mkdirSync(routeFileDir, { recursive: true });
1883
- const pageNameIndex = page.name.indexOf("+page");
1884
- const pageNamePrefix = pageNameIndex > 0 ? `${page.name.slice(0, pageNameIndex)}.` : "";
1885
- fs3.writeFileSync(
1886
- route.templateFilePath = path4.join(
1887
- routeFileDir,
1888
- pageNamePrefix + "route.marko"
1889
- ),
1890
- renderRouteTemplate(
1891
- route,
1892
- (to) => path4.posix.join(routeFileRelativePathPosix, to)
1893
- )
1894
- );
1895
- } else {
1896
- route.templateFilePath = page.filePath;
1897
- }
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
+ );
1898
1922
  }
1899
1923
  virtualFiles.set(
1900
- path4.posix.join(root, `${route.entryName}.js`),
1901
- renderRouteEntry(route, relativeEntryFilesDirPosix)
1924
+ path6.posix.join(root, getRouteVirtualFileName(route)),
1925
+ renderRouteEntry(route, root)
1902
1926
  );
1903
1927
  }
1904
1928
  for (const route of Object.values(routes2.special)) {
1905
- const { page, layouts, key } = route;
1906
- if (page) {
1907
- if (layouts.length) {
1908
- const relativePath = path4.relative(
1909
- resolvedRoutesDir,
1910
- page.filePath
1911
- );
1912
- const routeFileDir = path4.join(entryFilesDir, relativePath, "..");
1913
- const routeFileRelativePathPosix = normalizePath(
1914
- path4.relative(routeFileDir, root)
1915
- );
1916
- fs3.mkdirSync(routeFileDir, { recursive: true });
1917
- fs3.writeFileSync(
1918
- route.templateFilePath = path4.join(
1919
- routeFileDir,
1920
- `route.${key}.marko`
1921
- ),
1922
- renderRouteTemplate(
1923
- route,
1924
- (to) => path4.posix.join(routeFileRelativePathPosix, to)
1925
- )
1926
- );
1927
- } else {
1928
- route.templateFilePath = page.filePath;
1929
- }
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
+ );
1930
1937
  }
1931
1938
  }
1932
1939
  if (routes2.middleware.length) {
1933
1940
  for (const middleware of routes2.middleware) {
1934
1941
  if (!(await getExportsFromFile(context, middleware.filePath)).includes("default")) {
1935
1942
  context.warn(
1936
- `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)}'`
1937
1944
  );
1938
1945
  }
1939
1946
  }
1940
1947
  virtualFiles.set(
1941
- path4.posix.join(root, MIDDLEWARE_FILENAME),
1942
- renderMiddleware(routes2.middleware)
1948
+ path6.posix.join(root, MIDDLEWARE_FILENAME),
1949
+ renderMiddleware(routes2.middleware, root)
1943
1950
  );
1944
1951
  }
1945
1952
  virtualFiles.set(
1946
- path4.posix.join(root, ROUTER_FILENAME),
1947
- renderRouter(routes2, relativeEntryFilesDirPosix, {
1948
- trailingSlashes: opts.trailingSlashes || "RedirectWithout"
1953
+ path6.posix.join(root, ROUTER_FILENAME),
1954
+ renderRouter(routes2, root, {
1955
+ trailingSlashes
1949
1956
  })
1950
1957
  );
1951
1958
  await writeTypesFile(routes2);
1952
1959
  if (adapter == null ? void 0 : adapter.routesGenerated) {
1953
- await adapter.routesGenerated(
1954
- routes2,
1955
- new Map(virtualFiles.entries()),
1956
- {
1960
+ await adapter.routesGenerated({
1961
+ routes: routes2,
1962
+ virtualFiles: new Map(virtualFiles.entries()),
1963
+ meta: {
1957
1964
  buildTime: times.routesBuild,
1958
1965
  renderTime: times.routesRender
1959
1966
  }
1960
- );
1967
+ });
1961
1968
  if (!isBuild) {
1962
1969
  await ((_a = opts == null ? void 0 : opts.emitRoutes) == null ? void 0 : _a.call(opts, routes2.list));
1963
1970
  }
@@ -1967,7 +1974,7 @@ function markoRun(opts = {}) {
1967
1974
  throw err;
1968
1975
  }
1969
1976
  virtualFiles.set(
1970
- path4.posix.join(root, ROUTER_FILENAME),
1977
+ path6.posix.join(root, ROUTER_FILENAME),
1971
1978
  `throw ${JSON.stringify(prepareError(err))}`
1972
1979
  );
1973
1980
  }
@@ -2004,27 +2011,28 @@ function markoRun(opts = {}) {
2004
2011
  }
2005
2012
  }
2006
2013
  routesDir = opts.routesDir || "src/routes";
2014
+ trailingSlashes = opts.trailingSlashes || "RedirectWithout";
2007
2015
  store = new ReadOncePersistedStore(
2008
2016
  `vite-marko-run${opts.runtimeId ? `-${opts.runtimeId}` : ""}`
2009
2017
  );
2010
2018
  markoVitePluginOptions.runtimeId = opts.runtimeId;
2011
2019
  markoVitePluginOptions.basePathVar = opts.basePathVar;
2012
- resolvedRoutesDir = path4.resolve(root, routesDir);
2013
- outputDir = path4.join(root, ((_d = config2.build) == null ? void 0 : _d.outDir) || "dist");
2014
- 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");
2015
2023
  entryFilesDirPosix = normalizePath(entryFilesDir);
2016
2024
  relativeEntryFilesDirPosix = normalizePath(
2017
- path4.relative(root, entryFilesDir)
2025
+ path6.relative(root, entryFilesDir)
2018
2026
  );
2019
- typesDir = path4.join(root, ".marko-run");
2020
- devEntryFile = path4.join(root, "index.html");
2027
+ typesDir = path6.join(root, ".marko-run");
2028
+ devEntryFile = path6.join(root, "index.html");
2021
2029
  devEntryFilePosix = normalizePath(devEntryFile);
2022
2030
  let outDir = ((_e = config2.build) == null ? void 0 : _e.outDir) || "dist";
2023
2031
  const assetsDir = ((_f = config2.build) == null ? void 0 : _f.assetsDir) || "assets";
2024
2032
  let rollupOutputOptions = (_h = (_g = config2.build) == null ? void 0 : _g.rollupOptions) == null ? void 0 : _h.output;
2025
2033
  if (isBuild) {
2026
2034
  if (!isSSRBuild) {
2027
- outDir = path4.join(outDir, CLIENT_OUT_DIR);
2035
+ outDir = path6.join(outDir, CLIENT_OUT_DIR);
2028
2036
  }
2029
2037
  const defaultRollupOutputOptions = {
2030
2038
  assetFileNames({ name }) {
@@ -2149,7 +2157,7 @@ function markoRun(opts = {}) {
2149
2157
  devServer.watcher.on("all", async (type, filename) => {
2150
2158
  seenErrors.clear();
2151
2159
  const routableFileType = matchRoutableFile(
2152
- path4.parse(filename).base
2160
+ path6.parse(filename).base
2153
2161
  );
2154
2162
  if (filename.startsWith(resolvedRoutesDir) && routableFileType) {
2155
2163
  if (type === "add" || type === "unlink" || type === "change" && (routableFileType === RoutableFileTypes.Handler || routableFileType === RoutableFileTypes.Middleware)) {
@@ -2199,19 +2207,19 @@ function markoRun(opts = {}) {
2199
2207
  },
2200
2208
  async resolveId(importee, importer) {
2201
2209
  if (importee === "@marko/run/router") {
2202
- return normalizePath(path4.resolve(root, ROUTER_FILENAME));
2210
+ return normalizePath(path6.resolve(root, ROUTER_FILENAME));
2203
2211
  } else if (importee.endsWith(".marko") && importee.includes(relativeEntryFilesDirPosix)) {
2204
2212
  if (!importee.startsWith(root)) {
2205
- importee = path4.resolve(root, "." + importee);
2213
+ importee = path6.resolve(root, "." + importee);
2206
2214
  }
2207
2215
  return normalizePath(importee);
2208
2216
  }
2209
2217
  let virtualFilePath;
2210
2218
  if (importee.startsWith(virtualFilePrefix)) {
2211
2219
  virtualFilePath = importee.slice(virtualFilePrefix.length + 1);
2212
- importee = path4.resolve(root, virtualFilePath);
2220
+ importee = path6.resolve(root, virtualFilePath);
2213
2221
  } else if (!isBuild && importer && (importer === devEntryFile || normalizePath(importer) === devEntryFilePosix) && importee.startsWith(`/${markoRunFilePrefix}`)) {
2214
- importee = path4.resolve(root, "." + importee);
2222
+ importee = path6.resolve(root, "." + importee);
2215
2223
  }
2216
2224
  importee = normalizePath(importee);
2217
2225
  if (!buildVirtualFilesResult) {
@@ -2220,7 +2228,7 @@ function markoRun(opts = {}) {
2220
2228
  if (virtualFiles.has(importee)) {
2221
2229
  return importee;
2222
2230
  } else if (virtualFilePath) {
2223
- const filePath = path4.resolve(__dirname, "..", virtualFilePath);
2231
+ const filePath = path6.resolve(__dirname, "..", virtualFilePath);
2224
2232
  return await this.resolve(filePath, importer, {
2225
2233
  skipSelf: true
2226
2234
  });
@@ -2259,7 +2267,7 @@ function markoRun(opts = {}) {
2259
2267
  const builtEntries = Object.values(bundle).reduce(
2260
2268
  (acc, item) => {
2261
2269
  if (item.type === "chunk" && item.isEntry) {
2262
- acc.push(path4.join(options.dir, item.fileName));
2270
+ acc.push(path6.join(options.dir, item.fileName));
2263
2271
  }
2264
2272
  return acc;
2265
2273
  },
@@ -2287,12 +2295,12 @@ function markoRun(opts = {}) {
2287
2295
  fs3.rmSync(entryFilesDir, { recursive: true });
2288
2296
  }
2289
2297
  if ((adapter == null ? void 0 : adapter.buildEnd) && routes) {
2290
- await adapter.buildEnd(
2291
- resolvedConfig,
2292
- routes.list,
2293
- routeData.builtEntries,
2294
- routeData.sourceEntries
2295
- );
2298
+ await adapter.buildEnd({
2299
+ routes,
2300
+ config: resolvedConfig,
2301
+ builtEntries: routeData.builtEntries,
2302
+ sourceEntries: routeData.sourceEntries
2303
+ });
2296
2304
  }
2297
2305
  }
2298
2306
  }
@@ -2324,11 +2332,11 @@ async function ensureDir(dir) {
2324
2332
  }
2325
2333
  async function getPackageData(dir) {
2326
2334
  do {
2327
- const pkgPath = path4.join(dir, "package.json");
2335
+ const pkgPath = path6.join(dir, "package.json");
2328
2336
  if (fs3.existsSync(pkgPath)) {
2329
2337
  return JSON.parse(await fs3.promises.readFile(pkgPath, "utf-8"));
2330
2338
  }
2331
- } while (dir !== (dir = path4.dirname(dir)));
2339
+ } while (dir !== (dir = path6.dirname(dir)));
2332
2340
  return null;
2333
2341
  }
2334
2342
  async function resolveAdapter(root, options, log) {
@@ -2433,7 +2441,7 @@ async function getAvailablePort(port) {
2433
2441
  }
2434
2442
 
2435
2443
  // src/cli/commands.ts
2436
- var __dirname2 = path5.dirname(fileURLToPath2(import.meta.url));
2444
+ var __dirname2 = path7.dirname(fileURLToPath2(import.meta.url));
2437
2445
  var defaultConfigFileBases = ["vite.config"];
2438
2446
  var defaultConfigFileExts = [".js", ".cjs", ".mjs", ".ts", ".mts"];
2439
2447
  async function preview(entry, distEntry, cwd, configFile, port, outDir, envFile, args = []) {
@@ -2462,10 +2470,10 @@ async function preview(entry, distEntry, cwd, configFile, port, outDir, envFile,
2462
2470
  if (!entry) {
2463
2471
  entry = await ((_a = adapter.getEntryFile) == null ? void 0 : _a.call(adapter));
2464
2472
  }
2465
- const dir = path5.resolve(cwd, resolvedConfig.build.outDir);
2466
- 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"]);
2467
2475
  if (envFile) {
2468
- envFile = path5.resolve(cwd, envFile);
2476
+ envFile = path7.resolve(cwd, envFile);
2469
2477
  }
2470
2478
  const options = {
2471
2479
  cwd,
@@ -2475,7 +2483,10 @@ async function preview(entry, distEntry, cwd, configFile, port, outDir, envFile,
2475
2483
  envFile,
2476
2484
  entry
2477
2485
  };
2478
- return await adapter.startPreview(entryFile, options);
2486
+ return await adapter.startPreview({
2487
+ entry: entryFile,
2488
+ options
2489
+ });
2479
2490
  }
2480
2491
  async function dev(entry, cwd, configFile, port, envFile, args = []) {
2481
2492
  var _a;
@@ -2489,7 +2500,7 @@ async function dev(entry, cwd, configFile, port, envFile, args = []) {
2489
2500
  "serve"
2490
2501
  );
2491
2502
  if (envFile) {
2492
- envFile = path5.resolve(cwd, envFile);
2503
+ envFile = path7.resolve(cwd, envFile);
2493
2504
  }
2494
2505
  const [availablePort, adapter] = await Promise.all([
2495
2506
  getAvailablePort(
@@ -2519,7 +2530,7 @@ async function dev(entry, cwd, configFile, port, envFile, args = []) {
2519
2530
  port: availablePort,
2520
2531
  envFile
2521
2532
  };
2522
- return await adapter.startDev(entry, config2, options);
2533
+ return await adapter.startDev({ entry, config: config2, options });
2523
2534
  }
2524
2535
  async function build(entry, cwd, configFile, outDir, envFile) {
2525
2536
  var _a;
@@ -2543,7 +2554,7 @@ async function build(entry, cwd, configFile, outDir, envFile) {
2543
2554
  }
2544
2555
  }
2545
2556
  if (envFile) {
2546
- envFile = path5.resolve(cwd, envFile);
2557
+ envFile = path7.resolve(cwd, envFile);
2547
2558
  }
2548
2559
  const buildConfig = setExternalAdapterOptions(
2549
2560
  {
@@ -2581,7 +2592,7 @@ async function build(entry, cwd, configFile, outDir, envFile) {
2581
2592
  }
2582
2593
  function findFileWithExt(dir, base, extensions = defaultConfigFileExts) {
2583
2594
  for (const ext of extensions) {
2584
- const filePath = path5.join(dir, base + ext);
2595
+ const filePath = path7.join(dir, base + ext);
2585
2596
  if (fs5.existsSync(filePath)) {
2586
2597
  return filePath;
2587
2598
  }
@@ -2590,7 +2601,7 @@ function findFileWithExt(dir, base, extensions = defaultConfigFileExts) {
2590
2601
  }
2591
2602
  async function getViteConfig(dir, configFile, bases = defaultConfigFileBases) {
2592
2603
  if (configFile) {
2593
- const configFilePath = path5.join(dir, configFile);
2604
+ const configFilePath = path7.join(dir, configFile);
2594
2605
  if (!fs5.existsSync(configFilePath)) {
2595
2606
  throw new Error(`No config file found at '${configFilePath}'`);
2596
2607
  }
@@ -2602,7 +2613,7 @@ async function getViteConfig(dir, configFile, bases = defaultConfigFileBases) {
2602
2613
  return configFile;
2603
2614
  }
2604
2615
  }
2605
- return path5.join(__dirname2, "default.config.mjs");
2616
+ return path7.join(__dirname2, "default.config.mjs");
2606
2617
  }
2607
2618
  async function resolveAdapter2(config2) {
2608
2619
  const options = getExternalPluginOptions(config2);