@marko/run 0.2.6 → 0.2.8

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.
@@ -4,8 +4,228 @@ import { fileURLToPath } from "url";
4
4
 
5
5
  // src/adapter/dev-server.ts
6
6
  import { createServer } from "vite";
7
+
8
+ // src/adapter/polyfill.ts
9
+ import * as webStream from "stream/web";
10
+ import { webcrypto } from "crypto";
11
+ import * as undici from "undici";
12
+ globalThis.crypto ?? (globalThis.crypto = webcrypto);
13
+ globalThis.fetch ?? (globalThis.fetch = undici.fetch);
14
+ globalThis.Response ?? (globalThis.Response = undici.Response);
15
+ globalThis.Request ?? (globalThis.Request = undici.Request);
16
+ globalThis.Headers ?? (globalThis.Headers = undici.Headers);
17
+ globalThis.ReadableStream ?? (globalThis.ReadableStream = webStream.ReadableStream);
18
+ globalThis.TransformStream ?? (globalThis.TransformStream = webStream.TransformStream);
19
+ globalThis.WritableStream ?? (globalThis.WritableStream = webStream.WritableStream);
20
+ globalThis.FormData ?? (globalThis.FormData = undici.FormData);
21
+ globalThis.File ?? (globalThis.File = undici.File);
22
+
23
+ // src/adapter/middleware.ts
24
+ function getForwardedHeader(req, name) {
25
+ const value = req.headers["x-forwarded-" + name];
26
+ if (value) {
27
+ if (typeof value === "string") {
28
+ const index = value.indexOf(",");
29
+ return index < 0 ? value : value.slice(0, index);
30
+ }
31
+ return value[0];
32
+ }
33
+ }
34
+ function getOrigin(req, trustProxy) {
35
+ const protocol = req.protocol || trustProxy && getForwardedHeader(req, "proto") || (req.socket.encrypted ? "https" : "http");
36
+ let host = req.headers.host || trustProxy && getForwardedHeader(req, "host");
37
+ if (!host) {
38
+ if (process.env.NODE_ENV !== "production") {
39
+ host = "localhost";
40
+ console.warn(
41
+ `Could not automatically determine the origin host, using 'localhost'. Use the 'origin' option or the 'ORIGIN' environment variable to set the origin explicitly.`
42
+ );
43
+ } else {
44
+ throw new Error(
45
+ `Could not automatically determine the origin host. Use the 'origin' option or the 'ORIGIN' environment variable to set the origin explicitly.`
46
+ );
47
+ }
48
+ }
49
+ return `${protocol}://${host}`;
50
+ }
51
+ var inExpiresDateRgs = /Expires\s*=\s*(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s*$/i;
52
+ function setResponseHeaders(response, res) {
53
+ for (const [key, value] of response.headers) {
54
+ if (key !== "set-cookie") {
55
+ res.setHeader(key, value);
56
+ }
57
+ }
58
+ const setCookies = getSetCookie(response.headers);
59
+ if (setCookies == null ? void 0 : setCookies.length) {
60
+ res.setHeader("set-cookie", setCookies);
61
+ }
62
+ }
63
+ var getSetCookie = Headers.prototype.getSetCookie ? getSetCookie_platform : getSetCookie_fallback;
64
+ function getSetCookie_platform(headers) {
65
+ return headers.getSetCookie();
66
+ }
67
+ function getSetCookie_fallback(headers) {
68
+ const value = headers.get("set-cookie");
69
+ if (!value)
70
+ return void 0;
71
+ let sepIndex = value.indexOf(",") + 1;
72
+ if (!sepIndex)
73
+ return value;
74
+ let index = 0;
75
+ let setCookie = void 0;
76
+ let setCookies = void 0;
77
+ do {
78
+ const valuePart = value.slice(index, sepIndex - 1);
79
+ if (!inExpiresDateRgs.test(valuePart)) {
80
+ if (setCookies) {
81
+ setCookies.push(valuePart);
82
+ } else if (setCookie) {
83
+ setCookies = [setCookie, valuePart];
84
+ } else {
85
+ setCookie = valuePart;
86
+ }
87
+ index = sepIndex;
88
+ while (value.charCodeAt(index) === 32)
89
+ index++;
90
+ }
91
+ sepIndex = value.indexOf(",", sepIndex) + 1;
92
+ } while (sepIndex);
93
+ if (index) {
94
+ const valuePart = value.slice(index);
95
+ if (setCookies) {
96
+ setCookies.push(valuePart);
97
+ return setCookies;
98
+ }
99
+ return [setCookie, valuePart];
100
+ }
101
+ return value;
102
+ }
103
+ function createMiddleware(fetch2, options = {}) {
104
+ const {
105
+ origin = process.env.ORIGIN,
106
+ trustProxy = process.env.TRUST_PROXY === "1"
107
+ } = options;
108
+ return async (req, res, next) => {
109
+ var _a;
110
+ const controller = new AbortController();
111
+ const { signal } = controller;
112
+ const url = new URL(req.url, origin || getOrigin(req, trustProxy));
113
+ const ip = req.ip || trustProxy && getForwardedHeader(req, "for") || req.socket.remoteAddress || "";
114
+ req.on("error", onErrorOrClose);
115
+ req.socket.on("error", onErrorOrClose);
116
+ res.on("error", onErrorOrClose);
117
+ res.on("close", onErrorOrClose);
118
+ signal.addEventListener("abort", onSignalAborted);
119
+ function onErrorOrClose(err) {
120
+ req.off("error", onErrorOrClose);
121
+ req.socket.off("error", onErrorOrClose);
122
+ res.off("error", onErrorOrClose);
123
+ res.off("close", onErrorOrClose);
124
+ if (err) {
125
+ signal.removeEventListener("abort", onSignalAborted);
126
+ controller.abort(err);
127
+ }
128
+ }
129
+ function onSignalAborted() {
130
+ if (next) {
131
+ next(signal.reason);
132
+ } else {
133
+ if (!res.destroyed && res.socket) {
134
+ res.socket.destroySoon();
135
+ }
136
+ console.error(signal.reason);
137
+ }
138
+ }
139
+ let setDevClientId;
140
+ if (process.env.NODE_ENV !== "production" && globalThis.__marko_run_dev__ && ((_a = req.headers.accept) == null ? void 0 : _a.includes("text/html"))) {
141
+ setDevClientId = globalThis.__marko_run_dev__.onClient((ws) => {
142
+ if (signal.aborted) {
143
+ sendError();
144
+ } else {
145
+ signal.addEventListener("abort", sendError);
146
+ }
147
+ function sendError() {
148
+ const { message, stack = "" } = signal.reason;
149
+ ws.send(
150
+ JSON.stringify({
151
+ type: "error",
152
+ err: { message, stack }
153
+ })
154
+ );
155
+ }
156
+ });
157
+ }
158
+ let body;
159
+ if (req.method !== "GET" && req.method !== "HEAD") {
160
+ if (req.readableDidRead) {
161
+ body = bodyConsumedErrorStream;
162
+ } else {
163
+ body = req;
164
+ }
165
+ }
166
+ const request = new Request(url, {
167
+ method: req.method,
168
+ headers: req.headers,
169
+ body,
170
+ // @ts-expect-error: Node requires this for streams
171
+ duplex: "half",
172
+ signal
173
+ });
174
+ const response = await fetch2(request, {
175
+ ip,
176
+ request: req,
177
+ response: res
178
+ });
179
+ if (!response) {
180
+ if (next) {
181
+ next();
182
+ }
183
+ return;
184
+ }
185
+ if (process.env.NODE_ENV !== "production" && setDevClientId) {
186
+ setDevClientId(response);
187
+ }
188
+ res.statusCode = response.status;
189
+ setResponseHeaders(response, res);
190
+ if (!response.body) {
191
+ if (!response.headers.has("content-length")) {
192
+ res.setHeader("content-length", "0");
193
+ }
194
+ res.end();
195
+ return;
196
+ } else if (res.destroyed) {
197
+ controller.abort(new Error("Response stream destroyed"));
198
+ return;
199
+ }
200
+ writeResponse(response.body.getReader(), res, controller);
201
+ };
202
+ }
203
+ async function writeResponse(reader, res, controller) {
204
+ try {
205
+ while (!controller.signal.aborted) {
206
+ const { done, value } = await reader.read();
207
+ if (done) {
208
+ res.end();
209
+ return;
210
+ } else if (!res.write(value)) {
211
+ res.once("drain", () => writeResponse(reader, res, controller));
212
+ return;
213
+ } else if (res.flush) {
214
+ res.flush();
215
+ }
216
+ }
217
+ } catch (err) {
218
+ controller.abort(err);
219
+ }
220
+ }
221
+ var bodyConsumedErrorStream = new ReadableStream({
222
+ start(controller) {
223
+ controller.error(new Error("The request body stream was already consumed by something before Marko Run."));
224
+ }
225
+ });
226
+
227
+ // src/adapter/dev-server.ts
7
228
  import stripAnsi from "strip-ansi";
8
- var activeDevServers = /* @__PURE__ */ new Set();
9
229
  function createViteDevMiddleware(devServer, load, factory) {
10
230
  let value;
11
231
  let middleware;
@@ -34,34 +254,239 @@ async function createViteDevServer(config2) {
34
254
  appType: "custom",
35
255
  server: { ...config2 == null ? void 0 : config2.server, middlewareMode: true }
36
256
  });
37
- const originalClose = devServer.close;
38
- devServer.close = () => {
39
- activeDevServers.delete(devServer);
40
- return originalClose.call(devServer);
41
- };
42
- activeDevServers.add(devServer);
257
+ getDevGlobal().addDevServer(devServer);
43
258
  return devServer;
44
259
  }
45
260
  async function createDevServer(config2) {
46
261
  const devServer = await createViteDevServer(config2);
47
- const { createMiddleware } = await devServer.ssrLoadModule(
48
- "@marko/run/adapter/middleware"
262
+ const routerMiddleware = createMiddleware(
263
+ (request, platform) => globalThis.__marko_run__.fetch(request, platform)
49
264
  );
50
- let fetch;
51
- const nodeMiddleware = createMiddleware(
52
- (request, platform) => fetch(request, platform),
53
- { devServer }
54
- );
55
- const middleware = createViteDevMiddleware(
56
- devServer,
57
- async () => await devServer.ssrLoadModule("@marko/run/router"),
58
- (module) => {
59
- fetch = module.fetch;
60
- return nodeMiddleware;
265
+ devServer.middlewares.use(async (req, res, next) => {
266
+ await devServer.ssrLoadModule("@marko/run/router");
267
+ routerMiddleware(req, res, (err) => {
268
+ if (err) {
269
+ res.statusCode = 500;
270
+ if (err instanceof Error) {
271
+ devServer.ssrFixStacktrace(err);
272
+ res.end(err.stack && stripAnsi(err.stack));
273
+ } else {
274
+ res.end();
275
+ }
276
+ } else {
277
+ next == null ? void 0 : next();
278
+ }
279
+ });
280
+ });
281
+ return devServer;
282
+ }
283
+ var ClientIdCookieName = "marko-run-client-id";
284
+ function getClientId(req) {
285
+ if (req.headers.cookie) {
286
+ const cookie = req.headers.cookie.split(/;\s+/).find((c) => c.startsWith(ClientIdCookieName));
287
+ if (cookie) {
288
+ return cookie.slice(ClientIdCookieName.length + 1);
61
289
  }
290
+ }
291
+ }
292
+ var devGlobal;
293
+ function getDevGlobal() {
294
+ if (!devGlobal) {
295
+ let handleConnection2 = function(ws, req) {
296
+ if (callbacks == null ? void 0 : callbacks.length) {
297
+ const id = getClientId(req);
298
+ const now = Date.now();
299
+ const nextCallbacks = [];
300
+ for (const entry of callbacks) {
301
+ if (entry.id === id) {
302
+ entry.callback(ws);
303
+ } else if (entry.expires > now) {
304
+ nextCallbacks.push(entry);
305
+ }
306
+ }
307
+ callbacks = nextCallbacks;
308
+ }
309
+ };
310
+ var handleConnection = handleConnection2;
311
+ const devServers = /* @__PURE__ */ new Set();
312
+ let callbacks = [];
313
+ globalThis.__marko_run_dev__ = devGlobal = {
314
+ devServers,
315
+ addDevServer(devServer) {
316
+ const originalClose = devServer.close;
317
+ devServer.close = () => {
318
+ devServers.delete(devServer);
319
+ return originalClose.call(devServer);
320
+ };
321
+ devServers.add(devServer);
322
+ devServer.ws.on("connection", handleConnection2);
323
+ },
324
+ clear() {
325
+ callbacks = [];
326
+ for (const devServer of devServers) {
327
+ devServer.ws.off("connection", handleConnection2);
328
+ devServer.close();
329
+ }
330
+ },
331
+ onClient(callback) {
332
+ const expires = Date.now() + 1e3;
333
+ const id = Math.floor(Math.random() * expires).toString(36);
334
+ callbacks.push({
335
+ id,
336
+ expires,
337
+ callback
338
+ });
339
+ return (response) => {
340
+ response.headers.append(
341
+ "set-cookie",
342
+ `${ClientIdCookieName}=${id}; Path=/; Max-Age=100; HttpOnly`
343
+ );
344
+ };
345
+ }
346
+ };
347
+ }
348
+ return devGlobal;
349
+ }
350
+
351
+ // src/adapter/utils.ts
352
+ import supporsColor from "supports-color";
353
+ import kleur from "kleur";
354
+ function logInfoBox(address) {
355
+ const color = !!supporsColor.stdout;
356
+ let message = kleur.bold("Marko Run");
357
+ if (true) {
358
+ message += ` v${"0.2.8"}`;
359
+ }
360
+ message += "\n\n";
361
+ message += kleur.dim("Server listening at");
362
+ message += "\n";
363
+ message += kleur.cyan(kleur.underline(address));
364
+ const lines = drawMarkoBox(message, { color, fill: color });
365
+ console.log(lines.join("\n"));
366
+ }
367
+ function drawMarkoBox(message, options) {
368
+ const textPaddingWidth = 3;
369
+ const logoPaddingWidth = 2;
370
+ const textPadding = " ".repeat(textPaddingWidth);
371
+ const logoPadding = " ".repeat(logoPaddingWidth);
372
+ const logo = drawMarkoLogo(options);
373
+ const textLines = message.split(/\n/);
374
+ const textWidths = textLines.map(
375
+ (line) => line.replace(/\x1b\[\d+m/g, "").length
62
376
  );
63
- devServer.middlewares.use(middleware);
64
- return devServer;
377
+ const textWidth = Math.max(...textWidths);
378
+ const height = Math.max(textLines.length + 2, logo.lines.length);
379
+ const width = textPaddingWidth * 2 + logoPaddingWidth + textWidth + logo.width;
380
+ const hBorder = "\u2500".repeat(width);
381
+ const vBorder = "\u2502";
382
+ const lineDiff = logo.lines.length - textLines.length;
383
+ const textStartLine = lineDiff > 0 ? Math.floor(lineDiff / 2) : 1;
384
+ const textEndLine = height - (lineDiff > 0 ? Math.ceil(lineDiff / 2) : 1);
385
+ const logoEndLine = logo.lines.length;
386
+ const logoFill = " ".repeat(logo.width);
387
+ const textFill = " ".repeat(textWidth);
388
+ const lines = [`\u256D${hBorder}\u256E`];
389
+ for (let i = 0; i < height; i++) {
390
+ let line = vBorder;
391
+ line += logoPadding;
392
+ if (i < logoEndLine) {
393
+ line += logo.lines[i];
394
+ } else {
395
+ line += logoFill;
396
+ }
397
+ line += textPadding;
398
+ if (i >= textStartLine && i < textEndLine) {
399
+ let index = i - textStartLine;
400
+ line += textLines[index];
401
+ line += " ".repeat(textWidth - textWidths[index]);
402
+ } else {
403
+ line += textFill;
404
+ }
405
+ line += textPadding;
406
+ line += vBorder;
407
+ lines.push(line);
408
+ }
409
+ lines.push(`\u2570${hBorder}\u256F`);
410
+ return lines;
411
+ }
412
+ function drawMarkoLogo(options = {}) {
413
+ const { fill = true, color = true } = options;
414
+ const source = `
415
+ TT____ YY____ R____
416
+ C\u2571T\u2572 \u2572G\u2571Y\u2572 \u2572 R\u2572 \u2572
417
+ C\u2571 T\u2572 G\u2571 Y\u2572 \u2572 R\u2572 \u2572
418
+ C\u2571 \u2571T\u2572G\u2571 \u2571Y\u2572 \u2572 R\u2572 \u2572
419
+ B\u2572 \u2572 GG\u203E\u203E\u203E\u203E O\u2571 \u2571 P\u2571 \u2571
420
+ B\u2572 \u2572 OOO\u2571 \u2571 P\u2571 \u2571
421
+ B\u2572 \u2572 OOO\u2571 \u2571 P\u2571 \u2571
422
+ B\u203E\u203E\u203E\u203E OOO\u203E\u203E\u203E\u203E P\u203E\u203E\u203E\u203E
423
+ `;
424
+ const resetEscape = "\x1B[0m";
425
+ const colorEscapeCodes = Object.entries({
426
+ B: "#06cfe5",
427
+ C: "#05a5f0",
428
+ T: "#19d89c",
429
+ G: "#81dc09",
430
+ Y: "#ffd900",
431
+ O: "#ff9500",
432
+ R: "#f3154d",
433
+ P: "#ce176c"
434
+ }).reduce((acc, [key, hex]) => {
435
+ const r = parseInt(hex.slice(1, 3), 16);
436
+ const g = parseInt(hex.slice(3, 5), 16);
437
+ const b = parseInt(hex.slice(5, 7), 16);
438
+ acc[key] = `\x1B[38;2;${r};${g};${b}m`;
439
+ return acc;
440
+ }, {});
441
+ const lines = [];
442
+ const lineWidths = [];
443
+ let line = "";
444
+ let lineWidth = 0;
445
+ let width = 0;
446
+ for (let i = 0; i < source.length; i++) {
447
+ let char = source[i];
448
+ if (char === "\n") {
449
+ if (line) {
450
+ if (color) {
451
+ line += resetEscape;
452
+ }
453
+ width = Math.max(lineWidth, width);
454
+ lines.push(line);
455
+ lineWidths.push(lineWidth);
456
+ line = "";
457
+ lineWidth = 0;
458
+ }
459
+ } else if (/[A-Z]/.test(char)) {
460
+ while (source[i + 1] === char)
461
+ i++;
462
+ if (color) {
463
+ line += colorEscapeCodes[char];
464
+ }
465
+ if (fill) {
466
+ let fillChar = "";
467
+ for (; i < source.length; i++) {
468
+ char = source[i + 1];
469
+ if (fillChar && char !== " ") {
470
+ break;
471
+ } else if (!fillChar) {
472
+ fillChar = char;
473
+ }
474
+ line += fillChar;
475
+ lineWidth++;
476
+ }
477
+ }
478
+ } else {
479
+ line += char;
480
+ lineWidth++;
481
+ }
482
+ }
483
+ for (let i = 0; i < lines.length; i++) {
484
+ const padding = width - lineWidths[i];
485
+ if (padding > 0) {
486
+ lines[i] += " ".repeat(width - lineWidths[i]);
487
+ }
488
+ }
489
+ return { lines, width };
65
490
  }
66
491
 
67
492
  // src/vite/utils/server.ts
@@ -219,6 +644,7 @@ function adapter() {
219
644
  },
220
645
  async startDev(entry, config2, options) {
221
646
  const { port = 3e3, envFile } = options;
647
+ globalThis.__marko_run_vite_config__ = config2;
222
648
  if (entry) {
223
649
  const { nodeArgs } = parseNodeArgs(options.args);
224
650
  let worker;
@@ -262,7 +688,7 @@ function adapter() {
262
688
  return new Promise((resolve) => {
263
689
  const listener = devServer.middlewares.listen(port, () => {
264
690
  const address = listener.address();
265
- console.log(`Dev server started: http://localhost:${address.port}`);
691
+ logInfoBox(`http://localhost:${address.port}`);
266
692
  resolve({
267
693
  port,
268
694
  async close() {
@@ -277,15 +703,17 @@ function adapter() {
277
703
  const { nodeArgs } = parseNodeArgs(options.args);
278
704
  const args = [...nodeArgs, entry];
279
705
  const server = await spawnServer("node", args, port, envFile);
280
- console.log(`Preview server started: http://localhost:${server.port}`);
706
+ if (!options.sourceEntry) {
707
+ logInfoBox(`http://localhost:${port}`);
708
+ }
281
709
  return server;
282
710
  }
283
711
  };
284
712
  }
285
713
  export {
286
- activeDevServers,
287
714
  createDevServer,
288
715
  createViteDevMiddleware,
289
716
  createViteDevServer,
290
- adapter as default
717
+ adapter as default,
718
+ getDevGlobal
291
719
  };
@@ -1,6 +1,5 @@
1
1
  import { createServer } from "vite";
2
-
3
- let activeDevServers;
2
+ import { getDevGlobal } from "@marko/run/adapter";
4
3
 
5
4
  process
6
5
  .on("message", (message) => {
@@ -16,8 +15,12 @@ process
16
15
  async function start(entry, config) {
17
16
  globalThis.__marko_run_vite_config__ = config;
18
17
  let changed = false;
19
- const loader = await createServer({ ...config, ssr: { external: ['@marko/run/router'] }});
20
- ({ activeDevServers } = await import("@marko/run/adapter"));
18
+ const loader = await createServer({
19
+ ...config,
20
+ ssr: { external: ["@marko/run/router"] },
21
+ })
22
+
23
+ await loader.listen(0);
21
24
  await loader.ssrLoadModule(entry);
22
25
 
23
26
  loader.watcher.on("change", (path) => {
@@ -29,10 +32,9 @@ async function start(entry, config) {
29
32
  }
30
33
 
31
34
  function shutdown() {
32
- if (activeDevServers) {
33
- for (const devServer of activeDevServers) {
34
- devServer.ws.send({ type: "full-reload" });
35
- }
36
- activeDevServers.clear();
35
+ const devGlobal = getDevGlobal();
36
+ for (const devServer of devGlobal.devServers) {
37
+ devServer.ws.send({ type: "full-reload" });
37
38
  }
39
+ devGlobal.clear();
38
40
  }