diff --git a/.appveyor.yml b/.appveyor.yml index 42168b1..411d1a8 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -5,7 +5,6 @@ environment: - nodejs_version: '16' - nodejs_version: '14' - nodejs_version: '12' - - nodejs_version: '10' install: - ps: Install-Product node $env:nodejs_version diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 73d7ce9..824c9d5 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: - node-version: [10.x, 12.x, 14.x, 16.x] + node-version: [12.x, 14.x, 16.x] steps: - uses: actions/checkout@v2 diff --git a/.npmignore b/.npmignore index e53b828..2e65f5b 100644 --- a/.npmignore +++ b/.npmignore @@ -1,8 +1,12 @@ +.appveyor.yml .eslintrc .eslintcache +.github +.husky .nyc_output .prettierrc .travis.yml +images package-lock.json test test-results.xml diff --git a/.travis.yml b/.travis.yml index 2152446..30cf704 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,5 +7,4 @@ node_js: - 16 - 14 - 12 - - 10 sudo: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 2781a9f..be6fcca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # node-dev +## v7.0.0 / 2021-05-04 + +- [CLI] Improve command-line parsing, restore support for --require with a space +- [README] Move images into repo and fix URLs +- [dependencies] Update `minimist` from `v1.1.3` to `v1.2.5` +- [.npmignore] Add more config files + +### Developer Updates + +- [CI] Add github workflows +- [CI] Add appveyor +- [CI] Start testing against node v16 +- [CI] Stop testing against node v10 +- [`test/spawn`] Split `index` into multiple files +- [`test/utils`] Replaced directory of files with a single module that contains two methods: `spawn` and `touchFile` +- [`test/utils/run`] Moved `run` function directly into the `run` file +- [devDependenies] Update `eslint` from `v7.23.0` to `v7.25.0` + ## v6.7.0 / 2021-04-07 - [New Option] `--debounce` to control how long to wait before restarting diff --git a/README.md b/README.md index 591557b..3026ee0 100644 --- a/README.md +++ b/README.md @@ -66,9 +66,9 @@ npm install -g node-dev Status and error messages can be displayed as desktop notification using [node-notifier](https://www.npmjs.org/package/node-notifier): -![Screenshot](http://fgnass.github.com/images/node-dev.png) +![Screenshot](./images/node-dev.png) -![Screenshot](http://fgnass.github.com/images/node-dev-linux.png) +![Screenshot](./images/node-dev-linux.png) **Requirements:** diff --git a/images/node-dev-linux.png b/images/node-dev-linux.png new file mode 100644 index 0000000..1852ed4 Binary files /dev/null and b/images/node-dev-linux.png differ diff --git a/images/node-dev.png b/images/node-dev.png new file mode 100644 index 0000000..ae0e0f6 Binary files /dev/null and b/images/node-dev.png differ diff --git a/lib/cli.js b/lib/cli.js index c3c7e03..0b7016b 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -1,88 +1,79 @@ +const assert = require('assert'); const minimist = require('minimist'); const { resolve } = require('path'); -const { defaultConfig, getConfig } = require('./cfg'); +const { getConfig } = require('./cfg'); -const configKeys = Object.keys(defaultConfig); +const arrayify = v => (Array.isArray(v) ? [...v] : [v]); +const argify = key => ({ arg: `--${key}`, key }); -function resolvePath(unresolvedPath) { - return resolve(process.cwd(), unresolvedPath); -} +const resolvePath = p => resolve(process.cwd(), p); -const doubleDash = s => /^--/.test(s); -const dash = s => /^-[^-]*$/.test(s); +const nodeAlias = { require: 'r' }; +const nodeBoolean = ['expose_gc']; +const nodeOptional = ['inspect', 'inspect-brk']; +const nodeString = ['require']; -function getFirstNonOptionArgIndex(args) { - for (let i = 2; i < args.length; i += 1) { - if (!doubleDash(args[i]) && !dash(args[i]) && !dash(args[i - 1] || '')) return i; - } +const nodeDevBoolean = ['clear', 'dedupe', 'fork', 'notify', 'poll', 'respawn', 'vm']; +const nodeDevNumber = ['debounce', 'deps', 'interval']; +const nodeDevString = ['content', 'graceful_ipc', 'ignore', 'timestamp']; - return args.length - 1; -} +const alias = Object.assign({}, nodeAlias); +const boolean = [...nodeBoolean, ...nodeDevBoolean]; +const string = [...nodeString, ...nodeDevString]; -function unique(k) { - const seen = []; - return o => { - if (!seen.includes(o[k])) { - seen.push(o[k]); - return true; - } - return false; - }; -} +const nodeArgsReducer = opts => (out, { arg, key }) => { + const value = opts[key]; -module.exports = argv => { - const unknownArgs = []; + if (typeof value === 'boolean') { + value && out.push(arg); + } else if (typeof value !== 'undefined') { + arrayify(value).forEach(v => { + if (arg.includes('=')) { + out.push(`${arg.split('=')[0]}=${v}`); + } else { + out.push(`${arg}=${v}`); + } + }); + } - const scriptIndex = getFirstNonOptionArgIndex(argv); + delete opts[key]; - const script = argv[scriptIndex]; - const scriptArgs = argv.slice(scriptIndex + 1); - const devArgs = argv.slice(2, scriptIndex); + return out; +}; - const opts = minimist(devArgs, { - boolean: ['clear', 'dedupe', 'fork', 'notify', 'poll', 'respawn', 'vm'], - string: ['content', 'graceful_ipc', 'ignore', 'timestamp'], - default: getConfig(script), - unknown: arg => { - const key = Object.keys(minimist([arg]))[1]; +const nodeOptionalFactory = args => arg => { + const isNodeOptional = nodeOptional.includes(arg.substring(2)); + if (isNodeOptional) args.push(arg); + return !isNodeOptional; +}; - if (!configKeys.includes(key)) { - unknownArgs.push({ arg, key }); - } - } - }); +const unknownFactory = args => arg => { + const [, key] = Object.keys(minimist([arg])); + key && !nodeDevNumber.includes(key) && args.push({ arg, key }); +}; - const nodeArgs = unknownArgs.filter(unique('key')).reduce((out, { arg, key }) => { - const value = opts[key]; +module.exports = argv => { + const nodeOptionalArgs = []; + const args = argv.slice(2).filter(nodeOptionalFactory(nodeOptionalArgs)); - if (typeof value !== 'boolean' && !arg.includes('=')) { - if (Array.isArray(value)) { - value.forEach(v => out.push(arg, v)); - } else { - out.push(arg, value); - } - } else { - out.push(arg); - } + const unknownArgs = []; + const unknown = unknownFactory(unknownArgs); + + const { + _: [script, ...scriptArgs] + } = minimist(args, { alias, boolean, string, unknown }); - return out; - }, []); + assert(script, 'Could not parse command line arguments'); - unknownArgs.forEach(({ key }) => { - delete opts[key]; - }); + const opts = minimist(args, { alias, boolean, default: getConfig(script) }); - const resolveOption = name => - [...(Array.isArray(opts[name]) ? opts[name] : [opts[name]])].map(resolvePath); + const nodeArgs = [...nodeBoolean.map(argify), ...nodeString.map(argify), ...unknownArgs] + .sort((a, b) => a.key - b.key) + .reduce(nodeArgsReducer(opts), [...nodeOptionalArgs]); - opts.ignore = resolveOption('ignore'); - opts.content = resolveOption('content'); + const content = arrayify(opts.content === true ? '.' : opts.content).map(resolvePath); + const ignore = arrayify(opts.ignore).map(resolvePath); - return { - script, - scriptArgs, - nodeArgs, - opts - }; + return { nodeArgs, opts: { ...opts, content, ignore }, script, scriptArgs }; }; diff --git a/lib/content.js b/lib/content.js index 2c95dec..6230a7a 100644 --- a/lib/content.js +++ b/lib/content.js @@ -1,48 +1,31 @@ -const fs = require('fs'); -const crypto = require('crypto'); +const { createHash } = require('crypto'); +const { createReadStream } = require('fs'); -const contentMap = {}; +const getMD5 = file => + new Promise((resolve, reject) => { + const hash = createHash('md5'); + const stream = createReadStream(file); -const getFileHash = (srcPath, cb) => { - const stream = fs.createReadStream(srcPath); - const md5sum = crypto.createHash('md5'); - stream.on('data', data => md5sum.update(data)); - stream.on('error', cb).on('close', () => { - cb(null, md5sum.digest('hex')); + stream.on('close', () => resolve(hash.digest('hex'))); + stream.on('data', hash.update.bind(hash)); + stream.on('error', reject); }); -}; -const getFileHashCaught = (file, hashCb) => { - getFileHash(file, (err, hash) => { - if (!err) { - hashCb(hash); - } else { - console.error('Error getting file hash:', err); - hashCb(null); - } - }); -}; +const contentFactory = prefixes => { + const pathMatch = file => prefixes.some(prefix => file.startsWith(prefix)); -const clearContentMap = () => { - Object.keys(contentMap).forEach(key => delete contentMap[key]); -}; + const contentMap = {}; -const checkContentChanged = (file, changedCb) => { - getFileHashCaught(file, hash => { - const currentHash = contentMap[file]; - if (hash) { - contentMap[file] = hash; - changedCb(currentHash !== hash); - } else { - changedCb(true); - } - }); -}; + const contentChange = file => + getMD5(file) + .catch(() => null) + .then(hash => { + const prevHash = contentMap[file]; + contentMap[file] = hash; + return hash !== prevHash; + }); -const addToContentMap = file => { - getFileHashCaught(file, hash => { - if (hash) contentMap[file] = hash; - }); + return file => new Promise(resolve => resolve(!pathMatch(file) || contentChange(file))); }; -module.exports = { addToContentMap, checkContentChanged, clearContentMap }; +module.exports = { contentFactory }; diff --git a/lib/index.js b/lib/index.js index 2801d2e..1c27187 100644 --- a/lib/index.js +++ b/lib/index.js @@ -4,7 +4,7 @@ const { extname } = require('path'); const semver = require('semver'); const { clearFactory } = require('./clear'); -const { addToContentMap, checkContentChanged, clearContentMap } = require('./content'); +const { contentFactory } = require('./content'); const { configureDeps, configureIgnore } = require('./ignore'); const ipc = require('./ipc'); const localPath = require('./local-path'); @@ -53,7 +53,6 @@ module.exports = function ( const log = logFactory({ timestamp }); const notify = notifyFactory(notifyEnabled, log); - const isContent = configureIgnore(content); const isIgnored = configureIgnore(ignore); const isTooDeep = configureDeps(deps); @@ -64,20 +63,18 @@ module.exports = function ( const watcher = filewatcher({ debounce, forcePolling, interval }); let isPaused = false; + let contentChanged = null; // The child_process let child; - watcher.on('change', file => { - const onChange = () => { - if (isPaused) return; - isPaused = true; - - clearContentMap(); + watcher.on('change', file => + contentChanged(file).then(changed => { + if (!changed) return; clearOutput(); notify('Restarting', `${file} has been modified`); - watcher.removeAll(); + isPaused = true; if (child) { // Child is still running, restart upon exit child.on('exit', start); @@ -86,18 +83,8 @@ module.exports = function ( // Child is already stopped, probably due to a previous error start(); } - }; - - if (isPaused) return; - - if (isContent(file)) { - checkContentChanged(file, changed => { - if (changed) onChange(); - }); - return; - } - onChange(); - }); + }) + ); watcher.on('fallback', limit => { log.warn('node-dev ran out of file handles after watching %s files.', limit); @@ -111,6 +98,7 @@ module.exports = function ( */ function start() { isPaused = false; + contentChanged = contentFactory(content); const cmd = nodeArgs.concat(wrapper, script, scriptArgs); if (extname(script) === '.mjs') { @@ -140,10 +128,9 @@ module.exports = function ( // Listen for `required` messages and watch the required file. ipc.on(child, 'required', ({ required }) => { - if (isPaused || isIgnored(required) || isTooDeep(required)) return; - watcher.add(required); - if (isContent(required)) { - addToContentMap(required); + if (!isPaused && !isIgnored(required) && !isTooDeep(required)) { + contentChanged(required); + watcher.add(required); } }); diff --git a/package.json b/package.json index 68f2bc2..e68c880 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-dev", - "version": "6.7.0", + "version": "7.0.0", "description": "Restarts your app when files are modified", "keywords": [ "restart", @@ -24,7 +24,7 @@ }, "main": "./lib", "engines": { - "node": ">=10" + "node": ">=12" }, "scripts": { "lint": "eslint lib test bin/node-dev", @@ -35,14 +35,14 @@ "dateformat": "^3.0.3", "dynamic-dedupe": "^0.3.0", "filewatcher": "~3.0.0", - "minimist": "^1.1.3", + "minimist": "^1.2.5", "node-notifier": "^8.0.1", "resolve": "^1.0.0", "semver": "^7.3.5" }, "devDependencies": { "@types/node": "^14.14.37", - "eslint": "^7.23.0", + "eslint": "^7.25.0", "eslint-plugin-import": "^2.22.1", "husky": "^6.0.0", "lint-staged": "^10.5.4", diff --git a/test/change-file.js b/test/change-file.js deleted file mode 100644 index d865c5f..0000000 --- a/test/change-file.js +++ /dev/null @@ -1,26 +0,0 @@ -const { join } = require('path'); -const fs = require('fs'); - -const dir = join(__dirname, 'fixture'); - -const replaceInFile = (filePath, from, to) => { - fs.writeFileSync(filePath, fs.readFileSync(filePath, 'utf-8').replace(from, to)); -}; - -const updateFile = (filename, from, to) => { - setTimeout(() => replaceInFile(join(dir, filename), from, to), 500); -}; - -const changeFile = filename => { - updateFile(filename, 'change', 'revert'); -}; - -const revertFile = filename => { - updateFile(filename, 'revert', 'change'); -}; - -const revertFileImmediate = filename => { - replaceInFile(join(dir, filename), 'revert', 'change'); -}; - -module.exports = { changeFile, revertFile, revertFileImmediate }; diff --git a/test/cli.js b/test/cli.js index 5000bdd..72e1333 100644 --- a/test/cli.js +++ b/test/cli.js @@ -69,19 +69,19 @@ tap.test('cli overrides .node-dev.json from false to true', t => { tap.test('-r ts-node/register --inspect test/fixture/server.js', t => { const argv = 'node bin/node-dev -r ts-node/register --inspect test/fixture/server.js'.split(' '); const { nodeArgs } = cli(argv); - t.same(nodeArgs, ['-r', 'ts-node/register', '--inspect']); + t.same(nodeArgs, ['--inspect', '--require=ts-node/register']); t.end(); }); tap.test('--inspect -r ts-node/register test/fixture/server.js', t => { const argv = 'node bin/node-dev --inspect -r ts-node/register test/fixture/server.js'.split(' '); const { nodeArgs } = cli(argv); - t.same(nodeArgs, ['--inspect', '-r', 'ts-node/register']); + t.same(nodeArgs, ['--inspect', '--require=ts-node/register']); t.end(); }); tap.test('--expose_gc gc.js foo', t => { - const argv = 'node bin/node-dev --expose_gc test/fixture/gc.js test/fixture/foo'.split(' '); + const argv = 'node bin/node-dev --expose_gc test/fixture/gc.js foo'.split(' '); const { nodeArgs } = cli(argv); t.same(nodeArgs, ['--expose_gc']); t.end(); @@ -135,7 +135,7 @@ tap.test('--content=output', t => { tap.test('--content', t => { const { opts: { content } - } = cli(['node', 'bin/node-dev', '--content', 'test']); + } = cli(['node', 'bin/node-dev', '--content', '--', 'test']); t.same(content, [path.resolve(process.cwd())]); t.end(); @@ -158,3 +158,147 @@ tap.test('--debounce=2000', t => { t.equal(debounce, 2000); t.end(); }); + +tap.test('--require source-map-support/register', t => { + const { nodeArgs } = cli([ + 'node', + 'bin/node-dev', + '--require', + 'source-map-support/register', + 'test' + ]); + + t.same(nodeArgs, ['--require=source-map-support/register']); + t.end(); +}); + +tap.test('--require=source-map-support/register', t => { + const { nodeArgs } = cli([ + 'node', + 'bin/node-dev', + '--require=source-map-support/register', + 'test' + ]); + + t.same(nodeArgs, ['--require=source-map-support/register']); + t.end(); +}); + +tap.test('-r source-map-support/register', t => { + const { nodeArgs } = cli(['node', 'bin/node-dev', '-r', 'source-map-support/register', 'test']); + + t.same(nodeArgs, ['--require=source-map-support/register']); + t.end(); +}); + +tap.test('-r=source-map-support/register', t => { + const { nodeArgs } = cli(['node', 'bin/node-dev', '-r=source-map-support/register', 'test']); + + t.same(nodeArgs, ['--require=source-map-support/register']); + t.end(); +}); + +tap.test('--inspect=127.0.0.1:12345', t => { + const { nodeArgs } = cli(['node', 'bin/node-dev', '--inspect=127.0.0.1:12345', 'test']); + + t.same(nodeArgs, ['--inspect=127.0.0.1:12345']); + t.end(); +}); + +tap.test('--inspect', t => { + const { nodeArgs } = cli(['node', 'bin/node-dev', '--inspect', 'test']); + + t.same(nodeArgs, ['--inspect']); + t.end(); +}); + +tap.test('--require source-map-support/register --require ts-node/register', t => { + const { nodeArgs } = cli([ + 'node', + 'bin/node-dev', + '--require', + 'source-map-support/register', + '--require', + 'ts-node/register', + 'test' + ]); + + t.same(nodeArgs, ['--require=source-map-support/register', '--require=ts-node/register']); + t.end(); +}); + +// This should display usage information at some point +tap.test('No script or option should fail', t => { + t.throws(() => cli(['node', 'bin/node-dev'])); + t.end(); +}); + +tap.test('Just an option should fail', t => { + t.throws(() => cli(['node', 'bin/node-dev', '--option'])); + t.end(); +}); + +tap.test('Just an option with a value should fail', t => { + t.throws(() => cli(['node', 'bin/node-dev', '--option=value'])); + t.end(); +}); + +tap.test('An unknown argument with a value instead of a script should fail.', t => { + t.throws(() => cli(['node', 'bin/node-dev', '--unknown-arg', 'value'])); + t.end(); +}); + +tap.test('An unknown argument with a value', t => { + const { nodeArgs } = cli(['node', 'bin/node-dev', '--unknown-arg=value', 'test']); + + t.same(nodeArgs, ['--unknown-arg=value']); + t.end(); +}); + +tap.test('An unknown argument without a value can use -- to delimit', t => { + // use -- to delimit the end of options + const { nodeArgs } = cli(['node', 'bin/node-dev', '--unknown-arg', '--', 'test']); + + t.same(nodeArgs, ['--unknown-arg']); + t.end(); +}); + +tap.test('Single dash with value', t => { + const { nodeArgs } = cli(['node', 'bin/node-dev', '-u', 'value', 'test']); + + t.same(nodeArgs, ['-u=value']); + t.end(); +}); + +tap.test('Single dash with = and value', t => { + const { nodeArgs } = cli(['node', 'bin/node-dev', '-u=value', 'test']); + + t.same(nodeArgs, ['-u=value']); + t.end(); +}); + +tap.test('Single dash without value should fail', t => { + t.throws(() => cli(['node', 'bin/node-dev', '-u', 'test'])); + t.end(); +}); + +tap.test('Single dash without value can use -- to delimit', t => { + const { nodeArgs } = cli(['node', 'bin/node-dev', '-u', '--', 'test']); + + t.same(nodeArgs, ['-u']); + t.end(); +}); + +tap.test('Repeated single dash', t => { + const { nodeArgs } = cli(['node', 'bin/node-dev', '-u=value1', '-u=value2', 'test']); + + t.same(nodeArgs, ['-u=value1', '-u=value2']); + t.end(); +}); + +tap.test('Repeated single dash without =', t => { + const { nodeArgs } = cli(['node', 'bin/node-dev', '-u', 'value1', '-u', 'value2', 'test']); + + t.same(nodeArgs, ['-u=value1', '-u=value2']); + t.end(); +}); diff --git a/test/fixture/content.js b/test/fixture/content.js new file mode 100644 index 0000000..c1bb720 --- /dev/null +++ b/test/fixture/content.js @@ -0,0 +1,8 @@ +const message = require('./content/message.js'); + +console.log(message); + +setTimeout(() => process.exit(1), 10000); + +process.once('SIGTERM', () => process.exit()); +process.once('beforeExit', () => console.log('exit')); diff --git a/test/fixture/content/message.js b/test/fixture/content/message.js new file mode 100644 index 0000000..7fb0df7 --- /dev/null +++ b/test/fixture/content/message.js @@ -0,0 +1 @@ +module.exports = 'Please change content/message.js now'; diff --git a/test/fixture/message-content.js b/test/fixture/message-content.js deleted file mode 100644 index 911aedb..0000000 --- a/test/fixture/message-content.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = 'Please change message-content.js now'; diff --git a/test/fixture/server.js b/test/fixture/server.js index 829b5dc..f8e669b 100644 --- a/test/fixture/server.js +++ b/test/fixture/server.js @@ -1,7 +1,6 @@ const { createServer } = require('http'); const message = require('./message'); -const content = require('./message-content'); // Changes to this module should not cause a server restart: require('./ignored-module'); @@ -17,7 +16,6 @@ server const { address, port } = server.address(); console.log('Server listening on %s:%s', address, port); console.log(message); - console.log(content); }) .listen(0); diff --git a/test/spawn/content.js b/test/spawn/content.js index 61b45ff..b3b0719 100644 --- a/test/spawn/content.js +++ b/test/spawn/content.js @@ -1,45 +1,45 @@ +const { readFile, writeFile } = require('fs/promises'); +const { join } = require('path'); const tap = require('tap'); -const { spawn, touchFile } = require('../utils'); -const { changeFile, revertFile, revertFileImmediate } = require('../change-file'); +const { spawn } = require('../utils'); -tap.test('should not restart with --content on file touch', t => { - const ps = spawn('--content server.js', out => { - if (out.match(/touch message.js/)) { - touchFile('message-content.js'); - const exitTimeout = setTimeout(() => { - ps.kill('SIGTERM'); - t.end(); - }, 1500); - return out2 => { - clearTimeout(exitTimeout); - if (out2.match(/Restarting/)) { - return { exit: t.fail.bind(t) }; - } else { - return { exit: t.end.bind(t) }; - } - }; - } +const path = join(__dirname, '..', 'fixture', 'content', 'message.js'); + +const replaceInFile = (path, from, to) => + readFile(path, 'utf-8').then(contents => writeFile(path, contents.replace(from, to))); + +tap.test('Does not restart when content does not change', t => { + replaceInFile(path, 'revert', 'change').then(() => { + const ps = spawn('--content=content/message.js content', out => { + if (out.includes('change content/message.js')) { + setTimeout(() => { + ps.kill('SIGTERM'); + t.end(); + }, 1500); + replaceInFile(path, 'change', 'change'); + return () => ({ exit: t.fail.bind(t) }); + } + }); }); }); -tap.test('should restart the server with --content twice', t => { - revertFileImmediate('message-content.js'); - spawn('--content server.js', out => { - console.log('out', out); - if (out.match(/change message-content.js/)) { - changeFile('message-content.js'); - return out2 => { - console.log('out2', out2); - if (out2.match(/Restarting/)) { - revertFile('message-content.js'); - return out3 => { - if (out3.match(/Restarting/)) { - return { exit: t.end.bind(t) }; - } - }; - } - }; - } +tap.test('Restarts when content changes', t => { + replaceInFile(path, 'revert', 'change').then(() => { + spawn('--content=content/message.js content', out => { + if (out.includes('change content/message.js')) { + replaceInFile(path, 'change', 'revert'); + return out2 => { + if (out2.includes('revert content/message.js')) { + replaceInFile(path, 'revert', 'change'); + return out3 => { + if (out3.match('change content/message.js')) { + return { exit: t.end.bind(t) }; + } + }; + } + }; + } + }); }); }); diff --git a/test/spawn/index.js b/test/spawn/index.js index 4f10174..f9ed3fb 100644 --- a/test/spawn/index.js +++ b/test/spawn/index.js @@ -4,6 +4,7 @@ require('./clear'); require('./cli-require'); require('./cluster'); require('./conceal'); +require('./content'); require('./errors'); require('./esmodule'); require('./exit-code'); @@ -16,7 +17,6 @@ require('./node-env'); require('./relay-stdin'); require('./require-extensions'); require('./restart-twice'); -require('./content'); require('./sigterm'); require('./timestamps'); require('./typescript');