@marko/run 0.2.7 → 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,6 +4,227 @@ 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
229
  function createViteDevMiddleware(devServer, load, factory) {
9
230
  let value;
@@ -38,22 +259,25 @@ async function createViteDevServer(config2) {
38
259
  }
39
260
  async function createDevServer(config2) {
40
261
  const devServer = await createViteDevServer(config2);
41
- const { createMiddleware } = await devServer.ssrLoadModule(
42
- "@marko/run/adapter/middleware"
43
- );
44
- let fetch;
45
- const nodeMiddleware = createMiddleware(
46
- (request, platform) => fetch(request, platform)
262
+ const routerMiddleware = createMiddleware(
263
+ (request, platform) => globalThis.__marko_run__.fetch(request, platform)
47
264
  );
48
- const middleware = createViteDevMiddleware(
49
- devServer,
50
- async () => await devServer.ssrLoadModule("@marko/run/router"),
51
- (module) => {
52
- fetch = module.fetch;
53
- return nodeMiddleware;
54
- }
55
- );
56
- devServer.middlewares.use(middleware);
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
+ });
57
281
  return devServer;
58
282
  }
59
283
  var ClientIdCookieName = "marko-run-client-id";
@@ -124,6 +348,147 @@ function getDevGlobal() {
124
348
  return devGlobal;
125
349
  }
126
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
376
+ );
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 };
490
+ }
491
+
127
492
  // src/vite/utils/server.ts
128
493
  import net from "net";
129
494
  import cp from "child_process";
@@ -279,6 +644,7 @@ function adapter() {
279
644
  },
280
645
  async startDev(entry, config2, options) {
281
646
  const { port = 3e3, envFile } = options;
647
+ globalThis.__marko_run_vite_config__ = config2;
282
648
  if (entry) {
283
649
  const { nodeArgs } = parseNodeArgs(options.args);
284
650
  let worker;
@@ -322,7 +688,7 @@ function adapter() {
322
688
  return new Promise((resolve) => {
323
689
  const listener = devServer.middlewares.listen(port, () => {
324
690
  const address = listener.address();
325
- console.log(`Dev server started: http://localhost:${address.port}`);
691
+ logInfoBox(`http://localhost:${address.port}`);
326
692
  resolve({
327
693
  port,
328
694
  async close() {
@@ -337,7 +703,9 @@ function adapter() {
337
703
  const { nodeArgs } = parseNodeArgs(options.args);
338
704
  const args = [...nodeArgs, entry];
339
705
  const server = await spawnServer("node", args, port, envFile);
340
- console.log(`Preview server started: http://localhost:${server.port}`);
706
+ if (!options.sourceEntry) {
707
+ logInfoBox(`http://localhost:${port}`);
708
+ }
341
709
  return server;
342
710
  }
343
711
  };
@@ -1,6 +1,5 @@
1
1
  import { createServer } from "vite";
2
-
3
- let getDevGlobal;
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
- ({ getDevGlobal } = 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,11 +32,9 @@ async function start(entry, config) {
29
32
  }
30
33
 
31
34
  function shutdown() {
32
- if (getDevGlobal) {
33
- const devGlobal = getDevGlobal();
34
- for (const devServer of devGlobal.devServers) {
35
- devServer.ws.send({ type: "full-reload" });
36
- }
37
- devGlobal.clear();
35
+ const devGlobal = getDevGlobal();
36
+ for (const devServer of devGlobal.devServers) {
37
+ devServer.ws.send({ type: "full-reload" });
38
38
  }
39
+ devGlobal.clear();
39
40
  }
@@ -147,7 +147,6 @@ function createMiddleware(fetch2, options = {}) {
147
147
  const { signal } = controller;
148
148
  const url = new URL(req.url, origin || getOrigin(req, trustProxy));
149
149
  const ip = req.ip || trustProxy && getForwardedHeader(req, "for") || req.socket.remoteAddress || "";
150
- const headers = req.headers;
151
150
  req.on("error", onErrorOrClose);
152
151
  req.socket.on("error", onErrorOrClose);
153
152
  res.on("error", onErrorOrClose);
@@ -192,10 +191,18 @@ function createMiddleware(fetch2, options = {}) {
192
191
  }
193
192
  });
194
193
  }
194
+ let body;
195
+ if (req.method !== "GET" && req.method !== "HEAD") {
196
+ if (req.readableDidRead) {
197
+ body = bodyConsumedErrorStream;
198
+ } else {
199
+ body = req;
200
+ }
201
+ }
195
202
  const request = new Request(url, {
196
203
  method: req.method,
197
- headers,
198
- body: req.method === "GET" || req.method === "HEAD" ? void 0 : req,
204
+ headers: req.headers,
205
+ body,
199
206
  // @ts-expect-error: Node requires this for streams
200
207
  duplex: "half",
201
208
  signal
@@ -247,6 +254,11 @@ async function writeResponse(reader, res, controller) {
247
254
  controller.abort(err);
248
255
  }
249
256
  }
257
+ var bodyConsumedErrorStream = new ReadableStream({
258
+ start(controller) {
259
+ controller.error(new Error("The request body stream was already consumed by something before Marko Run."));
260
+ }
261
+ });
250
262
  // Annotate the CommonJS export names for ESM import in node:
251
263
  0 && (module.exports = {
252
264
  createMiddleware,
@@ -104,7 +104,6 @@ function createMiddleware(fetch2, options = {}) {
104
104
  const { signal } = controller;
105
105
  const url = new URL(req.url, origin || getOrigin(req, trustProxy));
106
106
  const ip = req.ip || trustProxy && getForwardedHeader(req, "for") || req.socket.remoteAddress || "";
107
- const headers = req.headers;
108
107
  req.on("error", onErrorOrClose);
109
108
  req.socket.on("error", onErrorOrClose);
110
109
  res.on("error", onErrorOrClose);
@@ -149,10 +148,18 @@ function createMiddleware(fetch2, options = {}) {
149
148
  }
150
149
  });
151
150
  }
151
+ let body;
152
+ if (req.method !== "GET" && req.method !== "HEAD") {
153
+ if (req.readableDidRead) {
154
+ body = bodyConsumedErrorStream;
155
+ } else {
156
+ body = req;
157
+ }
158
+ }
152
159
  const request = new Request(url, {
153
160
  method: req.method,
154
- headers,
155
- body: req.method === "GET" || req.method === "HEAD" ? void 0 : req,
161
+ headers: req.headers,
162
+ body,
156
163
  // @ts-expect-error: Node requires this for streams
157
164
  duplex: "half",
158
165
  signal
@@ -204,6 +211,11 @@ async function writeResponse(reader, res, controller) {
204
211
  controller.abort(err);
205
212
  }
206
213
  }
214
+ var bodyConsumedErrorStream = new ReadableStream({
215
+ start(controller) {
216
+ controller.error(new Error("The request body stream was already consumed by something before Marko Run."));
217
+ }
218
+ });
207
219
  export {
208
220
  createMiddleware,
209
221
  getOrigin,
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+
16
+ // src/adapter/runtime.ts
17
+ var runtime_exports = {};
18
+ module.exports = __toCommonJS(runtime_exports);
19
+
20
+ // scripts/importMetaURL.js
21
+ var import_url = require("url");
22
+ var __importMetaURL = (0, import_url.pathToFileURL)(__filename);
@@ -0,0 +1 @@
1
+ export {};
File without changes
@@ -0,0 +1,10 @@
1
+ export declare function logInfoBox(address: string): void;
2
+ export declare function drawMarkoBox(message: string, options?: LogoOptions): string[];
3
+ export interface LogoOptions {
4
+ fill?: boolean;
5
+ color?: boolean;
6
+ }
7
+ export declare function drawMarkoLogo(options?: LogoOptions): {
8
+ lines: string[];
9
+ width: number;
10
+ };