@marko/run 0.0.1-beta2 → 0.0.1-beta4

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.
@@ -1,13 +1,16 @@
1
1
  // src/vite/plugin.ts
2
2
  import path2 from "path";
3
3
  import crypto from "crypto";
4
+ import fs2 from "fs";
5
+ import glob from "glob";
4
6
  import { mergeConfig, normalizePath } from "vite";
5
7
  import markoVitePlugin, { FileStore } from "@marko/vite";
6
8
 
7
9
  // src/vite/constants.ts
8
- var markoServeFilePrefix = "__marko-serve__";
9
- var virtualFilePrefix = "virtual:marko-serve";
10
+ var markoRunFilePrefix = "__marko-run__";
11
+ var virtualFilePrefix = "virtual:marko-run";
10
12
  var virtualRoutesPrefix = `${virtualFilePrefix}/routes`;
13
+ var virtualRuntimePrefix = `${virtualFilePrefix}/internal`;
11
14
  var httpVerbs = ["get", "post", "put", "delete"];
12
15
  var serverEntryQuery = "?marko-server-entry";
13
16
  var browserEntryQuery = "?marko-browser-entry";
@@ -47,15 +50,20 @@ function isSpecialType(type) {
47
50
  return type === RoutableFileTypes.NotFound || type === RoutableFileTypes.Error;
48
51
  }
49
52
  async function buildRoutes(walk, basePath) {
50
- const dirStack = basePath ? basePath.split("/") : [];
53
+ if (basePath) {
54
+ basePath = basePath.replace(/^\/+|\/+$/g, "");
55
+ }
56
+ const dirStack = [];
51
57
  const pathStack = [];
52
58
  const paramStack = [];
53
59
  const layoutsStack = [];
54
60
  const middlewareStack = [];
55
61
  const routes = /* @__PURE__ */ new Map();
56
62
  const special = {};
63
+ const middleware = /* @__PURE__ */ new Set();
57
64
  let isRoot = true;
58
- let nextId = 1;
65
+ let nextFileId = 1;
66
+ let nextRouteIndex = 1;
59
67
  let current;
60
68
  let children = [];
61
69
  await walk({
@@ -83,10 +91,13 @@ async function buildRoutes(walk, basePath) {
83
91
  if (!entries) {
84
92
  current.files.set(type, entries = []);
85
93
  }
94
+ const relativePath = current.originalPath ? `${current.originalPath}/${entry.name}` : entry.name;
86
95
  entries.push({
96
+ id: String(nextFileId++),
87
97
  type,
88
98
  filePath: entry.path,
89
- importPath: `${current.originalPath}/${entry.name}`,
99
+ relativePath,
100
+ importPath: `${basePath}/${relativePath}`,
90
101
  name: entry.name,
91
102
  verbs: type === RoutableFileTypes.Page ? ["get"] : void 0
92
103
  });
@@ -97,20 +108,24 @@ async function buildRoutes(walk, basePath) {
97
108
  return;
98
109
  }
99
110
  const { path: path3, files } = current;
100
- const middleware = (_a = files.get(RoutableFileTypes.Middleware)) == null ? void 0 : _a[0];
101
- const layout = (_b = files.get(RoutableFileTypes.Layout)) == null ? void 0 : _b[0];
111
+ const localMiddleware = (_a = files.get(RoutableFileTypes.Middleware)) == null ? void 0 : _a[0];
112
+ const localLayout = (_b = files.get(RoutableFileTypes.Layout)) == null ? void 0 : _b[0];
102
113
  const handler = (_c = files.get(RoutableFileTypes.Handler)) == null ? void 0 : _c[0];
103
114
  const page = (_d = files.get(RoutableFileTypes.Page)) == null ? void 0 : _d[0];
104
115
  const middlewareStackLength = middlewareStack.length;
105
116
  const layoutsStackLength = layoutsStack.length;
106
- middleware && middlewareStack.push(middleware);
107
- layout && layoutsStack.push(layout);
117
+ if (localMiddleware) {
118
+ middlewareStack.push(localMiddleware);
119
+ }
120
+ if (localLayout) {
121
+ layoutsStack.push(localLayout);
122
+ }
108
123
  if (handler || page) {
109
124
  const key = path3.replace(/(\$\$?)[^\/]*/g, "$1").replace(/^\/+/, "").replace(/[^a-z0-9_$\/]+/gi, "").replace(/\//g, "__") || "index";
110
125
  if (routes.has(key)) {
111
126
  console.warn(`Duplicate route for path ${path3} -- ignoring`, current);
112
127
  } else {
113
- const index = nextId++;
128
+ const index = nextRouteIndex++;
114
129
  routes.set(key, {
115
130
  index,
116
131
  key,
@@ -123,6 +138,9 @@ async function buildRoutes(walk, basePath) {
123
138
  handler,
124
139
  score: scorePath(path3, index)
125
140
  });
141
+ for (const mw of middlewareStack) {
142
+ middleware.add(mw);
143
+ }
126
144
  }
127
145
  }
128
146
  if (isRoot) {
@@ -158,7 +176,7 @@ async function buildRoutes(walk, basePath) {
158
176
  if (name.charCodeAt(0) === 36) {
159
177
  if (name.charCodeAt(1) === 36) {
160
178
  paramStack.push({
161
- name: name.slice(2) || "*",
179
+ name: name.slice(2) || "rest",
162
180
  index: -1
163
181
  });
164
182
  } else if (name.length > 1) {
@@ -188,7 +206,8 @@ async function buildRoutes(walk, basePath) {
188
206
  });
189
207
  return {
190
208
  list: [...routes.values()],
191
- special
209
+ special,
210
+ middleware: [...middleware]
192
211
  };
193
212
  }
194
213
 
@@ -238,6 +257,17 @@ function createWriter(sink, options) {
238
257
  let firstOpenIndex = 0;
239
258
  const branches = [];
240
259
  const openWriters = /* @__PURE__ */ new Map();
260
+ function write(data) {
261
+ if (!writer.__isActive) {
262
+ throw new Error("Cannot write to branch that has been joined");
263
+ }
264
+ if (openWriters.size) {
265
+ buffer += data;
266
+ } else {
267
+ sink(data);
268
+ }
269
+ return writer;
270
+ }
241
271
  const writer = {
242
272
  __isActive: true,
243
273
  get indent() {
@@ -254,24 +284,16 @@ function createWriter(sink, options) {
254
284
  }
255
285
  }
256
286
  },
257
- write(data) {
258
- if (!writer.__isActive) {
259
- throw new Error("Cannot write to branch that has been joined");
260
- }
261
- if (openWriters.size) {
262
- buffer += data;
263
- } else {
264
- sink(data);
287
+ write(data, indent = false) {
288
+ if (indent && indentString) {
289
+ write(indentString);
265
290
  }
266
- return writer;
291
+ return write(data);
267
292
  },
268
293
  writeLines(...lines) {
269
294
  for (const line of lines) {
270
295
  if (line) {
271
- if (indentString) {
272
- writer.write(indentString);
273
- }
274
- writer.write(line);
296
+ writer.write(line, true);
275
297
  }
276
298
  writer.write("\n");
277
299
  }
@@ -418,16 +440,13 @@ function hasVerb(route, verb) {
418
440
  }
419
441
 
420
442
  // src/vite/codegen/index.ts
421
- var DefaultCodegenOptions = {
422
- trailingSlashes: "RedirectWithout"
423
- };
424
443
  function renderRouteTemplate(route) {
425
444
  if (!route.page) {
426
445
  throw new Error(`Route ${route.key} has no page to render`);
427
446
  }
428
447
  const writer = createStringWriter();
429
448
  writer.writeLines(
430
- `// ${virtualFilePrefix}/${markoServeFilePrefix}route__${route.key}.marko`
449
+ `// ${virtualFilePrefix}/${markoRunFilePrefix}route__${route.key}.marko`
431
450
  );
432
451
  writer.branch("imports");
433
452
  writer.writeLines("");
@@ -459,25 +478,47 @@ function renderRouteEntry(route) {
459
478
  }
460
479
  const writer = createStringWriter();
461
480
  writer.writeLines(
462
- `// ${virtualFilePrefix}/${markoServeFilePrefix}route__${key}.js`
481
+ `// ${virtualFilePrefix}/${markoRunFilePrefix}route__${key}.js`
463
482
  );
464
483
  const imports = writer.branch("imports");
465
- let i = 1;
466
- for (const { importPath } of middleware) {
467
- imports.writeLines(`import middleware$${i} from './${importPath}';`);
468
- i++;
484
+ const runtimeImports = [];
485
+ if (handler) {
486
+ runtimeImports.push("normalize");
469
487
  }
470
- if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.length) {
471
- const names = handler.verbs.map(
472
- (verb) => `${verb === "delete" ? "del" : verb} as handler$${verb}`
488
+ if (handler || middleware.length) {
489
+ runtimeImports.push("call");
490
+ }
491
+ if (!page || verbs.length > 1) {
492
+ runtimeImports.push("noContent");
493
+ }
494
+ if (runtimeImports.length) {
495
+ imports.writeLines(
496
+ `import { ${runtimeImports.join(", ")} } from '${virtualRuntimePrefix}';`
473
497
  );
498
+ }
499
+ if (middleware.length) {
500
+ const names = middleware.map((m) => `mware$${m.id}`);
501
+ imports.writeLines(
502
+ `import { ${names.join(
503
+ ", "
504
+ )} } from '${virtualFilePrefix}/${markoRunFilePrefix}middleware.js';`
505
+ );
506
+ }
507
+ if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.length) {
508
+ writer.writeLines("");
509
+ const names = [];
510
+ 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});`);
514
+ }
474
515
  imports.writeLines(
475
516
  `import { ${names.join(", ")} } from './${handler.importPath}';`
476
517
  );
477
518
  }
478
519
  if (page) {
479
520
  imports.writeLines(
480
- `import page from '${virtualFilePrefix}/${markoServeFilePrefix}route__${key}.marko${serverEntryQuery}';`
521
+ `import page from '${virtualFilePrefix}/${markoRunFilePrefix}route__${key}.marko${serverEntryQuery}';`
481
522
  );
482
523
  }
483
524
  if (meta) {
@@ -485,21 +526,27 @@ function renderRouteEntry(route) {
485
526
  `export { default as meta$${index} } from './${meta.importPath}';`
486
527
  );
487
528
  }
488
- if (!page || verbs.length > 1) {
489
- writer.writeLines("").writeBlockStart(`function create204Response() {`).writeBlockStart(`return new Response(null, {`).writeLines(`status: 204`).writeBlockEnd(`})`).writeBlockEnd(`}`);
490
- }
491
529
  for (const verb of verbs) {
492
530
  writeRouteEntryHandler(writer, route, verb);
493
531
  }
494
532
  return writer.end();
495
533
  }
496
- function writePageResponseContinuation(writer) {
534
+ function writePageResponse(writer, wrapFn) {
497
535
  writer.writeBlock(
498
- `return new Response(page.stream(ctx), {`,
536
+ `${wrapFn ? `const ${wrapFn} = () =>` : `return`} new Response(page.stream(buildInput()), {`,
499
537
  ["status: 200,", 'headers: { "content-type": "text/html;charset=UTF-8" }'],
500
538
  "});"
501
539
  );
502
540
  }
541
+ function writeMiddleware(writer, middleware, next, wrapFn) {
542
+ if (wrapFn) {
543
+ writer.writeLines(
544
+ `const ${wrapFn} = () => call(${middleware}, ${next}, context);`
545
+ );
546
+ } else {
547
+ writer.writeLines(`return call(${middleware}, ${next}, context);`);
548
+ }
549
+ }
503
550
  function writeRouteEntryHandler(writer, route, verb) {
504
551
  var _a;
505
552
  const { key, index, page, handler, middleware } = route;
@@ -507,47 +554,38 @@ function writeRouteEntryHandler(writer, route, verb) {
507
554
  let nextName;
508
555
  let currentName;
509
556
  let hasBody = false;
510
- writer.writeLines("").writeBlockStart(`export async function ${verb}$${index}(ctx) {`);
557
+ writer.writeLines("").writeBlockStart(
558
+ `export async function ${verb}$${index}(context, buildInput) {`
559
+ );
511
560
  const continuations = writer.branch("cont");
512
561
  if (page && verb === "get") {
513
- currentName = "createPageResponse";
562
+ currentName = "__page";
514
563
  if ((_a = handler == null ? void 0 : handler.verbs) == null ? void 0 : _a.includes(verb)) {
515
- continuations.writeBlockStart(`async function ${currentName}() {`);
516
- writePageResponseContinuation(continuations);
517
- continuations.writeBlockEnd("}");
564
+ const name = `handler$${verb}`;
565
+ writePageResponse(continuations, currentName);
518
566
  if (len) {
519
567
  nextName = currentName;
520
- currentName = `__handler$${verb}`;
521
- continuations.writeBlock(
522
- `async function ${currentName}() {`,
523
- [`return await handler$${verb}(ctx, ${nextName});`],
524
- "}"
525
- );
568
+ currentName = `__${name}`;
569
+ writeMiddleware(continuations, name, nextName, currentName);
526
570
  } else {
527
- writer.writeLines(`return await handler$${verb}(ctx, ${currentName})`);
571
+ writeMiddleware(writer, name, currentName);
528
572
  hasBody = true;
529
573
  }
530
574
  } else if (len) {
531
- continuations.writeBlockStart(`async function ${currentName}() {`);
532
- writePageResponseContinuation(continuations);
533
- continuations.writeBlockEnd("}");
575
+ writePageResponse(continuations, currentName);
534
576
  nextName = currentName;
535
577
  } else {
536
- writePageResponseContinuation(continuations);
578
+ writePageResponse(continuations);
537
579
  hasBody = true;
538
580
  }
539
581
  } else if (handler) {
540
- currentName = `__handler$${verb}`;
582
+ const name = `handler$${verb}`;
583
+ currentName = `__${name}`;
584
+ nextName = "noContent";
541
585
  if (len) {
542
- continuations.writeBlock(
543
- `async function ${currentName}() {`,
544
- [`return await handler$${verb}(ctx, create204Response);`],
545
- "}"
546
- );
586
+ writeMiddleware(continuations, name, nextName, currentName);
547
587
  } else {
548
- writer.writeLines(
549
- `return await handler$${verb}(ctx, create204Response);`
550
- );
588
+ writeMiddleware(writer, name, nextName);
551
589
  hasBody = true;
552
590
  }
553
591
  } else {
@@ -555,24 +593,26 @@ function writeRouteEntryHandler(writer, route, verb) {
555
593
  }
556
594
  if (!hasBody) {
557
595
  let i = len;
558
- while (--i) {
596
+ while (i--) {
597
+ const { id } = middleware[i];
598
+ const name = `mware$${id}`;
559
599
  nextName = currentName;
560
- currentName = `__middleware${i + 1}`;
561
- continuations.writeLines("").writeBlock(
562
- `async function ${currentName}() {`,
563
- [`return await middleware$${i + 1}(ctx, ${nextName});`],
564
- "}"
565
- );
600
+ currentName = i ? `__${name}` : "";
601
+ writeMiddleware(continuations, name, nextName, currentName);
566
602
  }
567
- writer.writeLines(`return await middleware$1(ctx, ${currentName});`);
568
603
  }
569
604
  continuations.join();
570
605
  writer.writeBlockEnd("}");
571
606
  }
572
- function renderRouter(routes, options = DefaultCodegenOptions) {
607
+ function renderRouter(routes, options = {
608
+ trailingSlashes: "RedirectWithout"
609
+ }) {
573
610
  const writer = createStringWriter();
574
611
  writer.writeLines(`// @marko/run/router`);
575
612
  const imports = writer.branch("imports");
613
+ imports.writeLines(
614
+ `import { RequestNotHandled, RequestNotMatched, createInput } from 'virtual:marko-run/internal';`
615
+ );
576
616
  for (const route of routes.list) {
577
617
  const verbs = getVerbs(route);
578
618
  const names = verbs.map((verb) => `${verb}$${route.index}`);
@@ -580,15 +620,15 @@ function renderRouter(routes, options = DefaultCodegenOptions) {
580
620
  imports.writeLines(
581
621
  `import { ${names.join(
582
622
  ", "
583
- )} } from '${virtualFilePrefix}/${markoServeFilePrefix}route__${route.key}.js';`
623
+ )} } from '${virtualFilePrefix}/${markoRunFilePrefix}route__${route.key}.js';`
584
624
  );
585
625
  }
586
626
  for (const { key } of Object.values(routes.special)) {
587
627
  imports.writeLines(
588
- `import page$${key} from '${virtualFilePrefix}/${markoServeFilePrefix}special__${key}.marko${serverEntryQuery}';`
628
+ `import page$${key} from '${virtualFilePrefix}/${markoRunFilePrefix}special__${key}.marko${serverEntryQuery}';`
589
629
  );
590
630
  }
591
- writer.writeLines("").writeBlockStart(`function matchRoute(method, pathname) {`).writeBlockStart(`switch (method.toLowerCase()) {`);
631
+ writer.writeLines(``).writeBlockStart(`function findRoute(method, pathname) {`).writeBlockStart(`switch (method.toLowerCase()) {`);
592
632
  for (const verb of httpVerbs) {
593
633
  const filteredRoutes = routes.list.filter((route) => hasVerb(route, verb));
594
634
  if (filteredRoutes.length) {
@@ -598,120 +638,105 @@ function renderRouter(routes, options = DefaultCodegenOptions) {
598
638
  writer.writeBlockEnd("}");
599
639
  }
600
640
  }
601
- writer.writeBlockEnd("}").writeBlockEnd("}");
602
- writer.writeLines("").writeBlockStart(`async function invokeRoute(route, url, request) {`);
603
- const errorRoute = routes.special[RoutableFileTypes.Error];
604
- if (errorRoute) {
605
- writer.writeBlockStart(`try {`);
641
+ writer.writeBlockEnd("}").writeLines("return null;").writeBlockEnd("}");
642
+ writer.write(`
643
+ export function matchRoute(method, pathname) {
644
+ if (!pathname) {
645
+ pathname = '/';
646
+ } else if (pathname.charAt(0) !== '/') {
647
+ pathname = '/' + pathname;
606
648
  }
607
- writer.writeBlock(
608
- `if (route) {`,
609
- [
610
- `const [handler, params = {}, meta] = route;`,
611
- `const response = await handler({ request, url, params, meta });`,
612
- `if (response) return response;`
613
- ],
614
- `}`
615
- );
616
- const notFoundRoute = routes.special[RoutableFileTypes.NotFound];
617
- if (notFoundRoute) {
618
- writer.writeBlockStart(
619
- `if (request.headers.get('Accept')?.includes('text/html')) {`
620
- ).writeBlock(
621
- `return new Response(page$404.stream({ request, url, params: {} }), {`,
622
- [
623
- `status: 404,`,
624
- `headers: { "content-type": "text/html;charset=UTF-8" },`
625
- ],
626
- `});`
627
- ).writeBlockEnd("}");
649
+ return findRoute(method, pathname);
650
+ }
651
+
652
+ export async function invokeRoute(route, context) {
653
+ try {
654
+ const buildInput = createInput(context);
655
+ if (route) {
656
+ context.params = route.params;
657
+ context.meta = route.meta;
658
+ try {
659
+ const response = await route.handler(context, buildInput);
660
+ if (response) return response;
661
+ } catch (error) {
662
+ if (error === RequestNotHandled) {
663
+ return;
664
+ } else if (error !== RequestNotMatched) {
665
+ throw error;
666
+ }
667
+ }
668
+ `).indent = 2;
669
+ if (routes.special[RoutableFileTypes.NotFound]) {
670
+ writer.write(
671
+ ` } else {
672
+ context.params = {};
673
+ context.meta = {};
674
+ }
675
+ if (context.request.headers.get('Accept')?.includes('text/html')) {
676
+ return new Response(page$404.stream(buildInput()), {
677
+ status: 404,
678
+ headers: { "content-type": "text/html;charset=UTF-8" },
679
+ });
680
+ }
681
+ `
682
+ );
683
+ } else {
684
+ writer.writeBlockEnd("}");
628
685
  }
629
- writer.writeLines(`return null;`);
630
- if (errorRoute) {
631
- writer.indent--;
632
- writer.writeBlockStart(`} catch (err) {`).writeBlockStart(
633
- `if (request.headers.get('Accept')?.includes('text/html')) {`
686
+ writer.indent--;
687
+ writer.writeBlockStart(`} catch (error) {`);
688
+ if (routes.special[RoutableFileTypes.Error]) {
689
+ writer.writeBlockStart(
690
+ `if (context.request.headers.get('Accept')?.includes('text/html')) {`
634
691
  ).writeBlock(
635
- `return new Response(page$500.stream({ request, url, params: {}, error: err }), {`,
692
+ `return new Response(page$500.stream(buildInput({ error })), {`,
636
693
  [
637
694
  `status: 500,`,
638
695
  `headers: { "content-type": "text/html;charset=UTF-8" },`
639
696
  ],
640
697
  `});`
641
- ).writeBlockEnd("}").writeLines(`throw err;`).writeBlockEnd("}");
642
- }
643
- writer.writeBlockEnd("}");
644
- writer.write(`
645
- export function getMatchedRoute(method, url) {
646
- const route = matchRoute(method, url.pathname);
647
- if (route) {
648
- const [handler, params = {}, meta] = route;
649
- return {
650
- handler,
651
- params,
652
- meta,
653
- async invoke(request) {
654
- return await invokeRoute(route, url, request);
655
- }
656
- }
698
+ ).writeBlockEnd("}");
657
699
  }
658
- return null;
659
- }
660
-
661
- export async function handler(context) {
662
- const response = await router(context.request);
663
- //if (response.body) {
664
- // context.waitUntil?.(once(Readable.from(response.body), "end"));
665
- //}
666
- return response;
667
- }
668
-
669
- export async function router(request) {
700
+ writer.writeLines(`throw error;`).writeBlockEnd("}").writeBlockEnd("}").write(`
701
+ export async function router(context) {
670
702
  try {
671
- const url = new URL(request.url);
672
- let { pathname } = url;
673
-
674
- if (pathname !== '/') {
675
- if (pathname.endsWith('/')) {
676
- pathname = pathname.replace(/\\/+$/, '');
677
- `);
703
+ const { url, method } = context;
704
+ let { pathname } = url;`);
678
705
  switch (options.trailingSlashes) {
679
706
  case "RedirectWithout":
680
707
  writer.write(`
681
- url.pathname = pathname;
682
- return Response.redirect(url.href);
683
- `);
708
+ if (pathname !== '/' && pathname.endsWith('/')) {
709
+ url.pathname = pathname.slice(0, -1);
710
+ return Response.redirect(url);
711
+ }`);
684
712
  break;
685
713
  case "RedirectWith":
686
714
  writer.write(`
687
- } else {
688
- url.pathname = pathname + '/';
689
- return Response.redirect(url.href);
690
- `);
715
+ if (pathname !== '/' && !pathname.endsWith('/')) {
716
+ url.pathname = pathname + '/';
717
+ return Response.redirect(url);
718
+ }`);
691
719
  break;
692
720
  case "RewriteWithout":
693
721
  writer.write(`
694
- url.pathname = pathname;
695
- `);
722
+ if (pathname !== '/' && pathname.endsWith('/')) {
723
+ url.pathname = pathname = pathname.slice(0, -1);
724
+ }`);
696
725
  break;
697
726
  case "RewriteWith":
698
727
  writer.write(`
699
- } else {
700
- url.pathname = pathname + '/';
701
- `);
728
+ if (pathname !== '/' && !pathname.endsWith('/')) {
729
+ url.pathname = pathname = pathname + '/';
730
+ }`);
702
731
  break;
703
732
  }
704
- writer.write(`
705
- }
706
- }
707
- const route = matchRoute(request.method, pathname);
708
- return await invokeRoute(route, url, request) || new Response(null, {
709
- statusText: \`Not Found (No route matched \${pathname})\`,
710
- status: 404
711
- });
712
- } catch (err) {
733
+ writer.write(`
734
+
735
+ const route = matchRoute(method, pathname);
736
+ return await invokeRoute(route, context);
737
+ } catch (error) {
713
738
  const message = import.meta.env.DEV
714
- ? \`Internal Server Error (\${err.message})\`
739
+ ? \`Internal Server Error (\${error.message})\`
715
740
  : "Internal Server Error";
716
741
 
717
742
  return new Response(
@@ -719,7 +744,7 @@ export async function router(request) {
719
744
  error: {
720
745
  message,
721
746
  stack: import.meta.env.DEV
722
- ? \`This will only be seen in development mode\\n\\n\${err.stack}\`
747
+ ? \`This will only be seen in development mode\\n\\n\${error.stack}\`
723
748
  : ""
724
749
  }
725
750
  }),
@@ -732,101 +757,259 @@ export async function router(request) {
732
757
  }`);
733
758
  return writer.end();
734
759
  }
735
- function writeRouterVerb(writer, trie, verb, level = 0, pathIndex = 0, useSwitch) {
736
- const { key, route: value, static: children, dynamic, catchAll } = trie;
737
- pathIndex += key.length;
738
- if (level <= 0) {
739
- level = 0;
740
- if (value) {
741
- writer.writeLines(
742
- `if (pathname === '/') return ${renderMatch(verb, value)}; // ${value.path}`
743
- );
744
- }
745
- if (children || dynamic) {
746
- writer.writeLines(
747
- `const segments = pathname.split('/');`,
748
- `const len = segments.length;`
749
- );
750
- }
751
- } else {
752
- if (!key) {
753
- writer.writeBlockStart(`if (segments[${level}]) {`);
754
- } else if (useSwitch) {
755
- writer.writeBlockStart(`case '${key}':`);
756
- } else {
757
- writer.writeBlockStart(
758
- `if (segments[${level}]?.toLowerCase() === '${key}') {`
759
- );
760
- }
761
- if (value) {
760
+ function writeRouterVerb(writer, trie, verb, level = 0, offset = 1) {
761
+ const { route, dynamic, catchAll } = trie;
762
+ let closeCount = 0;
763
+ if (level === 0) {
764
+ writer.writeLines(`const len = pathname.length;`);
765
+ if (route) {
762
766
  writer.writeLines(
763
- `if (len === ${level + 1}) return ${renderMatch(verb, value)}; // ${value.path}`
767
+ `if (len === 1) return ${renderMatch(verb, route)}; // ${route.path}`
764
768
  );
769
+ } else if (trie.static || dynamic) {
770
+ writer.writeBlockStart(`if (len > 1) {`);
771
+ closeCount++;
765
772
  }
766
773
  }
767
- if (children || dynamic) {
768
- if (children) {
769
- if (children.size > 1) {
770
- writer.writeBlockStart(
771
- `switch(segments[${level + 1}]?.toLowerCase()) {`
772
- );
773
- for (const child of children.values()) {
774
- writeRouterVerb(writer, child, verb, level + 1, pathIndex, true);
774
+ if (trie.static || dynamic) {
775
+ const next = level + 1;
776
+ const index = `i${next}`;
777
+ let terminal;
778
+ let children;
779
+ writer.writeLines(`const ${index} = pathname.indexOf('/', ${offset}) + 1;`);
780
+ if (trie.static) {
781
+ for (const child of trie.static.values()) {
782
+ if (child.route) {
783
+ (terminal ?? (terminal = [])).push(child);
775
784
  }
776
- writer.writeBlockEnd("}");
777
- } else {
778
- for (const child of children.values()) {
779
- writeRouterVerb(writer, child, verb, level + 1, pathIndex);
785
+ if (child.static || child.dynamic || child.catchAll) {
786
+ (children ?? (children = [])).push(child);
787
+ }
788
+ }
789
+ }
790
+ if (terminal || (dynamic == null ? void 0 : dynamic.route)) {
791
+ closeCount++;
792
+ writer.writeBlockStart(`if (!${index} || ${index} === len) {`);
793
+ let value = `pathname.slice(${offset}, ${index} ? -1 : len)`;
794
+ if (dynamic == null ? void 0 : dynamic.route) {
795
+ const segment = `s${next}`;
796
+ writer.writeLines(`const ${segment} = ${value};`);
797
+ value = segment;
798
+ }
799
+ if (terminal) {
800
+ const useSwitch = terminal.length > 1;
801
+ if (useSwitch) {
802
+ writer.writeBlockStart(`switch (${value}.toLowerCase()) {`);
803
+ }
804
+ for (const { key, route: route2 } of terminal) {
805
+ if (useSwitch) {
806
+ writer.write(`case '${key}': `, true);
807
+ } else {
808
+ writer.write(`if (${value}.toLowerCase() === '${key}') `, true);
809
+ }
810
+ writer.write(
811
+ `return ${renderMatch(verb, route2)}; // ${route2.path}
812
+ `
813
+ );
814
+ }
815
+ if (useSwitch) {
816
+ writer.writeBlockEnd("}");
780
817
  }
781
818
  }
819
+ if (dynamic == null ? void 0 : dynamic.route) {
820
+ writer.writeLines(
821
+ `if (${value}) return ${renderMatch(verb, dynamic.route)}; // ${dynamic.route.path}`
822
+ );
823
+ }
782
824
  }
783
- if (dynamic) {
784
- writeRouterVerb(writer, dynamic, verb, level + 1, pathIndex);
825
+ if (children || (dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic) || (dynamic == null ? void 0 : dynamic.catchAll)) {
826
+ if (terminal || (dynamic == null ? void 0 : dynamic.route)) {
827
+ writer.writeBlockEnd("} else {").indent++;
828
+ } else {
829
+ writer.writeBlockStart(`if (${index} && ${index} !== len) {`);
830
+ closeCount++;
831
+ }
832
+ let value = `pathname.slice(${offset}, ${index} - 1)`;
833
+ if ((dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic)) {
834
+ const segment = `s${next}`;
835
+ writer.writeLines(`const ${segment} = ${value};`);
836
+ value = segment;
837
+ }
838
+ if (children) {
839
+ const useSwitch = children.length > 1;
840
+ if (useSwitch) {
841
+ writer.writeBlockStart(`switch (${value}.toLowerCase()) {`);
842
+ }
843
+ for (const child of children) {
844
+ if (useSwitch) {
845
+ writer.writeBlockStart(`case '${child.key}': {`);
846
+ } else {
847
+ writer.writeBlockStart(
848
+ `if (${value}.toLowerCase() === '${child.key}') {`
849
+ );
850
+ }
851
+ const nextOffset = typeof offset === "string" ? index : offset + child.key.length + 1;
852
+ writeRouterVerb(writer, child, verb, next, nextOffset);
853
+ writer.writeBlockEnd("}");
854
+ }
855
+ if (useSwitch) {
856
+ writer.writeBlockEnd("}");
857
+ }
858
+ }
859
+ if ((dynamic == null ? void 0 : dynamic.static) || (dynamic == null ? void 0 : dynamic.dynamic) || (dynamic == null ? void 0 : dynamic.catchAll)) {
860
+ writer.writeBlockStart(`if (${value}) {`);
861
+ writeRouterVerb(writer, dynamic, verb, next, index);
862
+ writer.writeBlockEnd(`}`);
863
+ }
785
864
  }
786
865
  }
866
+ while (closeCount--) {
867
+ writer.writeBlockEnd("}");
868
+ }
787
869
  if (catchAll) {
788
870
  writer.writeLines(
789
- `return ${renderMatch(verb, catchAll, pathIndex)}; // ${catchAll.path}`
871
+ `return ${renderMatch(verb, catchAll, String(offset))}; // ${catchAll.path}`
790
872
  );
791
- if (level > 0) {
792
- writer.indent--;
793
- }
794
873
  } else if (level === 0) {
795
- writer.writeLines("return;");
796
- } else if (useSwitch) {
797
- writer.writeLines("break;").indent--;
798
- } else {
799
- writer.writeBlockEnd("}");
874
+ writer.writeLines("return null;");
800
875
  }
801
876
  }
877
+ function wrapPropertyName(name) {
878
+ return /^[^A-Za-z_$]|[^A-Za-z0-9$_]/.test(name) ? `'${name}'` : name;
879
+ }
802
880
  function renderParamsInfo(params, pathIndex) {
881
+ if (!params.length) {
882
+ return "{}";
883
+ }
803
884
  let result = "";
804
885
  let catchAll = "";
805
- let dynamicLength = "";
886
+ let sep = "{";
806
887
  for (const { name, index } of params) {
807
888
  if (index >= 0) {
808
- result += result ? ", " : "{ ";
809
- result += `'${name}': segments[${index + 1}]`;
810
- dynamicLength += ` + segments[${index + 1}].length`;
811
- } else if (pathIndex && pathIndex >= 0) {
889
+ result += `${sep} ${wrapPropertyName(name)}: s${index + 1}`;
890
+ sep || (sep = ",");
891
+ } else if (pathIndex) {
812
892
  catchAll = name;
813
893
  }
814
894
  }
815
895
  if (catchAll) {
816
- result += result ? ", " : "{ ";
817
- result += `'${catchAll}': pathname.slice(${pathIndex}${dynamicLength})`;
896
+ result += `${sep} ${wrapPropertyName(
897
+ catchAll
898
+ )}: pathname.slice(${pathIndex})`;
818
899
  }
819
900
  return result ? result + " }" : "{}";
820
901
  }
821
- function renderMatch(verb, { index, params, meta }, pathIndex) {
822
- const tuple = [`${verb}$${index}`];
823
- if (params == null ? void 0 : params.length) {
824
- tuple[1] = renderParamsInfo(params, pathIndex);
902
+ function renderParamsInfoType(params) {
903
+ if (!params.length) {
904
+ return "{}";
825
905
  }
826
- if (meta) {
827
- tuple[2] = `meta$${index}`;
906
+ let result = "{";
907
+ for (const { name } of params) {
908
+ result += ` ${wrapPropertyName(name)}: string;`;
909
+ }
910
+ return result + " }";
911
+ }
912
+ function renderMatch(verb, route, pathIndex) {
913
+ var _a;
914
+ const handler = `${verb}$${route.index}`;
915
+ const params = ((_a = route.params) == null ? void 0 : _a.length) ? renderParamsInfo(route.params, pathIndex) : "{}";
916
+ const meta = route.meta ? `meta$${route.index}` : "{}";
917
+ return `{ handler: ${handler}, params: ${params}, meta: ${meta} }`;
918
+ }
919
+ function renderMiddleware(middleware) {
920
+ const writer = createStringWriter();
921
+ writer.writeLines(
922
+ `// ${virtualFilePrefix}/${markoRunFilePrefix}middleware.js`
923
+ );
924
+ const imports = writer.branch("imports");
925
+ imports.writeLines(`import { normalize } from 'virtual:marko-run/internal';`);
926
+ writer.writeLines("");
927
+ for (const { id, importPath } of middleware) {
928
+ const importName = `mware${id}`;
929
+ imports.writeLines(`import ${importName} from './${importPath}';`);
930
+ writer.writeLines(`export const mware$${id} = normalize(${importName});`);
931
+ }
932
+ imports.join();
933
+ return writer.end();
934
+ }
935
+ function stripTsExtension(path3) {
936
+ const index = path3.lastIndexOf(".");
937
+ if (index !== -1) {
938
+ const ext = path3.slice(index + 1);
939
+ if (ext.toLowerCase() === "ts") {
940
+ return path3.slice(0, index);
941
+ }
942
+ }
943
+ return path3;
944
+ }
945
+ function renderRouteTypeInfo(routes, pathPrefix = ".") {
946
+ const writer = createStringWriter();
947
+ writer.writeLines(
948
+ `/*
949
+ WARNING: This file is automatically generated and any changes made to it will be overwritten without warning.
950
+ Do NOT manually edit this file or your changes will be lost.
951
+ */
952
+ `,
953
+ `import type { HandlerLike, Route } from "@marko/run";
954
+ `
955
+ );
956
+ const routesWriter = writer.branch("types");
957
+ const middlewareRouteTypes = /* @__PURE__ */ new Map();
958
+ for (const route of routes.list) {
959
+ const { meta, handler, params, middleware } = route;
960
+ const routeType = `Route${route.index}`;
961
+ const pathType = `\`${route.path.replace(
962
+ /\/\$(\$?)([^\/]*)/,
963
+ (_, catchAll, name) => catchAll ? `/:${name || "rest"}*` : `/:${name}`
964
+ )}\``;
965
+ const paramsType = params ? renderParamsInfoType(params) : "{}";
966
+ let metaType = "undefined";
967
+ if (meta) {
968
+ metaType = `typeof import('${pathPrefix}/${stripTsExtension(
969
+ meta.relativePath
970
+ )}')`;
971
+ if (/\.(ts|js|mjs)$/.test(meta.relativePath)) {
972
+ metaType += `['default']`;
973
+ }
974
+ }
975
+ if (handler) {
976
+ writeRouteTypeModule(writer, pathPrefix, handler.relativePath, routeType);
977
+ }
978
+ if (middleware) {
979
+ let i = 0;
980
+ for (const mw of middleware) {
981
+ const existing = middlewareRouteTypes.get(mw);
982
+ if (!existing) {
983
+ middlewareRouteTypes.set(mw, {
984
+ routeTypes: [routeType],
985
+ middleware: middleware.slice(0, i)
986
+ });
987
+ } else {
988
+ existing.routeTypes.push(routeType);
989
+ }
990
+ i++;
991
+ }
992
+ }
993
+ routesWriter.writeLines(
994
+ `interface ${routeType} extends Route<${paramsType}, ${metaType}, ${pathType}> {}`
995
+ );
828
996
  }
829
- return `[${tuple.join(", ")}]`;
997
+ for (const [file, { routeTypes }] of middlewareRouteTypes) {
998
+ const routeType = routeTypes.length > 1 ? routeTypes.join(" | ") : routeTypes[0];
999
+ writeRouteTypeModule(writer, pathPrefix, file.relativePath, routeType);
1000
+ }
1001
+ return writer.end();
1002
+ }
1003
+ function writeRouteTypeModule(writer, pathPrefix, path3, routeType) {
1004
+ writer.writeLines(`
1005
+ declare module '${pathPrefix}/${stripTsExtension(path3)}' {
1006
+ namespace Marko {
1007
+ type CurrentRoute = ${routeType};
1008
+ type Handler<_Params = CurrentRoute['params'], _Meta = CurrentRoute['meta']> = HandlerLike<CurrentRoute>;
1009
+ function route(handler: Handler): typeof handler;
1010
+ function route<_Params = CurrentRoute['params'], _Meta = CurrentRoute['meta']>(handler: Handler): typeof handler;
1011
+ }
1012
+ }`);
830
1013
  }
831
1014
 
832
1015
  // src/vite/utils/ast.ts
@@ -873,7 +1056,7 @@ function getExportIdentifiers(astProgramNode) {
873
1056
  import Table from "cli-table3";
874
1057
  import kleur from "kleur";
875
1058
  import { gzipSizeSync } from "gzip-size";
876
- import prettyBytes from "pretty-bytes";
1059
+ import format from "human-format";
877
1060
  var HttpVerbColors = {
878
1061
  get: kleur.green,
879
1062
  post: kleur.magenta,
@@ -900,7 +1083,7 @@ function logRoutesTable(routes, bundle) {
900
1083
  headings.push("Meta");
901
1084
  colAligns.push("center");
902
1085
  }
903
- headings.push("Size");
1086
+ headings.push("Size/GZip");
904
1087
  colAligns.push("right");
905
1088
  const table = new Table({
906
1089
  head: headings.map((title) => kleur.bold(kleur.white(title.toUpperCase()))),
@@ -931,7 +1114,7 @@ function logRoutesTable(routes, bundle) {
931
1114
  row.push(entryType.join(" -> "));
932
1115
  hasMiddleware && row.push(route.middleware.length || "");
933
1116
  hasMeta && row.push(route.meta ? "\u2713" : "");
934
- row.push(size || { hAlign: "center", content: "-" });
1117
+ row.push(size || "");
935
1118
  table.push(row);
936
1119
  }
937
1120
  }
@@ -947,54 +1130,75 @@ function logRoutesTable(routes, bundle) {
947
1130
  function computeRouteSize(route, bundle) {
948
1131
  if (route.page) {
949
1132
  for (const chunk of Object.values(bundle)) {
950
- if (chunk.type === "chunk" && chunk.modules[route.page.filePath]) {
951
- return computeChunkSize(chunk, bundle);
1133
+ if (chunk.type === "chunk") {
1134
+ for (const key of Object.keys(chunk.modules)) {
1135
+ if (key.startsWith(route.page.filePath)) {
1136
+ return computeChunkSize(chunk, bundle);
1137
+ }
1138
+ }
952
1139
  }
953
1140
  }
954
1141
  }
955
- return 0;
1142
+ return [0, 0];
1143
+ }
1144
+ function byteSize(str) {
1145
+ return new Blob([str]).size;
956
1146
  }
957
1147
  function computeChunkSize(chunk, bundle, seen = /* @__PURE__ */ new Set()) {
958
1148
  if (chunk.type === "asset") {
959
- return gzipSizeSync(chunk.source);
1149
+ return [
1150
+ byteSize(chunk.source),
1151
+ gzipSizeSync(chunk.source)
1152
+ ];
960
1153
  }
961
- let size = gzipSizeSync(chunk.code);
1154
+ const size = [byteSize(chunk.code), gzipSizeSync(chunk.code)];
962
1155
  for (const id of chunk.imports) {
963
1156
  if (!seen.has(id)) {
964
- size += computeChunkSize(bundle[id], bundle, seen);
1157
+ const [bytes, compBytes] = computeChunkSize(bundle[id], bundle, seen);
1158
+ size[0] += bytes;
1159
+ size[1] += compBytes;
965
1160
  seen.add(id);
966
1161
  }
967
1162
  }
968
1163
  return size;
969
1164
  }
970
- function prettySize(size) {
971
- const _size = prettyBytes(size, {
972
- minimumFractionDigits: 1,
973
- maximumFractionDigits: 1
974
- }).replace(/\sB$/, " B ");
975
- if (size < 20 * 1e3)
976
- return kleur.green(_size);
977
- if (size < 50 * 1e3)
978
- return kleur.yellow(_size);
979
- return kleur.bold(kleur.red(_size));
1165
+ function prettySize([bytes, compBytes]) {
1166
+ if (bytes <= 0) {
1167
+ return kleur.gray("0.0 kB");
1168
+ }
1169
+ const [size, prefix] = format(bytes, { decimals: 1 }).split(/\s+/);
1170
+ const compSize = format(compBytes, { decimals: 1, prefix, unit: "B" });
1171
+ let str = kleur.white(size) + kleur.gray("/");
1172
+ if (compBytes < 20 * 1e3)
1173
+ str += kleur.green(compSize);
1174
+ else if (compBytes < 50 * 1e3)
1175
+ str += kleur.yellow(compSize);
1176
+ else
1177
+ kleur.bold(kleur.red(compSize));
1178
+ return str;
980
1179
  }
981
1180
  function prettyPath(path3) {
982
1181
  return path3.replace(/\/\$\$(.*)$/, (_, p) => "/" + kleur.bold(kleur.dim(`*${p}`))).replace(/\/\$([^/]+)/g, (_, p) => "/" + kleur.bold(kleur.dim(`:${p}`)));
983
1182
  }
984
1183
 
985
1184
  // src/vite/utils/config.ts
986
- var KEY = "__MARKO_SERVE_OPTIONS__";
987
- function getMarkoServeOptions(viteConfig) {
988
- return viteConfig[KEY];
1185
+ var PluginConfigKey = "__MARKO_RUN_PLUGIN_CONFIG__";
1186
+ var AdapterConfigKey = "__MARKO_RUN_ADAPTER_CONFIG__";
1187
+ function getConfig(obj, key) {
1188
+ return obj[key];
989
1189
  }
990
- function setMarkoServeOptions(viteConfig, options) {
991
- viteConfig[KEY] = options;
992
- return viteConfig;
1190
+ function setConfig(obj, key, value) {
1191
+ obj[key] = value;
1192
+ return obj;
993
1193
  }
1194
+ var getExternalPluginOptions = (viteConfig) => getConfig(viteConfig, PluginConfigKey);
1195
+ var setExternalPluginOptions = (viteConfig, value) => setConfig(viteConfig, PluginConfigKey, value);
1196
+ var getExternalAdapterOptions = (viteConfig) => getConfig(viteConfig, AdapterConfigKey);
994
1197
 
995
1198
  // src/vite/plugin.ts
1199
+ import { fileURLToPath } from "url";
1200
+ var __dirname = fileURLToPath(new URL(".", import.meta.url));
996
1201
  var markoExt = ".marko";
997
- var markoServeFilePrefix2 = "__marko-serve__";
998
1202
  function isMarkoFile(id) {
999
1203
  return id.endsWith(markoExt);
1000
1204
  }
@@ -1003,8 +1207,10 @@ function markoServe(opts = {}) {
1003
1207
  let store;
1004
1208
  let root;
1005
1209
  let resolvedRoutesDir;
1210
+ let typesDir;
1006
1211
  let isBuild = false;
1007
1212
  let isSSRBuild = false;
1213
+ let tsConfigExists;
1008
1214
  let ssrEntryFiles;
1009
1215
  let devEntryFile;
1010
1216
  let devServer;
@@ -1013,6 +1219,7 @@ function markoServe(opts = {}) {
1013
1219
  let routeDataFilename = "routes.json";
1014
1220
  let extractVerbs;
1015
1221
  let resolvedConfig;
1222
+ let typesFile;
1016
1223
  let isStale = true;
1017
1224
  let isRendered = false;
1018
1225
  const virtualFiles = /* @__PURE__ */ new Map();
@@ -1020,6 +1227,20 @@ function markoServe(opts = {}) {
1020
1227
  routesBuild: 0,
1021
1228
  routesRender: 0
1022
1229
  };
1230
+ async function writeTypesFile() {
1231
+ if (tsConfigExists ?? (tsConfigExists = await globFileExists(
1232
+ root,
1233
+ "{.tsconfig*,tsconfig*.json}"
1234
+ ))) {
1235
+ const filepath = path2.join(typesDir, "routes.d.ts");
1236
+ const adapterTypeInfo = (adapter == null ? void 0 : adapter.writeTypeInfo) && await (adapter == null ? void 0 : adapter.writeTypeInfo());
1237
+ const data = renderRouteTypeInfo(routes, path2.relative(typesDir, routesDir)) + adapterTypeInfo;
1238
+ if (data !== typesFile || !fs2.existsSync(filepath)) {
1239
+ await ensureDir(typesDir);
1240
+ await fs2.promises.writeFile(filepath, typesFile = data);
1241
+ }
1242
+ }
1243
+ }
1023
1244
  async function setVirtualFiles(render = false) {
1024
1245
  for (const route of routes.list) {
1025
1246
  if (render && route.handler) {
@@ -1032,31 +1253,37 @@ function markoServe(opts = {}) {
1032
1253
  }
1033
1254
  if (route.page) {
1034
1255
  virtualFiles.set(
1035
- path2.join(root, `${markoServeFilePrefix2}route__${route.key}.marko`),
1256
+ path2.join(root, `${markoRunFilePrefix}route__${route.key}.marko`),
1036
1257
  render ? renderRouteTemplate(route) : ""
1037
1258
  );
1038
1259
  }
1039
1260
  virtualFiles.set(
1040
- path2.join(root, `${markoServeFilePrefix2}route__${route.key}.js`),
1261
+ path2.join(root, `${markoRunFilePrefix}route__${route.key}.js`),
1041
1262
  render ? renderRouteEntry(route) : ""
1042
1263
  );
1043
1264
  }
1044
1265
  for (const route of Object.values(routes.special)) {
1045
1266
  virtualFiles.set(
1046
- path2.join(root, `${markoServeFilePrefix2}special__${route.key}.marko`),
1267
+ path2.join(root, `${markoRunFilePrefix}special__${route.key}.marko`),
1047
1268
  render ? renderRouteTemplate(route) : ""
1048
1269
  );
1049
1270
  }
1050
1271
  virtualFiles.set(
1051
1272
  "@marko/run/router",
1052
- render ? renderRouter(routes, opts.codegen) : ""
1273
+ render ? renderRouter(routes, {
1274
+ trailingSlashes: opts.trailingSlashes || "RedirectWithout"
1275
+ }) : ""
1276
+ );
1277
+ virtualFiles.set(
1278
+ path2.join(root, `${markoRunFilePrefix}middleware.js`),
1279
+ render ? renderMiddleware(routes.middleware) : ""
1053
1280
  );
1054
1281
  }
1055
1282
  const buildVirtualFiles = single(async () => {
1056
1283
  const startTime = performance.now();
1057
1284
  routes = await buildRoutes(createFSWalker(resolvedRoutesDir), routesDir);
1058
1285
  times.routesBuild = performance.now() - startTime;
1059
- await setVirtualFiles(false);
1286
+ await Promise.all([writeTypesFile(), setVirtualFiles(false)]);
1060
1287
  isStale = false;
1061
1288
  isRendered = false;
1062
1289
  });
@@ -1070,23 +1297,30 @@ function markoServe(opts = {}) {
1070
1297
  {
1071
1298
  name: "marko-run-vite:pre",
1072
1299
  enforce: "pre",
1073
- async config(config, env) {
1300
+ async config(config2, env) {
1074
1301
  var _a, _b, _c;
1075
- const externalPluginOptions = getMarkoServeOptions(config);
1302
+ const externalPluginOptions = getExternalPluginOptions(config2);
1076
1303
  if (externalPluginOptions) {
1077
1304
  opts = mergeConfig(opts, externalPluginOptions);
1078
1305
  }
1079
- const adapterOptions = await ((_a = adapter == null ? void 0 : adapter.pluginOptions) == null ? void 0 : _a.call(adapter, opts));
1080
- if (adapterOptions) {
1081
- opts = mergeConfig(opts, adapterOptions);
1306
+ if (adapter) {
1307
+ const externalAdapterConfig = getExternalAdapterOptions(config2);
1308
+ if (externalAdapterConfig && adapter.configure) {
1309
+ adapter.configure(externalAdapterConfig);
1310
+ }
1311
+ const adapterOptions = await ((_a = adapter.pluginOptions) == null ? void 0 : _a.call(adapter, opts));
1312
+ if (adapterOptions) {
1313
+ opts = mergeConfig(opts, adapterOptions);
1314
+ }
1082
1315
  }
1083
- root = normalizePath(config.root || process.cwd());
1316
+ root = normalizePath(config2.root || process.cwd());
1084
1317
  store = opts.store || new FileStore(
1085
1318
  `marko-serve-vite-${crypto.createHash("SHA1").update(root).digest("hex")}`
1086
1319
  );
1087
1320
  isBuild = env.command === "build";
1088
- isSSRBuild = isBuild && Boolean((_b = config.build) == null ? void 0 : _b.ssr);
1321
+ isSSRBuild = isBuild && Boolean((_b = config2.build) == null ? void 0 : _b.ssr);
1089
1322
  resolvedRoutesDir = path2.resolve(root, routesDir);
1323
+ typesDir = path2.join(root, ".marko-run");
1090
1324
  devEntryFile = path2.join(root, "index.html");
1091
1325
  let pluginConfig = {
1092
1326
  logLevel: isBuild ? "warn" : void 0,
@@ -1097,18 +1331,18 @@ function markoServe(opts = {}) {
1097
1331
  emptyOutDir: isSSRBuild
1098
1332
  }
1099
1333
  };
1100
- const adapterConfig = await ((_c = adapter == null ? void 0 : adapter.viteConfig) == null ? void 0 : _c.call(adapter, config));
1334
+ const adapterConfig = await ((_c = adapter == null ? void 0 : adapter.viteConfig) == null ? void 0 : _c.call(adapter, config2));
1101
1335
  if (adapterConfig) {
1102
1336
  pluginConfig = mergeConfig(pluginConfig, adapterConfig);
1103
1337
  }
1104
- return setMarkoServeOptions(pluginConfig, opts);
1338
+ return setExternalPluginOptions(pluginConfig, opts);
1105
1339
  },
1106
- configResolved(config) {
1107
- resolvedConfig = config;
1340
+ configResolved(config2) {
1341
+ resolvedConfig = config2;
1108
1342
  const {
1109
1343
  ssr,
1110
1344
  rollupOptions: { input }
1111
- } = config.build;
1345
+ } = config2.build;
1112
1346
  if (typeof ssr === "string") {
1113
1347
  ssrEntryFiles = [ssr];
1114
1348
  } else if (typeof input === "string") {
@@ -1139,6 +1373,7 @@ function markoServe(opts = {}) {
1139
1373
  if (isStale) {
1140
1374
  for (const id of virtualFiles.keys()) {
1141
1375
  devServer.watcher.emit("change", id);
1376
+ break;
1142
1377
  }
1143
1378
  }
1144
1379
  }
@@ -1168,12 +1403,18 @@ function markoServe(opts = {}) {
1168
1403
  },
1169
1404
  async resolveId(importee, importer, { ssr }) {
1170
1405
  let resolved;
1171
- if (importee.startsWith(virtualFilePrefix)) {
1406
+ if (importee.startsWith(virtualRuntimePrefix)) {
1407
+ return this.resolve(
1408
+ path2.resolve(__dirname, "../runtime/internal"),
1409
+ importer,
1410
+ { skipSelf: true }
1411
+ );
1412
+ } else if (importee.startsWith(virtualFilePrefix)) {
1172
1413
  importee = path2.resolve(
1173
1414
  root,
1174
1415
  importee.slice(virtualFilePrefix.length + 1)
1175
1416
  );
1176
- } else if (!isBuild && importer === devEntryFile && importee.startsWith(`/${markoServeFilePrefix2}`)) {
1417
+ } else if (!isBuild && importer === devEntryFile && importee.startsWith(`/${markoRunFilePrefix}`)) {
1177
1418
  importee = path2.resolve(root, "." + importee);
1178
1419
  }
1179
1420
  if (isStale) {
@@ -1295,20 +1536,49 @@ function single(fn) {
1295
1536
  return result;
1296
1537
  };
1297
1538
  }
1539
+ async function globFileExists(root, pattern) {
1540
+ return new Promise((resolve, reject) => {
1541
+ glob(pattern, { root }, (err, matches) => {
1542
+ if (err) {
1543
+ reject(err);
1544
+ }
1545
+ resolve(matches.length > 0);
1546
+ });
1547
+ });
1548
+ }
1549
+ async function ensureDir(dir) {
1550
+ if (!fs2.existsSync(dir)) {
1551
+ await fs2.promises.mkdir(dir, { recursive: true });
1552
+ }
1553
+ }
1298
1554
 
1299
1555
  // src/vite/utils/server.ts
1300
1556
  import net from "net";
1301
1557
  import cp from "child_process";
1302
- async function spawnServer(cmd, port = 0, cwd = process.cwd(), wait = 3e4) {
1558
+ import { parse, config } from "dotenv";
1559
+ import fs3 from "fs";
1560
+ async function parseEnv(envFile) {
1561
+ if (fs3.existsSync(envFile)) {
1562
+ const content = await fs3.promises.readFile(envFile, "utf8");
1563
+ return parse(content);
1564
+ }
1565
+ }
1566
+ function loadEnv(envFile) {
1567
+ config({ path: envFile });
1568
+ }
1569
+ async function spawnServer(cmd, port = 0, env, cwd = process.cwd(), wait = 3e4) {
1303
1570
  if (port <= 0) {
1304
1571
  port = await getAvailablePort();
1305
1572
  }
1573
+ if (typeof env === "string") {
1574
+ env = await parseEnv(env);
1575
+ }
1306
1576
  const proc = cp.spawn(cmd, {
1307
1577
  cwd,
1308
1578
  shell: true,
1309
1579
  stdio: "inherit",
1310
1580
  windowsHide: true,
1311
- env: { NODE_ENV: "development", ...process.env, PORT: `${port}` }
1581
+ env: { ...env, NODE_ENV: "development", ...process.env, PORT: `${port}` }
1312
1582
  });
1313
1583
  const close = () => {
1314
1584
  proc.unref();
@@ -1355,5 +1625,7 @@ export {
1355
1625
  markoServe as default,
1356
1626
  getAvailablePort,
1357
1627
  isPortInUse,
1628
+ loadEnv,
1629
+ parseEnv,
1358
1630
  spawnServer
1359
1631
  };