diff --git a/packages/run/README.md b/packages/run/README.md index bf1a5398c..b8b436242 100644 --- a/packages/run/README.md +++ b/packages/run/README.md @@ -32,9 +32,9 @@ export default { input: 'src/index.js', output: { file: 'dist/index.js', - format: 'cjs' + format: 'cjs', }, - plugins: [run()] + plugins: [run()], }; ``` @@ -72,6 +72,23 @@ export default { }; ``` +### `allowRestarts` + +Type: `Boolean`
+Default: `false` + +If `true`, instructs the plugin to listen to `stdin` for the sequences listed below followed by enter (carriage return). + +#### `stdin` Input Actions + +When this option is enabled, `stdin` will listen for the following input and perform the associated action: + +- `restart` → Kills the currently running bundle and starts it again. _Note: This does not create a new bundle, the bundle is run again "as-is". This can be used to test configuration changes or other changes that are made without modifying your source_ + Also allowed: `rs`, `CTRL+K` + +- `clear` → Clears the screen of all text + Also allowed: `cls`, `CTRL+L` + ## Practical Example The feature is usually intended for development use, you may prefer to only include it when Rollup is being run in watch mode: diff --git a/packages/run/src/index.ts b/packages/run/src/index.ts index 20d9d018b..fef46302d 100644 --- a/packages/run/src/index.ts +++ b/packages/run/src/index.ts @@ -10,8 +10,10 @@ export default function run(opts: RollupRunOptions = {}): Plugin { let proc: ChildProcess; const args = opts.args || []; + const allowRestarts = opts.allowRestarts || false; const forkOptions = opts.options || opts; delete (forkOptions as RollupRunOptions).args; + delete (forkOptions as RollupRunOptions).allowRestarts; return { name: 'run', @@ -41,6 +43,11 @@ export default function run(opts: RollupRunOptions = {}): Plugin { }, writeBundle(outputOptions, bundle) { + const forkBundle = (dir: string, entryFileName: string) => { + if (proc) proc.kill(); + proc = fork(path.join(dir, entryFileName), args, forkOptions); + }; + const dir = outputOptions.dir || path.dirname(outputOptions.file!); const entryFileName = Object.keys(bundle).find((fileName) => { const chunk = bundle[fileName] as RenderedChunk; @@ -48,8 +55,22 @@ export default function run(opts: RollupRunOptions = {}): Plugin { }); if (entryFileName) { - if (proc) proc.kill(); - proc = fork(path.join(dir, entryFileName), args, forkOptions); + forkBundle(dir, entryFileName); + + if (allowRestarts) { + process.stdin.resume(); + process.stdin.setEncoding('utf8'); + + process.stdin.on('data', (data) => { + const line = data.toString().trim().toLowerCase(); + + if (line === 'rs' || line === 'restart' || data.toString().charCodeAt(0) === 11) { + forkBundle(dir, entryFileName); + } else if (line === 'cls' || line === 'clear' || data.toString().charCodeAt(0) === 12) { + console.clear(); + } + }); + } } else { this.error(`@rollup/plugin-run could not find output chunk`); } diff --git a/packages/run/test/test.js b/packages/run/test/test.js index 8cbd37571..eaf124fc1 100644 --- a/packages/run/test/test.js +++ b/packages/run/test/test.js @@ -110,6 +110,15 @@ test('detects changes - forks a new child process and kills older process', asyn t.is(mockChildProcess().kill.callCount, 1); }); +test('allow the allowRestart option', async (t) => { + const bundle = await rollup({ + input, + plugins: [run({ allowRestarts: true })] + }); + await bundle.write(outputOptions); + t.true(mockChildProcess.calledWithExactly(outputOptions.file, [], {})); +}); + test.after(async () => { await del(['output']); }); diff --git a/packages/run/types/index.d.ts b/packages/run/types/index.d.ts index 880d49790..c91da9987 100644 --- a/packages/run/types/index.d.ts +++ b/packages/run/types/index.d.ts @@ -5,6 +5,7 @@ import { Plugin } from 'rollup'; export interface RollupRunOptions extends ForkOptions { args?: readonly string[]; options?: ForkOptions; + allowRestarts?: boolean; } /**