create-volt 0.40.0 → 0.41.0

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/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@ All notable changes to `create-volt` are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/), and this project adheres to
5
5
  [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [0.41.0] - 2026-06-30
8
+
9
+ ### Added
10
+ - **PM2 support.** Scaffolds ship `ecosystem.config.cjs` + scripts: `npm run pm2`
11
+ (start under pm2 — fetched via npx if not installed, or uses your global pm2),
12
+ `pm2:restart` (clean reload, no port clash), `pm2:logs`, `pm2:stop`.
13
+ - **`npm run dev` on an already-running app reloads it instead of crashing.** A
14
+ second start detects the in-use port, pings the running instance's new
15
+ `/__volt/reload` route to refresh browsers, prints a note, and exits 0 — no
16
+ more `EADDRINUSE` stack trace.
17
+
7
18
  ## [0.40.0] - 2026-06-30
8
19
 
9
20
  ### Added
@@ -537,6 +548,7 @@ All notable changes to `create-volt` are documented here. The format follows
537
548
  watching and full-page hot reload. Supports `--skip-install` and `--force`,
538
549
  and auto-detects npm / pnpm / yarn / bun for the install step.
539
550
 
551
+ [0.41.0]: https://github.com/MIR-2025/volt/releases/tag/v0.41.0
540
552
  [0.40.0]: https://github.com/MIR-2025/volt/releases/tag/v0.40.0
541
553
  [0.39.1]: https://github.com/MIR-2025/volt/releases/tag/v0.39.1
542
554
  [0.39.0]: https://github.com/MIR-2025/volt/releases/tag/v0.39.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-volt",
3
- "version": "0.40.0",
3
+ "version": "0.41.0",
4
4
  "description": "Scaffold a new Volt app — no-build, signals-based UI with Socket.io hot reload.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,5 @@
1
+ // PM2 process config. Start under pm2 with `npm run pm2` (pm2 is fetched via npx
2
+ // if you don't have it installed); reload cleanly with `npm run pm2:restart` (no
3
+ // port clash), tail logs with `npm run pm2:logs`, remove with `npm run pm2:stop`.
4
+ const { name } = require("./package.json");
5
+ module.exports = { apps: [{ name, script: "server.js" }] };
@@ -7,7 +7,11 @@
7
7
  "main": "server.js",
8
8
  "scripts": {
9
9
  "start": "node server.js",
10
- "dev": "node server.js"
10
+ "dev": "node server.js",
11
+ "pm2": "npx --yes pm2 start ecosystem.config.cjs",
12
+ "pm2:restart": "npx --yes pm2 restart ecosystem.config.cjs",
13
+ "pm2:stop": "npx --yes pm2 delete ecosystem.config.cjs",
14
+ "pm2:logs": "npx --yes pm2 logs"
11
15
  },
12
16
  "dependencies": {
13
17
  "express": "^4.22.2",
@@ -184,6 +184,13 @@ async function startApp() {
184
184
  const io = new SocketServer(server);
185
185
  if (enabled.has("realtime") && store) (await addonMod("realtime")).attachRealtime(io, { store });
186
186
 
187
+ // Reload connected browsers on demand — used when a second `npm run dev` finds
188
+ // the app already running (see the EADDRINUSE handler below) instead of crashing.
189
+ app.get("/__volt/reload", (_req, res) => {
190
+ io.emit("volt:reload", { file: "__manual__" });
191
+ res.json({ ok: true });
192
+ });
193
+
187
194
  // third-party add-ons — register(ctx). When auth is on, requireAuth/sessionFromReq
188
195
  // are provided so add-ons can gate routes by login.
189
196
  let requireAuth = null;
@@ -237,6 +244,20 @@ async function startApp() {
237
244
  }
238
245
 
239
246
  const on = [...enabled];
247
+ // If the port's taken, the app is likely already running — reload it (tell the
248
+ // running instance to refresh browsers) and exit, instead of an EADDRINUSE crash.
249
+ server.on("error", async (e) => {
250
+ if (e.code === "EADDRINUSE") {
251
+ try {
252
+ await fetch(`http://localhost:${PORT}/__volt/reload`);
253
+ } catch {
254
+ /* old instance without the reload route, or not ours */
255
+ }
256
+ console.log(`\n[volt] already running at http://localhost:${PORT} — reloaded it. (Stop that process, or use pm2, to restart.)`);
257
+ process.exit(0);
258
+ }
259
+ throw e;
260
+ });
240
261
  server.listen(PORT, () => console.log(`⚡ Volt → http://localhost:${PORT}${on.length ? " (add-ons: " + on.join(", ") + ")" : ""}`));
241
262
  }
242
263
 
@@ -0,0 +1,5 @@
1
+ // PM2 process config. Start under pm2 with `npm run pm2` (pm2 is fetched via npx
2
+ // if you don't have it installed); reload cleanly with `npm run pm2:restart` (no
3
+ // port clash), tail logs with `npm run pm2:logs`, remove with `npm run pm2:stop`.
4
+ const { name } = require("./package.json");
5
+ module.exports = { apps: [{ name, script: "server.js" }] };
@@ -7,7 +7,11 @@
7
7
  "main": "server.js",
8
8
  "scripts": {
9
9
  "start": "node server.js",
10
- "dev": "node server.js"
10
+ "dev": "node server.js",
11
+ "pm2": "npx --yes pm2 start ecosystem.config.cjs",
12
+ "pm2:restart": "npx --yes pm2 restart ecosystem.config.cjs",
13
+ "pm2:stop": "npx --yes pm2 delete ecosystem.config.cjs",
14
+ "pm2:logs": "npx --yes pm2 logs"
11
15
  },
12
16
  "dependencies": {
13
17
  "express": "^4.22.2",
@@ -184,6 +184,13 @@ async function startApp() {
184
184
  const io = new SocketServer(server);
185
185
  if (enabled.has("realtime") && store) (await addonMod("realtime")).attachRealtime(io, { store });
186
186
 
187
+ // Reload connected browsers on demand — used when a second `npm run dev` finds
188
+ // the app already running (see the EADDRINUSE handler below) instead of crashing.
189
+ app.get("/__volt/reload", (_req, res) => {
190
+ io.emit("volt:reload", { file: "__manual__" });
191
+ res.json({ ok: true });
192
+ });
193
+
187
194
  // third-party add-ons — register(ctx). When auth is on, requireAuth/sessionFromReq
188
195
  // are provided so add-ons can gate routes by login.
189
196
  let requireAuth = null;
@@ -237,6 +244,20 @@ async function startApp() {
237
244
  }
238
245
 
239
246
  const on = [...enabled];
247
+ // If the port's taken, the app is likely already running — reload it (tell the
248
+ // running instance to refresh browsers) and exit, instead of an EADDRINUSE crash.
249
+ server.on("error", async (e) => {
250
+ if (e.code === "EADDRINUSE") {
251
+ try {
252
+ await fetch(`http://localhost:${PORT}/__volt/reload`);
253
+ } catch {
254
+ /* old instance without the reload route, or not ours */
255
+ }
256
+ console.log(`\n[volt] already running at http://localhost:${PORT} — reloaded it. (Stop that process, or use pm2, to restart.)`);
257
+ process.exit(0);
258
+ }
259
+ throw e;
260
+ });
240
261
  server.listen(PORT, () => console.log(`⚡ Volt → http://localhost:${PORT}${on.length ? " (add-ons: " + on.join(", ") + ")" : ""}`));
241
262
  }
242
263
 
@@ -0,0 +1,5 @@
1
+ // PM2 process config. Start under pm2 with `npm run pm2` (pm2 is fetched via npx
2
+ // if you don't have it installed); reload cleanly with `npm run pm2:restart` (no
3
+ // port clash), tail logs with `npm run pm2:logs`, remove with `npm run pm2:stop`.
4
+ const { name } = require("./package.json");
5
+ module.exports = { apps: [{ name, script: "server.js" }] };
@@ -7,7 +7,11 @@
7
7
  "main": "server.js",
8
8
  "scripts": {
9
9
  "start": "node server.js",
10
- "dev": "node server.js"
10
+ "dev": "node server.js",
11
+ "pm2": "npx --yes pm2 start ecosystem.config.cjs",
12
+ "pm2:restart": "npx --yes pm2 restart ecosystem.config.cjs",
13
+ "pm2:stop": "npx --yes pm2 delete ecosystem.config.cjs",
14
+ "pm2:logs": "npx --yes pm2 logs"
11
15
  },
12
16
  "dependencies": {
13
17
  "express": "^4.22.2",
@@ -184,6 +184,13 @@ async function startApp() {
184
184
  const io = new SocketServer(server);
185
185
  if (enabled.has("realtime") && store) (await addonMod("realtime")).attachRealtime(io, { store });
186
186
 
187
+ // Reload connected browsers on demand — used when a second `npm run dev` finds
188
+ // the app already running (see the EADDRINUSE handler below) instead of crashing.
189
+ app.get("/__volt/reload", (_req, res) => {
190
+ io.emit("volt:reload", { file: "__manual__" });
191
+ res.json({ ok: true });
192
+ });
193
+
187
194
  // third-party add-ons — register(ctx). When auth is on, requireAuth/sessionFromReq
188
195
  // are provided so add-ons can gate routes by login.
189
196
  let requireAuth = null;
@@ -237,6 +244,20 @@ async function startApp() {
237
244
  }
238
245
 
239
246
  const on = [...enabled];
247
+ // If the port's taken, the app is likely already running — reload it (tell the
248
+ // running instance to refresh browsers) and exit, instead of an EADDRINUSE crash.
249
+ server.on("error", async (e) => {
250
+ if (e.code === "EADDRINUSE") {
251
+ try {
252
+ await fetch(`http://localhost:${PORT}/__volt/reload`);
253
+ } catch {
254
+ /* old instance without the reload route, or not ours */
255
+ }
256
+ console.log(`\n[volt] already running at http://localhost:${PORT} — reloaded it. (Stop that process, or use pm2, to restart.)`);
257
+ process.exit(0);
258
+ }
259
+ throw e;
260
+ });
240
261
  server.listen(PORT, () => console.log(`⚡ Volt → http://localhost:${PORT}${on.length ? " (add-ons: " + on.join(", ") + ")" : ""}`));
241
262
  }
242
263
 
@@ -0,0 +1,5 @@
1
+ // PM2 process config. Start under pm2 with `npm run pm2` (pm2 is fetched via npx
2
+ // if you don't have it installed); reload cleanly with `npm run pm2:restart` (no
3
+ // port clash), tail logs with `npm run pm2:logs`, remove with `npm run pm2:stop`.
4
+ const { name } = require("./package.json");
5
+ module.exports = { apps: [{ name, script: "server.js" }] };
@@ -7,7 +7,11 @@
7
7
  "main": "server.js",
8
8
  "scripts": {
9
9
  "start": "node server.js",
10
- "dev": "node server.js"
10
+ "dev": "node server.js",
11
+ "pm2": "npx --yes pm2 start ecosystem.config.cjs",
12
+ "pm2:restart": "npx --yes pm2 restart ecosystem.config.cjs",
13
+ "pm2:stop": "npx --yes pm2 delete ecosystem.config.cjs",
14
+ "pm2:logs": "npx --yes pm2 logs"
11
15
  },
12
16
  "dependencies": {
13
17
  "express": "^4.22.2",
@@ -0,0 +1,5 @@
1
+ // PM2 process config. Start under pm2 with `npm run pm2` (pm2 is fetched via npx
2
+ // if you don't have it installed); reload cleanly with `npm run pm2:restart` (no
3
+ // port clash), tail logs with `npm run pm2:logs`, remove with `npm run pm2:stop`.
4
+ const { name } = require("./package.json");
5
+ module.exports = { apps: [{ name, script: "server.js" }] };
@@ -7,7 +7,11 @@
7
7
  "main": "server.js",
8
8
  "scripts": {
9
9
  "start": "node server.js",
10
- "dev": "node server.js"
10
+ "dev": "node server.js",
11
+ "pm2": "npx --yes pm2 start ecosystem.config.cjs",
12
+ "pm2:restart": "npx --yes pm2 restart ecosystem.config.cjs",
13
+ "pm2:stop": "npx --yes pm2 delete ecosystem.config.cjs",
14
+ "pm2:logs": "npx --yes pm2 logs"
11
15
  },
12
16
  "dependencies": {
13
17
  "express": "^4.22.2",
@@ -210,6 +210,13 @@ async function startApp() {
210
210
  const io = new SocketServer(server);
211
211
  if (enabled.has("realtime") && store) (await addonMod("realtime")).attachRealtime(io, { store });
212
212
 
213
+ // Reload connected browsers on demand — used when a second `npm run dev` finds
214
+ // the app already running (see the EADDRINUSE handler below) instead of crashing.
215
+ app.get("/__volt/reload", (_req, res) => {
216
+ io.emit("volt:reload", { file: "__manual__" });
217
+ res.json({ ok: true });
218
+ });
219
+
213
220
  // third-party add-ons — register(ctx). When auth is on, requireAuth/sessionFromReq
214
221
  // are provided so add-ons can gate routes by login.
215
222
  let requireAuth = null;
@@ -263,6 +270,20 @@ async function startApp() {
263
270
  }
264
271
 
265
272
  const on = [...enabled];
273
+ // If the port's taken, the app is likely already running — reload it (tell the
274
+ // running instance to refresh browsers) and exit, instead of an EADDRINUSE crash.
275
+ server.on("error", async (e) => {
276
+ if (e.code === "EADDRINUSE") {
277
+ try {
278
+ await fetch(`http://localhost:${PORT}/__volt/reload`);
279
+ } catch {
280
+ /* old instance without the reload route, or not ours */
281
+ }
282
+ console.log(`\n[volt] already running at http://localhost:${PORT} — reloaded it. (Stop that process, or use pm2, to restart.)`);
283
+ process.exit(0);
284
+ }
285
+ throw e;
286
+ });
266
287
  server.listen(PORT, () => console.log(`⚡ Volt → http://localhost:${PORT}${on.length ? " (add-ons: " + on.join(", ") + ")" : ""}`));
267
288
  }
268
289