From 3b0bc8d227b36a5a3bfbc477c8774b7e46ad0c80 Mon Sep 17 00:00:00 2001 From: Matt Kantor Date: Mon, 1 Apr 2024 11:44:25 -0400 Subject: [PATCH] Add fork of @rollup/plugin-run with build artifacts committed See , this is a built version of that branch. NPM can consume this by referring to a github URL in package.json. --- CHANGELOG.md | 124 +++++++++++++ README.md | 185 ++++++++++++++++++++ dist/cjs/index.js | 74 ++++++++ dist/cjs/index.js.map | 1 + dist/es/index.js | 69 ++++++++ dist/es/index.js.map | 1 + dist/es/package.json | 1 + package.json | 80 +++++++++ rollup.config.mjs | 7 + src/index.ts | 84 +++++++++ test/fixtures/change-detect-input.js | 1 + test/fixtures/facade-entry/dynamic.js | 3 + test/fixtures/facade-entry/index.js | 6 + test/fixtures/facade-entry/library.js | 2 + test/fixtures/input.js | 1 + test/fixtures/output/bundle.js | 5 + test/fixtures/output/change-detect-input.js | 5 + test/fixtures/output/dynamic-2Lf4t603.js | 5 + test/fixtures/output/index-KtU15qp4.js | 10 ++ test/fixtures/output/index.js | 4 + test/fixtures/output/input.js | 5 + test/test.js | 170 ++++++++++++++++++ tsconfig.json | 1 + types/index.d.ts | 16 ++ 24 files changed, 860 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 README.md create mode 100644 dist/cjs/index.js create mode 100644 dist/cjs/index.js.map create mode 100644 dist/es/index.js create mode 100644 dist/es/index.js.map create mode 100644 dist/es/package.json create mode 100644 package.json create mode 100644 rollup.config.mjs create mode 100644 src/index.ts create mode 100644 test/fixtures/change-detect-input.js create mode 100644 test/fixtures/facade-entry/dynamic.js create mode 100644 test/fixtures/facade-entry/index.js create mode 100644 test/fixtures/facade-entry/library.js create mode 100644 test/fixtures/input.js create mode 100644 test/fixtures/output/bundle.js create mode 100644 test/fixtures/output/change-detect-input.js create mode 100644 test/fixtures/output/dynamic-2Lf4t603.js create mode 100644 test/fixtures/output/index-KtU15qp4.js create mode 100644 test/fixtures/output/index.js create mode 100644 test/fixtures/output/input.js create mode 100644 test/test.js create mode 120000 tsconfig.json create mode 100644 types/index.d.ts diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..57e3da2 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,124 @@ +# @rollup/plugin-run ChangeLog + +## v3.1.0 + +_2024-04-01_ + +### Features + +- feat: add `input` option (51192d5) + +### Updates + +- refactor: extract reusable consts from a test (fe7360d) +- chore: add test for multiple entry points (7efa265) + +## v3.0.2 + +_2023-10-05_ + +### Bugfixes + +- fix: ensure rollup 4 compatibility [#1595](https://github.com/rollup/plugins/pull/1595) + +## v3.0.1 + +_2022-10-21_ + +### Updates + +- chore: update rollup dependencies ([3038271](https://github.com/rollup/plugins/commit/303827191ede6b2e4eade96c6968ed16a587683f)) + +## v3.0.0 + +_2022-10-10_ + +### Breaking Changes + +- fix: prepare for Rollup 3 [#1285](https://github.com/rollup/plugins/pull/1285) + +## v2.1.0 + +_2021-07-29_ + +### Features + +- feat: add `allowRestarts` option (#820) + +### Updates + +- chore: upgrade TypeScript (#714) +- chore: update dependencies (4428bff) + +## v2.0.2 + +_2020-04-12_ + +### Bugfixes + +- fix: use correct hook for reading options, handle facade chunks (#292) + +### Updates + +- refactor: use Typescript (#237) + +## v2.0.1 + +_2020-03-30_ + +### Bugfixes + +- fix: remove rollup v1 from peerDeps (0eb4d59) + +## v2.0.0 + +_2020-03-29_ + +- chore: republish rollup v2 changes, includes breaking Rollup function signature change + +## v1.2.2 + +_2020-03-29_ + +- chore: revert rollup v2 changes and republish. resolves breaking changes for rollup v1.x. + +## v1.2.1 + +_2020-03-24_ + +### Bugfixes + +- fix: plugin could fork before bundle generated (#281) +- fix: Support assets (#240) + +### Updates + +- chore: update devDeps (1b08a83) + +## v1.2.0 + +_2020-01-04_ + +### Features + +- feat: add typings (#115) + +### Updates + +- chore: update readme, linting (e7ff386) + +## 1.1.0 + +- Allow arguments and options to be passed to `child_process.fork` + +## 1.0.2 + +- Warn if Rollup version is too low + +## 1.0.1 + +- Handle output files with different names from input files + +## 1.0.0 + +- First release diff --git a/README.md b/README.md new file mode 100644 index 0000000..b5859b5 --- /dev/null +++ b/README.md @@ -0,0 +1,185 @@ +[npm]: https://img.shields.io/npm/v/@rollup/plugin-run +[npm-url]: https://www.npmjs.com/package/@rollup/plugin-run +[size]: https://packagephobia.now.sh/badge?p=@rollup/plugin-run +[size-url]: https://packagephobia.now.sh/result?p=@rollup/plugin-run + +[![npm][npm]][npm-url] +[![size][size]][size-url] +[![libera manifesto](https://img.shields.io/badge/libera-manifesto-lightgrey.svg)](https://liberamanifesto.com) + +# @rollup/plugin-run + +🍣 A Rollup plugin which runs your bundles in Node once they're built. + +Using this plugin gives much faster results compared to what you would do with [nodemon](https://nodemon.io/). + +## Requirements + +This plugin requires an [LTS](https://github.com/nodejs/Release) Node version (v14.0.0+) and Rollup v2.0.0+. + +## Install + +Using npm: + +```console +npm install @rollup/plugin-run --save-dev +``` + +## Usage + +Create a `rollup.config.js` [configuration file](https://www.rollupjs.org/guide/en/#configuration-files) and import the plugin: + +```js +import run from '@rollup/plugin-run'; + +export default { + input: 'src/index.js', + output: { + file: 'dist/index.js', + format: 'cjs' + }, + plugins: [run()] +}; +``` + +Then call `rollup` either via the [CLI](https://www.rollupjs.org/guide/en/#command-line-reference) or the [API](https://www.rollupjs.org/guide/en/#javascript-api). If the build produces any errors, the plugin will write a 'alias' character to stderr, which should be audible on most systems. + +The plugin `forks` a child process with the generated file, every time the bundle is rebuilt (after first closing the previous process, if it's not the first run). + +_Note: This plugin works with Rollup's code-splitting if you're using dynamic `import(...)` — the only constraint is that you have a single entry point specified in the config._ + +## Options + +This plugin supports pass through option available for [child_process.fork(...)](https://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options). + +Example: + +Debugging with sourcemaps using [source-map-support](https://www.npmjs.com/package/source-map-support): + +```diff +// rollup.config.js +import run from '@rollup/plugin-run'; + +export default { + input: 'src/index.js', + output: { + file: 'dist/index.js', + format: 'cjs', ++ sourcemap: true + }, + plugins: [ +- run() ++ run({ ++ execArgv: ['-r', 'source-map-support/register'] ++ }) + ] +}; +``` + +### `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` + +### `input` + +Type: `String`
+Default: `null` + +Specifies the entry point this plugin will use. Not necessary if you only have a single entry point. + +## 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 +// rollup.config.js +import run from '@rollup/plugin-run'; + ++const dev = process.env.ROLLUP_WATCH === 'true'; + +export default { + input: 'src/index.js', + output: { + file: 'dist/index.js', + format: 'cjs' + }, + plugins: [ +- run() ++ dev && run() + ] +}; +``` + +## Meta + +[CONTRIBUTING](/.github/CONTRIBUTING.md) + +[LICENSE (MIT)](/LICENSE) + +--- + +PR: + +feat(run): add `input` option + + + + + +## Rollup Plugin Name: `run` + +This PR contains: + +- [ ] bugfix +- [x] feature +- [ ] refactor +- [ ] documentation +- [ ] other + +Are tests included? + +- [x] yes (_bugfixes and features will not be merged without tests_) +- [ ] no + +Breaking Changes? + +- [ ] yes (_breaking changes will not be merged unless absolutely necessary_) +- [x] no + +If yes, then include "BREAKING CHANGES:" in the first commit message body, followed by a description of what is breaking. + +### Description + + + +This changeset adds an `input` option to the `run` plugin. This options allows specifying which entry point to run if you have multiple in your bundle. diff --git a/dist/cjs/index.js b/dist/cjs/index.js new file mode 100644 index 0000000..5cbe8c1 --- /dev/null +++ b/dist/cjs/index.js @@ -0,0 +1,74 @@ +'use strict'; + +Object.defineProperty(exports, '__esModule', { value: true }); + +var child_process = require('child_process'); +var path = require('path'); + +function run(opts = {}) { + let input; + let proc; + const args = opts.args || []; + const allowRestarts = opts.allowRestarts || false; + const overrideInput = opts.input; + const forkOptions = opts.options || opts; + delete forkOptions.args; + delete forkOptions.allowRestarts; + return { + name: 'run', + buildStart(options) { + let inputs = overrideInput !== null && overrideInput !== void 0 ? overrideInput : options.input; + if (typeof inputs === 'string') { + inputs = [inputs]; + } + if (typeof inputs === 'object') { + inputs = Object.values(inputs); + } + if (inputs.length > 1) { + throw new Error(`@rollup/plugin-run must have a single entry point; consider setting the \`input\` option`); + } + input = path.resolve(inputs[0]); + }, + generateBundle(_outputOptions, _bundle, isWrite) { + if (!isWrite) { + this.error(`@rollup/plugin-run currently only works with bundles that are written to disk`); + } + }, + writeBundle(outputOptions, bundle) { + const forkBundle = (dir, entryFileName) => { + if (proc) + proc.kill(); + proc = child_process.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]; + return chunk.isEntry && chunk.facadeModuleId === input; + }); + if (entryFileName) { + 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) { + // eslint-disable-next-line no-console + console.clear(); + } + }); + } + } + else { + this.error(`@rollup/plugin-run could not find output chunk`); + } + } + }; +} + +exports.default = run; +module.exports = Object.assign(exports.default, exports); +//# sourceMappingURL=index.js.map diff --git a/dist/cjs/index.js.map b/dist/cjs/index.js.map new file mode 100644 index 0000000..a3ba3ad --- /dev/null +++ b/dist/cjs/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sources":["../../src/index.ts"],"sourcesContent":[null],"names":["resolve","fork","join","dirname"],"mappings":"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"} \ No newline at end of file diff --git a/dist/es/index.js b/dist/es/index.js new file mode 100644 index 0000000..63688b5 --- /dev/null +++ b/dist/es/index.js @@ -0,0 +1,69 @@ +import { fork } from 'child_process'; +import { resolve, dirname, join } from 'path'; + +function run(opts = {}) { + let input; + let proc; + const args = opts.args || []; + const allowRestarts = opts.allowRestarts || false; + const overrideInput = opts.input; + const forkOptions = opts.options || opts; + delete forkOptions.args; + delete forkOptions.allowRestarts; + return { + name: 'run', + buildStart(options) { + let inputs = overrideInput !== null && overrideInput !== void 0 ? overrideInput : options.input; + if (typeof inputs === 'string') { + inputs = [inputs]; + } + if (typeof inputs === 'object') { + inputs = Object.values(inputs); + } + if (inputs.length > 1) { + throw new Error(`@rollup/plugin-run must have a single entry point; consider setting the \`input\` option`); + } + input = resolve(inputs[0]); + }, + generateBundle(_outputOptions, _bundle, isWrite) { + if (!isWrite) { + this.error(`@rollup/plugin-run currently only works with bundles that are written to disk`); + } + }, + writeBundle(outputOptions, bundle) { + const forkBundle = (dir, entryFileName) => { + if (proc) + proc.kill(); + proc = fork(join(dir, entryFileName), args, forkOptions); + }; + const dir = outputOptions.dir || dirname(outputOptions.file); + const entryFileName = Object.keys(bundle).find((fileName) => { + const chunk = bundle[fileName]; + return chunk.isEntry && chunk.facadeModuleId === input; + }); + if (entryFileName) { + 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) { + // eslint-disable-next-line no-console + console.clear(); + } + }); + } + } + else { + this.error(`@rollup/plugin-run could not find output chunk`); + } + } + }; +} + +export { run as default }; +//# sourceMappingURL=index.js.map diff --git a/dist/es/index.js.map b/dist/es/index.js.map new file mode 100644 index 0000000..1d45e18 --- /dev/null +++ b/dist/es/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sources":["../../src/index.ts"],"sourcesContent":[null],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/es/package.json b/dist/es/package.json new file mode 100644 index 0000000..7c34deb --- /dev/null +++ b/dist/es/package.json @@ -0,0 +1 @@ +{"type":"module"} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..e13c22b --- /dev/null +++ b/package.json @@ -0,0 +1,80 @@ +{ + "name": "@rollup/plugin-run", + "version": "3.1.0", + "publishConfig": { + "access": "public" + }, + "description": "Run your bundle after you've built it", + "license": "MIT", + "repository": { + "url": "rollup/plugins", + "directory": "packages/run" + }, + "author": "Rich Harris", + "homepage": "https://github.com/rollup/plugins/tree/master/packages/run/#readme", + "bugs": "https://github.com/rollup/plugins/issues", + "main": "./dist/cjs/index.js", + "module": "./dist/es/index.js", + "exports": { + "types": "./types/index.d.ts", + "import": "./dist/es/index.js", + "default": "./dist/cjs/index.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "scripts": { + "build": "rollup -c", + "ci:coverage": "nyc pnpm test && nyc report --reporter=text-lcov > coverage.lcov", + "ci:lint": "pnpm build && pnpm lint", + "ci:lint:commits": "commitlint --from=${CIRCLE_BRANCH} --to=${CIRCLE_SHA1}", + "ci:test": "pnpm test -- --verbose", + "prebuild": "del-cli dist", + "prepare": "if [ ! -d 'dist' ]; then pnpm build; fi", + "prerelease": "pnpm build", + "pretest": "pnpm build", + "release": "pnpm --workspace-root package:release $(pwd)", + "test": "ava" + }, + "files": [ + "dist", + "!dist/**/*.map", + "types", + "README.md", + "LICENSE" + ], + "keywords": [ + "rollup", + "plugin", + "run" + ], + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + }, + "dependencies": { + "@types/node": "14.18.30" + }, + "devDependencies": { + "@rollup/plugin-typescript": "^9.0.1", + "del": "^6.1.1", + "rollup": "^4.0.0-24", + "sinon": "^14.0.0", + "typescript": "^4.8.3" + }, + "types": "./types/index.d.ts", + "ava": { + "workerThreads": false, + "files": [ + "!**/fixtures/**", + "!**/output/**", + "!**/helpers/**", + "!**/recipes/**", + "!**/types.ts" + ] + } +} diff --git a/rollup.config.mjs b/rollup.config.mjs new file mode 100644 index 0000000..2a28aaa --- /dev/null +++ b/rollup.config.mjs @@ -0,0 +1,7 @@ +import { readFileSync } from 'fs'; + +import { createConfig } from '../../shared/rollup.config.mjs'; + +export default createConfig({ + pkg: JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8')) +}); diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..63c7dbe --- /dev/null +++ b/src/index.ts @@ -0,0 +1,84 @@ +import type { ChildProcess } from 'child_process'; +import { fork } from 'child_process'; +import { resolve, join, dirname } from 'path'; + +import type { Plugin, RenderedChunk } from 'rollup'; + +import type { RollupRunOptions } from '../types'; + +export default function run(opts: RollupRunOptions = {}): Plugin { + let input: string; + let proc: ChildProcess; + + const args = opts.args || []; + const allowRestarts = opts.allowRestarts || false; + const overrideInput = opts.input; + const forkOptions = opts.options || opts; + delete (forkOptions as RollupRunOptions).args; + delete (forkOptions as RollupRunOptions).allowRestarts; + + return { + name: 'run', + + buildStart(options) { + let inputs = overrideInput ?? options.input; + + if (typeof inputs === 'string') { + inputs = [inputs]; + } + + if (typeof inputs === 'object') { + inputs = Object.values(inputs); + } + + if (inputs.length > 1) { + throw new Error( + `@rollup/plugin-run must have a single entry point; consider setting the \`input\` option` + ); + } + + input = resolve(inputs[0]); + }, + + generateBundle(_outputOptions, _bundle, isWrite) { + if (!isWrite) { + this.error(`@rollup/plugin-run currently only works with bundles that are written to disk`); + } + }, + + writeBundle(outputOptions, bundle) { + const forkBundle = (dir: string, entryFileName: string) => { + if (proc) proc.kill(); + proc = fork(join(dir, entryFileName), args, forkOptions); + }; + + const dir = outputOptions.dir || dirname(outputOptions.file!); + const entryFileName = Object.keys(bundle).find((fileName) => { + const chunk = bundle[fileName] as RenderedChunk; + return chunk.isEntry && chunk.facadeModuleId === input; + }); + + if (entryFileName) { + 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) { + // eslint-disable-next-line no-console + console.clear(); + } + }); + } + } else { + this.error(`@rollup/plugin-run could not find output chunk`); + } + } + }; +} diff --git a/test/fixtures/change-detect-input.js b/test/fixtures/change-detect-input.js new file mode 100644 index 0000000..83a20c5 --- /dev/null +++ b/test/fixtures/change-detect-input.js @@ -0,0 +1 @@ +export const Greeting = () => 'Hola'; // eslint-disable-line \ No newline at end of file diff --git a/test/fixtures/facade-entry/dynamic.js b/test/fixtures/facade-entry/dynamic.js new file mode 100644 index 0000000..6c82d09 --- /dev/null +++ b/test/fixtures/facade-entry/dynamic.js @@ -0,0 +1,3 @@ +import log from './library'; + +log(0); diff --git a/test/fixtures/facade-entry/index.js b/test/fixtures/facade-entry/index.js new file mode 100644 index 0000000..a2002c1 --- /dev/null +++ b/test/fixtures/facade-entry/index.js @@ -0,0 +1,6 @@ +import log from './library'; + +log(0); +(async () => { + await import('./dynamic'); +})(); diff --git a/test/fixtures/facade-entry/library.js b/test/fixtures/facade-entry/library.js new file mode 100644 index 0000000..b92fec2 --- /dev/null +++ b/test/fixtures/facade-entry/library.js @@ -0,0 +1,2 @@ +const log = (value) => console.log(value); +export default log; diff --git a/test/fixtures/input.js b/test/fixtures/input.js new file mode 100644 index 0000000..0f5dc63 --- /dev/null +++ b/test/fixtures/input.js @@ -0,0 +1 @@ +export const Greeting = () => 'Hello'; // eslint-disable-line diff --git a/test/fixtures/output/bundle.js b/test/fixtures/output/bundle.js new file mode 100644 index 0000000..9339703 --- /dev/null +++ b/test/fixtures/output/bundle.js @@ -0,0 +1,5 @@ +'use strict'; + +const Greeting = () => 'Hola'; // eslint-disable-line + +exports.Greeting = Greeting; diff --git a/test/fixtures/output/change-detect-input.js b/test/fixtures/output/change-detect-input.js new file mode 100644 index 0000000..9339703 --- /dev/null +++ b/test/fixtures/output/change-detect-input.js @@ -0,0 +1,5 @@ +'use strict'; + +const Greeting = () => 'Hola'; // eslint-disable-line + +exports.Greeting = Greeting; diff --git a/test/fixtures/output/dynamic-2Lf4t603.js b/test/fixtures/output/dynamic-2Lf4t603.js new file mode 100644 index 0000000..966cf9e --- /dev/null +++ b/test/fixtures/output/dynamic-2Lf4t603.js @@ -0,0 +1,5 @@ +'use strict'; + +var index = require('./index-KtU15qp4.js'); + +index.log(0); diff --git a/test/fixtures/output/index-KtU15qp4.js b/test/fixtures/output/index-KtU15qp4.js new file mode 100644 index 0000000..0c2fc5a --- /dev/null +++ b/test/fixtures/output/index-KtU15qp4.js @@ -0,0 +1,10 @@ +'use strict'; + +const log = (value) => console.log(value); + +log(0); +(async () => { + await Promise.resolve().then(function () { return require('./dynamic-2Lf4t603.js'); }); +})(); + +exports.log = log; diff --git a/test/fixtures/output/index.js b/test/fixtures/output/index.js new file mode 100644 index 0000000..96bc8b7 --- /dev/null +++ b/test/fixtures/output/index.js @@ -0,0 +1,4 @@ +'use strict'; + +require('./index-KtU15qp4.js'); + diff --git a/test/fixtures/output/input.js b/test/fixtures/output/input.js new file mode 100644 index 0000000..b3f31bb --- /dev/null +++ b/test/fixtures/output/input.js @@ -0,0 +1,5 @@ +'use strict'; + +const Greeting = () => 'Hello'; // eslint-disable-line + +exports.Greeting = Greeting; diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..5878504 --- /dev/null +++ b/test/test.js @@ -0,0 +1,170 @@ +const fs = require('fs'); +const { EventEmitter } = require('events'); + +const { join } = require('path'); + +const childProcess = require('child_process'); + +const writeFile = require('util').promisify(fs.writeFile); + +const del = require('del'); +const { rollup } = require('rollup'); +const test = require('ava'); +const sinon = require('sinon'); + +const run = require('../'); + +const cwd = join(__dirname, 'fixtures/'); +const outputDir = join(cwd, 'output'); +const file = join(outputDir, 'bundle.js'); +const input = join(cwd, 'input.js'); + +process.chdir(cwd); + +const outputOptions = { file, format: 'cjs' }; +const outputDirOptions = { dir: outputDir, format: 'cjs' }; + +let mockChildProcess; +test.before(() => { + mockChildProcess = sinon + .stub(childProcess, ['fork']) + .returns({ ...new EventEmitter(), kill: sinon.fake() }); +}); + +test('builds the bundle and forks a child process', async (t) => { + const bundle = await rollup({ + input, + plugins: [run()] + }); + await bundle.write(outputOptions); + t.true(mockChildProcess.calledWithExactly(outputOptions.file, [], {})); +}); + +test('takes input from the latest options', async (t) => { + const bundle = await rollup({ + input: 'incorrect', + plugins: [ + run(), + { + options(options) { + // eslint-disable-next-line no-param-reassign + options.input = input; + return options; + } + } + ] + }); + await bundle.write(outputOptions); + t.true(mockChildProcess.calledWithExactly(outputOptions.file, [], {})); +}); + +test('checks entry point facade module', async (t) => { + const bundle = await rollup({ + input: join(cwd, 'facade-entry/index.js'), + preserveEntrySignatures: 'strict', + plugins: [run()] + }); + await bundle.write(outputDirOptions); + t.true(mockChildProcess.calledWithExactly(join(outputDir, 'index.js'), [], {})); +}); + +test('allows pass-through options for child_process.fork', async (t) => { + const forkOptions = { + cwd, + detached: false, + silent: false + }; + const bundle = await rollup({ + input, + plugins: [run(forkOptions)] + }); + await bundle.write(outputOptions); + t.true(mockChildProcess.calledWithExactly(outputOptions.file, [], forkOptions)); +}); + +test('throws an error when bundle is not written to disk', async (t) => { + const bundle = await rollup({ + input, + plugins: [run()] + }); + await t.throwsAsync( + async () => { + await bundle.generate(outputOptions); + }, + { + instanceOf: Error, + message: '@rollup/plugin-run currently only works with bundles that are written to disk' + } + ); +}); + +test('throws an error when input option is invalid', async (t) => { + const testInput = join(cwd, 'change-detect-input.js'); + const bundle = await rollup({ + input: [input, testInput], + plugins: [run({ input: 'something that is not an input' })] + }); + await t.throwsAsync( + async () => { + await bundle.write(outputDirOptions); + }, + { + instanceOf: Error, + message: '@rollup/plugin-run could not find output chunk' + } + ); +}); + +test('throws an error when there are multiple entry points', async (t) => { + const testInput = join(cwd, 'change-detect-input.js'); + await t.throwsAsync( + async () => { + await rollup({ + input: [input, testInput], + plugins: [run()] + }); + }, + { + instanceOf: Error, + message: + '@rollup/plugin-run must have a single entry point; consider setting the `input` option' + } + ); +}); + +test('detects changes - forks a new child process and kills older process', async (t) => { + // eslint-disable-next-line no-shadow + const testInput = join(cwd, 'change-detect-input.js'); + const bundle = await rollup({ + input: testInput, + plugins: [run()] + }); + await bundle.write(outputOptions); + await writeFile(testInput, "export const Greeting = () => 'Hola'; // eslint-disable-line"); + await bundle.write(outputOptions); + t.true(mockChildProcess.calledWithExactly(outputOptions.file, [], {})); + 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('allow the input option', async (t) => { + const testInput = join(cwd, 'change-detect-input.js'); + const bundle = await rollup({ + input: [input, testInput], + plugins: [run({ input })] + }); + await bundle.write(outputDirOptions); + t.true(mockChildProcess.calledWithExactly(join(outputDir, 'input.js'), [], { input })); +}); + +test.after(async () => { + await del(['output']); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 120000 index 0000000..7cd38da --- /dev/null +++ b/tsconfig.json @@ -0,0 +1 @@ +../../shared/tsconfig.json \ No newline at end of file diff --git a/types/index.d.ts b/types/index.d.ts new file mode 100644 index 0000000..fe9fbe5 --- /dev/null +++ b/types/index.d.ts @@ -0,0 +1,16 @@ +import type { ForkOptions } from 'child_process'; + +import type { Plugin } from 'rollup'; + +export interface RollupRunOptions extends ForkOptions { + args?: readonly string[]; + options?: ForkOptions; + allowRestarts?: boolean; + input?: string; +} + +/** + * Run your bundles in Node once they're built + * @param options These are passed through to `child_process.fork(..)` + */ +export default function run(options?: RollupRunOptions): Plugin;