mono-jsx-dom 0.1.4 → 0.1.5

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.
package/bin/build.mjs CHANGED
@@ -60,11 +60,11 @@ async function run() {
60
60
  const flags = parseFlags();
61
61
  const start = performance.now();
62
62
  const runtime = flags.node ?? "fetch-server";
63
- await build({ runtime });
63
+ await build({ serverType: runtime });
64
64
  console.log("\x1B[32m\u2728 Build completed.\x1B[0m", "\x1B[90m(%d ms)\x1B[0m", performance.now() - start);
65
65
  }
66
66
  async function build(options) {
67
- const workDir = join(cwd(), options?.dir ?? ".");
67
+ const workDir = join(cwd(), options?.appName ?? ".");
68
68
  const outdir = join(workDir, options?.outdir ?? "dist");
69
69
  if (!await exists(join(workDir, "index.html"))) {
70
70
  console.error("index.html not found");
@@ -159,7 +159,9 @@ async function build(options) {
159
159
  for (const file of outputFiles) {
160
160
  const contentType = file.path.endsWith(".js") ? "application/javascript" : "text/css";
161
161
  const filename = relative(outdir, file.path);
162
- vfs[filename] = await createVFile(indexHTML, filename, file.text, contentType);
162
+ if (filename !== tw?.entryCSS) {
163
+ vfs[filename] = await createVFile(indexHTML, filename, file.text, contentType);
164
+ }
163
165
  }
164
166
  if (tw) {
165
167
  const css = await tw.build();
@@ -173,10 +175,10 @@ async function build(options) {
173
175
  'import buildJSON$ from "./build.json" with { type: "json" };',
174
176
  "server$.setVFS(new Map(Object.entries(buildJSON$)));"
175
177
  ].join("");
176
- await buildServerJS(workDir, outdir, options?.runtime, extraJS);
178
+ await buildServerJS(workDir, outdir, options?.serverType, extraJS);
177
179
  await dispose();
178
180
  }
179
- async function buildServerJS(workDir, outdir, runtime = "fetch-server", extraJS, watch) {
181
+ async function buildServerJS(workDir, outdir, serverType = "fetch-server", extraJS, watch) {
180
182
  const stdin2 = {
181
183
  sourcefile: join(workDir, "server.js"),
182
184
  contents: 'import server from "mono-jsx-dom/server;export default server;',
@@ -185,7 +187,7 @@ async function buildServerJS(workDir, outdir, runtime = "fetch-server", extraJS,
185
187
  for (const loader of ["ts", "tsx", "js", "jsx"]) {
186
188
  const sourcefile = join(workDir, "server." + loader);
187
189
  if (await exists(sourcefile)) {
188
- if (runtime === "node") {
190
+ if (serverType === "node") {
189
191
  stdin2.sourcefile = join(workDir, "server-node.mjs");
190
192
  stdin2.resolveDir = workDir;
191
193
  stdin2.contents = [
@@ -235,6 +237,7 @@ async function buildServerJS(workDir, outdir, runtime = "fetch-server", extraJS,
235
237
  } else {
236
238
  await ctx.rebuild();
237
239
  await ctx.dispose();
240
+ await esbuild.stop();
238
241
  }
239
242
  }
240
243
  function initTailwindBuilder(workDir, entryCSS) {
@@ -297,7 +300,7 @@ async function paseIndexHtml(workDir) {
297
300
  }
298
301
  const relativePath = relative(workDir, join(workDir, href));
299
302
  entryPoints[relativePath] = relativePath;
300
- return `<link${attrs} href="/${relativePath}">`;
303
+ return `<link${attrs}href="/${relativePath}">`;
301
304
  });
302
305
  content = content.replace(/<script(\s[^>]*?)src="([^"]+\.(ts|tsx|js|jsx|mjs))"\s*>/g, (tag, attrs, src) => {
303
306
  if (isUrl(src)) {
@@ -306,7 +309,7 @@ async function paseIndexHtml(workDir) {
306
309
  const relativePath = relative(workDir, join(workDir, src));
307
310
  const resolvedPath = relativePath.slice(0, relativePath.lastIndexOf(".")) + ".js";
308
311
  entryPoints[relativePath] = resolvedPath;
309
- return `<script${attrs} src="/${resolvedPath}">`;
312
+ return `<script${attrs}src="/${resolvedPath}">`;
310
313
  });
311
314
  return { content, entryPoints };
312
315
  }
@@ -318,7 +321,7 @@ async function createVFile(indexHTML, filename, content, contentType) {
318
321
  if (ext !== ".css" && ext !== ".js") {
319
322
  filename = filename.slice(0, -ext.length) + ".js";
320
323
  }
321
- indexHTML.content = indexHTML.content.replace('"/' + filename + '"', '"/' + filename + "?hash=" + hash.slice(0, 8) + '"');
324
+ indexHTML.content = indexHTML.content.replace('"/' + filename + '"', '"/' + filename + "?hash=" + contentHash.slice(0, 8) + '"');
322
325
  }
323
326
  return {
324
327
  content,
package/bin/cli CHANGED
@@ -10,7 +10,7 @@ switch (argv[2]) {
10
10
  import("./dev.mjs").then(command => command.run())
11
11
  break;
12
12
  case "build": {
13
- import("./build.mjs").then(command => command.run( ))
13
+ import("./build.mjs").then(command => command.run())
14
14
  break;
15
15
  }
16
16
  default:
package/bin/dev.mjs CHANGED
@@ -50,9 +50,9 @@ async function exists(filename) {
50
50
  // bin/dev.ts
51
51
  import { build } from "./build.mjs";
52
52
  async function dev(options) {
53
- const ac = new AbortController();
54
- const isBun = "Bun" in globalThis;
55
- const workDir = join(cwd(), options?.dir ?? ".");
53
+ const runtime = "Deno" in globalThis ? "deno" : "Bun" in globalThis ? "bun" : "node";
54
+ const ac = options?.ac ?? new AbortController();
55
+ const workDir = join(cwd(), options?.appName ?? ".");
56
56
  const outdir = join(workDir, options?.outdir ?? "dist");
57
57
  const port = options?.port ?? 3e3;
58
58
  const devServerPort = 8798;
@@ -77,8 +77,11 @@ async function dev(options) {
77
77
  });
78
78
  await serve({
79
79
  port: devServerPort,
80
+ hostname: "localhost",
80
81
  idleTimeout: 32,
81
82
  // 32 seconds
83
+ onListen: (_localAddress) => {
84
+ },
82
85
  fetch: async (req) => {
83
86
  const { pathname } = new URL(req.url);
84
87
  if (pathname === "/__dev_vfs.json") {
@@ -162,30 +165,49 @@ async function dev(options) {
162
165
  }
163
166
  });
164
167
  const serverScript = await resolveModule(join(workDir, "server"), [".ts", ".mjs", ".js"]) ?? join(workDir, "node_modules/mono-jsx-dom/server/index.mjs");
168
+ const args = ["--watch", serverScript];
169
+ if (runtime === "node") {
170
+ args.push("--port", port.toString());
171
+ } else {
172
+ args.unshift("--port", port.toString());
173
+ }
174
+ if (runtime === "deno") {
175
+ args.unshift("serve", "-A");
176
+ }
165
177
  const serverProcess = spawn(
166
- isBun ? "bun" : "node",
167
- ["--watch", serverScript, "--port", port.toString()],
178
+ runtime,
179
+ args,
168
180
  {
169
181
  cwd: workDir,
170
182
  env: { ...env, DEV_SERVER: devServerUrl },
171
183
  stdio: "inherit"
172
184
  }
173
185
  );
174
- serverProcess.on("close", () => ac.abort());
186
+ const onClose = () => ac.abort();
187
+ serverProcess.on("close", onClose);
188
+ ac.signal.addEventListener("abort", () => {
189
+ serverProcess.off("close", onClose);
190
+ serverProcess.kill();
191
+ });
175
192
  };
176
193
  const onError = (error) => {
177
194
  console.error(error);
178
195
  ac.abort();
179
196
  };
180
- if (!isBun) {
197
+ if (runtime === "node") {
181
198
  const [major, minor] = versions.node.split(".").map(Number);
182
199
  if (major < 22 || major === 22 && minor < 18) {
183
200
  console.error("Node.js version 22.18.0 or higher is required to use the dev command.");
184
201
  exit2(1);
185
202
  }
186
203
  }
204
+ process.on("SIGINT", () => {
205
+ console.log("\x1B[90mShutting down dev server...\x1B[0m");
206
+ ac.abort();
207
+ exit2(0);
208
+ });
187
209
  await build({
188
- dir: options?.dir,
210
+ appName: options?.appName,
189
211
  outdir: options?.outdir,
190
212
  dev: {
191
213
  signal: ac.signal,
@@ -194,9 +216,23 @@ async function dev(options) {
194
216
  }).catch(onError);
195
217
  }
196
218
  async function serve(options) {
197
- const serve2 = globalThis.Bun?.serve;
198
- if (serve2) {
199
- const server = serve2(options);
219
+ const denoServe = globalThis.Deno?.serve;
220
+ const bunServe = globalThis.Bun?.serve;
221
+ if (denoServe) {
222
+ const { fetch, port, signal, hostname, onListen } = options;
223
+ denoServe(
224
+ {
225
+ port,
226
+ signal,
227
+ hostname,
228
+ onListen: onListen ?? ((localAddress) => {
229
+ console.log(`Server is running on http://${hostname ?? "localhost"}:${localAddress.port}`);
230
+ })
231
+ },
232
+ fetch
233
+ );
234
+ } else if (bunServe) {
235
+ const server = bunServe(options);
200
236
  options.signal?.addEventListener("abort", () => server.stop());
201
237
  } else {
202
238
  await import("../server/node-fetch-server.mjs").then((m) => m.serve(options));
package/bin/init.mjs CHANGED
@@ -69,6 +69,7 @@ async function input(prompt, placeholder = "") {
69
69
  return text;
70
70
  } finally {
71
71
  stdin.setRawMode(wasRaw ?? false);
72
+ stdin.pause();
72
73
  }
73
74
  }
74
75
  const rl = createInterface({ input: stdin, output: stdout });
@@ -89,6 +90,7 @@ async function confirm(prompt) {
89
90
  stdout.write(hint);
90
91
  const wasRaw = stdin.isRaw;
91
92
  stdin.setRawMode(true);
93
+ stdin.resume();
92
94
  try {
93
95
  const yes = await new Promise((resolve) => {
94
96
  stdin.once("data", (buf) => {
@@ -117,6 +119,7 @@ async function confirm(prompt) {
117
119
  return yes;
118
120
  } finally {
119
121
  stdin.setRawMode(wasRaw ?? false);
122
+ stdin.pause();
120
123
  }
121
124
  }
122
125
  const rl = createInterface({ input: stdin, output: stdout });
@@ -164,8 +167,8 @@ var template = {
164
167
  version: "0.0.0",
165
168
  private: true,
166
169
  scripts: {
167
- dev: (bun ? "bun --bun" : "") + " mono-jsx-dom dev",
168
- build: (bun ? "bun --bun" : "") + " mono-jsx-dom build",
170
+ dev: (bun ? "bun --bun " : "") + "mono-jsx-dom dev",
171
+ build: (bun ? "bun --bun " : "") + "mono-jsx-dom build",
169
172
  start: bun ? "bun --bun mono-jsx-dom build && bun dist/server.mjs" : "mono-jsx-dom build --node && node dist/server.mjs"
170
173
  }
171
174
  },
@@ -222,17 +225,40 @@ export default {
222
225
  };
223
226
  async function run() {
224
227
  const appName = argv2[3] ?? await input("Enter the name of the app", "mono-app");
225
- return init(appName);
226
- }
227
- async function init(appName) {
228
- const appDir = join(cwd(), appName);
229
- if (await exists(appDir) && !await confirm(`Directory ${appName} already exists. Overwrite?`)) {
228
+ const dir = join(cwd(), appName);
229
+ if (await exists(dir) && !await confirm(`Directory ${appName} already exists. Overwrite?`)) {
230
230
  return;
231
231
  }
232
+ const tailwindCSS = await confirm("Use TailwindCSS for styling?");
233
+ const wrangler = await confirm("Add Cloudflare Workers integration?");
234
+ const npmCmd = bun ? "bun" : "npm";
235
+ await init({
236
+ dir,
237
+ appName,
238
+ tailwindCSS,
239
+ wrangler,
240
+ onInstall: () => {
241
+ console.log("\x1B[90mInstalling dependencies...\x1B[0m");
242
+ }
243
+ });
244
+ console.log("");
245
+ console.log("\u2728 \x1B[32mSetup completed.\x1B[0m");
246
+ console.log("You can now start or build the app with the following commands:");
247
+ console.log("");
248
+ console.log(`cd ${appName}`);
249
+ console.log(`${npmCmd} dev${bun ? " " : ""} \x1B[90m# start the app in development mode.\x1B[0m`);
250
+ if (wrangler) {
251
+ console.log(`${npmCmd}${bun ? " run" : ""} deploy \x1B[90m# deploy the app to Cloudflare Workers.\x1B[0m`);
252
+ } else {
253
+ console.log(`${npmCmd}${bun ? " run" : ""} build \x1B[90m# build the app for production.\x1B[0m`);
254
+ console.log(`${npmCmd} start${bun ? " " : ""} \x1B[90m# build and start the app in production mode.\x1B[0m`);
255
+ }
256
+ console.log("");
257
+ }
258
+ async function init(options) {
259
+ const { dir, appName, tailwindCSS, wrangler } = options;
232
260
  const scaffold = { ...template };
233
- const withTailwind = await confirm("Use TailwindCSS for styling?");
234
- const withWrangler = await confirm("Add Cloudflare Workers integration?");
235
- if (withWrangler) {
261
+ if (wrangler) {
236
262
  scaffold["wrangler.jsonc"] = JSON.stringify(
237
263
  {
238
264
  $schema: "./node_modules/wrangler/config-schema.json",
@@ -251,17 +277,17 @@ async function init(appName) {
251
277
  2
252
278
  );
253
279
  }
254
- if (!withTailwind) {
280
+ if (!tailwindCSS) {
255
281
  scaffold["app/style.css"] = "/* app styles */\n";
256
282
  }
257
- await ensureDir(appDir);
283
+ await ensureDir(dir);
258
284
  await Promise.all(
259
285
  Object.entries(scaffold).map(async ([filename, content]) => {
260
- const filepath = join(appDir, filename);
286
+ const filepath = join(dir, filename);
261
287
  if (filename === "package.json") {
262
288
  const pkg = JSON.parse(content);
263
289
  pkg.name = appName;
264
- if (withWrangler) {
290
+ if (wrangler) {
265
291
  pkg.scripts.dev = "wrangler dev";
266
292
  pkg.scripts.deploy = "wrangler deploy";
267
293
  delete pkg.scripts.build;
@@ -277,7 +303,7 @@ async function init(appName) {
277
303
  );
278
304
  let tsConfig = /* @__PURE__ */ Object.create(null);
279
305
  try {
280
- const data = await readFile(join(appDir, "tsconfig.json"), "utf8");
306
+ const data = await readFile(join(dir, "tsconfig.json"), "utf8");
281
307
  tsConfig = JSON.parse(data);
282
308
  } catch {
283
309
  }
@@ -289,44 +315,40 @@ async function init(appName) {
289
315
  compilerOptions.noEmit ??= true;
290
316
  compilerOptions.jsx = "react-jsx";
291
317
  compilerOptions.jsxImportSource = "mono-jsx-dom";
292
- await writeFile(join(appDir, "tsconfig.json"), JSON.stringify(tsConfig, null, 2));
293
- console.log("\x1B[90mInstalling dependencies...\x1B[0m");
294
- const cmd = install(appDir, withTailwind, withWrangler);
295
- const isBun = cmd === "bun";
296
- console.log("");
297
- console.log("\u2728 \x1B[32mSetup completed.\x1B[0m");
298
- console.log("You can now start or build the app with the following commands:");
299
- console.log("");
300
- console.log(`cd ${appName}`);
301
- console.log(`${cmd} dev${isBun ? " " : ""} \x1B[90m# start the app in development mode.\x1B[0m`);
302
- if (withWrangler) {
303
- console.log(`${cmd}${isBun ? " run" : ""} deploy \x1B[90m# deploy the app to Cloudflare Workers.\x1B[0m`);
304
- } else {
305
- console.log(`${cmd}${isBun ? " run" : ""} build \x1B[90m# build the app for production.\x1B[0m`);
306
- console.log(`${cmd} start${isBun ? " " : ""} \x1B[90m# build and start the app in production mode.\x1B[0m`);
307
- }
308
- console.log("");
318
+ await writeFile(join(dir, "tsconfig.json"), JSON.stringify(tsConfig, null, 2));
319
+ const depsOptions = { tailwindCSS, wrangler };
320
+ options.onInstall?.(depsOptions);
321
+ installDependencies(dir, depsOptions);
309
322
  }
310
- function install(cwd2, withTailwind, withWrangler) {
311
- let cmd = "npm";
312
- if (bun) {
313
- cmd = "bun";
314
- }
315
- spawnSync(cmd, ["add", "mono-jsx-dom"], { cwd: cwd2 });
323
+ function installDependencies(cwd2, options) {
324
+ const { tailwindCSS, wrangler, extraDependencies, extraDevDependencies } = options;
325
+ const npmCmd = bun ? "bun" : "npm";
326
+ const deps = ["mono-jsx-dom"];
316
327
  const devDeps = ["esbuild"];
317
- if (withTailwind) {
328
+ if (tailwindCSS) {
318
329
  devDeps.push("tailwindcss", "oxide-wasm");
319
330
  }
320
- if (withWrangler) {
331
+ if (wrangler) {
321
332
  devDeps.push("wrangler");
322
333
  }
323
- spawnSync(cmd, ["add", "-D", ...devDeps], { cwd: cwd2 });
324
- if (withWrangler) {
325
- spawnSync(cmd, ["wrangler", "types"], { cwd: cwd2 });
334
+ if (extraDependencies) {
335
+ deps.push(...extraDependencies);
336
+ }
337
+ if (extraDevDependencies) {
338
+ devDeps.push(...extraDevDependencies);
339
+ }
340
+ spawnSync(npmCmd, ["add", ...deps], { cwd: cwd2 });
341
+ spawnSync(npmCmd, ["add", "-D", ...devDeps], { cwd: cwd2 });
342
+ if (wrangler) {
343
+ if (npmCmd === "bun") {
344
+ spawnSync(npmCmd, ["--bun", "wrangler", "types"], { cwd: cwd2 });
345
+ } else {
346
+ spawnSync(npmCmd, ["wrangler", "types"], { cwd: cwd2 });
347
+ }
326
348
  }
327
- return cmd;
328
349
  }
329
350
  export {
330
351
  init,
352
+ installDependencies,
331
353
  run
332
354
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mono-jsx-dom",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "A JSX runtime for browsers.",
5
5
  "keywords": [
6
6
  "jsx",
@@ -115,12 +115,16 @@ async function sendResponse(res, response) {
115
115
  res.end();
116
116
  }
117
117
  function serve(options) {
118
- const port = options?.port ?? getDefaultPort();
119
- const server = createServer(createRequestListener(options.fetch, options));
118
+ const { port = getDefaultPort(), hostname, signal, fetch, onListen } = options;
119
+ const server = createServer(createRequestListener(fetch, options));
120
120
  server.listen(port, options?.hostname, () => {
121
- console.log(`Server is running on http://${options?.hostname ?? "localhost"}:${port}`);
121
+ if (onListen) {
122
+ onListen({ port });
123
+ } else {
124
+ console.log(`Server is running on http://${hostname ?? "localhost"}:${port}`);
125
+ }
122
126
  });
123
- options?.signal?.addEventListener("abort", () => {
127
+ signal?.addEventListener("abort", () => {
124
128
  server.close();
125
129
  });
126
130
  return new Promise((resolve, reject) => {