@marko/run 0.1.4 → 0.1.6

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.
@@ -5,6 +5,7 @@ import { createMiddleware } from "@marko/run/adapter/middleware";
5
5
  import { fetch } from "@marko/run/router";
6
6
  import { dirname } from 'path';
7
7
  import { fileURLToPath } from 'url';
8
+ import zlib from 'zlib';
8
9
 
9
10
  const __dirname = dirname(fileURLToPath(import.meta.url));
10
11
 
@@ -12,6 +13,7 @@ const { PORT = 3456 } = process.env;
12
13
 
13
14
  const middleware = createMiddleware(fetch);
14
15
  const compress = compression({
16
+ flush: zlib.constants.Z_PARTIAL_FLUSH,
15
17
  threshold: 500,
16
18
  });
17
19
  const staticServe = createStaticServe(__dirname, {
@@ -0,0 +1,36 @@
1
+ import { createServer } from "vite";
2
+
3
+ let activeDevServers;
4
+
5
+ process
6
+ .on("message", (message) => {
7
+ switch (message.type) {
8
+ case "start":
9
+ return start(message.entry, message.config);
10
+ case "shutdown":
11
+ return shutdown();
12
+ }
13
+ })
14
+ .send("ready");
15
+
16
+ async function start(entry, config) {
17
+ let changed = false;
18
+ const loader = await createServer(config);
19
+ ({ activeDevServers } = await loader.ssrLoadModule("@marko/run/adapter"));
20
+ await loader.ssrLoadModule(entry);
21
+
22
+ loader.watcher.on("change", (path) => {
23
+ if (!changed && loader.moduleGraph.getModulesByFile(path)) {
24
+ changed = true;
25
+ process.send("restart");
26
+ }
27
+ });
28
+ }
29
+
30
+ function shutdown() {
31
+ if (activeDevServers) {
32
+ for (const devServer of activeDevServers) {
33
+ devServer.ws.send({ type: "full-reload" });
34
+ }
35
+ }
36
+ }
@@ -1,4 +1,6 @@
1
- import { ViteDevServer } from "vite";
1
+ import { type InlineConfig, type ViteDevServer } from "vite";
2
2
  import type { NodeMiddleware } from "./middleware";
3
+ export declare const activeDevServers: Set<ViteDevServer>;
3
4
  export declare function createViteDevMiddleware<T>(devServer: ViteDevServer, load: (prev: T | undefined) => Promise<T>, factory: (value: T) => NodeMiddleware): NodeMiddleware;
4
- export declare function createDevServer(configFile?: string): Promise<import("vite").Connect.Server>;
5
+ export declare function createViteDevServer(config?: InlineConfig): Promise<ViteDevServer>;
6
+ export declare function createDevServer(config?: InlineConfig): Promise<ViteDevServer>;
@@ -26,8 +26,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
26
26
  // src/adapter/index.ts
27
27
  var adapter_exports = {};
28
28
  __export(adapter_exports, {
29
+ activeDevServers: () => activeDevServers,
29
30
  createDevServer: () => createDevServer,
30
31
  createViteDevMiddleware: () => createViteDevMiddleware,
32
+ createViteDevServer: () => createViteDevServer,
31
33
  default: () => adapter
32
34
  });
33
35
  module.exports = __toCommonJS(adapter_exports);
@@ -37,6 +39,7 @@ var import_url = require("url");
37
39
  // src/adapter/dev-server.ts
38
40
  var import_vite = require("vite");
39
41
  var import_strip_ansi = __toESM(require("strip-ansi"), 1);
42
+ var activeDevServers = /* @__PURE__ */ new Set();
40
43
  function createViteDevMiddleware(devServer, load, factory) {
41
44
  let value;
42
45
  let middleware;
@@ -59,12 +62,22 @@ function createViteDevMiddleware(devServer, load, factory) {
59
62
  }
60
63
  };
61
64
  }
62
- async function createDevServer(configFile) {
65
+ async function createViteDevServer(config2) {
63
66
  const devServer = await (0, import_vite.createServer)({
64
- configFile,
67
+ ...config2,
65
68
  appType: "custom",
66
69
  server: { middlewareMode: true }
67
70
  });
71
+ const originalClose = devServer.close;
72
+ devServer.close = () => {
73
+ activeDevServers.delete(devServer);
74
+ return originalClose.call(devServer);
75
+ };
76
+ activeDevServers.add(devServer);
77
+ return devServer;
78
+ }
79
+ async function createDevServer(config2) {
80
+ const devServer = await createViteDevServer(config2);
68
81
  const { createMiddleware } = await devServer.ssrLoadModule(
69
82
  "@marko/run/adapter/middleware"
70
83
  );
@@ -73,7 +86,8 @@ async function createDevServer(configFile) {
73
86
  async () => await devServer.ssrLoadModule("@marko/run/router"),
74
87
  (module2) => createMiddleware(module2.fetch, { devServer })
75
88
  );
76
- return devServer.middlewares.use(middleware);
89
+ devServer.middlewares.use(middleware);
90
+ return devServer;
77
91
  }
78
92
 
79
93
  // src/vite/utils/server.ts
@@ -81,6 +95,7 @@ var import_net = __toESM(require("net"), 1);
81
95
  var import_child_process = __toESM(require("child_process"), 1);
82
96
  var import_dotenv = require("dotenv");
83
97
  var import_fs = __toESM(require("fs"), 1);
98
+ var import_cluster = __toESM(require("cluster"), 1);
84
99
  async function parseEnv(envFile) {
85
100
  if (import_fs.default.existsSync(envFile)) {
86
101
  const content = await import_fs.default.promises.readFile(envFile, "utf8");
@@ -90,14 +105,14 @@ async function parseEnv(envFile) {
90
105
  function loadEnv(envFile) {
91
106
  (0, import_dotenv.config)({ path: envFile });
92
107
  }
93
- async function spawnServer(cmd, port = 0, env, cwd = process.cwd(), wait = 3e4) {
108
+ async function spawnServer(cmd, args = [], port = 0, env, cwd = process.cwd(), wait = 3e4) {
94
109
  if (port <= 0) {
95
110
  port = await getAvailablePort();
96
111
  }
97
112
  if (typeof env === "string") {
98
113
  env = await parseEnv(env);
99
114
  }
100
- const proc = import_child_process.default.spawn(cmd, {
115
+ const proc = import_child_process.default.spawn(cmd, args, {
101
116
  cwd,
102
117
  shell: true,
103
118
  stdio: "inherit",
@@ -108,30 +123,67 @@ async function spawnServer(cmd, port = 0, env, cwd = process.cwd(), wait = 3e4)
108
123
  proc.unref();
109
124
  proc.kill();
110
125
  };
126
+ try {
127
+ await waitForServer(port, wait);
128
+ } catch (err) {
129
+ close();
130
+ throw err;
131
+ }
132
+ return {
133
+ port,
134
+ close
135
+ };
136
+ }
137
+ async function spawnServerWorker(module2, args = [], port = 0, env) {
138
+ if (port <= 0) {
139
+ port = await getAvailablePort();
140
+ }
141
+ if (typeof env === "string") {
142
+ env = await parseEnv(env);
143
+ }
144
+ const originalExec = import_cluster.default.settings.exec;
145
+ const originalArgs = import_cluster.default.settings.execArgv;
146
+ try {
147
+ import_cluster.default.settings.exec = module2;
148
+ import_cluster.default.settings.execArgv = args;
149
+ const worker = import_cluster.default.fork({ ...env, NODE_ENV: "development", ...process.env, PORT: `${port}` });
150
+ return new Promise((resolve) => {
151
+ function ready(message) {
152
+ if (message === "ready") {
153
+ worker.off("message", ready);
154
+ resolve(worker);
155
+ }
156
+ }
157
+ worker.on("message", ready);
158
+ });
159
+ } finally {
160
+ import_cluster.default.settings.exec = originalExec;
161
+ import_cluster.default.settings.execArgv = originalArgs;
162
+ }
163
+ }
164
+ async function waitForServer(port, wait = 0) {
111
165
  let remaining = wait > 0 ? wait : Infinity;
112
- while (!await isPortInUse(port)) {
166
+ let connection;
167
+ while (!(connection = await getConnection(port))) {
113
168
  if (remaining >= 100) {
114
169
  remaining -= 100;
115
170
  await sleep(100);
116
171
  } else {
117
- close();
118
172
  throw new Error(
119
173
  `site-write: timeout while wating for server to start on port "${port}".`
120
174
  );
121
175
  }
122
176
  }
123
- return {
124
- port,
125
- close
126
- };
177
+ return connection;
127
178
  }
128
- async function isPortInUse(port) {
179
+ async function getConnection(port) {
129
180
  return new Promise((resolve) => {
130
- const connection = import_net.default.connect(port).setNoDelay(true).setKeepAlive(false).on("error", () => done(false)).on("connect", () => done(true));
131
- function done(connected) {
181
+ const connection = import_net.default.connect(port).setNoDelay(true).setKeepAlive(false).on("error", () => {
132
182
  connection.end();
133
- resolve(connected);
134
- }
183
+ resolve(null);
184
+ }).on("connect", () => {
185
+ resolve(connection);
186
+ });
135
187
  });
136
188
  }
137
189
  async function getAvailablePort() {
@@ -147,34 +199,97 @@ function sleep(ms) {
147
199
  }
148
200
 
149
201
  // src/adapter/index.ts
202
+ var import_parse_node_args = __toESM(require("parse-node-args"), 1);
150
203
  var import_meta = {};
151
204
  var __dirname = (0, import_url.fileURLToPath)(new URL(".", import_meta.url));
205
+ var defaultEntry = import_path.default.join(__dirname, "default-entry");
206
+ var loadDevWorker = import_path.default.join(__dirname, "load-dev-worker.mjs");
152
207
  function adapter() {
153
208
  return {
154
209
  name: "base-adapter",
155
210
  async getEntryFile() {
156
- const entry = import_path.default.join(__dirname, "default-entry");
157
- return entry;
211
+ return defaultEntry;
158
212
  },
159
- async startDev(configFile, port, envFile) {
213
+ async startDev(entry, config2, options) {
214
+ const { port = 3e3, envFile } = options;
215
+ if (entry) {
216
+ const { nodeArgs } = (0, import_parse_node_args.default)(options.args);
217
+ let worker;
218
+ async function start() {
219
+ const nextWorker = await spawnServerWorker(
220
+ loadDevWorker,
221
+ nodeArgs,
222
+ port,
223
+ envFile
224
+ );
225
+ nextWorker.on("message", (messsage) => {
226
+ if (messsage === "restart") {
227
+ start();
228
+ }
229
+ }).send({ type: "start", entry, config: config2 });
230
+ await waitForWorker(nextWorker, port);
231
+ if (worker) {
232
+ const prevWorker = worker;
233
+ let timeout;
234
+ worker.once("disconnect", () => {
235
+ clearTimeout(timeout);
236
+ });
237
+ worker.send({ type: "shutdown" });
238
+ worker.disconnect();
239
+ timeout = setTimeout(() => {
240
+ prevWorker.kill();
241
+ }, 2e3);
242
+ }
243
+ worker = nextWorker;
244
+ }
245
+ await start();
246
+ return {
247
+ port,
248
+ close() {
249
+ worker.kill();
250
+ }
251
+ };
252
+ }
253
+ const devServer = await createDevServer(config2);
160
254
  envFile && await loadEnv(envFile);
161
- const server = await createDevServer(configFile);
162
255
  return new Promise((resolve) => {
163
- const listener = server.listen(port, () => {
256
+ const listener = devServer.middlewares.listen(port, () => {
164
257
  const address = listener.address();
165
258
  console.log(`Dev server started: http://localhost:${address.port}`);
166
- resolve();
259
+ resolve({
260
+ port,
261
+ async close() {
262
+ await devServer.close();
263
+ }
264
+ });
167
265
  });
168
266
  });
169
267
  },
170
- async startPreview(_dir, entry, port, envFile) {
171
- const server = await spawnServer(`node ${entry}`, port, envFile);
268
+ async startPreview(entry, options) {
269
+ const { port, envFile } = options;
270
+ const { nodeArgs } = (0, import_parse_node_args.default)(options.args);
271
+ const args = [...nodeArgs, entry];
272
+ const server = await spawnServer("node", args, port, envFile);
172
273
  console.log(`Preview server started: http://localhost:${server.port}`);
274
+ return server;
173
275
  }
174
276
  };
175
277
  }
278
+ async function waitForWorker(worker, port) {
279
+ return new Promise((resolve) => {
280
+ function listening(address) {
281
+ if (address.port === port) {
282
+ worker.off("listening", listening);
283
+ resolve();
284
+ }
285
+ }
286
+ worker.on("listening", listening);
287
+ });
288
+ }
176
289
  // Annotate the CommonJS export names for ESM import in node:
177
290
  0 && (module.exports = {
291
+ activeDevServers,
178
292
  createDevServer,
179
- createViteDevMiddleware
293
+ createViteDevMiddleware,
294
+ createViteDevServer
180
295
  });
@@ -1,5 +1,6 @@
1
1
  import type { Adapter } from "../vite";
2
- export { createDevServer, createViteDevMiddleware } from "./dev-server";
3
- export type { Adapter };
4
- export type { NodePlatformInfo } from './middleware';
2
+ import { type SpawnedServer } from "../vite/utils/server";
3
+ export { activeDevServers, createDevServer, createViteDevServer, createViteDevMiddleware, } from "./dev-server";
4
+ export type { Adapter, SpawnedServer };
5
+ export type { NodePlatformInfo } from "./middleware";
5
6
  export default function adapter(): Adapter;
@@ -5,6 +5,7 @@ import { fileURLToPath } from "url";
5
5
  // src/adapter/dev-server.ts
6
6
  import { createServer } from "vite";
7
7
  import stripAnsi from "strip-ansi";
8
+ var activeDevServers = /* @__PURE__ */ new Set();
8
9
  function createViteDevMiddleware(devServer, load, factory) {
9
10
  let value;
10
11
  let middleware;
@@ -27,12 +28,22 @@ function createViteDevMiddleware(devServer, load, factory) {
27
28
  }
28
29
  };
29
30
  }
30
- async function createDevServer(configFile) {
31
+ async function createViteDevServer(config2) {
31
32
  const devServer = await createServer({
32
- configFile,
33
+ ...config2,
33
34
  appType: "custom",
34
35
  server: { middlewareMode: true }
35
36
  });
37
+ const originalClose = devServer.close;
38
+ devServer.close = () => {
39
+ activeDevServers.delete(devServer);
40
+ return originalClose.call(devServer);
41
+ };
42
+ activeDevServers.add(devServer);
43
+ return devServer;
44
+ }
45
+ async function createDevServer(config2) {
46
+ const devServer = await createViteDevServer(config2);
36
47
  const { createMiddleware } = await devServer.ssrLoadModule(
37
48
  "@marko/run/adapter/middleware"
38
49
  );
@@ -41,7 +52,8 @@ async function createDevServer(configFile) {
41
52
  async () => await devServer.ssrLoadModule("@marko/run/router"),
42
53
  (module) => createMiddleware(module.fetch, { devServer })
43
54
  );
44
- return devServer.middlewares.use(middleware);
55
+ devServer.middlewares.use(middleware);
56
+ return devServer;
45
57
  }
46
58
 
47
59
  // src/vite/utils/server.ts
@@ -49,6 +61,7 @@ import net from "net";
49
61
  import cp from "child_process";
50
62
  import { parse, config } from "dotenv";
51
63
  import fs from "fs";
64
+ import cluster from "cluster";
52
65
  async function parseEnv(envFile) {
53
66
  if (fs.existsSync(envFile)) {
54
67
  const content = await fs.promises.readFile(envFile, "utf8");
@@ -58,14 +71,14 @@ async function parseEnv(envFile) {
58
71
  function loadEnv(envFile) {
59
72
  config({ path: envFile });
60
73
  }
61
- async function spawnServer(cmd, port = 0, env, cwd = process.cwd(), wait = 3e4) {
74
+ async function spawnServer(cmd, args = [], port = 0, env, cwd = process.cwd(), wait = 3e4) {
62
75
  if (port <= 0) {
63
76
  port = await getAvailablePort();
64
77
  }
65
78
  if (typeof env === "string") {
66
79
  env = await parseEnv(env);
67
80
  }
68
- const proc = cp.spawn(cmd, {
81
+ const proc = cp.spawn(cmd, args, {
69
82
  cwd,
70
83
  shell: true,
71
84
  stdio: "inherit",
@@ -76,30 +89,67 @@ async function spawnServer(cmd, port = 0, env, cwd = process.cwd(), wait = 3e4)
76
89
  proc.unref();
77
90
  proc.kill();
78
91
  };
92
+ try {
93
+ await waitForServer(port, wait);
94
+ } catch (err) {
95
+ close();
96
+ throw err;
97
+ }
98
+ return {
99
+ port,
100
+ close
101
+ };
102
+ }
103
+ async function spawnServerWorker(module, args = [], port = 0, env) {
104
+ if (port <= 0) {
105
+ port = await getAvailablePort();
106
+ }
107
+ if (typeof env === "string") {
108
+ env = await parseEnv(env);
109
+ }
110
+ const originalExec = cluster.settings.exec;
111
+ const originalArgs = cluster.settings.execArgv;
112
+ try {
113
+ cluster.settings.exec = module;
114
+ cluster.settings.execArgv = args;
115
+ const worker = cluster.fork({ ...env, NODE_ENV: "development", ...process.env, PORT: `${port}` });
116
+ return new Promise((resolve) => {
117
+ function ready(message) {
118
+ if (message === "ready") {
119
+ worker.off("message", ready);
120
+ resolve(worker);
121
+ }
122
+ }
123
+ worker.on("message", ready);
124
+ });
125
+ } finally {
126
+ cluster.settings.exec = originalExec;
127
+ cluster.settings.execArgv = originalArgs;
128
+ }
129
+ }
130
+ async function waitForServer(port, wait = 0) {
79
131
  let remaining = wait > 0 ? wait : Infinity;
80
- while (!await isPortInUse(port)) {
132
+ let connection;
133
+ while (!(connection = await getConnection(port))) {
81
134
  if (remaining >= 100) {
82
135
  remaining -= 100;
83
136
  await sleep(100);
84
137
  } else {
85
- close();
86
138
  throw new Error(
87
139
  `site-write: timeout while wating for server to start on port "${port}".`
88
140
  );
89
141
  }
90
142
  }
91
- return {
92
- port,
93
- close
94
- };
143
+ return connection;
95
144
  }
96
- async function isPortInUse(port) {
145
+ async function getConnection(port) {
97
146
  return new Promise((resolve) => {
98
- const connection = net.connect(port).setNoDelay(true).setKeepAlive(false).on("error", () => done(false)).on("connect", () => done(true));
99
- function done(connected) {
147
+ const connection = net.connect(port).setNoDelay(true).setKeepAlive(false).on("error", () => {
100
148
  connection.end();
101
- resolve(connected);
102
- }
149
+ resolve(null);
150
+ }).on("connect", () => {
151
+ resolve(connection);
152
+ });
103
153
  });
104
154
  }
105
155
  async function getAvailablePort() {
@@ -115,33 +165,96 @@ function sleep(ms) {
115
165
  }
116
166
 
117
167
  // src/adapter/index.ts
168
+ import parseNodeArgs from "parse-node-args";
118
169
  var __dirname = fileURLToPath(new URL(".", import.meta.url));
170
+ var defaultEntry = path.join(__dirname, "default-entry");
171
+ var loadDevWorker = path.join(__dirname, "load-dev-worker.mjs");
119
172
  function adapter() {
120
173
  return {
121
174
  name: "base-adapter",
122
175
  async getEntryFile() {
123
- const entry = path.join(__dirname, "default-entry");
124
- return entry;
176
+ return defaultEntry;
125
177
  },
126
- async startDev(configFile, port, envFile) {
178
+ async startDev(entry, config2, options) {
179
+ const { port = 3e3, envFile } = options;
180
+ if (entry) {
181
+ const { nodeArgs } = parseNodeArgs(options.args);
182
+ let worker;
183
+ async function start() {
184
+ const nextWorker = await spawnServerWorker(
185
+ loadDevWorker,
186
+ nodeArgs,
187
+ port,
188
+ envFile
189
+ );
190
+ nextWorker.on("message", (messsage) => {
191
+ if (messsage === "restart") {
192
+ start();
193
+ }
194
+ }).send({ type: "start", entry, config: config2 });
195
+ await waitForWorker(nextWorker, port);
196
+ if (worker) {
197
+ const prevWorker = worker;
198
+ let timeout;
199
+ worker.once("disconnect", () => {
200
+ clearTimeout(timeout);
201
+ });
202
+ worker.send({ type: "shutdown" });
203
+ worker.disconnect();
204
+ timeout = setTimeout(() => {
205
+ prevWorker.kill();
206
+ }, 2e3);
207
+ }
208
+ worker = nextWorker;
209
+ }
210
+ await start();
211
+ return {
212
+ port,
213
+ close() {
214
+ worker.kill();
215
+ }
216
+ };
217
+ }
218
+ const devServer = await createDevServer(config2);
127
219
  envFile && await loadEnv(envFile);
128
- const server = await createDevServer(configFile);
129
220
  return new Promise((resolve) => {
130
- const listener = server.listen(port, () => {
221
+ const listener = devServer.middlewares.listen(port, () => {
131
222
  const address = listener.address();
132
223
  console.log(`Dev server started: http://localhost:${address.port}`);
133
- resolve();
224
+ resolve({
225
+ port,
226
+ async close() {
227
+ await devServer.close();
228
+ }
229
+ });
134
230
  });
135
231
  });
136
232
  },
137
- async startPreview(_dir, entry, port, envFile) {
138
- const server = await spawnServer(`node ${entry}`, port, envFile);
233
+ async startPreview(entry, options) {
234
+ const { port, envFile } = options;
235
+ const { nodeArgs } = parseNodeArgs(options.args);
236
+ const args = [...nodeArgs, entry];
237
+ const server = await spawnServer("node", args, port, envFile);
139
238
  console.log(`Preview server started: http://localhost:${server.port}`);
239
+ return server;
140
240
  }
141
241
  };
142
242
  }
243
+ async function waitForWorker(worker, port) {
244
+ return new Promise((resolve) => {
245
+ function listening(address) {
246
+ if (address.port === port) {
247
+ worker.off("listening", listening);
248
+ resolve();
249
+ }
250
+ }
251
+ worker.on("listening", listening);
252
+ });
253
+ }
143
254
  export {
255
+ activeDevServers,
144
256
  createDevServer,
145
257
  createViteDevMiddleware,
258
+ createViteDevServer,
146
259
  adapter as default
147
260
  };
@@ -0,0 +1,37 @@
1
+ import { createServer } from "vite";
2
+
3
+ let activeDevServers;
4
+
5
+ process
6
+ .on("message", (message) => {
7
+ switch (message.type) {
8
+ case "start":
9
+ return start(message.entry, message.config);
10
+ case "shutdown":
11
+ return shutdown();
12
+ }
13
+ })
14
+ .send("ready");
15
+
16
+ async function start(entry, config) {
17
+ let changed = false;
18
+ const loader = await createServer(config);
19
+ ({ activeDevServers } = await loader.ssrLoadModule("@marko/run/adapter"));
20
+ await loader.ssrLoadModule(entry);
21
+
22
+ loader.watcher.on("change", (path) => {
23
+ if (!changed && loader.moduleGraph.getModulesByFile(path)) {
24
+ changed = true;
25
+ process.send("restart");
26
+ }
27
+ });
28
+ }
29
+
30
+ function shutdown() {
31
+ if (activeDevServers) {
32
+ for (const devServer of activeDevServers) {
33
+ devServer.ws.send({ type: "full-reload" });
34
+ }
35
+ activeDevServers.clear();
36
+ }
37
+ }