@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.
@@ -33,6 +33,9 @@ import { resolveToEsbuildTarget } from "esbuild-plugin-browserslist";
33
33
  import fs3 from "fs";
34
34
  import { glob } from "glob";
35
35
  import path6 from "path";
36
+ import {
37
+ RolldownMagicString
38
+ } from "rolldown";
36
39
  import { fileURLToPath } from "url";
37
40
  import {
38
41
  buildErrorMessage,
@@ -81,10 +84,10 @@ var httpVerbs = [
81
84
  "options"
82
85
  ];
83
86
  var RoutableFileTypes = {
84
- Page: "page",
85
- Layout: "layout",
86
- Handler: "handler",
87
87
  Middleware: "middleware",
88
+ Handler: "handler",
89
+ Layout: "layout",
90
+ Page: "page",
88
91
  Meta: "meta",
89
92
  NotFound: "404",
90
93
  Error: "500"
@@ -273,19 +276,21 @@ function normalizedRelativePath(from, to) {
273
276
  const relativePath = normalizePath(path2.relative(from, to));
274
277
  return relativePath.startsWith(".") ? relativePath : "./" + relativePath;
275
278
  }
276
- function renderRouteTemplate(route, markoApi) {
279
+ function renderRouteTemplate(route, markoApi, dev2 = false) {
277
280
  if (!route.page) {
278
281
  throw new Error(`Route ${route.key} has no page to render`);
279
282
  }
280
- if (!route.templateFilePath) {
281
- throw new Error(`Route ${route.key} has no template file path`);
282
- }
283
283
  const writer = createStringWriter();
284
284
  if (markoApi) {
285
285
  writer.writeLines(`<!-- use ${markoApi} -->
286
286
  `);
287
287
  }
288
- writer.branch("imports");
288
+ const importWriter = writer.branch("imports");
289
+ if (dev2) {
290
+ importWriter.writeLines(
291
+ `client import "virtual:marko-run/runtime/client";`
292
+ );
293
+ }
289
294
  writer.writeLines("");
290
295
  writeEntryTemplateTag(
291
296
  writer,
@@ -333,7 +338,10 @@ function renderRouteEntry(route, rootDir) {
333
338
  runtimeImports.push("normalizeMeta");
334
339
  }
335
340
  if (handler || middleware.length) {
336
- runtimeImports.push("call");
341
+ runtimeImports.push("call", "mergeOptions");
342
+ }
343
+ if (page) {
344
+ runtimeImports.push("render");
337
345
  }
338
346
  if (!page || verbs.some((verb) => verb !== "get" && verb !== "head")) {
339
347
  runtimeImports.push("noContent");
@@ -372,7 +380,7 @@ function renderRouteEntry(route, rootDir) {
372
380
  }
373
381
  if (page) {
374
382
  imports.writeLines(
375
- `import page from "${normalizedRelativePath(rootDir, route.templateFilePath || page.filePath)}";`
383
+ `import page from "${normalizedRelativePath(rootDir, route.templateFilePath)}";`
376
384
  );
377
385
  }
378
386
  if (meta) {
@@ -390,11 +398,34 @@ function renderRouteEntry(route, rootDir) {
390
398
  `export const { ${metaVerbsExports} } = normalizeMeta(${metaName});`
391
399
  );
392
400
  }
401
+ const optionsWriter = writer.branch("options").writeLines("");
393
402
  for (const verb of verbs) {
403
+ writeRouteOptions(optionsWriter, route, verb);
394
404
  writeRouteEntryHandler(writer, route, verb);
395
405
  }
406
+ optionsWriter.join();
396
407
  return writer.end();
397
408
  }
409
+ function writeRouteOptions(writer, route, verb) {
410
+ var _a, _b;
411
+ const hasHandler = (_b = (_a = route.handler) == null ? void 0 : _a.verbs) == null ? void 0 : _b.includes(verb);
412
+ writer.write(`export const ${verb}${route.index}_options = `);
413
+ if (route.middleware.length || hasHandler) {
414
+ writer.write(`mergeOptions(`);
415
+ let sep = "";
416
+ for (const { id } of route.middleware) {
417
+ writer.write(`${sep}mware${id}`);
418
+ sep = ", ";
419
+ }
420
+ if (hasHandler) {
421
+ writer.write(`${sep}${verb}Handler`);
422
+ }
423
+ writer.write(");");
424
+ } else {
425
+ writer.write("{};");
426
+ }
427
+ writer.write("\n");
428
+ }
398
429
  function writeRouteEntryHandler(writer, route, verb) {
399
430
  var _a, _b, _c, _d;
400
431
  const { key, index, page, handler, middleware } = route;
@@ -414,13 +445,13 @@ function writeRouteEntryHandler(writer, route, verb) {
414
445
  if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes(verb)) {
415
446
  const name = `${verb}Handler`;
416
447
  continuations.writeLines(
417
- `const ${currentName} = () => context.render(page, {});`
448
+ `const ${currentName} = (data) => render(context, page, {}, data);`
418
449
  );
419
450
  if (len) {
420
451
  nextName = currentName;
421
452
  currentName = `__${name}`;
422
453
  continuations.writeLines(
423
- `const ${currentName} = () => call(${name}, ${nextName}, context);`
454
+ `const ${currentName} = (data) => call(${name}, ${nextName}, context, data);`
424
455
  );
425
456
  } else {
426
457
  if (verb === "head") {
@@ -437,10 +468,10 @@ function writeRouteEntryHandler(writer, route, verb) {
437
468
  hasBody = true;
438
469
  } else if (len) {
439
470
  continuations.writeLines(
440
- `const ${currentName} = () => context.render(page, {});`
471
+ `const ${currentName} = (data) => render(context, page, {}, data);`
441
472
  );
442
473
  } else {
443
- writer.writeLines(`return context.render(page, {});`);
474
+ writer.writeLines(`return render(context, page, {});`);
444
475
  hasBody = true;
445
476
  }
446
477
  } else if ((_b = handler == null ? void 0 : handler.verbs) == null ? void 0 : _b.includes(verb)) {
@@ -449,7 +480,7 @@ function writeRouteEntryHandler(writer, route, verb) {
449
480
  nextName = "noContent";
450
481
  if (len) {
451
482
  continuations.writeLines(
452
- `const ${currentName} = () => call(${name}, ${nextName}, context);`
483
+ `const ${currentName} = (data) => call(${name}, ${nextName}, context, data);`
453
484
  );
454
485
  } else {
455
486
  if (verb === "head") {
@@ -476,7 +507,7 @@ function writeRouteEntryHandler(writer, route, verb) {
476
507
  currentName = i ? `__${name}` : "";
477
508
  if (currentName) {
478
509
  continuations.writeLines(
479
- `const ${currentName} = () => call(${name}, ${nextName}, context);`
510
+ `const ${currentName} = (data) => call(${name}, ${nextName}, context, data);`
480
511
  );
481
512
  } else if (verb === "head") {
482
513
  continuations.writeLines(
@@ -509,6 +540,7 @@ function renderRouter(routes, rootDir, runtimeInclude, options = {
509
540
  for (const verb of verbs) {
510
541
  const verbName = `${verb}${route.index}`;
511
542
  routeImports.push(verbName);
543
+ routeImports.push(`${verbName}_options`);
512
544
  if (route.meta) {
513
545
  routeImports.push(`${verbName}_meta`);
514
546
  }
@@ -519,7 +551,7 @@ function renderRouter(routes, rootDir, runtimeInclude, options = {
519
551
  }
520
552
  for (const route of Object.values(routes.special)) {
521
553
  imports.writeLines(
522
- `import page${route.key} from "${normalizedRelativePath(rootDir, route.templateFilePath || route.page.filePath)}";`
554
+ `import page${route.key} from "${normalizedRelativePath(rootDir, route.templateFilePath)}";`
523
555
  );
524
556
  }
525
557
  writer.writeLines(
@@ -804,10 +836,10 @@ function renderParams(params, pathIndex) {
804
836
  return result ? result + " }" : "{}";
805
837
  }
806
838
  function renderMatch(verb, route, path10, pathIndex) {
807
- const handler = `${verb}${route.index}`;
839
+ const name = `${verb}${route.index}`;
808
840
  const params = path10.params ? renderParams(path10.params, pathIndex) : "{}";
809
- const meta = route.meta ? `${verb}${route.index}_meta` : "{}";
810
- return `{ handler: ${handler}, params: ${params}, meta: ${meta}, path: '${path10.path}' }`;
841
+ const meta = route.meta ? `${name}_meta` : "{}";
842
+ return `{ handler: ${name}, path: '${path10.path}', params: ${params}, options: ${name}_options, meta: ${meta} }`;
811
843
  }
812
844
  function renderMiddleware(middleware, rootDir) {
813
845
  const writer = createStringWriter();
@@ -838,8 +870,14 @@ function stripTsExtension(path10) {
838
870
  }
839
871
  return path10;
840
872
  }
873
+ function* routeFileIter(route) {
874
+ yield* route.middleware;
875
+ if (route.handler) yield route.handler;
876
+ yield* route.layouts;
877
+ if (route.page) yield route.page;
878
+ if (route.meta) yield route.meta;
879
+ }
841
880
  async function renderRouteTypeInfo(routes, outDir, adapter) {
842
- var _a, _b, _c, _d;
843
881
  const writer = createStringWriter();
844
882
  writer.writeLines(
845
883
  `/*
@@ -848,10 +886,11 @@ async function renderRouteTypeInfo(routes, outDir, adapter) {
848
886
  */
849
887
  `,
850
888
  `import { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform } from "@marko/run/namespace";`,
851
- `import type * as Run from "@marko/run";`
889
+ `import type * as $ from "@marko/run";`,
890
+ ""
852
891
  );
853
892
  const headWriter = writer.branch("head");
854
- writer.writeLines("\n").writeBlockStart(`declare module "@marko/run" {`);
893
+ writer.writeLines("").writeBlockStart(`declare module "@marko/run" {`);
855
894
  if (adapter && adapter.typeInfo) {
856
895
  const platformType = await adapter.typeInfo(
857
896
  (data) => headWriter.write(data)
@@ -861,139 +900,120 @@ async function renderRouteTypeInfo(routes, outDir, adapter) {
861
900
  `);
862
901
  }
863
902
  }
864
- headWriter.join();
865
- writer.writeBlockStart(`interface AppData extends Run.DefineApp<{`).writeBlockStart("routes: {");
866
- const routesWriter = writer.branch("routes");
867
- writer.writeBlockEnd("}").writeBlockEnd(`}> {}`).writeBlockEnd(`}`);
868
- const routeTypes = /* @__PURE__ */ new Map();
869
- for (const route of routes.list) {
870
- let routeType = "";
871
- let routeDefinition = "";
872
- if (route.page || route.handler) {
873
- const verbs = [];
874
- if (route.page || ((_b = (_a = route.handler) == null ? void 0 : _a.verbs) == null ? void 0 : _b.includes("get"))) {
875
- verbs.push(`"get"`);
876
- }
877
- if ((_d = (_c = route.handler) == null ? void 0 : _c.verbs) == null ? void 0 : _d.includes("post")) {
878
- verbs.push(`"post"`);
879
- }
880
- routeDefinition = `{ verb: ${verbs.join(" | ")};`;
881
- if (route.meta) {
882
- const metaPath = stripTsExtension(
883
- normalizedRelativePath(outDir, route.meta.filePath)
884
- );
885
- let metaType = `typeof import("${metaPath}")`;
886
- if (/\.(ts|js|mjs)$/.test(route.meta.name)) {
887
- metaType += `["default"]`;
888
- }
889
- routeDefinition += ` meta: ${metaType};`;
890
- }
891
- routeDefinition += " }";
892
- }
893
- const pathType = `"${route.path.path}"`;
894
- routeType += routeType ? " | " + pathType : pathType;
895
- routesWriter.writeLines(`${pathType}: ${routeDefinition};`);
896
- for (const file of [route.handler, route.page]) {
897
- if (file) {
898
- const existing = routeTypes.get(file);
899
- if (!existing) {
900
- routeTypes.set(file, [routeType]);
901
- } else {
902
- existing.push(routeType);
903
- }
903
+ const fileInfoByType = /* @__PURE__ */ new Map();
904
+ let fileIndex = 1;
905
+ function addFile(file) {
906
+ let group = fileInfoByType.get(file.type);
907
+ if (!group) {
908
+ fileInfoByType.set(file.type, group = /* @__PURE__ */ new Map());
909
+ }
910
+ let info = group.get(file);
911
+ if (!info) {
912
+ info = {
913
+ id: "",
914
+ typeName: null,
915
+ modulePath: stripTsExtension(
916
+ normalizedRelativePath(outDir, file.filePath)
917
+ ),
918
+ routes: /* @__PURE__ */ new Set()
919
+ };
920
+ switch (file.type) {
921
+ case RoutableFileTypes.Middleware:
922
+ info.id = `M${group.size + 1}`;
923
+ info.typeName = "Middleware";
924
+ break;
925
+ case RoutableFileTypes.Handler:
926
+ info.id = `H${group.size + 1}`;
927
+ info.typeName = "Handler";
928
+ break;
929
+ case RoutableFileTypes.Meta:
930
+ info.id = `D${group.size + 1}`;
931
+ info.typeName = "Meta";
932
+ break;
933
+ case RoutableFileTypes.Layout:
934
+ info.id = `L${group.size + 1}`;
935
+ info.typeName = "Template";
936
+ break;
937
+ case RoutableFileTypes.Page:
938
+ info.id = `P${group.size + 1}`;
939
+ info.typeName = "Template";
940
+ break;
941
+ default:
942
+ info.id = `F${fileIndex++}`;
943
+ break;
904
944
  }
945
+ group.set(file, info);
905
946
  }
906
- for (const files of [route.middleware, route.layouts]) {
907
- if (files) {
908
- for (const file of files) {
909
- const existing = routeTypes.get(file);
910
- if (!existing) {
911
- routeTypes.set(file, [routeType]);
912
- } else {
913
- existing.push(routeType);
914
- }
915
- }
947
+ return info;
948
+ }
949
+ writer.writeBlockStart(`interface App extends $.DefineRoutes<{`);
950
+ for (const route of routes.list) {
951
+ let routeDefFiles = "";
952
+ for (const file of routeFileIter(route)) {
953
+ const fileInfo = addFile(file);
954
+ fileInfo.routes.add(route);
955
+ if (routeDefFiles) {
956
+ routeDefFiles += ", ";
916
957
  }
958
+ routeDefFiles += fileInfo.id;
917
959
  }
960
+ writer.writeLines(`"${route.path.path}": [${routeDefFiles}];`);
918
961
  }
919
962
  for (const special of Object.values(routes.special)) {
920
- routeTypes.set(special.page, []);
921
- }
922
- routesWriter.join();
923
- const handlerWriter = writer.branch("handler");
924
- const middlewareWriter = writer.branch("middleware");
925
- const pageWriter = writer.branch("page");
926
- const layoutWriter = writer.branch("layout");
927
- for (const [file, types] of routeTypes) {
928
- const modulePath = stripTsExtension(
929
- normalizedRelativePath(outDir, file.filePath)
930
- );
931
- const routeType = `Run.Routes[${types.join(" | ")}]`;
932
- switch (file.type) {
933
- case RoutableFileTypes.Handler:
934
- writeModuleDeclaration(handlerWriter, modulePath, routeType);
935
- break;
936
- case RoutableFileTypes.Middleware:
937
- writeModuleDeclaration(middlewareWriter, modulePath, routeType);
938
- break;
939
- case RoutableFileTypes.Page:
940
- writeModuleDeclaration(pageWriter, modulePath, routeType);
941
- break;
942
- case RoutableFileTypes.Layout:
943
- writeModuleDeclaration(
944
- layoutWriter,
945
- modulePath,
946
- routeType,
947
- `
948
- export interface Input extends Run.LayoutInput<typeof import("${modulePath}")> {}`
963
+ addFile(special.page);
964
+ }
965
+ writer.writeBlockEnd(`}> {}`).writeBlockEnd(`}`);
966
+ for (const fileType of Object.values(RoutableFileTypes)) {
967
+ const fileGroup = fileInfoByType.get(fileType);
968
+ if (!fileGroup) continue;
969
+ for (const info of fileGroup.values()) {
970
+ writer.writeLines("");
971
+ if (info.typeName) {
972
+ writer.writeLines(
973
+ `type ${info.id} = $.${info.typeName}<"${info.id}", typeof import("${info.modulePath}")>;`
949
974
  );
950
- break;
951
- case RoutableFileTypes.Error:
952
- writeModuleDeclaration(
953
- writer,
954
- modulePath,
955
- "globalThis.MarkoRun.Route",
956
- `
975
+ }
976
+ if (fileType === RoutableFileTypes.Meta) continue;
977
+ writer.write(`declare module "${info.modulePath}" {`);
978
+ switch (fileType) {
979
+ case RoutableFileTypes.Layout:
980
+ writer.write(`
981
+ interface Input extends $.LayoutInput<${info.id}> {}`);
982
+ break;
983
+ case RoutableFileTypes.Error:
984
+ writer.write(`
957
985
  export interface Input {
958
986
  error: unknown;
959
- }`
960
- );
961
- break;
962
- case RoutableFileTypes.NotFound:
963
- writeModuleDeclaration(writer, modulePath, "Run.Route");
964
- break;
965
- }
966
- }
967
- handlerWriter.join();
968
- middlewareWriter.join();
969
- pageWriter.join();
970
- layoutWriter.join();
971
- return writer.end();
972
- }
973
- function writeModuleDeclaration(writer, name, routeType, moduleTypes) {
974
- writer.writeLines("").write(`declare module "${name}" {`);
975
- if (moduleTypes) {
976
- writer.write(moduleTypes);
987
+ }`);
988
+ break;
989
+ }
990
+ if (info.typeName) {
991
+ writer.write(`
992
+ const Run: $.Namespace<${info.id}>;
993
+ namespace Run {
994
+ type Context = $.ContextForFile<${info.id}>${info.typeName === "Template" ? " & Marko.Global" : ""};
977
995
  }
978
- if (routeType) {
979
- const isMarko = name.endsWith(".marko");
980
- writer.write(`
996
+ `);
997
+ }
998
+ writer.write(`
999
+ /** @deprecated use \`Run\` namespace instead */
981
1000
  namespace MarkoRun {
982
1001
  export { NotHandled, NotMatched, GetPaths, PostPaths, GetablePath, GetableHref, PostablePath, PostableHref, Platform };
983
- export type Route = ${routeType};
984
- export type Context = Run.MultiRouteContext<Route>${isMarko ? " & Marko.Global" : ""};
985
- export type Handler = Run.HandlerLike<Route>;`);
986
- for (const verb of httpVerbs) {
1002
+ export type Route = ${info.routes.size ? `$.Routes["${[...info.routes].map((route) => route.path.path).join('" | "')}"]` : "globalThis.MarkoRun.Route"};
1003
+ export type Context = ${info.modulePath.endsWith(".marko") ? "Run.Context" : "$.MultiRouteContext<Route>"};
1004
+ export type Handler = $.HandlerLike<Route>;`);
1005
+ for (const verb of httpVerbs) {
1006
+ writer.write(`
1007
+ export type ${verb.toUpperCase()} = $.HandlerLike<Route, "${verb.toUpperCase()}">;`);
1008
+ }
987
1009
  writer.write(`
988
- export type ${verb.toUpperCase()} = Run.HandlerLike<Route, "${verb.toUpperCase()}">;`);
989
- }
990
- writer.write(`
991
- /** @deprecated use \`((context, next) => { ... }) satisfies MarkoRun.Handler\` instead */
992
- export const route: Run.HandlerTypeFn<Route>;
993
1010
  }`);
994
- }
995
- writer.writeLines(`
1011
+ writer.writeLines(`
996
1012
  }`);
1013
+ }
1014
+ }
1015
+ headWriter.join();
1016
+ return writer.end();
997
1017
  }
998
1018
  function createRouteTrie(routes) {
999
1019
  const root = {
@@ -1478,7 +1498,7 @@ async function buildRoutes(sources, outDir) {
1478
1498
  middleware: [],
1479
1499
  layouts: [...currentLayouts],
1480
1500
  page: file,
1481
- templateFilePath: currentLayouts.size ? path3.join(outDir, `${type}.marko`) : void 0
1501
+ templateFilePath: path3.join(outDir, `${type}.marko`)
1482
1502
  };
1483
1503
  layoutsUsed = true;
1484
1504
  }
@@ -1511,7 +1531,7 @@ async function buildRoutes(sources, outDir) {
1511
1531
  meta: dir.files.get(RoutableFileTypes.Meta),
1512
1532
  page,
1513
1533
  handler,
1514
- templateFilePath: page && currentLayouts.size ? path3.join(outDir, key + ".marko") : void 0
1534
+ templateFilePath: page && path3.join(outDir, key + ".marko")
1515
1535
  });
1516
1536
  layoutsUsed = !!page;
1517
1537
  for (const middleware2 of currentMiddleware) {
@@ -1550,7 +1570,7 @@ function createFSWalker(dir) {
1550
1570
  onDir,
1551
1571
  maxDepth = 50
1552
1572
  }) {
1553
- async function walk(dir2, depth) {
1573
+ async function walk2(dir2, depth) {
1554
1574
  const onExit = onEnter == null ? void 0 : onEnter(dir2);
1555
1575
  if (onExit !== false) {
1556
1576
  const dirs = [];
@@ -1571,13 +1591,13 @@ function createFSWalker(dir) {
1571
1591
  }
1572
1592
  if ((onDir == null ? void 0 : onDir()) !== false && --depth > 0) {
1573
1593
  for (const entry of dirs) {
1574
- await walk(entry, depth);
1594
+ await walk2(entry, depth);
1575
1595
  }
1576
1596
  }
1577
1597
  onExit == null ? void 0 : onExit();
1578
1598
  }
1579
1599
  }
1580
- await walk(
1600
+ await walk2(
1581
1601
  {
1582
1602
  path: dir,
1583
1603
  name: path4.basename(dir)
@@ -1635,6 +1655,304 @@ var setExternalPluginOptions = (viteConfig, value) => setConfig(viteConfig, Plug
1635
1655
  var getExternalAdapterOptions = (viteConfig) => getConfig(viteConfig, AdapterConfigKey);
1636
1656
  var setExternalAdapterOptions = (viteConfig, value) => setConfig(viteConfig, AdapterConfigKey, value);
1637
1657
 
1658
+ // src/runtime/url-builder.ts
1659
+ var encode = encodeURIComponent;
1660
+ var pathParts = /* @__PURE__ */ new Map();
1661
+ function parsePathParts(path10) {
1662
+ let parts = pathParts.get(path10);
1663
+ if (!parts) {
1664
+ let lastEnd = 0;
1665
+ let paramStart;
1666
+ pathParts.set(path10, parts = [[]]);
1667
+ while (lastEnd >= 0 && (paramStart = path10.indexOf("/$", lastEnd) + 1)) {
1668
+ parts.push(path10.slice(lastEnd, paramStart++));
1669
+ if (path10.charAt(paramStart) === "$") {
1670
+ paramStart++;
1671
+ lastEnd = -1;
1672
+ } else {
1673
+ lastEnd = path10.indexOf("/", paramStart);
1674
+ }
1675
+ parts[0].push(path10.slice(paramStart, lastEnd < 0 ? void 0 : lastEnd));
1676
+ }
1677
+ parts.push(lastEnd >= 0 ? path10.slice(lastEnd) : "");
1678
+ }
1679
+ return parts;
1680
+ }
1681
+ function joinHref(path10, options) {
1682
+ let result = path10;
1683
+ if (options.search) {
1684
+ const query = "" + new URLSearchParams(options.search);
1685
+ if (query) result += "?" + query;
1686
+ }
1687
+ if (options.hash) result += "#" + encode(options.hash);
1688
+ return result;
1689
+ }
1690
+ function href(path10, ...[options]) {
1691
+ return options ? "params" in options ? ((parts) => href_keys(parts, options, ...parts[0]))(
1692
+ parsePathParts(path10)
1693
+ ) : joinHref(path10, options) : path10;
1694
+ }
1695
+ function href_path(strings, ...params) {
1696
+ let i = 0;
1697
+ let j = 0;
1698
+ let result = strings[i++];
1699
+ if (!result || Array.isArray(result)) result = strings[i++];
1700
+ while (i < strings.length) {
1701
+ const param = params[j++];
1702
+ result += (Array.isArray(param) ? param.map(encode).join("/") : encode(param)) + strings[i++];
1703
+ }
1704
+ return result;
1705
+ }
1706
+ function href_values(strings, options, ...params) {
1707
+ return joinHref(href_path(strings, ...params), options);
1708
+ }
1709
+ function href_keys(strings, options, ...keys) {
1710
+ return href_values(strings, options, ...keys.map((k) => options.params[k]));
1711
+ }
1712
+
1713
+ // src/vite/utils/href-replace.ts
1714
+ function findHrefReplacements(code, ast) {
1715
+ const replacements = [];
1716
+ walk(ast, (node) => {
1717
+ var _a, _b;
1718
+ if (node.type !== "CallExpression") return;
1719
+ const callee = node.callee;
1720
+ if (callee.type !== "MemberExpression" || callee.computed || callee.object.type !== "Identifier" || callee.object.name !== "Run" || callee.property.type !== "Identifier" || callee.property.name !== "href") {
1721
+ return;
1722
+ }
1723
+ const args = node.arguments;
1724
+ if (args.length === 0 || args.length > 2 || args.some((a) => a.type === "SpreadElement")) {
1725
+ return;
1726
+ }
1727
+ const pathString = (_a = tryStaticEval(args[0])) == null ? void 0 : _a.value;
1728
+ if (typeof pathString !== "string") {
1729
+ replacements.push({
1730
+ helper: "href",
1731
+ edits: [{ start: callee.start, end: callee.end, code: "href" }]
1732
+ });
1733
+ return;
1734
+ }
1735
+ if (args.length === 1) {
1736
+ replacements.push({
1737
+ helper: false,
1738
+ edits: [
1739
+ {
1740
+ start: node.start,
1741
+ end: node.end,
1742
+ code: JSON.stringify(pathString)
1743
+ }
1744
+ ]
1745
+ });
1746
+ return;
1747
+ }
1748
+ const optionsNode = args[1];
1749
+ const optionsObject = (_b = tryStaticEval(optionsNode)) == null ? void 0 : _b.value;
1750
+ if (optionsObject && typeof optionsObject === "object" && !Array.isArray(optionsObject)) {
1751
+ replacements.push({
1752
+ helper: false,
1753
+ edits: [
1754
+ {
1755
+ start: node.start,
1756
+ end: node.end,
1757
+ code: JSON.stringify(href(pathString, optionsObject))
1758
+ }
1759
+ ]
1760
+ });
1761
+ return;
1762
+ }
1763
+ const parsed = parsePathPattern(pathString);
1764
+ if (optionsNode.type === "ObjectExpression") {
1765
+ const params = tryExtractObjectProperty(optionsNode, "params");
1766
+ if (params && parsed.params.every((p) => params.map.has(p))) {
1767
+ const pathPart = buildPathTemplate(code, parsed, params.map);
1768
+ if (params.only) {
1769
+ replacements.push({
1770
+ helper: "href_path",
1771
+ edits: [
1772
+ {
1773
+ start: node.start,
1774
+ end: node.end,
1775
+ code: "href_path`" + pathPart + "`"
1776
+ }
1777
+ ]
1778
+ });
1779
+ } else {
1780
+ const props = optionsNode.properties;
1781
+ const remaining = props.filter((_, i) => i !== params.index);
1782
+ if (remaining.length === 1 && remaining[0].type === "SpreadElement") {
1783
+ replacements.push({
1784
+ helper: "href_values",
1785
+ edits: [
1786
+ {
1787
+ start: node.start,
1788
+ end: remaining[0].argument.start,
1789
+ code: "href_values`${"
1790
+ },
1791
+ {
1792
+ start: remaining[0].argument.end,
1793
+ end: node.end,
1794
+ code: "}" + pathPart + "`"
1795
+ }
1796
+ ]
1797
+ });
1798
+ } else {
1799
+ replacements.push({
1800
+ helper: "href_values",
1801
+ edits: [
1802
+ {
1803
+ start: node.start,
1804
+ end: optionsNode.start,
1805
+ code: "href_values`${"
1806
+ },
1807
+ {
1808
+ start: optionsNode.end,
1809
+ end: node.end,
1810
+ code: "}" + pathPart + "`"
1811
+ },
1812
+ params.index < props.length - 1 ? {
1813
+ start: props[params.index].start,
1814
+ end: props[params.index + 1].start,
1815
+ code: ""
1816
+ } : {
1817
+ start: props[params.index - 1].end,
1818
+ end: props[params.index].end,
1819
+ code: ""
1820
+ }
1821
+ ]
1822
+ });
1823
+ }
1824
+ }
1825
+ return;
1826
+ }
1827
+ }
1828
+ replacements.push({
1829
+ helper: "href_keys",
1830
+ edits: [
1831
+ { start: node.start, end: optionsNode.start, code: "href_keys`${" },
1832
+ {
1833
+ start: optionsNode.end,
1834
+ end: node.end,
1835
+ code: `}${buildPathTemplate(code, parsed)}\``
1836
+ }
1837
+ ]
1838
+ });
1839
+ });
1840
+ return replacements;
1841
+ }
1842
+ function walk(node, visitor) {
1843
+ if (!node || typeof node !== "object") return;
1844
+ if (node.type) {
1845
+ visitor(node);
1846
+ }
1847
+ for (const key of Object.keys(node)) {
1848
+ if (key === "type" || key === "start" || key === "end" || key === "loc" || key === "range") {
1849
+ continue;
1850
+ }
1851
+ const child = node[key];
1852
+ if (Array.isArray(child)) {
1853
+ for (const item of child) {
1854
+ if (item && typeof item === "object" && item.type) {
1855
+ walk(item, visitor);
1856
+ }
1857
+ }
1858
+ } else if (child && typeof child === "object" && child.type) {
1859
+ walk(child, visitor);
1860
+ }
1861
+ }
1862
+ }
1863
+ function parsePathPattern(path10) {
1864
+ const parts = parsePathParts(path10);
1865
+ return { segments: parts.slice(1), params: parts[0] };
1866
+ }
1867
+ function buildPathTemplate(code, parsed, paramsMap) {
1868
+ let template = "";
1869
+ for (let i = 0; i < parsed.params.length; i++) {
1870
+ template += parsed.segments[i];
1871
+ if (paramsMap) {
1872
+ const paramProp = paramsMap.get(parsed.params[i]);
1873
+ const valueNode = paramProp.shorthand ? paramProp.key : paramProp.value;
1874
+ template += "${" + code.slice(valueNode.start, valueNode.end) + "}";
1875
+ } else {
1876
+ template += '${"' + parsed.params[i] + '"}';
1877
+ }
1878
+ }
1879
+ if (parsed.segments.length > parsed.params.length) {
1880
+ template += parsed.segments[parsed.segments.length - 1];
1881
+ }
1882
+ return template;
1883
+ }
1884
+ function getStaticKey(prop) {
1885
+ if (prop.computed) return null;
1886
+ if (prop.key.type === "Identifier") return prop.key.name;
1887
+ if (prop.key.type === "Literal" && typeof prop.key.value === "string")
1888
+ return prop.key.value;
1889
+ return null;
1890
+ }
1891
+ function tryStaticEval(node) {
1892
+ switch (node.type) {
1893
+ case "Literal":
1894
+ return { value: node.value };
1895
+ case "TemplateLiteral":
1896
+ return node.expressions.length ? null : { value: node.quasis[0].value.cooked };
1897
+ case "ObjectExpression": {
1898
+ const value = {};
1899
+ for (const prop of node.properties) {
1900
+ if (prop.type === "SpreadElement" || prop.shorthand) return null;
1901
+ const key = getStaticKey(prop);
1902
+ if (!key) return null;
1903
+ const val = tryStaticEval(prop.value);
1904
+ if (!val) return null;
1905
+ value[key] = val.value;
1906
+ }
1907
+ return { value };
1908
+ }
1909
+ case "ArrayExpression": {
1910
+ const value = [];
1911
+ for (const elem of node.elements) {
1912
+ if (!elem || elem.type === "SpreadElement") return null;
1913
+ const val = tryStaticEval(elem);
1914
+ if (!val) return null;
1915
+ value.push(val.value);
1916
+ }
1917
+ return { value };
1918
+ }
1919
+ case "UnaryExpression":
1920
+ if (node.prefix && node.operator === "-") {
1921
+ const arg = tryStaticEval(node.argument);
1922
+ if (arg && typeof arg.value === "number") {
1923
+ return { value: -arg.value };
1924
+ }
1925
+ }
1926
+ return null;
1927
+ default:
1928
+ return null;
1929
+ }
1930
+ }
1931
+ function tryExtractObjectProperty(obj, propertyName) {
1932
+ const props = obj.properties;
1933
+ for (let i = props.length - 1; i >= 0; i--) {
1934
+ const prop = props[i];
1935
+ if (prop.type === "SpreadElement") return null;
1936
+ if (getStaticKey(prop) !== propertyName) continue;
1937
+ if (prop.value.type !== "ObjectExpression") return null;
1938
+ const map = /* @__PURE__ */ new Map();
1939
+ for (const paramProp of prop.value.properties) {
1940
+ if (paramProp.type === "SpreadElement") return null;
1941
+ const paramKey = getStaticKey(paramProp);
1942
+ if (!paramKey) return null;
1943
+ map.set(paramKey, paramProp);
1944
+ }
1945
+ let only = i === props.length - 1;
1946
+ let j = i;
1947
+ while (only && j--) {
1948
+ const prop2 = props[j];
1949
+ only = prop2.type === "Property" && getStaticKey(prop2) === propertyName;
1950
+ }
1951
+ return { map, index: i, only };
1952
+ }
1953
+ return null;
1954
+ }
1955
+
1638
1956
  // src/vite/utils/log.ts
1639
1957
  import zlib from "node:zlib";
1640
1958
  import Table from "cli-table3";
@@ -1919,7 +2237,7 @@ function markoRun(opts = {}) {
1919
2237
  var _a, _b;
1920
2238
  routeMarkoApiCache ?? (routeMarkoApiCache = /* @__PURE__ */ new Map());
1921
2239
  if (!routeMarkoApiCache.has(route)) {
1922
- const markoAPI = route.templateFilePath && ((_b = (_a = await loadModule(context, normalizePath(route.layouts[0].filePath))) == null ? void 0 : _a.meta) == null ? void 0 : _b.markoAPI);
2240
+ 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;
1923
2241
  routeMarkoApiCache.set(route, markoAPI);
1924
2242
  return markoAPI;
1925
2243
  }
@@ -1941,7 +2259,6 @@ function markoRun(opts = {}) {
1941
2259
  let buildVirtualFilesResult;
1942
2260
  function buildVirtualFiles() {
1943
2261
  return buildVirtualFilesResult ?? (buildVirtualFilesResult = (async () => {
1944
- var _a, _b;
1945
2262
  virtualFiles.clear();
1946
2263
  if (fs3.existsSync(resolvedRoutesDir)) {
1947
2264
  routes = await buildRoutes(
@@ -1966,9 +2283,8 @@ function markoRun(opts = {}) {
1966
2283
  entryTemplates = /* @__PURE__ */ new Set();
1967
2284
  entryTemplateImporters = /* @__PURE__ */ new Set();
1968
2285
  for (const route of routes.list) {
1969
- const routeEntryPath = route.templateFilePath || ((_a = route.page) == null ? void 0 : _a.filePath);
1970
- if (routeEntryPath) {
1971
- entryTemplates.add(normalizePath(routeEntryPath));
2286
+ if (route.templateFilePath) {
2287
+ entryTemplates.add(normalizePath(route.templateFilePath));
1972
2288
  }
1973
2289
  for (const middleware of route.middleware) {
1974
2290
  entryTemplateImporters.add(normalizePath(middleware.filePath));
@@ -1982,9 +2298,8 @@ function markoRun(opts = {}) {
1982
2298
  );
1983
2299
  }
1984
2300
  for (const route of Object.values(routes.special)) {
1985
- const routeEntryPath = route.templateFilePath || ((_b = route.page) == null ? void 0 : _b.filePath);
1986
- if (routeEntryPath) {
1987
- entryTemplates.add(normalizePath(routeEntryPath));
2301
+ if (route.templateFilePath) {
2302
+ entryTemplates.add(normalizePath(route.templateFilePath));
1988
2303
  }
1989
2304
  }
1990
2305
  if (routes.middleware.length) {
@@ -2039,7 +2354,8 @@ function markoRun(opts = {}) {
2039
2354
  route.templateFilePath,
2040
2355
  renderRouteTemplate(
2041
2356
  route,
2042
- await getMarkoApiForRoute(context, route)
2357
+ await getMarkoApiForRoute(context, route),
2358
+ !isBuild
2043
2359
  )
2044
2360
  );
2045
2361
  }
@@ -2049,18 +2365,17 @@ function markoRun(opts = {}) {
2049
2365
  );
2050
2366
  }
2051
2367
  for (const route of Object.values(routes2.special)) {
2052
- if (route.templateFilePath) {
2053
- fs3.mkdirSync(path6.dirname(route.templateFilePath), {
2054
- recursive: true
2055
- });
2056
- fs3.writeFileSync(
2057
- route.templateFilePath,
2058
- renderRouteTemplate(
2059
- route,
2060
- await getMarkoApiForRoute(context, route)
2061
- )
2062
- );
2063
- }
2368
+ fs3.mkdirSync(path6.dirname(route.templateFilePath), {
2369
+ recursive: true
2370
+ });
2371
+ fs3.writeFileSync(
2372
+ route.templateFilePath,
2373
+ renderRouteTemplate(
2374
+ route,
2375
+ await getMarkoApiForRoute(context, route),
2376
+ !isBuild
2377
+ )
2378
+ );
2064
2379
  }
2065
2380
  if (routes2.middleware.length) {
2066
2381
  for (const middleware of routes2.middleware) {
@@ -2365,6 +2680,48 @@ function markoRun(opts = {}) {
2365
2680
  {
2366
2681
  name: `${PLUGIN_NAME_PREFIX}:post`,
2367
2682
  enforce: "post",
2683
+ async transform(code) {
2684
+ if (!isBuild || isSSRBuild || !code.includes("Run.href")) {
2685
+ return;
2686
+ }
2687
+ try {
2688
+ const replacements = findHrefReplacements(
2689
+ code,
2690
+ this.parse(code, { lang: "js" })
2691
+ );
2692
+ if (replacements.length) {
2693
+ const helpers = /* @__PURE__ */ new Set();
2694
+ const s = new RolldownMagicString(code);
2695
+ for (const { helper, edits } of replacements) {
2696
+ for (const { start, end, code: code2 } of edits) {
2697
+ if (code2) {
2698
+ s.overwrite(start, end, code2);
2699
+ } else {
2700
+ s.remove(start, end);
2701
+ }
2702
+ }
2703
+ if (helper) {
2704
+ helpers.add(helper);
2705
+ }
2706
+ }
2707
+ if (helpers.size) {
2708
+ s.prepend(
2709
+ `import { ${[...helpers].join(", ")} } from "virtual:marko-run/runtime/url-builder";`
2710
+ );
2711
+ }
2712
+ return {
2713
+ code: s
2714
+ };
2715
+ }
2716
+ return null;
2717
+ } catch {
2718
+ return {
2719
+ code: new RolldownMagicString(code).prepend(
2720
+ 'import "virtual:marko-run/runtime/client";'
2721
+ )
2722
+ };
2723
+ }
2724
+ },
2368
2725
  generateBundle(options, bundle) {
2369
2726
  if (options.sourcemap && options.sourcemap !== "inline") {
2370
2727
  for (const key of Object.keys(bundle)) {