@marko/run 0.0.1-beta6 → 0.0.1-beta7

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.
@@ -123,7 +123,14 @@ async function buildRoutes(walk, basePath) {
123
123
  if (handler || page) {
124
124
  const key = path3.replace(/(\$\$?)[^\/]*/g, "$1").replace(/^\/+/, "").replace(/[^a-z0-9_$\/]+/gi, "").replace(/\//g, "__") || "index";
125
125
  if (routes.has(key)) {
126
- console.warn(`Duplicate route for path ${path3} -- ignoring`, current);
126
+ const existing = routes.get(key);
127
+ const existingFiles = [existing.handler, existing.page].filter(Boolean).map((f) => f.filePath);
128
+ const currentFiles = [handler, page].filter(Boolean).map((f) => f.filePath);
129
+ throw new Error(`Duplicate routes for path ${path3} were defined. A route established by:
130
+ ${existingFiles}
131
+ collides with
132
+ ${currentFiles.join(" and ")}
133
+ `);
127
134
  } else {
128
135
  const index = nextRouteIndex++;
129
136
  routes.set(key, {
@@ -171,14 +178,16 @@ async function buildRoutes(walk, basePath) {
171
178
  const prevChildren = children;
172
179
  const prevCurrent = current;
173
180
  const prevIsRoot = isRoot;
174
- if (name.charCodeAt(0) === 95 || name.charCodeAt(0) === 40 && name.charCodeAt(name.length - 1) === 41 || name.toLowerCase() === "index") {
181
+ if (name.charCodeAt(0) === 95) {
175
182
  } else {
176
183
  if (name.charCodeAt(0) === 36) {
177
184
  if (name.charCodeAt(1) === 36) {
178
- paramStack.push({
179
- name: name.slice(2) || "rest",
180
- index: -1
181
- });
185
+ if (name.length > 2) {
186
+ paramStack.push({
187
+ name: name.slice(2),
188
+ index: -1
189
+ });
190
+ }
182
191
  } else if (name.length > 1) {
183
192
  paramStack.push({
184
193
  name: name.slice(1),
@@ -497,7 +506,7 @@ function renderRouteEntry(route) {
497
506
  );
498
507
  }
499
508
  if (middleware.length) {
500
- const names = middleware.map((m) => `mware$${m.id}`);
509
+ const names = middleware.map((m) => `mware${m.id}`);
501
510
  imports.writeLines(
502
511
  `import { ${names.join(
503
512
  ", "
@@ -508,9 +517,9 @@ function renderRouteEntry(route) {
508
517
  writer.writeLines("");
509
518
  const names = [];
510
519
  for (const verb of handler.verbs) {
511
- const name = verb === "delete" ? "del" : verb;
512
- names.push(name);
513
- writer.writeLines(`const handler$${name} = normalize(${name});`);
520
+ const importName = verb.toUpperCase();
521
+ names.push(importName);
522
+ writer.writeLines(`const ${verb}Handler = normalize(${importName});`);
514
523
  }
515
524
  imports.writeLines(
516
525
  `import { ${names.join(", ")} } from './${handler.importPath}';`
@@ -523,7 +532,7 @@ function renderRouteEntry(route) {
523
532
  }
524
533
  if (meta) {
525
534
  imports.writeLines(
526
- `export { default as meta$${index} } from './${meta.importPath}';`
535
+ `export { default as meta${index} } from './${meta.importPath}';`
527
536
  );
528
537
  }
529
538
  for (const verb of verbs) {
@@ -554,14 +563,19 @@ function writeRouteEntryHandler(writer, route, verb) {
554
563
  let nextName;
555
564
  let currentName;
556
565
  let hasBody = false;
557
- writer.writeLines("").writeBlockStart(
558
- `export async function ${verb}$${index}(context, buildInput) {`
559
- );
566
+ writer.writeLines("");
567
+ if (page) {
568
+ writer.writeBlockStart(
569
+ `export async function ${verb}${index}(context, buildInput) {`
570
+ );
571
+ } else {
572
+ writer.writeBlockStart(`export async function ${verb}${index}(context) {`);
573
+ }
560
574
  const continuations = writer.branch("cont");
561
575
  if (page && verb === "get") {
562
576
  currentName = "__page";
563
577
  if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes(verb)) {
564
- const name = `handler$${verb}`;
578
+ const name = `${verb}Handler`;
565
579
  writePageResponse(continuations, currentName);
566
580
  if (len) {
567
581
  nextName = currentName;
@@ -579,7 +593,7 @@ function writeRouteEntryHandler(writer, route, verb) {
579
593
  hasBody = true;
580
594
  }
581
595
  } else if (handler) {
582
- const name = `handler$${verb}`;
596
+ const name = `${verb}Handler`;
583
597
  currentName = `__${name}`;
584
598
  nextName = "noContent";
585
599
  if (len) {
@@ -595,7 +609,7 @@ function writeRouteEntryHandler(writer, route, verb) {
595
609
  let i = len;
596
610
  while (i--) {
597
611
  const { id } = middleware[i];
598
- const name = `mware$${id}`;
612
+ const name = `mware${id}`;
599
613
  nextName = currentName;
600
614
  currentName = i ? `__${name}` : "";
601
615
  writeMiddleware(continuations, name, nextName, currentName);
@@ -611,12 +625,12 @@ function renderRouter(routes, options = {
611
625
  writer.writeLines(`// @marko/run/router`);
612
626
  const imports = writer.branch("imports");
613
627
  imports.writeLines(
614
- `import { RequestNotHandled, RequestNotMatched, createInput } from 'virtual:marko-run/internal';`
628
+ `import { NotHandled, NotMatched, createInput } from 'virtual:marko-run/internal';`
615
629
  );
616
630
  for (const route of routes.list) {
617
631
  const verbs = getVerbs(route);
618
- const names = verbs.map((verb) => `${verb}$${route.index}`);
619
- route.meta && names.push(`meta$${route.index}`);
632
+ const names = verbs.map((verb) => `${verb}${route.index}`);
633
+ route.meta && names.push(`meta${route.index}`);
620
634
  imports.writeLines(
621
635
  `import { ${names.join(
622
636
  ", "
@@ -625,10 +639,16 @@ function renderRouter(routes, options = {
625
639
  }
626
640
  for (const { key } of Object.values(routes.special)) {
627
641
  imports.writeLines(
628
- `import page$${key} from '${virtualFilePrefix}/${markoRunFilePrefix}special__${key}.marko${serverEntryQuery}';`
642
+ `import page${key} from '${virtualFilePrefix}/${markoRunFilePrefix}special__${key}.marko${serverEntryQuery}';`
629
643
  );
630
644
  }
631
- writer.writeLines(``).writeBlockStart(`function findRoute(method, pathname) {`).writeBlockStart(`switch (method.toLowerCase()) {`);
645
+ writer.writeLines(``).writeBlockStart(`export function match(method, pathname) {`).writeLines(
646
+ `if (!pathname) {
647
+ pathname = '/';
648
+ } else if (pathname.charAt(0) !== '/') {
649
+ pathname = '/' + pathname;
650
+ }`
651
+ ).writeBlockStart(`switch (method.toLowerCase()) {`);
632
652
  for (const verb of httpVerbs) {
633
653
  const filteredRoutes = routes.list.filter((route) => hasVerb(route, verb));
634
654
  if (filteredRoutes.length) {
@@ -640,16 +660,12 @@ function renderRouter(routes, options = {
640
660
  }
641
661
  writer.writeBlockEnd("}").writeLines("return null;").writeBlockEnd("}");
642
662
  writer.write(`
643
- export function matchRoute(method, pathname) {
644
- if (!pathname) {
645
- pathname = '/';
646
- } else if (pathname.charAt(0) !== '/') {
647
- pathname = '/' + pathname;
648
- }
649
- return findRoute(method, pathname);
650
- }
651
-
652
- export async function invokeRoute(route, context) {
663
+ export async function invoke(route, request, platform, url = new URL(request.url)) {
664
+ const context = {
665
+ url,
666
+ request,
667
+ platform
668
+ };
653
669
  try {
654
670
  const buildInput = createInput(context);
655
671
  if (route) {
@@ -659,9 +675,9 @@ export async function invokeRoute(route, context) {
659
675
  const response = await route.handler(context, buildInput);
660
676
  if (response) return response;
661
677
  } catch (error) {
662
- if (error === RequestNotHandled) {
678
+ if (error === NotHandled) {
663
679
  return;
664
- } else if (error !== RequestNotMatched) {
680
+ } else if (error !== NotMatched) {
665
681
  throw error;
666
682
  }
667
683
  }
@@ -673,7 +689,7 @@ export async function invokeRoute(route, context) {
673
689
  context.meta = {};
674
690
  }
675
691
  if (context.request.headers.get('Accept')?.includes('text/html')) {
676
- return new Response(page$404.stream(buildInput()), {
692
+ return new Response(page404.stream(buildInput()), {
677
693
  status: 404,
678
694
  headers: { "content-type": "text/html;charset=UTF-8" },
679
695
  });
@@ -689,7 +705,7 @@ export async function invokeRoute(route, context) {
689
705
  writer.writeBlockStart(
690
706
  `if (context.request.headers.get('Accept')?.includes('text/html')) {`
691
707
  ).writeBlock(
692
- `return new Response(page$500.stream(buildInput({ error })), {`,
708
+ `return new Response(page500.stream(buildInput({ error })), {`,
693
709
  [
694
710
  `status: 500,`,
695
711
  `headers: { "content-type": "text/html;charset=UTF-8" },`
@@ -698,9 +714,9 @@ export async function invokeRoute(route, context) {
698
714
  ).writeBlockEnd("}");
699
715
  }
700
716
  writer.writeLines(`throw error;`).writeBlockEnd("}").writeBlockEnd("}").write(`
701
- export async function router(context) {
717
+ export async function fetch(request, platform) {
702
718
  try {
703
- const { url, method } = context;
719
+ const url = new URL(request.url);
704
720
  let { pathname } = url;`);
705
721
  switch (options.trailingSlashes) {
706
722
  case "RedirectWithout":
@@ -732,8 +748,8 @@ export async function router(context) {
732
748
  }
733
749
  writer.write(`
734
750
 
735
- const route = matchRoute(method, pathname);
736
- return await invokeRoute(route, context);
751
+ const route = match(request.method, pathname);
752
+ return await invoke(route, request, platform, url);
737
753
  } catch (error) {
738
754
  const message = import.meta.env.DEV
739
755
  ? \`Internal Server Error (\${error.message})\`
@@ -911,9 +927,9 @@ function renderParamsInfoType(params) {
911
927
  }
912
928
  function renderMatch(verb, route, pathIndex) {
913
929
  var _a;
914
- const handler = `${verb}$${route.index}`;
930
+ const handler = `${verb}${route.index}`;
915
931
  const params = ((_a = route.params) == null ? void 0 : _a.length) ? renderParamsInfo(route.params, pathIndex) : "{}";
916
- const meta = route.meta ? `meta$${route.index}` : "{}";
932
+ const meta = route.meta ? `meta${route.index}` : "{}";
917
933
  return `{ handler: ${handler}, params: ${params}, meta: ${meta} }`;
918
934
  }
919
935
  function renderMiddleware(middleware) {
@@ -925,9 +941,9 @@ function renderMiddleware(middleware) {
925
941
  imports.writeLines(`import { normalize } from 'virtual:marko-run/internal';`);
926
942
  writer.writeLines("");
927
943
  for (const { id, importPath } of middleware) {
928
- const importName = `mware${id}`;
944
+ const importName = `middleware${id}`;
929
945
  imports.writeLines(`import ${importName} from './${importPath}';`);
930
- writer.writeLines(`export const mware$${id} = normalize(${importName});`);
946
+ writer.writeLines(`export const mware${id} = normalize(${importName});`);
931
947
  }
932
948
  imports.join();
933
949
  return writer.end();
@@ -942,7 +958,7 @@ function stripTsExtension(path3) {
942
958
  }
943
959
  return path3;
944
960
  }
945
- function renderRouteTypeInfo(routes, pathPrefix = ".") {
961
+ function renderRouteTypeInfo(routes, pathPrefix = ".", adapterTypes = "") {
946
962
  var _a, _b;
947
963
  const writer = createStringWriter();
948
964
  writer.writeLines(
@@ -950,8 +966,10 @@ function renderRouteTypeInfo(routes, pathPrefix = ".") {
950
966
  WARNING: This file is automatically generated and any changes made to it will be overwritten without warning.
951
967
  Do NOT manually edit this file or your changes will be lost.
952
968
  */
953
- `,
954
- `import type { HandlerLike, Route, RouteContext, ValidatePath, ValidateHref } from "@marko/run";
969
+ `,
970
+ `import type { HandlerLike, Route, RouteContext, ValidatePath, ValidateHref } from "@marko/run";`,
971
+ adapterTypes,
972
+ `
955
973
 
956
974
  declare global {
957
975
  namespace MarkoRun {`
@@ -1021,7 +1039,7 @@ declare module '${pathPrefix}/${page.relativePath}' {
1021
1039
 
1022
1040
  namespace MarkoRun {
1023
1041
  type CurrentRoute = ${routeType};
1024
- type CurrentContext = RouteContext<CurrentRoute>;
1042
+ type CurrentContext = RouteContext<RouteContext['platform'], CurrentRoute>;
1025
1043
  }
1026
1044
  }`);
1027
1045
  }
@@ -1086,7 +1104,7 @@ declare module '${pathPrefix}/${file.relativePath}' {
1086
1104
 
1087
1105
  namespace MarkoRun {
1088
1106
  type CurrentRoute = ${routeTypes.join(" | ")};
1089
- type CurrentContext = RouteContext<CurrentRoute>;
1107
+ type CurrentContext = RouteContext<RouteContext['platform'], CurrentRoute>;
1090
1108
  }
1091
1109
  }`);
1092
1110
  }
@@ -1104,7 +1122,7 @@ declare module '${pathPrefix}/${route.page.relativePath}' {
1104
1122
 
1105
1123
  namespace MarkoRun {
1106
1124
  type CurrentRoute = Route;
1107
- type CurrentContext = RouteContext<CurrentRoute>;
1125
+ type CurrentContext = RouteContext<RouteContext['platform'], CurrentRoute>;
1108
1126
  }
1109
1127
  }`);
1110
1128
  }
@@ -1117,7 +1135,7 @@ function writeRouteTypeModule(writer, pathPrefix, path3, routeType) {
1117
1135
  declare module '${pathPrefix}/${stripTsExtension(path3)}' {
1118
1136
  namespace MarkoRun {
1119
1137
  type CurrentRoute = ${routeType};
1120
- type CurrentContext = RouteContext<CurrentRoute>;
1138
+ type CurrentContext = RouteContext<RouteContext['platform'], CurrentRoute>;
1121
1139
  type Handler<_Params = CurrentRoute['params'], _Meta = CurrentRoute['meta']> = HandlerLike<CurrentRoute>;
1122
1140
  function route(handler: Handler): typeof handler;
1123
1141
  function route<_Params = CurrentRoute['params'], _Meta = CurrentRoute['meta']>(handler: Handler): typeof handler;
@@ -1347,7 +1365,7 @@ function markoServe(opts = {}) {
1347
1365
  ))) {
1348
1366
  const filepath = path2.join(typesDir, "routes.d.ts");
1349
1367
  const adapterTypeInfo = (adapter == null ? void 0 : adapter.writeTypeInfo) && await (adapter == null ? void 0 : adapter.writeTypeInfo());
1350
- const data = renderRouteTypeInfo(routes, path2.relative(typesDir, routesDir)) + adapterTypeInfo;
1368
+ const data = renderRouteTypeInfo(routes, path2.relative(typesDir, routesDir), adapterTypeInfo);
1351
1369
  if (data !== typesFile || !fs2.existsSync(filepath)) {
1352
1370
  await ensureDir(typesDir);
1353
1371
  await fs2.promises.writeFile(filepath, typesFile = data);
@@ -1614,8 +1632,8 @@ async function getVerbsFromFileBuild(context, filePath) {
1614
1632
  if (result) {
1615
1633
  const exportIds = getExportIdentifiers(result.ast);
1616
1634
  for (const id of exportIds) {
1617
- const verb = id === "del" ? "delete" : id;
1618
- if (httpVerbs.includes(verb)) {
1635
+ const verb = id.toLowerCase();
1636
+ if (id === verb.toUpperCase() && httpVerbs.includes(verb)) {
1619
1637
  verbs.push(verb);
1620
1638
  }
1621
1639
  }
@@ -1626,12 +1644,19 @@ async function getVerbsFromFileDev(devServer, filePath) {
1626
1644
  const verbs = [];
1627
1645
  const result = await devServer.transformRequest(filePath, { ssr: true });
1628
1646
  if (result && result.code) {
1629
- const verbMatchReg = /__vite_ssr_exports__,\s+["'](get|post|put|del)["']/g;
1647
+ const verbMatchReg = /__vite_ssr_exports__,\s+["'](GET|POST|PUT|DELETE)["']/gi;
1630
1648
  let match = verbMatchReg.exec(result.code);
1631
1649
  while (match) {
1632
- const verb = match[1] === "del" ? "delete" : match[1];
1650
+ const id = match[1];
1651
+ const verb = id.toLowerCase();
1633
1652
  if (httpVerbs.includes(verb)) {
1634
- verbs.push(verb);
1653
+ if (id === verb.toUpperCase()) {
1654
+ verbs.push(verb);
1655
+ } else {
1656
+ console.warn(
1657
+ `Found export '${id}' in handler ${filePath} which is close to '${verb.toUpperCase()}'. Exported handlers need to be uppercase: GET, POST, PUT or DELETE.`
1658
+ );
1659
+ }
1635
1660
  }
1636
1661
  match = verbMatchReg.exec(result.code);
1637
1662
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marko/run",
3
- "version": "0.0.1-beta6",
3
+ "version": "0.0.1-beta7",
4
4
  "description": "File-based routing for Marko based on Vite",
5
5
  "keywords": [],
6
6
  "author": "Ryan Turnquist <rturnq@gmail.com>",