rsbuild-plugin-start-server 1.0.0-alpha.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 yicchi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ export { pluginStartServer } from './plugin.js';
2
+ export type { PluginStartServerOptions } from './plugin.js';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import{fork as e}from"node:child_process";import{isAbsolute as t,join as r}from"node:path";function i(e){return!0===e?{signal:"SIGUSR2",reload:!0}:!1===e?{signal:"SIGTERM",reload:!1}:{signal:e,reload:!1}}function o(o){let{script:s,nodeArgs:n=[],args:l=[],env:c={},clearOnRestart:a=!0,restartDebounceMs:d=150,autoRestart:p=!0,signal:u=!1,killTimeoutMs:f=1500,cwd:m,enableSourceMaps:h=!0,manualRestart:g=!0}=o,v=t(s)?s:r(process.cwd(),s);return{name:"plugin-start-server",setup(t){let r,o,{logger:s}=t,T=!1;function w(){s.info(`Run ${v}...`),(r=e(v,l,{execArgv:h?["--enable-source-maps",...n]:n,stdio:"inherit",cwd:m||process.cwd(),env:{...process.env,...c}})).on("exit",(e,t)=>{T?T=!1:r&&s.warn(`App exited unexpectedly (code=${e}, signal=${t}). It will be relaunched on the next successful build.`),r=void 0})}function I(){return new Promise(e=>{let t=r,o=t?.pid;if(!t||!o)return r=void 0,e();let{signal:n,reload:l}=i(u),c=!1,a=()=>{c||(c=!0,clearTimeout(d),e())};if(l){try{process.kill(o,n)}catch(e){s.error("Failed to signal child:\n",e)}return e()}t.once("exit",a);try{process.kill(o,n)}catch(e){return s.error("Failed to kill child:\n",e),a()}let d=setTimeout(()=>{try{process.kill(o,"SIGKILL")}catch{}a()},f)})}async function S(){o&&clearTimeout(o),await new Promise(e=>{o=setTimeout(e,d)}),o=void 0;let{reload:e}=i(u);if(e){s.info("Reloading app (SIGUSR2)..."),T=!0,await I();return}s.info("Restarting app..."),T=!0,await I(),a&&process.stdout.write("\x1b[2J\x1b[0;0H"),w()}let x=!1;if(g&&process.stdin.isTTY)try{process.stdin.setEncoding("utf8"),process.stdin.on("data",e=>{"rs"===e.trim()&&(s.info("Received manual restart command (rs)."),S())}),x=!0}catch{}t.onAfterBuild(e=>{e.stats?.hasErrors()||(x&&s.info('Type "rs" and press Enter to manually restart the app.'),function(){if(r&&r.pid){p&&S();return}w()}())});let y=()=>{if(o&&(clearTimeout(o),o=void 0),r?.pid){let{signal:e}=i(u);try{process.kill(r.pid,e)}catch{}r=void 0}};t.onCloseBuild(y),t.onExit(y);let R=e=>{y(),process.removeListener(e,R),process.kill(process.pid,e)};for(let e of["SIGINT","SIGTERM","SIGQUIT"])process.once(e,R)}}}export{o as pluginStartServer};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/plugin.ts"],"sourcesContent":["import { fork } from 'node:child_process';\nimport type { ChildProcess } from 'node:child_process';\nimport { isAbsolute, join } from 'node:path';\n\nimport type { RsbuildPlugin } from '@rsbuild/core';\n\nconst CLEAR_SCREEN = '\\u001b[2J\\u001b[0;0H';\n\n/**\n * Resolve the user-facing `signal` option into a concrete Node signal.\n * - `false` (default) -> SIGTERM (full kill + re-fork)\n * - `true` -> SIGUSR2 (in-place reload; the script is expected to\n * handle it and refresh itself, so we do NOT re-fork)\n * - string -> that exact signal, treated as a kill + re-fork\n */\nexport function resolveSignal(sig: NodeJS.Signals | boolean): {\n signal: NodeJS.Signals;\n reload: boolean;\n} {\n if (sig === true) return { signal: 'SIGUSR2', reload: true };\n if (sig === false) return { signal: 'SIGTERM', reload: false };\n return { signal: sig, reload: false };\n}\n\nexport interface PluginStartServerOptions {\n /** Relative path (from project root) to the built entry file. */\n script: string;\n /**\n * Extra Node.js args, e.g. ['--inspect=9229']\n */\n nodeArgs?: string[];\n /**\n * Arguments passed to the application script.\n */\n args?: string[];\n /**\n * Environment variables to merge into process.env\n */\n env?: Record<string, string | undefined>;\n /**\n * Clear console before each restart\n * @default true\n */\n clearOnRestart?: boolean;\n /**\n * Debounce time (ms) between rapid rebuilds\n * @default 150\n */\n restartDebounceMs?: number;\n /**\n * Auto restart on rebuild if process already running\n * @default true\n */\n autoRestart?: boolean;\n /**\n * How to stop the child process before a restart.\n *\n * - `false` (default): send `SIGTERM` and re-fork a fresh process once the\n * old one has exited.\n * - `true`: send `SIGUSR2` and let the running process reload itself\n * in-place (nodemon-style). No new process is spawned.\n * - any signal string (e.g. `'SIGINT'`): send that signal and re-fork.\n *\n * Use `true` only when your script installs a `SIGUSR2` handler.\n */\n signal?: NodeJS.Signals | boolean;\n /**\n * Grace period (ms) after sending the kill signal before force-killing\n * the child with `SIGKILL`. Only relevant when not in reload mode.\n * @default 1500\n */\n killTimeoutMs?: number;\n /**\n * Working directory for spawned process\n */\n cwd?: string;\n /**\n * Enable source maps support in the child process\n * @default true\n */\n enableSourceMaps?: boolean;\n /**\n * Enable the `rs` + Enter manual restart shortcut on stdin.\n * @default true\n */\n manualRestart?: boolean;\n}\n\nexport function pluginStartServer(\n options: PluginStartServerOptions,\n): RsbuildPlugin {\n const {\n script,\n nodeArgs = [],\n args = [],\n env = {},\n clearOnRestart = true,\n restartDebounceMs = 150,\n autoRestart = true,\n signal = false,\n killTimeoutMs = 1500,\n cwd,\n enableSourceMaps = true,\n manualRestart = true,\n } = options;\n\n const entryPoint = isAbsolute(script) ? script : join(process.cwd(), script);\n\n return {\n name: 'plugin-start-server',\n setup(api) {\n const { logger } = api;\n\n let child: ChildProcess | undefined;\n let timer: NodeJS.Timeout | undefined;\n let restarting = false;\n\n function startServer() {\n logger.info(`Run ${entryPoint}...`);\n\n const execArgv = enableSourceMaps\n ? ['--enable-source-maps', ...nodeArgs]\n : nodeArgs;\n\n child = fork(entryPoint, args, {\n execArgv,\n stdio: 'inherit',\n cwd: cwd || process.cwd(),\n env: { ...process.env, ...env },\n });\n\n // Track the child lifecycle so we can clear stale state, surface\n // crashes, and avoid treating a dead process as \"running\" on the\n // next build.\n child.on('exit', (code, sig) => {\n // `restarting` means we initiated the exit; otherwise it crashed.\n if (restarting) {\n restarting = false;\n } else if (child) {\n logger.warn(\n `App exited unexpectedly (code=${code}, signal=${sig}). It will be relaunched on the next successful build.`,\n );\n }\n child = undefined;\n });\n }\n\n /**\n * Stop the current child and resolve once it has actually exited\n * (or after the kill timeout). This closes the race where a new\n * process is forked before the old one has released its port.\n */\n function stopServer(): Promise<void> {\n return new Promise((resolve) => {\n const target = child;\n const pid = target?.pid;\n if (!target || !pid) {\n child = undefined;\n return resolve();\n }\n\n const { signal: sig, reload } = resolveSignal(signal);\n let settled = false;\n const done = () => {\n if (settled) return;\n settled = true;\n clearTimeout(forceTimer);\n resolve();\n };\n\n // In reload mode (SIGUSR2) the process stays alive on purpose,\n // so there is nothing to wait for.\n if (reload) {\n try {\n process.kill(pid, sig);\n } catch (e) {\n logger.error('Failed to signal child:\\n', e);\n }\n return resolve();\n }\n\n target.once('exit', done);\n try {\n process.kill(pid, sig);\n } catch (e) {\n logger.error('Failed to kill child:\\n', e);\n return done();\n }\n\n // Force-kill fallback so a misbehaving process can't block a\n // restart forever.\n const forceTimer = setTimeout(() => {\n try {\n process.kill(pid, 'SIGKILL');\n } catch {\n /* already gone */\n }\n done();\n }, killTimeoutMs);\n });\n }\n\n async function restartServer() {\n if (timer) clearTimeout(timer);\n // Defer so a burst of rebuilds collapses into a single restart.\n await new Promise<void>((resolve) => {\n timer = setTimeout(resolve, restartDebounceMs);\n });\n timer = undefined;\n\n const { reload } = resolveSignal(signal);\n if (reload) {\n // In-place reload: the process reloads itself, nothing to re-fork.\n logger.info('Reloading app (SIGUSR2)...');\n restarting = true;\n await stopServer();\n return;\n }\n\n logger.info('Restarting app...');\n restarting = true;\n await stopServer();\n if (clearOnRestart) process.stdout.write(CLEAR_SCREEN);\n startServer();\n }\n\n function afterBuild() {\n if (child && child.pid) {\n if (autoRestart) {\n void restartServer();\n }\n return;\n }\n\n // First start (or previous process has died).\n startServer();\n }\n\n // --- Manual restart shortcut (\"rs\" + Enter) ----------------------\n let stdinRegistered = false;\n if (manualRestart && process.stdin.isTTY) {\n try {\n process.stdin.setEncoding('utf8');\n process.stdin.on('data', (data: string) => {\n if (data.trim() === 'rs') {\n logger.info('Received manual restart command (rs).');\n void restartServer();\n }\n });\n stdinRegistered = true;\n } catch {\n /* stdin not usable in this environment */\n }\n }\n\n // --- Lifecycle hooks ---------------------------------------------\n // Backend dev uses `rsbuild build --watch`: the first build starts the\n // server, and each subsequent successful rebuild restarts it.\n // (Rsbuild's built-in dev server targets front-end HMR and is not used\n // for back-end workflows.)\n api.onAfterBuild((buildArgs) => {\n if (buildArgs.stats?.hasErrors()) return;\n if (stdinRegistered) {\n logger.info('Type \"rs\" and press Enter to manually restart the app.');\n }\n afterBuild();\n });\n\n // Rsbuild-managed cleanup hooks. These are preferred over manually\n // hijacking process signals because they integrate with the CLI and\n // the JS API (build.close()).\n const cleanup = () => {\n if (timer) {\n clearTimeout(timer);\n timer = undefined;\n }\n if (child?.pid) {\n const { signal: sig } = resolveSignal(signal);\n try {\n process.kill(child.pid, sig);\n } catch {\n /* already gone */\n }\n child = undefined;\n }\n };\n\n api.onCloseBuild(cleanup);\n // onExit is synchronous-only; use it as a last-resort fallback for\n // cases where the close hooks don't run (e.g. SIGKILL of the parent).\n api.onExit(cleanup);\n\n // Best-effort signal fallback. Use `once` and remove listeners so we\n // never leak handlers across multiple Rsbuild instances in the same\n // process, and so we don't preempt Rsbuild's own shutdown logic.\n const signals: NodeJS.Signals[] = ['SIGINT', 'SIGTERM', 'SIGQUIT'];\n const onSignal = (sig: NodeJS.Signals) => {\n cleanup();\n // Re-raise to default behavior so the process actually terminates\n // and other handlers (incl. Rsbuild's) can react.\n process.removeListener(sig, onSignal);\n process.kill(process.pid, sig);\n };\n for (const sig of signals) {\n process.once(sig, onSignal);\n }\n },\n };\n}\n"],"names":["resolveSignal","sig","pluginStartServer","options","script","nodeArgs","args","env","clearOnRestart","restartDebounceMs","autoRestart","signal","killTimeoutMs","cwd","enableSourceMaps","manualRestart","entryPoint","isAbsolute","join","process","api","child","timer","logger","restarting","startServer","fork","execArgv","code","undefined","stopServer","Promise","resolve","target","pid","reload","settled","done","clearTimeout","forceTimer","e","setTimeout","restartServer","stdinRegistered","data","buildArgs","afterBuild","cleanup","onSignal"],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ import type { RsbuildPlugin } from '@rsbuild/core';
2
+ /**
3
+ * Resolve the user-facing `signal` option into a concrete Node signal.
4
+ * - `false` (default) -> SIGTERM (full kill + re-fork)
5
+ * - `true` -> SIGUSR2 (in-place reload; the script is expected to
6
+ * handle it and refresh itself, so we do NOT re-fork)
7
+ * - string -> that exact signal, treated as a kill + re-fork
8
+ */
9
+ export declare function resolveSignal(sig: NodeJS.Signals | boolean): {
10
+ signal: NodeJS.Signals;
11
+ reload: boolean;
12
+ };
13
+ export interface PluginStartServerOptions {
14
+ /** Relative path (from project root) to the built entry file. */
15
+ script: string;
16
+ /**
17
+ * Extra Node.js args, e.g. ['--inspect=9229']
18
+ */
19
+ nodeArgs?: string[];
20
+ /**
21
+ * Arguments passed to the application script.
22
+ */
23
+ args?: string[];
24
+ /**
25
+ * Environment variables to merge into process.env
26
+ */
27
+ env?: Record<string, string | undefined>;
28
+ /**
29
+ * Clear console before each restart
30
+ * @default true
31
+ */
32
+ clearOnRestart?: boolean;
33
+ /**
34
+ * Debounce time (ms) between rapid rebuilds
35
+ * @default 150
36
+ */
37
+ restartDebounceMs?: number;
38
+ /**
39
+ * Auto restart on rebuild if process already running
40
+ * @default true
41
+ */
42
+ autoRestart?: boolean;
43
+ /**
44
+ * How to stop the child process before a restart.
45
+ *
46
+ * - `false` (default): send `SIGTERM` and re-fork a fresh process once the
47
+ * old one has exited.
48
+ * - `true`: send `SIGUSR2` and let the running process reload itself
49
+ * in-place (nodemon-style). No new process is spawned.
50
+ * - any signal string (e.g. `'SIGINT'`): send that signal and re-fork.
51
+ *
52
+ * Use `true` only when your script installs a `SIGUSR2` handler.
53
+ */
54
+ signal?: NodeJS.Signals | boolean;
55
+ /**
56
+ * Grace period (ms) after sending the kill signal before force-killing
57
+ * the child with `SIGKILL`. Only relevant when not in reload mode.
58
+ * @default 1500
59
+ */
60
+ killTimeoutMs?: number;
61
+ /**
62
+ * Working directory for spawned process
63
+ */
64
+ cwd?: string;
65
+ /**
66
+ * Enable source maps support in the child process
67
+ * @default true
68
+ */
69
+ enableSourceMaps?: boolean;
70
+ /**
71
+ * Enable the `rs` + Enter manual restart shortcut on stdin.
72
+ * @default true
73
+ */
74
+ manualRestart?: boolean;
75
+ }
76
+ export declare function pluginStartServer(options: PluginStartServerOptions): RsbuildPlugin;
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "rsbuild-plugin-start-server",
3
+ "version": "1.0.0-alpha.0",
4
+ "description": "Automatically (re)start your server once Rsbuild's build completes.",
5
+ "keywords": [
6
+ "node",
7
+ "rsbuild",
8
+ "rsbuild-plugin",
9
+ "server",
10
+ "watch"
11
+ ],
12
+ "license": "MIT",
13
+ "author": "YiCChi",
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "type": "module",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js"
22
+ },
23
+ "./package.json": "./package.json"
24
+ },
25
+ "publishConfig": {
26
+ "access": "public",
27
+ "registry": "https://registry.npmjs.org/"
28
+ },
29
+ "devDependencies": {
30
+ "@rsbuild/core": "2.0.15",
31
+ "@rslib/core": "0.23.0",
32
+ "@rslint/core": "0.6.2",
33
+ "@rstest/core": "0.10.6",
34
+ "@types/node": "24.13.2",
35
+ "typescript": "6.0.3"
36
+ },
37
+ "peerDependencies": {
38
+ "@rsbuild/core": "^1.5.0"
39
+ },
40
+ "scripts": {
41
+ "dev": "rslib build --watch",
42
+ "build": "rslib build",
43
+ "typecheck": "tsc",
44
+ "lint": "rslint",
45
+ "fmt": "oxfmt --write .",
46
+ "test": "rstest run",
47
+ "test:watch": "rstest"
48
+ }
49
+ }