@marko/run 0.10.0 → 0.11.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -65,6 +65,7 @@ var import_esbuild_plugin_browserslist = require("esbuild-plugin-browserslist");
65
65
  var import_fs4 = __toESM(require("fs"), 1);
66
66
  var import_glob = require("glob");
67
67
  var import_path6 = __toESM(require("path"), 1);
68
+ var import_rolldown = require("rolldown");
68
69
  var import_url2 = require("url");
69
70
  var import_vite2 = require("vite");
70
71
 
@@ -110,10 +111,10 @@ var httpVerbs = [
110
111
  "options"
111
112
  ];
112
113
  var RoutableFileTypes = {
113
- Page: "page",
114
- Layout: "layout",
115
- Handler: "handler",
116
114
  Middleware: "middleware",
115
+ Handler: "handler",
116
+ Layout: "layout",
117
+ Page: "page",
117
118
  Meta: "meta",
118
119
  NotFound: "404",
119
120
  Error: "500"
@@ -302,19 +303,21 @@ function normalizedRelativePath(from, to) {
302
303
  const relativePath = normalizePath(import_path2.default.relative(from, to));
303
304
  return relativePath.startsWith(".") ? relativePath : "./" + relativePath;
304
305
  }
305
- function renderRouteTemplate(route, markoApi) {
306
+ function renderRouteTemplate(route, markoApi, dev = false) {
306
307
  if (!route.page) {
307
308
  throw new Error(`Route ${route.key} has no page to render`);
308
309
  }
309
- if (!route.templateFilePath) {
310
- throw new Error(`Route ${route.key} has no template file path`);
311
- }
312
310
  const writer = createStringWriter();
313
311
  if (markoApi) {
314
312
  writer.writeLines(`<!-- use ${markoApi} -->
315
313
  `);
316
314
  }
317
- writer.branch("imports");
315
+ const importWriter = writer.branch("imports");
316
+ if (dev) {
317
+ importWriter.writeLines(
318
+ `client import "virtual:marko-run/runtime/client";`
319
+ );
320
+ }
318
321
  writer.writeLines("");
319
322
  writeEntryTemplateTag(
320
323
  writer,
@@ -362,7 +365,10 @@ function renderRouteEntry(route, rootDir) {
362
365
  runtimeImports.push("normalizeMeta");
363
366
  }
364
367
  if (handler || middleware.length) {
365
- runtimeImports.push("call");
368
+ runtimeImports.push("call", "mergeOptions");
369
+ }
370
+ if (page) {
371
+ runtimeImports.push("render");
366
372
  }
367
373
  if (!page || verbs.some((verb) => verb !== "get" && verb !== "head")) {
368
374
  runtimeImports.push("noContent");
@@ -401,7 +407,7 @@ function renderRouteEntry(route, rootDir) {
401
407
  }
402
408
  if (page) {
403
409
  imports.writeLines(
404
- `import page from "${normalizedRelativePath(rootDir, route.templateFilePath || page.filePath)}";`
410
+ `import page from "${normalizedRelativePath(rootDir, route.templateFilePath)}";`
405
411
  );
406
412
  }
407
413
  if (meta) {
@@ -419,11 +425,34 @@ function renderRouteEntry(route, rootDir) {
419
425
  `export const { ${metaVerbsExports} } = normalizeMeta(${metaName});`
420
426
  );
421
427
  }
428
+ const optionsWriter = writer.branch("options").writeLines("");
422
429
  for (const verb of verbs) {
430
+ writeRouteOptions(optionsWriter, route, verb);
423
431
  writeRouteEntryHandler(writer, route, verb);
424
432
  }
433
+ optionsWriter.join();
425
434
  return writer.end();
426
435
  }
436
+ function writeRouteOptions(writer, route, verb) {
437
+ var _a, _b;
438
+ const hasHandler = (_b = (_a = route.handler) == null ? void 0 : _a.verbs) == null ? void 0 : _b.includes(verb);
439
+ writer.write(`export const ${verb}${route.index}_options = `);
440
+ if (route.middleware.length || hasHandler) {
441
+ writer.write(`mergeOptions(`);
442
+ let sep = "";
443
+ for (const { id } of route.middleware) {
444
+ writer.write(`${sep}mware${id}`);
445
+ sep = ", ";
446
+ }
447
+ if (hasHandler) {
448
+ writer.write(`${sep}${verb}Handler`);
449
+ }
450
+ writer.write(");");
451
+ } else {
452
+ writer.write("{};");
453
+ }
454
+ writer.write("\n");
455
+ }
427
456
  function writeRouteEntryHandler(writer, route, verb) {
428
457
  var _a, _b, _c, _d;
429
458
  const { key, index, page, handler, middleware } = route;
@@ -443,13 +472,13 @@ function writeRouteEntryHandler(writer, route, verb) {
443
472
  if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes(verb)) {
444
473
  const name = `${verb}Handler`;
445
474
  continuations.writeLines(
446
- `const ${currentName} = () => context.render(page, {});`
475
+ `const ${currentName} = (data) => render(context, page, {}, data);`
447
476
  );
448
477
  if (len) {
449
478
  nextName = currentName;
450
479
  currentName = `__${name}`;
451
480
  continuations.writeLines(
452
- `const ${currentName} = () => call(${name}, ${nextName}, context);`
481
+ `const ${currentName} = (data) => call(${name}, ${nextName}, context, data);`
453
482
  );
454
483
  } else {
455
484
  if (verb === "head") {
@@ -466,10 +495,10 @@ function writeRouteEntryHandler(writer, route, verb) {
466
495
  hasBody = true;
467
496
  } else if (len) {
468
497
  continuations.writeLines(
469
- `const ${currentName} = () => context.render(page, {});`
498
+ `const ${currentName} = (data) => render(context, page, {}, data);`
470
499
  );
471
500
  } else {
472
- writer.writeLines(`return context.render(page, {});`);
501
+ writer.writeLines(`return render(context, page, {});`);
473
502
  hasBody = true;
474
503
  }
475
504
  } else if ((_b = handler == null ? void 0 : handler.verbs) == null ? void 0 : _b.includes(verb)) {
@@ -478,7 +507,7 @@ function writeRouteEntryHandler(writer, route, verb) {
478
507
  nextName = "noContent";
479
508
  if (len) {
480
509
  continuations.writeLines(
481
- `const ${currentName} = () => call(${name}, ${nextName}, context);`
510
+ `const ${currentName} = (data) => call(${name}, ${nextName}, context, data);`
482
511
  );
483
512
  } else {
484
513
  if (verb === "head") {
@@ -505,7 +534,7 @@ function writeRouteEntryHandler(writer, route, verb) {
505
534
  currentName = i ? `__${name}` : "";
506
535
  if (currentName) {
507
536
  continuations.writeLines(
508
- `const ${currentName} = () => call(${name}, ${nextName}, context);`
537
+ `const ${currentName} = (data) => call(${name}, ${nextName}, context, data);`
509
538
  );
510
539
  } else if (verb === "head") {
511
540
  continuations.writeLines(
@@ -538,6 +567,7 @@ function renderRouter(routes, rootDir, runtimeInclude, options = {
538
567
  for (const verb of verbs) {
539
568
  const verbName = `${verb}${route.index}`;
540
569
  routeImports.push(verbName);
570
+ routeImports.push(`${verbName}_options`);
541
571
  if (route.meta) {
542
572
  routeImports.push(`${verbName}_meta`);
543
573
  }
@@ -548,7 +578,7 @@ function renderRouter(routes, rootDir, runtimeInclude, options = {
548
578
  }
549
579
  for (const route of Object.values(routes.special)) {
550
580
  imports.writeLines(
551
- `import page${route.key} from "${normalizedRelativePath(rootDir, route.templateFilePath || route.page.filePath)}";`
581
+ `import page${route.key} from "${normalizedRelativePath(rootDir, route.templateFilePath)}";`
552
582
  );
553
583
  }
554
584
  writer.writeLines(
@@ -833,10 +863,10 @@ function renderParams(params, pathIndex) {
833
863
  return result ? result + " }" : "{}";
834
864
  }
835
865
  function renderMatch(verb, route, path7, pathIndex) {
836
- const handler = `${verb}${route.index}`;
866
+ const name = `${verb}${route.index}`;
837
867
  const params = path7.params ? renderParams(path7.params, pathIndex) : "{}";
838
- const meta = route.meta ? `${verb}${route.index}_meta` : "{}";
839
- return `{ handler: ${handler}, params: ${params}, meta: ${meta}, path: '${path7.path}' }`;
868
+ const meta = route.meta ? `${name}_meta` : "{}";
869
+ return `{ handler: ${name}, path: '${path7.path}', params: ${params}, options: ${name}_options, meta: ${meta} }`;
840
870
  }
841
871
  function renderMiddleware(middleware, rootDir) {
842
872
  const writer = createStringWriter();
@@ -867,8 +897,14 @@ function stripTsExtension(path7) {
867
897
  }
868
898
  return path7;
869
899
  }
900
+ function* routeFileIter(route) {
901
+ yield* route.middleware;
902
+ if (route.handler) yield route.handler;
903
+ yield* route.layouts;
904
+ if (route.page) yield route.page;
905
+ if (route.meta) yield route.meta;
906
+ }
870
907
  async function renderRouteTypeInfo(routes, outDir, adapter) {
871
- var _a, _b, _c, _d;
872
908
  const writer = createStringWriter();
873
909
  writer.writeLines(
874
910
  `/*
@@ -877,10 +913,11 @@ async function renderRouteTypeInfo(routes, outDir, adapter) {
877
913
  */
878
914
  `,
879
915
  `import { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform } from "@marko/run/namespace";`,
880
- `import type * as Run from "@marko/run";`
916
+ `import type * as $ from "@marko/run";`,
917
+ ""
881
918
  );
882
919
  const headWriter = writer.branch("head");
883
- writer.writeLines("\n").writeBlockStart(`declare module "@marko/run" {`);
920
+ writer.writeLines("").writeBlockStart(`declare module "@marko/run" {`);
884
921
  if (adapter && adapter.typeInfo) {
885
922
  const platformType = await adapter.typeInfo(
886
923
  (data) => headWriter.write(data)
@@ -890,139 +927,120 @@ async function renderRouteTypeInfo(routes, outDir, adapter) {
890
927
  `);
891
928
  }
892
929
  }
893
- headWriter.join();
894
- writer.writeBlockStart(`interface AppData extends Run.DefineApp<{`).writeBlockStart("routes: {");
895
- const routesWriter = writer.branch("routes");
896
- writer.writeBlockEnd("}").writeBlockEnd(`}> {}`).writeBlockEnd(`}`);
897
- const routeTypes = /* @__PURE__ */ new Map();
898
- for (const route of routes.list) {
899
- let routeType = "";
900
- let routeDefinition = "";
901
- if (route.page || route.handler) {
902
- const verbs = [];
903
- if (route.page || ((_b = (_a = route.handler) == null ? void 0 : _a.verbs) == null ? void 0 : _b.includes("get"))) {
904
- verbs.push(`"get"`);
905
- }
906
- if ((_d = (_c = route.handler) == null ? void 0 : _c.verbs) == null ? void 0 : _d.includes("post")) {
907
- verbs.push(`"post"`);
908
- }
909
- routeDefinition = `{ verb: ${verbs.join(" | ")};`;
910
- if (route.meta) {
911
- const metaPath = stripTsExtension(
912
- normalizedRelativePath(outDir, route.meta.filePath)
913
- );
914
- let metaType = `typeof import("${metaPath}")`;
915
- if (/\.(ts|js|mjs)$/.test(route.meta.name)) {
916
- metaType += `["default"]`;
917
- }
918
- routeDefinition += ` meta: ${metaType};`;
919
- }
920
- routeDefinition += " }";
921
- }
922
- const pathType = `"${route.path.path}"`;
923
- routeType += routeType ? " | " + pathType : pathType;
924
- routesWriter.writeLines(`${pathType}: ${routeDefinition};`);
925
- for (const file of [route.handler, route.page]) {
926
- if (file) {
927
- const existing = routeTypes.get(file);
928
- if (!existing) {
929
- routeTypes.set(file, [routeType]);
930
- } else {
931
- existing.push(routeType);
932
- }
930
+ const fileInfoByType = /* @__PURE__ */ new Map();
931
+ let fileIndex = 1;
932
+ function addFile(file) {
933
+ let group = fileInfoByType.get(file.type);
934
+ if (!group) {
935
+ fileInfoByType.set(file.type, group = /* @__PURE__ */ new Map());
936
+ }
937
+ let info = group.get(file);
938
+ if (!info) {
939
+ info = {
940
+ id: "",
941
+ typeName: null,
942
+ modulePath: stripTsExtension(
943
+ normalizedRelativePath(outDir, file.filePath)
944
+ ),
945
+ routes: /* @__PURE__ */ new Set()
946
+ };
947
+ switch (file.type) {
948
+ case RoutableFileTypes.Middleware:
949
+ info.id = `M${group.size + 1}`;
950
+ info.typeName = "Middleware";
951
+ break;
952
+ case RoutableFileTypes.Handler:
953
+ info.id = `H${group.size + 1}`;
954
+ info.typeName = "Handler";
955
+ break;
956
+ case RoutableFileTypes.Meta:
957
+ info.id = `D${group.size + 1}`;
958
+ info.typeName = "Meta";
959
+ break;
960
+ case RoutableFileTypes.Layout:
961
+ info.id = `L${group.size + 1}`;
962
+ info.typeName = "Template";
963
+ break;
964
+ case RoutableFileTypes.Page:
965
+ info.id = `P${group.size + 1}`;
966
+ info.typeName = "Template";
967
+ break;
968
+ default:
969
+ info.id = `F${fileIndex++}`;
970
+ break;
933
971
  }
972
+ group.set(file, info);
934
973
  }
935
- for (const files of [route.middleware, route.layouts]) {
936
- if (files) {
937
- for (const file of files) {
938
- const existing = routeTypes.get(file);
939
- if (!existing) {
940
- routeTypes.set(file, [routeType]);
941
- } else {
942
- existing.push(routeType);
943
- }
944
- }
974
+ return info;
975
+ }
976
+ writer.writeBlockStart(`interface App extends $.DefineRoutes<{`);
977
+ for (const route of routes.list) {
978
+ let routeDefFiles = "";
979
+ for (const file of routeFileIter(route)) {
980
+ const fileInfo = addFile(file);
981
+ fileInfo.routes.add(route);
982
+ if (routeDefFiles) {
983
+ routeDefFiles += ", ";
945
984
  }
985
+ routeDefFiles += fileInfo.id;
946
986
  }
987
+ writer.writeLines(`"${route.path.path}": [${routeDefFiles}];`);
947
988
  }
948
989
  for (const special of Object.values(routes.special)) {
949
- routeTypes.set(special.page, []);
950
- }
951
- routesWriter.join();
952
- const handlerWriter = writer.branch("handler");
953
- const middlewareWriter = writer.branch("middleware");
954
- const pageWriter = writer.branch("page");
955
- const layoutWriter = writer.branch("layout");
956
- for (const [file, types] of routeTypes) {
957
- const modulePath = stripTsExtension(
958
- normalizedRelativePath(outDir, file.filePath)
959
- );
960
- const routeType = `Run.Routes[${types.join(" | ")}]`;
961
- switch (file.type) {
962
- case RoutableFileTypes.Handler:
963
- writeModuleDeclaration(handlerWriter, modulePath, routeType);
964
- break;
965
- case RoutableFileTypes.Middleware:
966
- writeModuleDeclaration(middlewareWriter, modulePath, routeType);
967
- break;
968
- case RoutableFileTypes.Page:
969
- writeModuleDeclaration(pageWriter, modulePath, routeType);
970
- break;
971
- case RoutableFileTypes.Layout:
972
- writeModuleDeclaration(
973
- layoutWriter,
974
- modulePath,
975
- routeType,
976
- `
977
- export interface Input extends Run.LayoutInput<typeof import("${modulePath}")> {}`
990
+ addFile(special.page);
991
+ }
992
+ writer.writeBlockEnd(`}> {}`).writeBlockEnd(`}`);
993
+ for (const fileType of Object.values(RoutableFileTypes)) {
994
+ const fileGroup = fileInfoByType.get(fileType);
995
+ if (!fileGroup) continue;
996
+ for (const info of fileGroup.values()) {
997
+ writer.writeLines("");
998
+ if (info.typeName) {
999
+ writer.writeLines(
1000
+ `type ${info.id} = $.${info.typeName}<"${info.id}", typeof import("${info.modulePath}")>;`
978
1001
  );
979
- break;
980
- case RoutableFileTypes.Error:
981
- writeModuleDeclaration(
982
- writer,
983
- modulePath,
984
- "globalThis.MarkoRun.Route",
985
- `
1002
+ }
1003
+ if (fileType === RoutableFileTypes.Meta) continue;
1004
+ writer.write(`declare module "${info.modulePath}" {`);
1005
+ switch (fileType) {
1006
+ case RoutableFileTypes.Layout:
1007
+ writer.write(`
1008
+ interface Input extends $.LayoutInput<${info.id}> {}`);
1009
+ break;
1010
+ case RoutableFileTypes.Error:
1011
+ writer.write(`
986
1012
  export interface Input {
987
1013
  error: unknown;
988
- }`
989
- );
990
- break;
991
- case RoutableFileTypes.NotFound:
992
- writeModuleDeclaration(writer, modulePath, "Run.Route");
993
- break;
994
- }
995
- }
996
- handlerWriter.join();
997
- middlewareWriter.join();
998
- pageWriter.join();
999
- layoutWriter.join();
1000
- return writer.end();
1001
- }
1002
- function writeModuleDeclaration(writer, name, routeType, moduleTypes) {
1003
- writer.writeLines("").write(`declare module "${name}" {`);
1004
- if (moduleTypes) {
1005
- writer.write(moduleTypes);
1014
+ }`);
1015
+ break;
1016
+ }
1017
+ if (info.typeName) {
1018
+ writer.write(`
1019
+ const Run: $.Namespace<${info.id}>;
1020
+ namespace Run {
1021
+ type Context = $.ContextForFile<${info.id}>${info.typeName === "Template" ? " & Marko.Global" : ""};
1006
1022
  }
1007
- if (routeType) {
1008
- const isMarko = name.endsWith(".marko");
1009
- writer.write(`
1023
+ `);
1024
+ }
1025
+ writer.write(`
1026
+ /** @deprecated use \`Run\` namespace instead */
1010
1027
  namespace MarkoRun {
1011
1028
  export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
1012
- export type Route = ${routeType};
1013
- export type Context = Run.MultiRouteContext<Route>${isMarko ? " & Marko.Global" : ""};
1014
- export type Handler = Run.HandlerLike<Route>;`);
1015
- for (const verb of httpVerbs) {
1029
+ export type Route = ${info.routes.size ? `$.Routes["${[...info.routes].map((route) => route.path.path).join('" | "')}"]` : "globalThis.MarkoRun.Route"};
1030
+ export type Context = ${info.modulePath.endsWith(".marko") ? "Run.Context" : "$.MultiRouteContext<Route>"};
1031
+ export type Handler = $.HandlerLike<Route>;`);
1032
+ for (const verb of httpVerbs) {
1033
+ writer.write(`
1034
+ export type ${verb.toUpperCase()} = $.HandlerLike<Route, "${verb.toUpperCase()}">;`);
1035
+ }
1016
1036
  writer.write(`
1017
- export type ${verb.toUpperCase()} = Run.HandlerLike<Route, "${verb.toUpperCase()}">;`);
1018
- }
1019
- writer.write(`
1020
- /** @deprecated use \`((context, next) => { ... }) satisfies MarkoRun.Handler\` instead */
1021
- export const route: Run.HandlerTypeFn<Route>;
1022
1037
  }`);
1023
- }
1024
- writer.writeLines(`
1038
+ writer.writeLines(`
1025
1039
  }`);
1040
+ }
1041
+ }
1042
+ headWriter.join();
1043
+ return writer.end();
1026
1044
  }
1027
1045
  function createRouteTrie(routes) {
1028
1046
  const root = {
@@ -1507,7 +1525,7 @@ async function buildRoutes(sources, outDir) {
1507
1525
  middleware: [],
1508
1526
  layouts: [...currentLayouts],
1509
1527
  page: file,
1510
- templateFilePath: currentLayouts.size ? import_path3.default.join(outDir, `${type}.marko`) : void 0
1528
+ templateFilePath: import_path3.default.join(outDir, `${type}.marko`)
1511
1529
  };
1512
1530
  layoutsUsed = true;
1513
1531
  }
@@ -1540,7 +1558,7 @@ async function buildRoutes(sources, outDir) {
1540
1558
  meta: dir.files.get(RoutableFileTypes.Meta),
1541
1559
  page,
1542
1560
  handler,
1543
- templateFilePath: page && currentLayouts.size ? import_path3.default.join(outDir, key + ".marko") : void 0
1561
+ templateFilePath: page && import_path3.default.join(outDir, key + ".marko")
1544
1562
  });
1545
1563
  layoutsUsed = !!page;
1546
1564
  for (const middleware2 of currentMiddleware) {
@@ -1579,7 +1597,7 @@ function createFSWalker(dir) {
1579
1597
  onDir,
1580
1598
  maxDepth = 50
1581
1599
  }) {
1582
- async function walk(dir2, depth) {
1600
+ async function walk2(dir2, depth) {
1583
1601
  const onExit = onEnter == null ? void 0 : onEnter(dir2);
1584
1602
  if (onExit !== false) {
1585
1603
  const dirs = [];
@@ -1600,13 +1618,13 @@ function createFSWalker(dir) {
1600
1618
  }
1601
1619
  if ((onDir == null ? void 0 : onDir()) !== false && --depth > 0) {
1602
1620
  for (const entry of dirs) {
1603
- await walk(entry, depth);
1621
+ await walk2(entry, depth);
1604
1622
  }
1605
1623
  }
1606
1624
  onExit == null ? void 0 : onExit();
1607
1625
  }
1608
1626
  }
1609
- await walk(
1627
+ await walk2(
1610
1628
  {
1611
1629
  path: dir,
1612
1630
  name: import_path4.default.basename(dir)
@@ -1663,6 +1681,304 @@ var getExternalPluginOptions = (viteConfig) => getConfig(viteConfig, PluginConfi
1663
1681
  var setExternalPluginOptions = (viteConfig, value) => setConfig(viteConfig, PluginConfigKey, value);
1664
1682
  var getExternalAdapterOptions = (viteConfig) => getConfig(viteConfig, AdapterConfigKey);
1665
1683
 
1684
+ // src/runtime/url-builder.ts
1685
+ var encode = encodeURIComponent;
1686
+ var pathParts = /* @__PURE__ */ new Map();
1687
+ function parsePathParts(path7) {
1688
+ let parts = pathParts.get(path7);
1689
+ if (!parts) {
1690
+ let lastEnd = 0;
1691
+ let paramStart;
1692
+ pathParts.set(path7, parts = [[]]);
1693
+ while (lastEnd >= 0 && (paramStart = path7.indexOf("/$", lastEnd) + 1)) {
1694
+ parts.push(path7.slice(lastEnd, paramStart++));
1695
+ if (path7.charAt(paramStart) === "$") {
1696
+ paramStart++;
1697
+ lastEnd = -1;
1698
+ } else {
1699
+ lastEnd = path7.indexOf("/", paramStart);
1700
+ }
1701
+ parts[0].push(path7.slice(paramStart, lastEnd < 0 ? void 0 : lastEnd));
1702
+ }
1703
+ parts.push(lastEnd >= 0 ? path7.slice(lastEnd) : "");
1704
+ }
1705
+ return parts;
1706
+ }
1707
+ function joinHref(path7, options) {
1708
+ let result = path7;
1709
+ if (options.search) {
1710
+ const query = "" + new URLSearchParams(options.search);
1711
+ if (query) result += "?" + query;
1712
+ }
1713
+ if (options.hash) result += "#" + encode(options.hash);
1714
+ return result;
1715
+ }
1716
+ function href(path7, ...[options]) {
1717
+ return options ? "params" in options ? ((parts) => href_keys(parts, options, ...parts[0]))(
1718
+ parsePathParts(path7)
1719
+ ) : joinHref(path7, options) : path7;
1720
+ }
1721
+ function href_path(strings, ...params) {
1722
+ let i = 0;
1723
+ let j = 0;
1724
+ let result = strings[i++];
1725
+ if (!result || Array.isArray(result)) result = strings[i++];
1726
+ while (i < strings.length) {
1727
+ const param = params[j++];
1728
+ result += (Array.isArray(param) ? param.map(encode).join("/") : encode(param)) + strings[i++];
1729
+ }
1730
+ return result;
1731
+ }
1732
+ function href_values(strings, options, ...params) {
1733
+ return joinHref(href_path(strings, ...params), options);
1734
+ }
1735
+ function href_keys(strings, options, ...keys) {
1736
+ return href_values(strings, options, ...keys.map((k) => options.params[k]));
1737
+ }
1738
+
1739
+ // src/vite/utils/href-replace.ts
1740
+ function findHrefReplacements(code, ast) {
1741
+ const replacements = [];
1742
+ walk(ast, (node) => {
1743
+ var _a, _b;
1744
+ if (node.type !== "CallExpression") return;
1745
+ const callee = node.callee;
1746
+ if (callee.type !== "MemberExpression" || callee.computed || callee.object.type !== "Identifier" || callee.object.name !== "Run" || callee.property.type !== "Identifier" || callee.property.name !== "href") {
1747
+ return;
1748
+ }
1749
+ const args = node.arguments;
1750
+ if (args.length === 0 || args.length > 2 || args.some((a) => a.type === "SpreadElement")) {
1751
+ return;
1752
+ }
1753
+ const pathString = (_a = tryStaticEval(args[0])) == null ? void 0 : _a.value;
1754
+ if (typeof pathString !== "string") {
1755
+ replacements.push({
1756
+ helper: "href",
1757
+ edits: [{ start: callee.start, end: callee.end, code: "href" }]
1758
+ });
1759
+ return;
1760
+ }
1761
+ if (args.length === 1) {
1762
+ replacements.push({
1763
+ helper: false,
1764
+ edits: [
1765
+ {
1766
+ start: node.start,
1767
+ end: node.end,
1768
+ code: JSON.stringify(pathString)
1769
+ }
1770
+ ]
1771
+ });
1772
+ return;
1773
+ }
1774
+ const optionsNode = args[1];
1775
+ const optionsObject = (_b = tryStaticEval(optionsNode)) == null ? void 0 : _b.value;
1776
+ if (optionsObject && typeof optionsObject === "object" && !Array.isArray(optionsObject)) {
1777
+ replacements.push({
1778
+ helper: false,
1779
+ edits: [
1780
+ {
1781
+ start: node.start,
1782
+ end: node.end,
1783
+ code: JSON.stringify(href(pathString, optionsObject))
1784
+ }
1785
+ ]
1786
+ });
1787
+ return;
1788
+ }
1789
+ const parsed = parsePathPattern(pathString);
1790
+ if (optionsNode.type === "ObjectExpression") {
1791
+ const params = tryExtractObjectProperty(optionsNode, "params");
1792
+ if (params && parsed.params.every((p) => params.map.has(p))) {
1793
+ const pathPart = buildPathTemplate(code, parsed, params.map);
1794
+ if (params.only) {
1795
+ replacements.push({
1796
+ helper: "href_path",
1797
+ edits: [
1798
+ {
1799
+ start: node.start,
1800
+ end: node.end,
1801
+ code: "href_path`" + pathPart + "`"
1802
+ }
1803
+ ]
1804
+ });
1805
+ } else {
1806
+ const props = optionsNode.properties;
1807
+ const remaining = props.filter((_, i) => i !== params.index);
1808
+ if (remaining.length === 1 && remaining[0].type === "SpreadElement") {
1809
+ replacements.push({
1810
+ helper: "href_values",
1811
+ edits: [
1812
+ {
1813
+ start: node.start,
1814
+ end: remaining[0].argument.start,
1815
+ code: "href_values`${"
1816
+ },
1817
+ {
1818
+ start: remaining[0].argument.end,
1819
+ end: node.end,
1820
+ code: "}" + pathPart + "`"
1821
+ }
1822
+ ]
1823
+ });
1824
+ } else {
1825
+ replacements.push({
1826
+ helper: "href_values",
1827
+ edits: [
1828
+ {
1829
+ start: node.start,
1830
+ end: optionsNode.start,
1831
+ code: "href_values`${"
1832
+ },
1833
+ {
1834
+ start: optionsNode.end,
1835
+ end: node.end,
1836
+ code: "}" + pathPart + "`"
1837
+ },
1838
+ params.index < props.length - 1 ? {
1839
+ start: props[params.index].start,
1840
+ end: props[params.index + 1].start,
1841
+ code: ""
1842
+ } : {
1843
+ start: props[params.index - 1].end,
1844
+ end: props[params.index].end,
1845
+ code: ""
1846
+ }
1847
+ ]
1848
+ });
1849
+ }
1850
+ }
1851
+ return;
1852
+ }
1853
+ }
1854
+ replacements.push({
1855
+ helper: "href_keys",
1856
+ edits: [
1857
+ { start: node.start, end: optionsNode.start, code: "href_keys`${" },
1858
+ {
1859
+ start: optionsNode.end,
1860
+ end: node.end,
1861
+ code: `}${buildPathTemplate(code, parsed)}\``
1862
+ }
1863
+ ]
1864
+ });
1865
+ });
1866
+ return replacements;
1867
+ }
1868
+ function walk(node, visitor) {
1869
+ if (!node || typeof node !== "object") return;
1870
+ if (node.type) {
1871
+ visitor(node);
1872
+ }
1873
+ for (const key of Object.keys(node)) {
1874
+ if (key === "type" || key === "start" || key === "end" || key === "loc" || key === "range") {
1875
+ continue;
1876
+ }
1877
+ const child = node[key];
1878
+ if (Array.isArray(child)) {
1879
+ for (const item of child) {
1880
+ if (item && typeof item === "object" && item.type) {
1881
+ walk(item, visitor);
1882
+ }
1883
+ }
1884
+ } else if (child && typeof child === "object" && child.type) {
1885
+ walk(child, visitor);
1886
+ }
1887
+ }
1888
+ }
1889
+ function parsePathPattern(path7) {
1890
+ const parts = parsePathParts(path7);
1891
+ return { segments: parts.slice(1), params: parts[0] };
1892
+ }
1893
+ function buildPathTemplate(code, parsed, paramsMap) {
1894
+ let template = "";
1895
+ for (let i = 0; i < parsed.params.length; i++) {
1896
+ template += parsed.segments[i];
1897
+ if (paramsMap) {
1898
+ const paramProp = paramsMap.get(parsed.params[i]);
1899
+ const valueNode = paramProp.shorthand ? paramProp.key : paramProp.value;
1900
+ template += "${" + code.slice(valueNode.start, valueNode.end) + "}";
1901
+ } else {
1902
+ template += '${"' + parsed.params[i] + '"}';
1903
+ }
1904
+ }
1905
+ if (parsed.segments.length > parsed.params.length) {
1906
+ template += parsed.segments[parsed.segments.length - 1];
1907
+ }
1908
+ return template;
1909
+ }
1910
+ function getStaticKey(prop) {
1911
+ if (prop.computed) return null;
1912
+ if (prop.key.type === "Identifier") return prop.key.name;
1913
+ if (prop.key.type === "Literal" && typeof prop.key.value === "string")
1914
+ return prop.key.value;
1915
+ return null;
1916
+ }
1917
+ function tryStaticEval(node) {
1918
+ switch (node.type) {
1919
+ case "Literal":
1920
+ return { value: node.value };
1921
+ case "TemplateLiteral":
1922
+ return node.expressions.length ? null : { value: node.quasis[0].value.cooked };
1923
+ case "ObjectExpression": {
1924
+ const value = {};
1925
+ for (const prop of node.properties) {
1926
+ if (prop.type === "SpreadElement" || prop.shorthand) return null;
1927
+ const key = getStaticKey(prop);
1928
+ if (!key) return null;
1929
+ const val = tryStaticEval(prop.value);
1930
+ if (!val) return null;
1931
+ value[key] = val.value;
1932
+ }
1933
+ return { value };
1934
+ }
1935
+ case "ArrayExpression": {
1936
+ const value = [];
1937
+ for (const elem of node.elements) {
1938
+ if (!elem || elem.type === "SpreadElement") return null;
1939
+ const val = tryStaticEval(elem);
1940
+ if (!val) return null;
1941
+ value.push(val.value);
1942
+ }
1943
+ return { value };
1944
+ }
1945
+ case "UnaryExpression":
1946
+ if (node.prefix && node.operator === "-") {
1947
+ const arg = tryStaticEval(node.argument);
1948
+ if (arg && typeof arg.value === "number") {
1949
+ return { value: -arg.value };
1950
+ }
1951
+ }
1952
+ return null;
1953
+ default:
1954
+ return null;
1955
+ }
1956
+ }
1957
+ function tryExtractObjectProperty(obj, propertyName) {
1958
+ const props = obj.properties;
1959
+ for (let i = props.length - 1; i >= 0; i--) {
1960
+ const prop = props[i];
1961
+ if (prop.type === "SpreadElement") return null;
1962
+ if (getStaticKey(prop) !== propertyName) continue;
1963
+ if (prop.value.type !== "ObjectExpression") return null;
1964
+ const map = /* @__PURE__ */ new Map();
1965
+ for (const paramProp of prop.value.properties) {
1966
+ if (paramProp.type === "SpreadElement") return null;
1967
+ const paramKey = getStaticKey(paramProp);
1968
+ if (!paramKey) return null;
1969
+ map.set(paramKey, paramProp);
1970
+ }
1971
+ let only = i === props.length - 1;
1972
+ let j = i;
1973
+ while (only && j--) {
1974
+ const prop2 = props[j];
1975
+ only = prop2.type === "Property" && getStaticKey(prop2) === propertyName;
1976
+ }
1977
+ return { map, index: i, only };
1978
+ }
1979
+ return null;
1980
+ }
1981
+
1666
1982
  // src/vite/utils/log.ts
1667
1983
  var import_node_zlib = __toESM(require("node:zlib"), 1);
1668
1984
  var import_cli_table3 = __toESM(require("cli-table3"), 1);
@@ -1947,7 +2263,7 @@ function markoRun(opts = {}) {
1947
2263
  var _a, _b;
1948
2264
  routeMarkoApiCache ?? (routeMarkoApiCache = /* @__PURE__ */ new Map());
1949
2265
  if (!routeMarkoApiCache.has(route)) {
1950
- const markoAPI = route.templateFilePath && ((_b = (_a = await loadModule(context, normalizePath(route.layouts[0].filePath))) == null ? void 0 : _a.meta) == null ? void 0 : _b.markoAPI);
2266
+ const markoAPI = route.layouts.length ? (_b = (_a = await loadModule(context, normalizePath(route.layouts[0].filePath))) == null ? void 0 : _a.meta) == null ? void 0 : _b.markoAPI : void 0;
1951
2267
  routeMarkoApiCache.set(route, markoAPI);
1952
2268
  return markoAPI;
1953
2269
  }
@@ -1969,7 +2285,6 @@ function markoRun(opts = {}) {
1969
2285
  let buildVirtualFilesResult;
1970
2286
  function buildVirtualFiles() {
1971
2287
  return buildVirtualFilesResult ?? (buildVirtualFilesResult = (async () => {
1972
- var _a, _b;
1973
2288
  virtualFiles.clear();
1974
2289
  if (import_fs4.default.existsSync(resolvedRoutesDir)) {
1975
2290
  routes = await buildRoutes(
@@ -1994,9 +2309,8 @@ function markoRun(opts = {}) {
1994
2309
  entryTemplates = /* @__PURE__ */ new Set();
1995
2310
  entryTemplateImporters = /* @__PURE__ */ new Set();
1996
2311
  for (const route of routes.list) {
1997
- const routeEntryPath = route.templateFilePath || ((_a = route.page) == null ? void 0 : _a.filePath);
1998
- if (routeEntryPath) {
1999
- entryTemplates.add(normalizePath(routeEntryPath));
2312
+ if (route.templateFilePath) {
2313
+ entryTemplates.add(normalizePath(route.templateFilePath));
2000
2314
  }
2001
2315
  for (const middleware of route.middleware) {
2002
2316
  entryTemplateImporters.add(normalizePath(middleware.filePath));
@@ -2010,9 +2324,8 @@ function markoRun(opts = {}) {
2010
2324
  );
2011
2325
  }
2012
2326
  for (const route of Object.values(routes.special)) {
2013
- const routeEntryPath = route.templateFilePath || ((_b = route.page) == null ? void 0 : _b.filePath);
2014
- if (routeEntryPath) {
2015
- entryTemplates.add(normalizePath(routeEntryPath));
2327
+ if (route.templateFilePath) {
2328
+ entryTemplates.add(normalizePath(route.templateFilePath));
2016
2329
  }
2017
2330
  }
2018
2331
  if (routes.middleware.length) {
@@ -2067,7 +2380,8 @@ function markoRun(opts = {}) {
2067
2380
  route.templateFilePath,
2068
2381
  renderRouteTemplate(
2069
2382
  route,
2070
- await getMarkoApiForRoute(context, route)
2383
+ await getMarkoApiForRoute(context, route),
2384
+ !isBuild
2071
2385
  )
2072
2386
  );
2073
2387
  }
@@ -2077,18 +2391,17 @@ function markoRun(opts = {}) {
2077
2391
  );
2078
2392
  }
2079
2393
  for (const route of Object.values(routes2.special)) {
2080
- if (route.templateFilePath) {
2081
- import_fs4.default.mkdirSync(import_path6.default.dirname(route.templateFilePath), {
2082
- recursive: true
2083
- });
2084
- import_fs4.default.writeFileSync(
2085
- route.templateFilePath,
2086
- renderRouteTemplate(
2087
- route,
2088
- await getMarkoApiForRoute(context, route)
2089
- )
2090
- );
2091
- }
2394
+ import_fs4.default.mkdirSync(import_path6.default.dirname(route.templateFilePath), {
2395
+ recursive: true
2396
+ });
2397
+ import_fs4.default.writeFileSync(
2398
+ route.templateFilePath,
2399
+ renderRouteTemplate(
2400
+ route,
2401
+ await getMarkoApiForRoute(context, route),
2402
+ !isBuild
2403
+ )
2404
+ );
2092
2405
  }
2093
2406
  if (routes2.middleware.length) {
2094
2407
  for (const middleware of routes2.middleware) {
@@ -2393,6 +2706,48 @@ function markoRun(opts = {}) {
2393
2706
  {
2394
2707
  name: `${PLUGIN_NAME_PREFIX}:post`,
2395
2708
  enforce: "post",
2709
+ async transform(code) {
2710
+ if (!isBuild || isSSRBuild || !code.includes("Run.href")) {
2711
+ return;
2712
+ }
2713
+ try {
2714
+ const replacements = findHrefReplacements(
2715
+ code,
2716
+ this.parse(code, { lang: "js" })
2717
+ );
2718
+ if (replacements.length) {
2719
+ const helpers = /* @__PURE__ */ new Set();
2720
+ const s = new import_rolldown.RolldownMagicString(code);
2721
+ for (const { helper, edits } of replacements) {
2722
+ for (const { start, end, code: code2 } of edits) {
2723
+ if (code2) {
2724
+ s.overwrite(start, end, code2);
2725
+ } else {
2726
+ s.remove(start, end);
2727
+ }
2728
+ }
2729
+ if (helper) {
2730
+ helpers.add(helper);
2731
+ }
2732
+ }
2733
+ if (helpers.size) {
2734
+ s.prepend(
2735
+ `import { ${[...helpers].join(", ")} } from "virtual:marko-run/runtime/url-builder";`
2736
+ );
2737
+ }
2738
+ return {
2739
+ code: s
2740
+ };
2741
+ }
2742
+ return null;
2743
+ } catch {
2744
+ return {
2745
+ code: new import_rolldown.RolldownMagicString(code).prepend(
2746
+ 'import "virtual:marko-run/runtime/client";'
2747
+ )
2748
+ };
2749
+ }
2750
+ },
2396
2751
  generateBundle(options, bundle) {
2397
2752
  if (options.sourcemap && options.sourcemap !== "inline") {
2398
2753
  for (const key of Object.keys(bundle)) {