@marko/run 0.2.8 → 0.2.10

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,5 +1,6 @@
1
1
  // src/adapter/index.ts
2
2
  import path from "path";
3
+ import fs2 from "fs";
3
4
  import { fileURLToPath } from "url";
4
5
 
5
6
  // src/adapter/dev-server.ts
@@ -263,8 +264,7 @@ async function createDevServer(config2) {
263
264
  (request, platform) => globalThis.__marko_run__.fetch(request, platform)
264
265
  );
265
266
  devServer.middlewares.use(async (req, res, next) => {
266
- await devServer.ssrLoadModule("@marko/run/router");
267
- routerMiddleware(req, res, (err) => {
267
+ function handleError(err) {
268
268
  if (err) {
269
269
  res.statusCode = 500;
270
270
  if (err instanceof Error) {
@@ -276,7 +276,13 @@ async function createDevServer(config2) {
276
276
  } else {
277
277
  next == null ? void 0 : next();
278
278
  }
279
- });
279
+ }
280
+ try {
281
+ await devServer.ssrLoadModule("@marko/run/router");
282
+ } catch (err) {
283
+ return handleError(err);
284
+ }
285
+ routerMiddleware(req, res, handleError);
280
286
  });
281
287
  return devServer;
282
288
  }
@@ -351,16 +357,22 @@ function getDevGlobal() {
351
357
  // src/adapter/utils.ts
352
358
  import supporsColor from "supports-color";
353
359
  import kleur from "kleur";
354
- function logInfoBox(address) {
360
+ function logInfoBox(address, explorer) {
355
361
  const color = !!supporsColor.stdout;
356
362
  let message = kleur.bold("Marko Run");
357
363
  if (true) {
358
- message += ` v${"0.2.8"}`;
364
+ message += ` v${"0.2.10"}`;
359
365
  }
360
366
  message += "\n\n";
361
367
  message += kleur.dim("Server listening at");
362
368
  message += "\n";
363
369
  message += kleur.cyan(kleur.underline(address));
370
+ if (explorer) {
371
+ message += "\n\n";
372
+ message += kleur.dim("Explore your routes");
373
+ message += "\n";
374
+ message += kleur.dim(kleur.green(kleur.underline(explorer)));
375
+ }
364
376
  const lines = drawMarkoBox(message, { color, fill: color });
365
377
  console.log(lines.join("\n"));
366
378
  }
@@ -380,7 +392,7 @@ function drawMarkoBox(message, options) {
380
392
  const hBorder = "\u2500".repeat(width);
381
393
  const vBorder = "\u2502";
382
394
  const lineDiff = logo.lines.length - textLines.length;
383
- const textStartLine = lineDiff > 0 ? Math.floor(lineDiff / 2) : 1;
395
+ const textStartLine = lineDiff > 0 ? Math.max(Math.floor(lineDiff / 2), 1) : 1;
384
396
  const textEndLine = height - (lineDiff > 0 ? Math.ceil(lineDiff / 2) : 1);
385
397
  const logoEndLine = logo.lines.length;
386
398
  const logoFill = " ".repeat(logo.width);
@@ -536,7 +548,7 @@ async function spawnServer(cmd, args = [], port = 0, env, cwd = process.cwd(), w
536
548
  close
537
549
  };
538
550
  }
539
- async function spawnServerWorker(module, args = [], port = 0, env) {
551
+ async function spawnServerWorker(module, args = [], port = 0, env, wait = true) {
540
552
  if (port <= 0) {
541
553
  port = await getAvailablePort();
542
554
  }
@@ -549,15 +561,18 @@ async function spawnServerWorker(module, args = [], port = 0, env) {
549
561
  cluster.settings.exec = module;
550
562
  cluster.settings.execArgv = args;
551
563
  const worker = cluster.fork({ ...env, NODE_ENV: "development", ...process.env, PORT: `${port}` });
552
- return new Promise((resolve) => {
553
- function ready(message) {
554
- if (message === "ready") {
555
- worker.off("message", ready);
556
- resolve(worker);
564
+ if (wait) {
565
+ return new Promise((resolve) => {
566
+ function ready(message) {
567
+ if (message === "ready") {
568
+ worker.off("message", ready);
569
+ resolve(worker);
570
+ }
557
571
  }
558
- }
559
- worker.on("message", ready);
560
- });
572
+ worker.on("message", ready);
573
+ });
574
+ }
575
+ return worker;
561
576
  } finally {
562
577
  cluster.settings.exec = originalExec;
563
578
  cluster.settings.execArgv = originalArgs;
@@ -632,7 +647,14 @@ function sleep(ms) {
632
647
  }
633
648
 
634
649
  // src/adapter/index.ts
650
+ import { createRequire } from "module";
635
651
  import parseNodeArgs from "parse-node-args";
652
+
653
+ // src/vite/constants.ts
654
+ var markoRunFilePrefix = "__marko-run__";
655
+ var virtualFilePrefix = "virtual:marko-run";
656
+
657
+ // src/adapter/index.ts
636
658
  var __dirname = path.dirname(fileURLToPath(import.meta.url));
637
659
  var defaultEntry = path.join(__dirname, "default-entry");
638
660
  var loadDevWorker = path.join(__dirname, "load-dev-worker.mjs");
@@ -645,6 +667,7 @@ function adapter() {
645
667
  async startDev(entry, config2, options) {
646
668
  const { port = 3e3, envFile } = options;
647
669
  globalThis.__marko_run_vite_config__ = config2;
670
+ const explorerPromise = startExplorer();
648
671
  if (entry) {
649
672
  const { nodeArgs } = parseNodeArgs(options.args);
650
673
  let worker;
@@ -675,41 +698,116 @@ function adapter() {
675
698
  }
676
699
  worker = nextWorker;
677
700
  }
678
- await start();
701
+ const [explorer2] = await Promise.all([explorerPromise, start()]);
679
702
  return {
680
703
  port,
681
- close() {
682
- worker.kill();
704
+ async close() {
705
+ await Promise.all([worker.kill(), explorer2 == null ? void 0 : explorer2.close()]);
683
706
  }
684
707
  };
685
708
  }
686
709
  const devServer = await createDevServer(config2);
687
710
  envFile && await loadEnv(envFile);
688
- return new Promise((resolve) => {
711
+ const listen = new Promise((resolve) => {
689
712
  const listener = devServer.middlewares.listen(port, () => {
690
- const address = listener.address();
691
- logInfoBox(`http://localhost:${address.port}`);
692
- resolve({
693
- port,
694
- async close() {
695
- await devServer.close();
696
- }
697
- });
713
+ resolve(listener.address());
698
714
  });
699
715
  });
716
+ const [explorer, address] = await Promise.all([explorerPromise, listen]);
717
+ logInfoBox(
718
+ `http://localhost:${address.port}`,
719
+ explorer && `http://localhost:${explorer.port}`
720
+ );
721
+ return {
722
+ port: address.port,
723
+ async close() {
724
+ await Promise.all([devServer.close(), explorer == null ? void 0 : explorer.close()]);
725
+ }
726
+ };
700
727
  },
701
728
  async startPreview(entry, options) {
702
729
  const { port = 3e3, envFile } = options;
703
730
  const { nodeArgs } = parseNodeArgs(options.args);
704
731
  const args = [...nodeArgs, entry];
705
- const server = await spawnServer("node", args, port, envFile);
732
+ const [explorer, server] = await Promise.all([
733
+ startExplorer(),
734
+ spawnServer("node", args, port, envFile)
735
+ ]);
706
736
  if (!options.sourceEntry) {
707
- logInfoBox(`http://localhost:${port}`);
737
+ logInfoBox(
738
+ `http://localhost:${port}`,
739
+ explorer && `http://localhost:${explorer.port}`
740
+ );
741
+ }
742
+ return explorer ? {
743
+ port: server.port,
744
+ async close() {
745
+ await Promise.all([server.close(), explorer.close()]);
746
+ }
747
+ } : server;
748
+ },
749
+ async routesGenerated(routes, virtualFiles, meta) {
750
+ if (process.env.MR_EXPLORER !== "true") {
751
+ return;
752
+ }
753
+ const promises = [];
754
+ const cacheDir = path.resolve(__dirname, "../../.cache/explorer");
755
+ const codeDir = path.join(cacheDir, "code");
756
+ if (fs2.existsSync(codeDir)) {
757
+ await fs2.promises.rm(codeDir, { recursive: true });
758
+ }
759
+ await fs2.promises.mkdir(codeDir, { recursive: true });
760
+ const data = {
761
+ meta,
762
+ routes: {},
763
+ files: {}
764
+ };
765
+ for (const [name, code] of virtualFiles) {
766
+ let fileName = "";
767
+ let index = name.indexOf(markoRunFilePrefix);
768
+ if (index >= 0) {
769
+ fileName = name.slice(index);
770
+ data.files[fileName] = `${virtualFilePrefix}/${fileName}`;
771
+ } else if (name.startsWith("@marko/run")) {
772
+ fileName = name.slice(11);
773
+ data.files[fileName] = name;
774
+ }
775
+ if (fileName) {
776
+ promises.push(
777
+ fs2.promises.writeFile(path.join(codeDir, fileName), code, {})
778
+ );
779
+ }
780
+ }
781
+ for (const route of routes.list) {
782
+ data.routes[route.index] = route;
783
+ }
784
+ for (const [id, route] of Object.entries(routes.special)) {
785
+ data.routes["s" + id] = route;
708
786
  }
709
- return server;
787
+ promises.push(
788
+ fs2.promises.writeFile(
789
+ path.join(cacheDir, "data.json"),
790
+ JSON.stringify(data),
791
+ {}
792
+ )
793
+ );
794
+ await Promise.all(promises);
710
795
  }
711
796
  };
712
797
  }
798
+ var require2 = createRequire(import.meta.url);
799
+ async function startExplorer() {
800
+ if (process.env.MR_EXPLORER === "true") {
801
+ const entry = require2.resolve("@marko/run-explorer");
802
+ const worker = await spawnServerWorker(entry, [], 1234, void 0, false);
803
+ return {
804
+ port: 1234,
805
+ async close() {
806
+ worker.kill();
807
+ }
808
+ };
809
+ }
810
+ }
713
811
  export {
714
812
  createDevServer,
715
813
  createViteDevMiddleware,
@@ -18,10 +18,25 @@ async function start(entry, config) {
18
18
  const loader = await createServer({
19
19
  ...config,
20
20
  ssr: { external: ["@marko/run/router"] },
21
- })
21
+ });
22
22
 
23
23
  await loader.listen(0);
24
- await loader.ssrLoadModule(entry);
24
+ loader.ssrLoadModule(entry).catch((err) => {
25
+ loader.ssrFixStacktrace(err);
26
+
27
+ console.error(err);
28
+
29
+ const devGlobal = getDevGlobal();
30
+ for (const devServer of devGlobal.devServers) {
31
+ const { message, stack = "" } = err;
32
+ devServer.ws.send(
33
+ JSON.stringify({
34
+ type: "error",
35
+ err: { message, stack },
36
+ })
37
+ );
38
+ }
39
+ });
25
40
 
26
41
  loader.watcher.on("change", (path) => {
27
42
  if (!changed && loader.moduleGraph.getModulesByFile(path)) {
@@ -1,4 +1,4 @@
1
- export declare function logInfoBox(address: string): void;
1
+ export declare function logInfoBox(address: string, explorer?: string): void;
2
2
  export declare function drawMarkoBox(message: string, options?: LogoOptions): string[];
3
3
  export interface LogoOptions {
4
4
  fill?: boolean;
@@ -403,10 +403,7 @@ function matchRoutableFile(filename) {
403
403
  function isSpecialType(type) {
404
404
  return type === RoutableFileTypes.NotFound || type === RoutableFileTypes.Error;
405
405
  }
406
- async function buildRoutes(walk, basePath = "") {
407
- if (basePath) {
408
- basePath = basePath.replace(/^\/+|\/+$/g, "");
409
- }
406
+ async function buildRoutes(sources) {
410
407
  const uniqueRoutes = /* @__PURE__ */ new Map();
411
408
  const routes = [];
412
409
  const special = {};
@@ -416,23 +413,30 @@ async function buildRoutes(walk, basePath = "") {
416
413
  const currentMiddleware = /* @__PURE__ */ new Set();
417
414
  const root = new VDir();
418
415
  const dirStack = [];
419
- let activeDirs = [root];
416
+ let basePath;
417
+ let importPrefix;
418
+ let activeDirs;
419
+ let isBaseDir;
420
420
  let nextFileId = 1;
421
421
  let nextRouteIndex = 1;
422
- let isBaseDir = true;
423
- await walk({
422
+ const walkOptions = {
424
423
  onEnter({ name }) {
425
- if (!name || isBaseDir) {
424
+ const prevDirStackLength = dirStack.length;
425
+ if (isBaseDir) {
426
426
  isBaseDir = false;
427
- return;
427
+ if (!basePath) {
428
+ return;
429
+ }
430
+ name = basePath;
431
+ } else {
432
+ dirStack.push(name);
428
433
  }
429
- dirStack.push(name);
430
434
  const previousDirs = activeDirs;
431
435
  const paths = parseFlatRoute(name);
432
436
  activeDirs = VDir.addPaths(previousDirs, paths);
433
437
  return () => {
434
438
  activeDirs = previousDirs;
435
- dirStack.pop();
439
+ dirStack.length = prevDirStackLength;
436
440
  };
437
441
  },
438
442
  onFile({ name, path: path4 }) {
@@ -460,14 +464,24 @@ async function buildRoutes(walk, basePath = "") {
460
464
  type,
461
465
  filePath: path4,
462
466
  relativePath,
463
- importPath: `${basePath}/${relativePath}`,
467
+ importPath: `${importPrefix}/${relativePath}`,
464
468
  verbs: type === RoutableFileTypes.Page ? ["get"] : void 0
465
469
  };
466
470
  for (const dir of dirs) {
467
471
  dir.addFile(file);
468
472
  }
469
473
  }
470
- });
474
+ };
475
+ if (!Array.isArray(sources)) {
476
+ sources = [sources];
477
+ }
478
+ for (const source of sources) {
479
+ importPrefix = source.importPrefix ? source.importPrefix.replace(/^\/+|\/+$/g, "") : "";
480
+ basePath = source.basePath || "";
481
+ activeDirs = [root];
482
+ isBaseDir = true;
483
+ await source.walker(walkOptions);
484
+ }
471
485
  traverse(root);
472
486
  return {
473
487
  list: routes,
@@ -1017,12 +1031,13 @@ globalThis.__marko_run__ = { match, fetch, invoke };
1017
1031
  } else if (pathname.charAt(0) !== '/') {
1018
1032
  pathname = '/' + pathname;
1019
1033
  }`
1020
- ).writeBlockStart(`switch (method.toLowerCase()) {`);
1034
+ ).writeBlockStart(`switch (method) {`);
1021
1035
  for (const verb of httpVerbs) {
1022
1036
  const filteredRoutes = routes.list.filter((route) => hasVerb(route, verb));
1023
1037
  if (filteredRoutes.length) {
1024
1038
  const trie = createRouteTrie(filteredRoutes);
1025
- writer.writeBlockStart(`case '${verb}': {`);
1039
+ writer.writeLines(`case '${verb.toUpperCase()}':`);
1040
+ writer.writeBlockStart(`case '${verb.toLowerCase()}': {`);
1026
1041
  writeRouterVerb(writer, trie, verb);
1027
1042
  writer.writeBlockEnd("}");
1028
1043
  }
@@ -1174,13 +1189,14 @@ function writeRouterVerb(writer, trie, verb, level = 0, offset = 1) {
1174
1189
  if (terminal) {
1175
1190
  const useSwitch = terminal.length > 1;
1176
1191
  if (useSwitch) {
1177
- writer.writeBlockStart(`switch (${value}.toLowerCase()) {`);
1192
+ writer.writeBlockStart(`switch (${value}) {`);
1178
1193
  }
1179
1194
  for (const { key, path: path4, route: route2 } of terminal) {
1195
+ const decodedKey = decodeURIComponent(key);
1180
1196
  if (useSwitch) {
1181
- writer.write(`case '${key}': `, true);
1197
+ writer.write(`case '${decodedKey}': `, true);
1182
1198
  } else {
1183
- writer.write(`if (${value}.toLowerCase() === '${key}') `, true);
1199
+ writer.write(`if (${value} === '${decodedKey}') `, true);
1184
1200
  }
1185
1201
  writer.write(
1186
1202
  `return ${renderMatch(verb, route2, path4)}; // ${path4.path}
@@ -1217,7 +1233,7 @@ function writeRouterVerb(writer, trie, verb, level = 0, offset = 1) {
1217
1233
  if (children) {
1218
1234
  const useSwitch = children.length > 1;
1219
1235
  if (useSwitch) {
1220
- writer.writeBlockStart(`switch (${value}.toLowerCase()) {`);
1236
+ writer.writeBlockStart(`switch (${value}) {`);
1221
1237
  }
1222
1238
  for (const child of children) {
1223
1239
  const decodedKey = decodeURIComponent(child.key);
@@ -1225,7 +1241,7 @@ function writeRouterVerb(writer, trie, verb, level = 0, offset = 1) {
1225
1241
  writer.writeBlockStart(`case '${decodedKey}': {`);
1226
1242
  } else {
1227
1243
  writer.writeBlockStart(
1228
- `if (${value}.toLowerCase() === '${decodedKey}') {`
1244
+ `if (${value} === '${decodedKey}') {`
1229
1245
  );
1230
1246
  }
1231
1247
  const nextOffset = typeof offset === "string" ? index : offset + child.key.length + 1;
@@ -1692,6 +1708,8 @@ function prettyPath(path4) {
1692
1708
  }
1693
1709
 
1694
1710
  // src/vite/plugin.ts
1711
+ import createDebug from "debug";
1712
+ var debug = createDebug("@marko/run");
1695
1713
  var __dirname = path2.dirname(fileURLToPath(import.meta.url));
1696
1714
  var PLUGIN_NAME_PREFIX = "marko-run-vite";
1697
1715
  var POSIX_SEP = "/";
@@ -1751,10 +1769,10 @@ function markoRun(opts = {}) {
1751
1769
  virtualFiles.clear();
1752
1770
  isRendered = false;
1753
1771
  const buildStartTime = performance.now();
1754
- routes = await buildRoutes(
1755
- createFSWalker(resolvedRoutesDir),
1756
- routesDir
1757
- );
1772
+ routes = await buildRoutes({
1773
+ walker: createFSWalker(resolvedRoutesDir),
1774
+ importPrefix: routesDir
1775
+ });
1758
1776
  times.routesBuild = performance.now() - buildStartTime;
1759
1777
  if (!routes.list.length) {
1760
1778
  throw new Error("No routes generated");
@@ -1800,6 +1818,12 @@ function markoRun(opts = {}) {
1800
1818
  times.routesRender = performance.now() - renderStartTime;
1801
1819
  if (render) {
1802
1820
  await writeTypesFile(routes);
1821
+ if (adapter == null ? void 0 : adapter.routesGenerated) {
1822
+ await adapter.routesGenerated(routes, new Map(virtualFiles.entries()), {
1823
+ buildTime: times.routesBuild,
1824
+ renderTime: times.routesRender
1825
+ });
1826
+ }
1803
1827
  if (!isBuild) {
1804
1828
  await ((_a = opts == null ? void 0 : opts.emitRoutes) == null ? void 0 : _a.call(opts, routes.list));
1805
1829
  }
@@ -2097,7 +2121,7 @@ function markoRun(opts = {}) {
2097
2121
  }
2098
2122
  await store.set(routeDataFilename, JSON.stringify(routeData));
2099
2123
  await ((_a = opts == null ? void 0 : opts.emitRoutes) == null ? void 0 : _a.call(opts, routes.list));
2100
- } else {
2124
+ } else if (process.env.MR_EXPLORER !== "true") {
2101
2125
  logRoutesTable(routes, bundle, options);
2102
2126
  }
2103
2127
  },
@@ -2204,19 +2228,19 @@ async function resolveAdapter(root, options, log) {
2204
2228
  if (name.startsWith("@marko/run-adapter") || name.indexOf("marko-run-adapter") !== -1) {
2205
2229
  try {
2206
2230
  const module2 = await import(name);
2207
- log && console.log(
2231
+ log && debug(
2208
2232
  `Using adapter ${name} listed in your package.json dependecies`
2209
2233
  );
2210
2234
  return module2.default();
2211
2235
  } catch (err) {
2212
- log && console.warn(`Attempt to use package '${name}' failed`, err);
2236
+ log && debug(`Attempt to use package '${name}' failed %O`, err);
2213
2237
  }
2214
2238
  }
2215
2239
  }
2216
2240
  }
2217
2241
  const defaultAdapter = "@marko/run/adapter";
2218
2242
  const module = await import(defaultAdapter);
2219
- log && console.log("Using default adapter");
2243
+ log && debug("Using default adapter");
2220
2244
  return module.default();
2221
2245
  }
2222
2246
  var markoEntryFileRegex = /__marko-run__([^.]+)\.(.+)\.marko\.([^.]+)$/;
@@ -2406,7 +2430,9 @@ prog.command("preview [entry]").describe("Start a production-like server for alr
2406
2430
  "-p, --port",
2407
2431
  "Port the server should listen on (defaults: `$PORT` env variable or 3000)"
2408
2432
  ).option("-f, --file", "Output file to start").action(async (entry, opts) => {
2433
+ var _a;
2409
2434
  process.env.NODE_ENV = "production";
2435
+ (_a = process.env).MR_EXPLORER ?? (_a.MR_EXPLORER = "true");
2410
2436
  const cwd = process.cwd();
2411
2437
  const args = process.argv.slice(entry ? 4 : 3);
2412
2438
  const config2 = await getViteConfig(cwd, opts.config);
@@ -2426,6 +2452,8 @@ prog.command("dev [entry]", "", { default: true }).describe("Start development s
2426
2452
  "-p, --port",
2427
2453
  "Port the dev server should listen on (defaults: 'preview.port' in config, or `$PORT` env variable, or 3000)"
2428
2454
  ).example("dev --config vite.config.js").action(async (entry, opts) => {
2455
+ var _a;
2456
+ (_a = process.env).MR_EXPLORER ?? (_a.MR_EXPLORER = "true");
2429
2457
  const cwd = process.cwd();
2430
2458
  const args = process.argv.slice(entry ? 4 : 3);
2431
2459
  const config2 = await getViteConfig(cwd, opts.config);
@@ -0,0 +1,17 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta
6
+ name="description"
7
+ content="Marko Run route explorer"
8
+ >
9
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
10
+ <title>Explore your routes</title>
11
+ </head>
12
+ <body>
13
+ <h2>Marko Run Route Explorer</h2>
14
+ <hr/>
15
+ <${input.renderBody} />
16
+ </body>
17
+ </html>
@@ -0,0 +1 @@
1
+ <h1>Hello World!!</h1>