diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index a3057d7df4..4c05784095 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -84,6 +84,11 @@ jobs: - name: Install webpack ${{ matrix.webpack-version }} run: npm i webpack@${{ matrix.webpack-version }} + - name: Link webpack-dev-server + run: | + npm link + npm link webpack-dev-server + - name: Run tests for webpack version ${{ matrix.webpack-version }} run: npm run test:coverage -- --ci diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 267a5d21fa..8f9a6f58ba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -29,6 +29,8 @@ Run the relevant [examples](https://github.com/webpack/webpack-dev-server/tree/m 2. Run `npm install` in the root `webpack-dev-server` folder. +3. Run `npm link && npm link webpack-dev-server` to link the current project to `node_modules`. + Once it is done, you can modify any file locally. In the `examples/` directory you'll find a lot of examples with instructions on how to run it. This can be very handy when testing if your code works. If you are modifying a file in the `client/` directory, be sure to run `npm run build:client` after it. This will recompile the files. diff --git a/bin/cli-flags.js b/bin/cli-flags.js index 589d04276c..03042b267c 100644 --- a/bin/cli-flags.js +++ b/bin/cli-flags.js @@ -37,6 +37,14 @@ module.exports = { describe: 'Do not refresh page if HMR fails', group: ADVANCED_GROUP, }, + { + name: 'setup-exit-signals', + type: Boolean, + describe: 'Close and exit the process on SIGINT and SIGTERM', + group: ADVANCED_GROUP, + defaultValue: true, + negative: true, + }, { name: 'stdin', type: Boolean, diff --git a/bin/options.js b/bin/options.js index cf2cfde8d8..20a0618cea 100644 --- a/bin/options.js +++ b/bin/options.js @@ -35,6 +35,11 @@ const options = { describe: 'Do not refresh page if HMR fails', group: ADVANCED_GROUP, }, + 'setup-exit-signals': { + type: 'boolean', + describe: 'Close and exit the process on SIGINT and SIGTERM', + group: ADVANCED_GROUP, + }, stdin: { type: 'boolean', describe: 'close when stdin ends', diff --git a/bin/webpack-dev-server.js b/bin/webpack-dev-server.js index 64dbbeb5fd..dd471ff4cc 100755 --- a/bin/webpack-dev-server.js +++ b/bin/webpack-dev-server.js @@ -1,126 +1,158 @@ #!/usr/bin/env node +/* Based on webpack/bin/webpack.js */ +/* eslint-disable no-console */ 'use strict'; -/* eslint-disable no-shadow, no-console */ - -const debug = require('debug')('webpack-dev-server'); -const importLocal = require('import-local'); -const yargs = require('yargs'); -const webpack = require('webpack'); -const Server = require('../lib/Server'); -const setupExitSignals = require('../lib/utils/setupExitSignals'); -const colors = require('../lib/utils/colors'); -const processOptions = require('../lib/utils/processOptions'); -const getVersions = require('../lib/utils/getVersions'); -const getColorsOption = require('../lib/utils/getColorsOption'); -const options = require('./options'); - -let server; -const serverData = { - server: null, +/** + * @param {string} command process to run + * @param {string[]} args command line arguments + * @returns {Promise} promise + */ +const runCommand = (command, args) => { + const cp = require('child_process'); + return new Promise((resolve, reject) => { + const executedCommand = cp.spawn(command, args, { + stdio: 'inherit', + shell: true, + }); + + executedCommand.on('error', (error) => { + reject(error); + }); + + executedCommand.on('exit', (code) => { + if (code === 0) { + resolve(); + } else { + reject(); + } + }); + }); }; -// we must pass an object that contains the server object as a property so that -// we can update this server property later, and setupExitSignals will be able to -// recognize that the server has been instantiated, because we will set -// serverData.server to the new server object. -setupExitSignals(serverData); -// Prefer the local installation of webpack-dev-server -if (importLocal(__filename)) { - debug('Using local install of webpack-dev-server'); +/** + * @param {string} packageName name of the package + * @returns {boolean} is the package installed? + */ +const isInstalled = (packageName) => { + try { + require.resolve(packageName); - return; -} + return true; + } catch (err) { + return false; + } +}; -try { - require.resolve('webpack-cli'); -} catch (err) { - console.error('The CLI moved into a separate package: webpack-cli'); - console.error( - "Please install 'webpack-cli' in addition to webpack itself to use the CLI" - ); - console.error('-> When using npm: npm i -D webpack-cli'); - console.error('-> When using yarn: yarn add -D webpack-cli'); +/** + * @param {CliOption} cli options + * @returns {void} + */ +const runCli = (cli) => { + if (cli.preprocess) { + cli.preprocess(); + } + const path = require('path'); + const pkgPath = require.resolve(`${cli.package}/package.json`); + // eslint-disable-next-line import/no-dynamic-require + const pkg = require(pkgPath); + // eslint-disable-next-line import/no-dynamic-require + require(path.resolve(path.dirname(pkgPath), pkg.bin[cli.binName])); +}; - process.exitCode = 1; -} +/** + * @typedef {Object} CliOption + * @property {string} name display name + * @property {string} package npm package name + * @property {string} binName name of the executable file + * @property {boolean} installed currently installed? + * @property {string} url homepage + * @property {function} preprocess preprocessor + */ + +/** @type {CliOption} */ +const cli = { + name: 'webpack-cli', + package: 'webpack-cli', + binName: 'webpack-cli', + installed: isInstalled('webpack-cli'), + url: 'https://github.com/webpack/webpack-cli', + preprocess() { + process.argv.splice(2, 0, 'serve'); + }, +}; -yargs.usage( - `${getVersions()}\nUsage: https://webpack.js.org/configuration/dev-server/` -); - -// webpack-cli@3.3 path : 'webpack-cli/bin/config/config-yargs' -let configYargsPath; -try { - require.resolve('webpack-cli/bin/config/config-yargs'); - configYargsPath = 'webpack-cli/bin/config/config-yargs'; -} catch (e) { - configYargsPath = 'webpack-cli/bin/config-yargs'; -} -// eslint-disable-next-line import/no-extraneous-dependencies -// eslint-disable-next-line import/no-dynamic-require -require(configYargsPath)(yargs); - -// It is important that this is done after the webpack yargs config, -// so it overrides webpack's version info. -yargs.version(getVersions()); -yargs.options(options); - -const argv = yargs.argv; - -// webpack-cli@3.3 path : 'webpack-cli/bin/utils/convert-argv' -let convertArgvPath; -try { - require.resolve('webpack-cli/bin/utils/convert-argv'); - convertArgvPath = 'webpack-cli/bin/utils/convert-argv'; -} catch (e) { - convertArgvPath = 'webpack-cli/bin/convert-argv'; -} -// eslint-disable-next-line import/no-extraneous-dependencies -// eslint-disable-next-line import/no-dynamic-require -const config = require(convertArgvPath)(yargs, argv, { - outputFilename: '/bundle.js', -}); +if (!cli.installed) { + const path = require('path'); + const fs = require('graceful-fs'); + const readLine = require('readline'); -function startDevServer(config, options) { - let compiler; + const notify = `CLI for webpack must be installed.\n ${cli.name} (${cli.url})\n`; - const configArr = config instanceof Array ? config : [config]; - const statsColors = getColorsOption(configArr); + console.error(notify); - try { - compiler = webpack(config); - } catch (err) { - if (err instanceof webpack.WebpackOptionsValidationError) { - console.error(colors.error(statsColors, err.message)); - // eslint-disable-next-line no-process-exit - process.exit(1); - } + let packageManager; - throw err; + if (fs.existsSync(path.resolve(process.cwd(), 'yarn.lock'))) { + packageManager = 'yarn'; + } else if (fs.existsSync(path.resolve(process.cwd(), 'pnpm-lock.yaml'))) { + packageManager = 'pnpm'; + } else { + packageManager = 'npm'; } - try { - server = new Server(compiler, options); - serverData.server = server; - } catch (err) { - if (err.name === 'ValidationError') { - console.error(colors.error(statsColors, err.message)); - // eslint-disable-next-line no-process-exit - process.exit(1); - } + const installOptions = [packageManager === 'yarn' ? 'add' : 'install', '-D']; - throw err; - } + console.error( + `We will use "${packageManager}" to install the CLI via "${packageManager} ${installOptions.join( + ' ' + )}".` + ); - server.listen(options.port, options.host, (err) => { - if (err) { - throw err; + const question = `Do you want to install 'webpack-cli' (yes/no): `; + + const questionInterface = readLine.createInterface({ + input: process.stdin, + output: process.stderr, + }); + + // In certain scenarios (e.g. when STDIN is not in terminal mode), the callback function will not be + // executed. Setting the exit code here to ensure the script exits correctly in those cases. The callback + // function is responsible for clearing the exit code if the user wishes to install webpack-cli. + process.exitCode = 1; + questionInterface.question(question, (answer) => { + questionInterface.close(); + + const normalizedAnswer = answer.toLowerCase().startsWith('y'); + + if (!normalizedAnswer) { + console.error( + "You need to install 'webpack-cli' to use webpack via CLI.\n" + + 'You can also install the CLI manually.' + ); + + return; } + process.exitCode = 0; + + console.log( + `Installing '${ + cli.package + }' (running '${packageManager} ${installOptions.join(' ')} ${ + cli.package + }')...` + ); + + runCommand(packageManager, installOptions.concat(cli.package)) + .then(() => { + runCli(cli); + }) + .catch((error) => { + console.error(error); + process.exitCode = 1; + }); }); +} else { + runCli(cli); } - -processOptions(config, argv, (config, options) => { - startDevServer(config, options); -}); diff --git a/examples/cli/stdin/README.md b/examples/cli/stdin/README.md new file mode 100644 index 0000000000..e9364fa6a7 --- /dev/null +++ b/examples/cli/stdin/README.md @@ -0,0 +1,16 @@ +# CLI: Stdin Option + +Specifying this option instructs the server to close when `stdin` ends. + +```console +npm run webpack-dev-server -- --stdin +``` + +## What Should Happen + +1. The server should begin running. +2. Press `CTL+D` on your keyboard. +3. The server should close. + +_Note: the keyboard shortcut for terminating `stdin` can vary depending on the +operating systems._ diff --git a/examples/cli/stdin/app.js b/examples/cli/stdin/app.js new file mode 100644 index 0000000000..da9a93b668 --- /dev/null +++ b/examples/cli/stdin/app.js @@ -0,0 +1,6 @@ +'use strict'; + +const target = document.querySelector('#target'); + +target.innerHTML = + 'Press CTL+D on your keyboard to close the server.'; diff --git a/examples/cli/stdin/webpack.config.js b/examples/cli/stdin/webpack.config.js new file mode 100644 index 0000000000..c02f31ab8e --- /dev/null +++ b/examples/cli/stdin/webpack.config.js @@ -0,0 +1,6 @@ +'use strict'; + +module.exports = { + context: __dirname, + entry: './app.js', +}; diff --git a/examples/util.js b/examples/util.js index 9a5e57ff1d..6413abc8ad 100644 --- a/examples/util.js +++ b/examples/util.js @@ -6,7 +6,6 @@ const fs = require('fs'); const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const marked = require('marked'); -const webpack = require('webpack'); module.exports = { setup(config) { @@ -65,7 +64,6 @@ module.exports = { marked(readme, { renderer }); - result.plugins.push(new webpack.NamedModulesPlugin()); result.plugins.push( new HtmlWebpackPlugin({ filename: 'index.html', diff --git a/lib/Server.js b/lib/Server.js index 63a60f7c60..d466c38e61 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -29,6 +29,8 @@ const getSocketServerImplementation = require('./utils/getSocketServerImplementa const getCompilerConfigArray = require('./utils/getCompilerConfigArray'); const getStatsOption = require('./utils/getStatsOption'); const getColorsOption = require('./utils/getColorsOption'); +const setupExitSignals = require('./utils/setupExitSignals'); +const findPort = require('./utils/findPort'); const schema = require('./options.json'); if (!process.env.WEBPACK_DEV_SERVER) { @@ -70,6 +72,8 @@ class Server { routes(this); killable(this.listeningApp); + setupExitSignals(this); + // Proxy WebSocket without the initial http request // https://github.com/chimurai/http-proxy-middleware#external-websocket-upgrade // eslint-disable-next-line func-names @@ -593,26 +597,44 @@ class Server { listen(port, hostname, fn) { this.hostname = hostname; + if (typeof port !== 'undefined' && port !== this.options.port) { + this.logger.warn( + 'The port specified in options and the port passed as an argument is different.' + ); + } - return this.listeningApp.listen(port, hostname, (err) => { - if (this.options.hot || this.options.liveReload) { - this.createSocketServer(); - } + return ( + findPort(port || this.options.port) + // eslint-disable-next-line no-shadow + .then((port) => { + this.port = port; - if (this.options.bonjour) { - runBonjour(this.options); - } + return this.listeningApp.listen(port, hostname, (err) => { + if (this.options.hot || this.options.liveReload) { + this.createSocketServer(); + } - this.showStatus(); + if (this.options.bonjour) { + runBonjour(this.options); + } - if (fn) { - fn.call(this.listeningApp, err); - } + this.showStatus(); - if (typeof this.options.onListening === 'function') { - this.options.onListening(this); - } - }); + if (fn) { + fn.call(this.listeningApp, err); + } + + if (typeof this.options.onListening === 'function') { + this.options.onListening(this); + } + }); + }) + .catch((err) => { + if (fn) { + fn.call(this.listeningApp, err); + } + }) + ); } close(cb) { diff --git a/lib/options.json b/lib/options.json index 3b9cfdf997..9e60c20ecf 100644 --- a/lib/options.json +++ b/lib/options.json @@ -322,6 +322,9 @@ "public": { "type": "string" }, + "setupExitSignals": { + "type": "boolean" + }, "static": { "anyOf": [ { @@ -349,6 +352,9 @@ } ] }, + "stdin": { + "type": "boolean" + }, "transportMode": { "anyOf": [ { @@ -406,7 +412,9 @@ "progress": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverprogress---cli-only)", "proxy": "should be {Object|Array} (https://webpack.js.org/configuration/dev-server/#devserverproxy)", "public": "should be {String} (https://webpack.js.org/configuration/dev-server/#devserverpublic)", + "setupExitSignals": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserversetupexitsignals)", "static": "should be {Boolean|String|Object|Array} (https://webpack.js.org/configuration/dev-server/#devserverstatic)", + "stdin": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserverstdin)", "transportMode": "should be {String|Object} (https://webpack.js.org/configuration/dev-server/#devservertransportmode)", "useLocalIp": "should be {Boolean} (https://webpack.js.org/configuration/dev-server/#devserveruselocalip)" } diff --git a/lib/utils/createConfig.js b/lib/utils/createConfig.js deleted file mode 100644 index f449761c0d..0000000000 --- a/lib/utils/createConfig.js +++ /dev/null @@ -1,143 +0,0 @@ -'use strict'; - -const path = require('path'); -const defaultTo = require('./defaultTo'); - -function createConfig(config, argv, { port }) { - const firstWpOpt = Array.isArray(config) ? config[0] : config; - const options = firstWpOpt.devServer || {}; - - // This updates both config and firstWpOpt - firstWpOpt.mode = defaultTo(firstWpOpt.mode, 'development'); - - if (argv.bonjour) { - options.bonjour = true; - } - - // CLI args host takes precedence over devServer host - if (argv.host) { - options.host = argv.host; - } - - // host defaults to localhost for CLI if none is provided - if (!options.host) { - options.host = 'localhost'; - } - - if (argv.public) { - options.public = argv.public; - } - - if (argv.liveReload === false) { - options.liveReload = false; - } - - if (argv.profile) { - options.profile = argv.profile; - } - - if (argv.progress) { - options.progress = argv.progress; - } - - if (argv.overlay) { - options.overlay = argv.overlay; - } - - options.dev = options.dev || {}; - - if (argv.stdin) { - process.stdin.on('end', () => { - // eslint-disable-next-line no-process-exit - process.exit(0); - }); - - process.stdin.resume(); - } - - const hasHot = typeof argv.hot !== 'undefined'; - const hasHotOnly = typeof argv.hotOnly !== 'undefined'; - - if (hasHot || hasHotOnly) { - options.hot = hasHotOnly ? 'only' : argv.hot; - } - - if (argv.clientLogging) { - if (options.client) { - options.client.logging = argv.clientLogging; - } else { - options.client = { - logging: argv.clientLogging, - }; - } - } - - if (argv.static === false) { - options.static = false; - } else if (argv.static) { - if (Array.isArray(argv.static)) { - options.static = argv.static; - } else { - options.static = [argv.static]; - } - options.static = options.static.map((staticDir) => path.resolve(staticDir)); - } - - if (argv.https) { - options.https = true; - } - - if (argv.http2) { - options.http2 = true; - } - - if (argv.historyApiFallback) { - options.historyApiFallback = true; - } - - if (argv.compress) { - options.compress = true; - } - - if (argv.firewall === '') { - // the user provided --firewall, indicating that they want it enabled - options.firewall = true; - } else if (argv.firewall === false) { - options.firewall = false; - } else if (typeof argv.firewall === 'string') { - options.firewall = [argv.firewall]; - } else if (argv.firewall) { - // argv.firewall is an array - options.firewall = argv.firewall; - } - - if (argv.openPage) { - options.open = true; - options.openPage = argv.openPage.split(','); - } - - if (typeof argv.open !== 'undefined') { - options.open = argv.open !== '' ? argv.open : true; - } - - if (options.open && !options.openPage) { - options.openPage = ''; - } - - if (argv.useLocalIp) { - options.useLocalIp = true; - } - - // Kind of weird, but ensures prior behavior isn't broken in cases - // that wouldn't throw errors. E.g. both argv.port and options.port - // were specified, but since argv.port is 8080, options.port will be - // tried first instead. - options.port = - argv.port === port - ? defaultTo(options.port, argv.port) - : defaultTo(argv.port, options.port); - - return options; -} - -module.exports = createConfig; diff --git a/lib/utils/processOptions.js b/lib/utils/processOptions.js deleted file mode 100644 index 33a8d4a145..0000000000 --- a/lib/utils/processOptions.js +++ /dev/null @@ -1,40 +0,0 @@ -'use strict'; - -const createConfig = require('./createConfig'); -const defaultPort = require('./defaultPort'); -const findPort = require('./findPort'); - -function processOptions(config, argv, callback) { - // processOptions {Promise} - if (typeof config.then === 'function') { - config - .then((conf) => processOptions(conf, argv, callback)) - .catch((err) => { - // eslint-disable-next-line no-console - console.error(err.stack || err); - // eslint-disable-next-line no-process-exit - process.exit(1); - }); - - return; - } - - // Taken out of yargs because we must know if - // it wasn't given by the user, in which case - // we should use portfinder. - const options = createConfig(config, argv, { port: defaultPort }); - - findPort(options.port) - .then((port) => { - options.port = port; - callback(config, options); - }) - .catch((err) => { - // eslint-disable-next-line no-console - console.error(err.stack || err); - // eslint-disable-next-line no-process-exit - process.exit(1); - }); -} - -module.exports = processOptions; diff --git a/lib/utils/setupExitSignals.js b/lib/utils/setupExitSignals.js index 03bb9bdd3c..edde6ab31e 100644 --- a/lib/utils/setupExitSignals.js +++ b/lib/utils/setupExitSignals.js @@ -2,20 +2,29 @@ const signals = ['SIGINT', 'SIGTERM']; -function setupExitSignals(serverData) { - signals.forEach((signal) => { - process.on(signal, () => { - if (serverData && serverData.server) { - serverData.server.close(() => { - // eslint-disable-next-line no-process-exit - process.exit(); - }); - } else { +function setupExitSignals(server) { + const closeAndExit = () => { + if (server) { + server.close(() => { // eslint-disable-next-line no-process-exit process.exit(); - } + }); + } else { + // eslint-disable-next-line no-process-exit + process.exit(); + } + }; + + if (server.options.setupExitSignals) { + signals.forEach((signal) => { + process.on(signal, closeAndExit); }); - }); + } + + if (server.options.stdin) { + process.stdin.on('end', closeAndExit); + process.stdin.resume(); + } } module.exports = setupExitSignals; diff --git a/package-lock.json b/package-lock.json index 888288d9f7..1f5623fc33 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3612,6 +3612,21 @@ "@xtuc/long": "4.2.2" } }, + "@webpack-cli/info": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.1.0.tgz", + "integrity": "sha512-uNWSdaYHc+f3LdIZNwhdhkjjLDDl3jP2+XBqAq9H8DjrJUvlOKdP8TNruy1yEaDfgpAIgbSAN7pye4FEHg9tYQ==", + "dev": true, + "requires": { + "envinfo": "^7.7.3" + } + }, + "@webpack-cli/serve": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.1.0.tgz", + "integrity": "sha512-7RfnMXCpJ/NThrhq4gYQYILB18xWyoQcBey81oIyVbmgbc6m5ZHHyFK+DyH7pLHJf0p14MxL4mTsoPAgBSTpIg==", + "dev": true + }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -3765,6 +3780,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -3811,6 +3827,12 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, + "array-back": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.1.tgz", + "integrity": "sha512-Z/JnaVEXv+A9xabHzN43FiiiWEE7gPCRXMrVmRm00tWbjZRul1iHm7ECzlyNq1p4a4ATXz+G9FJ3GqGOkOV3fg==", + "dev": true + }, "array-filter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", @@ -4631,7 +4653,8 @@ "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true }, "camelcase-keys": { "version": "6.2.2", @@ -4882,31 +4905,6 @@ } } }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, "clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", @@ -4939,6 +4937,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "requires": { "color-name": "1.1.3" } @@ -4946,7 +4945,14 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "colorette": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", + "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", + "dev": true }, "combined-stream": { "version": "1.0.8", @@ -4957,6 +4963,18 @@ "delayed-stream": "~1.0.0" } }, + "command-line-usage": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.1.tgz", + "integrity": "sha512-F59pEuAR9o1SF/bD0dQBDluhpT4jJQNWUHEuVBqpDmCUo6gPjCi+m9fCWnWZVR/oG6cMTUms4h+3NPl74wGXvA==", + "dev": true, + "requires": { + "array-back": "^4.0.1", + "chalk": "^2.4.2", + "table-layout": "^1.0.1", + "typical": "^5.2.0" + } + }, "commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -6216,7 +6234,8 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true }, "decamelize-keys": { "version": "1.1.0", @@ -6267,6 +6286,12 @@ "regexp.prototype.flags": "^1.2.0" } }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -6377,12 +6402,6 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, - "detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", - "dev": true - }, "detect-indent": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.0.0.tgz", @@ -6690,7 +6709,8 @@ "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true }, "emojis-list": { "version": "3.0.0", @@ -6711,59 +6731,6 @@ "once": "^1.4.0" } }, - "enhanced-resolve": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz", - "integrity": "sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" - }, - "dependencies": { - "memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", @@ -6779,6 +6746,12 @@ "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", "dev": true }, + "envinfo": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.7.3.tgz", + "integrity": "sha512-46+j5QxbPWza0PB1i15nZx0xQ4I/EfQxg9J8Had3b408SV63nEtor2e+oiY63amTo9KTuh2a3XLObNwduxYwwA==", + "dev": true + }, "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", @@ -7578,15 +7551,6 @@ } } }, - "expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, "expect": { "version": "26.3.0", "resolved": "https://registry.npmjs.org/expect/-/expect-26.3.0.tgz", @@ -7987,123 +7951,6 @@ "semver-regex": "^2.0.0" } }, - "findup-sync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", - "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", - "dev": true, - "requires": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, "flat-cache": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", @@ -8392,7 +8239,8 @@ "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true }, "get-own-enumerable-property-symbols": { "version": "3.0.2", @@ -8746,61 +8594,6 @@ "ini": "^1.3.4" } }, - "global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dev": true, - "requires": { - "global-prefix": "^3.0.0" - }, - "dependencies": { - "global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", - "dev": true, - "requires": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", - "dev": true, - "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - }, - "dependencies": { - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -8991,15 +8784,6 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "requires": { - "parse-passwd": "^1.0.0" - } - }, "hosted-git-info": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", @@ -9302,6 +9086,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "dev": true, "requires": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" @@ -9532,7 +9317,8 @@ "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true }, "is-generator-fn": { "version": "2.1.0", @@ -14420,12 +14206,6 @@ "lines-and-columns": "^1.1.6" } }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true - }, "parse5": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", @@ -15094,6 +14874,12 @@ "strip-indent": "^3.0.0" } }, + "reduce-flatten": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", + "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", + "dev": true + }, "regenerate": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", @@ -15375,7 +15161,8 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true }, "require-from-string": { "version": "2.0.2", @@ -15386,7 +15173,8 @@ "require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true }, "requires-port": { "version": "1.0.0", @@ -15406,37 +15194,16 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "requires": { - "resolve-from": "^5.0.0" - } - }, - "resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", "dev": true, "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" - }, - "dependencies": { - "global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", - "dev": true, - "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - } - } + "resolve-from": "^5.0.0" } }, "resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true }, "resolve-global": { "version": "1.0.0", @@ -15870,7 +15637,8 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true }, "set-value": { "version": "2.0.1", @@ -16659,6 +16427,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, "requires": { "emoji-regex": "^7.0.1", "is-fullwidth-code-point": "^2.0.0", @@ -16668,12 +16437,14 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true }, "strip-ansi": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, "requires": { "ansi-regex": "^4.1.0" } @@ -16910,6 +16681,18 @@ "string-width": "^3.0.0" } }, + "table-layout": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.1.tgz", + "integrity": "sha512-dEquqYNJiGwY7iPfZ3wbXDI944iqanTSchrACLL2nOB+1r+h1Nzu2eH+DuPPvWvm5Ry7iAPeFlgEtP5bIp5U7Q==", + "dev": true, + "requires": { + "array-back": "^4.0.1", + "deep-extend": "~0.6.0", + "typical": "^5.2.0", + "wordwrapjs": "^4.0.0" + } + }, "tapable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", @@ -17354,6 +17137,12 @@ "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", "dev": true }, + "typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true + }, "uglify-js": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.1.tgz", @@ -18265,151 +18054,121 @@ } }, "webpack-cli": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.12.tgz", - "integrity": "sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.2.0.tgz", + "integrity": "sha512-EIl3k88vaF4fSxWSgtAQR+VwicfLMTZ9amQtqS4o+TDPW9HGaEpbFBbAZ4A3ZOT5SOnMxNOzROsSTPiE8tBJPA==", "dev": true, "requires": { - "chalk": "^2.4.2", - "cross-spawn": "^6.0.5", - "enhanced-resolve": "^4.1.1", - "findup-sync": "^3.0.0", - "global-modules": "^2.0.0", - "import-local": "^2.0.0", - "interpret": "^1.4.0", - "loader-utils": "^1.4.0", - "supports-color": "^6.1.0", - "v8-compile-cache": "^2.1.1", - "yargs": "^13.3.2" + "@webpack-cli/info": "^1.1.0", + "@webpack-cli/serve": "^1.1.0", + "colorette": "^1.2.1", + "command-line-usage": "^6.1.0", + "commander": "^6.2.0", + "enquirer": "^2.3.6", + "execa": "^4.1.0", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "leven": "^3.1.0", + "rechoir": "^0.7.0", + "v8-compile-cache": "^2.2.0", + "webpack-merge": "^4.2.2" }, "dependencies": { + "commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", + "dev": true + }, "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", "dev": true }, - "import-local": { + "is-stream": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", - "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", - "dev": true, - "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" - } - }, - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", "dev": true }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "path-key": "^3.0.0" } }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "rechoir": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.0.tgz", + "integrity": "sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==", "dev": true, "requires": { - "find-up": "^3.0.0" + "resolve": "^1.9.0" } }, - "resolve-cwd": { + "shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", - "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { - "resolve-from": "^3.0.0" + "shebang-regex": "^3.0.0" } }, - "resolve-from": { + "shebang-regex": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "v8-compile-cache": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", + "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", "dev": true }, - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -18543,7 +18302,8 @@ "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true }, "which-pm-runs": { "version": "1.0.0", @@ -18576,6 +18336,16 @@ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", "dev": true }, + "wordwrapjs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.0.tgz", + "integrity": "sha512-Svqw723a3R34KvsMgpjFBYCgNOSdcW3mQFK4wIfhGQhtaFVOJmdYoXgi63ne3dTlWgatVcUc7t4HtQ/+bUVIzQ==", + "dev": true, + "requires": { + "reduce-flatten": "^2.0.0", + "typical": "^5.0.0" + } + }, "worker-farm": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", @@ -18585,31 +18355,6 @@ "errno": "~0.1.7" } }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -18673,7 +18418,8 @@ "y18n": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true }, "yallist": { "version": "3.1.1", @@ -18690,64 +18436,6 @@ "@babel/runtime": "^7.9.2" } }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - } - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, "yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", diff --git a/package.json b/package.json index c04e017ac6..969690dfed 100644 --- a/package.json +++ b/package.json @@ -40,13 +40,12 @@ "chokidar": "^3.4.2", "compression": "^1.7.4", "connect-history-api-fallback": "^1.6.0", - "debug": "^4.1.1", "del": "^5.1.0", "express": "^4.17.1", "find-cache-dir": "^3.3.1", + "graceful-fs": "^4.2.4", "html-entities": "^1.3.1", "http-proxy-middleware": "^1.0.5", - "import-local": "^3.0.2", "internal-ip": "^6.1.0", "ip": "^1.1.5", "is-absolute-url": "^3.0.3", @@ -64,8 +63,7 @@ "url": "^0.11.0", "util": "^0.12.3", "webpack-dev-middleware": "^4.0.2", - "ws": "^7.3.1", - "yargs": "^13.3.2" + "ws": "^7.3.1" }, "devDependencies": { "@babel/cli": "^7.10.5", @@ -108,7 +106,7 @@ "typescript": "^3.9.7", "url-loader": "^4.1.0", "webpack": "^4.44.1", - "webpack-cli": "^3.3.12", + "webpack-cli": "^4.2.0", "webpack-merge": "^4.2.2" }, "peerDependencies": { diff --git a/test/__snapshots__/Validation.test.js.snap b/test/__snapshots__/Validation.test.js.snap index 07ee0055a6..79840f2139 100644 --- a/test/__snapshots__/Validation.test.js.snap +++ b/test/__snapshots__/Validation.test.js.snap @@ -39,5 +39,5 @@ exports[`Validation validation should fail validation for invalid \`static\` con exports[`Validation validation should fail validation for no additional properties 1`] = ` "Invalid configuration object. Object has been initialized using a configuration object that does not match the API schema. - configuration has an unknown property 'additional'. These properties are valid: - object { bonjour?, client?, compress?, dev?, firewall?, headers?, historyApiFallback?, host?, hot?, http2?, https?, injectClient?, injectHot?, liveReload?, onAfterSetupMiddleware?, onBeforeSetupMiddleware?, onListening?, open?, openPage?, overlay?, port?, profile?, progress?, proxy?, public?, static?, transportMode?, useLocalIp? }" + object { bonjour?, client?, compress?, dev?, firewall?, headers?, historyApiFallback?, host?, hot?, http2?, https?, injectClient?, injectHot?, liveReload?, onAfterSetupMiddleware?, onBeforeSetupMiddleware?, onListening?, open?, openPage?, overlay?, port?, profile?, progress?, proxy?, public?, setupExitSignals?, static?, stdin?, transportMode?, useLocalIp? }" `; diff --git a/test/cli/__snapshots__/cli.test.js.snap b/test/cli/__snapshots__/cli.test.js.snap new file mode 100644 index 0000000000..711e506b35 --- /dev/null +++ b/test/cli/__snapshots__/cli.test.js.snap @@ -0,0 +1,91 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CLI --hot webpack 4 1`] = ` +" [webpack-dev-server] Project is running at http://localhost:8080/ + [webpack-dev-middleware] Hash: X + Version: webpack x.x.x Time: Xms + Built at: Thu Jan 01 1970 00:00:00 GMT + Asset Size Chunks Chunk Names + main.js X KiB main [emitted] main + Entrypoint main = main.js + [0] multi Xdir/client/default?http://localhost (webpack)/hot/dev-server.js ./foo.js X bytes {main} [built] + [../../../client/default/index.js?http://localhost] Xdir/client/default?http://localhost X KiB {main} [built] + [../../../client/default/overlay.js] Xdir/client/default/overlay.js X KiB {main} [built] + [../../../client/default/socket.js] Xdir/client/default/socket.js X KiB {main} [built] + [../../../client/default/utils/createSocketUrl.js] Xdir/client/default/utils/createSocketUrl.js X KiB {main} [built] + [../../../client/default/utils/log.js] Xdir/client/default/utils/log.js X bytes {main} [built] + [../../../client/default/utils/reloadApp.js] Xdir/client/default/utils/reloadApp.js X KiB {main} [built] + [../../../client/default/utils/sendMessage.js] Xdir/client/default/utils/sendMessage.js X bytes {main} [built] + [../../../client/transpiled-modules/strip-ansi.js] Xdir/client/transpiled-modules/strip-ansi.js X KiB {main} [built] + [../../../node_modules/webpack/hot sync ^\\\\.\\\\/log$] (webpack)/hot sync nonrecursive ^\\\\.\\\\/log$ X bytes {main} [built] + [../../../node_modules/webpack/hot/dev-server.js] (webpack)/hot/dev-server.js X KiB {main} [built] + [../../../node_modules/webpack/hot/emitter.js] (webpack)/hot/emitter.js X bytes {main} [built] + [../../../node_modules/webpack/hot/log-apply-result.js] (webpack)/hot/log-apply-result.js X KiB {main} [built] + [../../../node_modules/webpack/hot/log.js] (webpack)/hot/log.js X KiB {main} [built] + [./foo.js] X bytes {main} [built] + + 18 hidden modules + [webpack-dev-middleware] Compiled successfully." +`; + +exports[`CLI --hot webpack 5 1`] = ` +" [webpack-dev-server] Project is running at http://localhost:8080/ + [webpack-dev-middleware] asset main.js X KiB [emitted] (name: main) + runtime modules X KiB 10 modules + cacheable modules X KiB + modules by path ../../../node_modules/ X KiB 16 modules + modules by path ../../../client/ X KiB + modules by path ../../../client/default/ X KiB 8 modules + modules by path ../../../client/transpiled-modules/*.js X KiB 2 modules + modules by path ../../../client/clients/*.js X KiB + ../../../client/clients/WebsocketClient.js X KiB [built] [code generated] + ../../../client/clients/BaseClient.js X KiB [built] [code generated] + ./foo.js X bytes [built] [code generated] + ../../../node_modules/webpack/hot/ sync nonrecursive ^\\\\.\\\\/log$ X bytes [built] [code generated] + webpack x.x.x compiled successfully in X ms + [webpack-dev-middleware] Compiled successfully." +`; + +exports[`CLI --no-hot webpack 4 1`] = ` +" [webpack-dev-server] Project is running at http://localhost:8080/ + [webpack-dev-middleware] Hash: X + Version: webpack x.x.x Time: Xms + Built at: Thu Jan 01 1970 00:00:00 GMT + Asset Size Chunks Chunk Names + main.js X KiB main [emitted] main + Entrypoint main = main.js + [0] multi Xdir/client/default?http://localhost ./foo.js X bytes {main} [built] + [../../../client/clients/WebsocketClient.js] Xdir/client/clients/WebsocketClient.js X KiB {main} [built] + [../../../client/default/index.js?http://localhost] Xdir/client/default?http://localhost X KiB {main} [built] + [../../../client/default/overlay.js] Xdir/client/default/overlay.js X KiB {main} [built] + [../../../client/default/socket.js] Xdir/client/default/socket.js X KiB {main} [built] + [../../../client/default/utils/createSocketUrl.js] Xdir/client/default/utils/createSocketUrl.js X KiB {main} [built] + [../../../client/default/utils/getCurrentScriptSource.js] Xdir/client/default/utils/getCurrentScriptSource.js X bytes {main} [built] + [../../../client/default/utils/log.js] Xdir/client/default/utils/log.js X bytes {main} [built] + [../../../client/default/utils/reloadApp.js] Xdir/client/default/utils/reloadApp.js X KiB {main} [built] + [../../../client/default/utils/sendMessage.js] Xdir/client/default/utils/sendMessage.js X bytes {main} [built] + [../../../client/transpiled-modules/log.js] Xdir/client/transpiled-modules/log.js X KiB {main} [built] + [../../../client/transpiled-modules/strip-ansi.js] Xdir/client/transpiled-modules/strip-ansi.js X KiB {main} [built] + [../../../node_modules/ansi-html/index.js] Xdir/node_modules/ansi-html/index.js X KiB {main} [built] + [../../../node_modules/webpack/hot sync ^\\\\.\\\\/log$] (webpack)/hot sync nonrecursive ^\\\\.\\\\/log$ X bytes {main} [built] + [./foo.js] X bytes {main} [built] + + 16 hidden modules + [webpack-dev-middleware] Compiled successfully." +`; + +exports[`CLI --no-hot webpack 5 1`] = ` +" [webpack-dev-server] Project is running at http://localhost:8080/ + [webpack-dev-middleware] asset main.js X KiB [emitted] (name: main) + runtime modules X bytes 3 modules + cacheable modules X KiB + modules by path ../../../node_modules/ X KiB 14 modules + modules by path ../../../client/ X KiB + modules by path ../../../client/default/ X KiB 8 modules + modules by path ../../../client/transpiled-modules/*.js X KiB 2 modules + modules by path ../../../client/clients/*.js X KiB + ../../../client/clients/WebsocketClient.js X KiB [built] [code generated] + ../../../client/clients/BaseClient.js X KiB [built] [code generated] + ./foo.js X bytes [built] [code generated] + ../../../node_modules/webpack/hot/ sync nonrecursive ^\\\\.\\\\/log$ X bytes [built] [code generated] + webpack x.x.x compiled successfully in X ms + [webpack-dev-middleware] Compiled successfully." +`; diff --git a/test/cli/cli.test.js b/test/cli/cli.test.js index 2fa893f319..94b9e25c91 100644 --- a/test/cli/cli.test.js +++ b/test/cli/cli.test.js @@ -1,31 +1,94 @@ 'use strict'; -const { resolve } = require('path'); +const { join, resolve } = require('path'); const execa = require('execa'); const testBin = require('../helpers/test-bin'); +const isWebpack5 = require('../helpers/isWebpack5'); + +// skip if webpack-dev-server is not linked +let runCLITest = describe; +let basePath; +try { + basePath = join(require.resolve('webpack-dev-server'), '..', '..').replace( + /\\/g, + '/' + ); +} catch { + runCLITest = describe.skip; +} + +runCLITest('CLI', () => { + /* Based on webpack/test/StatsTestCases.test.js */ + /** + * Escapes regular expression metacharacters + * @param {string} str String to quote + * @returns {string} Escaped string + */ + const quotemeta = (str) => { + return str.replace(/[-[\]\\/{}()*+?.^$|]/g, '\\$&'); + }; + + const normalizeOutput = (output) => + output + // eslint-disable-next-line no-control-regex + .replace(/\u001b\[[0-9;]*m/g, '') + .replace(/[.0-9]+(\s?)(ms|KiB|bytes)/g, 'X$1$2') + .replace( + /(Built at:) (.*)$/gm, + '$1 Thu Jan 01 1970 00:00:00 GMT' + ) + .replace(/webpack [^ )]+/g, 'webpack x.x.x') + .replace(new RegExp(quotemeta(basePath), 'g'), 'Xdir') + .replace(/(Hash:) [a-z0-9]+/g, '$1 X') + .replace(/ dependencies:Xms/g, '') + .replace(/, additional resolving: X ms/g, ''); + + const webpack4Test = isWebpack5 ? it.skip : it; + const webpack5Test = isWebpack5 ? it : it.skip; + + webpack4Test('--hot webpack 4', (done) => { + testBin('--hot') + .then((output) => { + expect(output.exitCode).toEqual(0); + expect(normalizeOutput(output.stderr)).toMatchSnapshot(); + done(); + }) + .catch(done); + }); + + webpack4Test('--no-hot webpack 4', (done) => { + testBin('--no-hot') + .then((output) => { + expect(output.exitCode).toEqual(0); + expect(normalizeOutput(output.stderr)).toMatchSnapshot(); + done(); + }) + .catch(done); + }); -describe('CLI', () => { - it('--hot', (done) => { + webpack5Test('--hot webpack 5', (done) => { testBin('--hot') .then((output) => { expect(output.exitCode).toEqual(0); - expect(output.stderr).toContain('/hot/dev-server'); + expect(normalizeOutput(output.stderr)).toMatchSnapshot(); done(); }) .catch(done); }); - it('--no-hot', (done) => { + webpack5Test('--no-hot webpack 5', (done) => { testBin('--no-hot') .then((output) => { expect(output.exitCode).toEqual(0); - expect(output.stderr).not.toContain('/hot/dev-server'); + expect(normalizeOutput(output.stderr)).toMatchSnapshot(); done(); }) .catch(done); }); - it('--hot-only', (done) => { + // TODO: do not skip after the major version is bumped + // https://github.com/webpack/webpack-cli/commit/7c5a2bae49625ee4982d7478b7e741968731cea2 + it.skip('--hot-only', (done) => { testBin('--hot-only') .then((output) => { expect(output.exitCode).toEqual(0); @@ -147,7 +210,53 @@ describe('CLI', () => { }); }); - it('should use different random port when multiple instances are started on different processes', (done) => { + it('should exit the process when stdin ends if --stdin', (done) => { + const cliPath = resolve(__dirname, '../../bin/webpack-dev-server.js'); + const examplePath = resolve(__dirname, '../../examples/cli/public'); + const cp = execa('node', [cliPath, '--stdin'], { cwd: examplePath }); + + cp.stderr.on('data', (data) => { + const bits = data.toString(); + + if (/Compiled successfully/.test(bits)) { + expect(cp.pid !== 0).toBe(true); + + cp.stdin.write('hello'); + cp.stdin.end('world'); + } + }); + + cp.on('exit', () => { + done(); + }); + }); + + it('should exit the process when stdin ends if --stdin, even before the compilation is done', (done) => { + const cliPath = resolve(__dirname, '../../bin/webpack-dev-server.js'); + const cwd = resolve(__dirname, '../fixtures/cli'); + const cp = execa('node', [cliPath, '--stdin'], { cwd }); + + let killed = false; + + cp.stderr.on('data', () => { + if (!killed) { + expect(cp.pid !== 0).toBe(true); + + cp.stdin.write('hello'); + cp.stdin.end('world'); + } + + killed = true; + }); + + cp.on('exit', () => { + done(); + }); + }); + + // TODO: do not skip after @webpack-cli/serve passes null port by default + // https://github.com/webpack/webpack-cli/pull/2126 + it.skip('should use different random port when multiple instances are started on different processes', (done) => { const cliPath = resolve(__dirname, '../../bin/webpack-dev-server.js'); const cwd = resolve(__dirname, '../fixtures/cli'); diff --git a/test/fixtures/cli/webpack.config.js b/test/fixtures/cli/webpack.config.js index d57c559e15..19a519f105 100644 --- a/test/fixtures/cli/webpack.config.js +++ b/test/fixtures/cli/webpack.config.js @@ -4,7 +4,6 @@ module.exports = { mode: 'development', context: __dirname, entry: './foo.js', - stats: 'verbose', plugins: [ { apply(compiler) { diff --git a/test/ports-map.js b/test/ports-map.js index 8e018b652b..6376ff8bea 100644 --- a/test/ports-map.js +++ b/test/ports-map.js @@ -45,6 +45,7 @@ const portsList = { 'contentBasePublicPath-option': 1, bundle: 1, ModuleFederation: 1, + 'setupExitSignals-option': 1, }; let startPort = 8089; diff --git a/test/server/progress-option.test.js b/test/server/progress-option.test.js index 73366d4a9e..7c0fd99618 100644 --- a/test/server/progress-option.test.js +++ b/test/server/progress-option.test.js @@ -48,12 +48,12 @@ describe('progress', () => { }); compiler.run(() => {}); - const app = server.listen(port, 'localhost'); - - app.on('progress-update', ({ percent, msg }) => { - expect(percent).toBeGreaterThanOrEqual(0); - expect(percent).toBeLessThanOrEqual(100); - expect(typeof msg).toEqual('string'); + server.listen(port, 'localhost').then((app) => { + app.on('progress-update', ({ percent, msg }) => { + expect(percent).toBeGreaterThanOrEqual(0); + expect(percent).toBeLessThanOrEqual(100); + expect(typeof msg).toEqual('string'); + }); }); }); }); diff --git a/test/server/setupExitSignals-option.test.js b/test/server/setupExitSignals-option.test.js new file mode 100644 index 0000000000..c32fc14b80 --- /dev/null +++ b/test/server/setupExitSignals-option.test.js @@ -0,0 +1,60 @@ +'use strict'; + +const config = require('../fixtures/simple-config/webpack.config'); +const testServer = require('../helpers/test-server'); +const port = require('../ports-map')['setupExitSignals-option']; + +describe('setupExitSignals option', () => { + let server; + let exitSpy; + let killSpy; + let stdinResumeSpy; + const signals = ['SIGINT', 'SIGTERM']; + + beforeEach((done) => { + server = testServer.start( + config, + { + setupExitSignals: true, + stdin: true, + port, + }, + done + ); + exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {}); + stdinResumeSpy = jest + .spyOn(process.stdin, 'resume') + .mockImplementation(() => {}); + killSpy = jest.spyOn(server.listeningApp, 'kill'); + }); + + afterEach((done) => { + exitSpy.mockReset(); + stdinResumeSpy.mockReset(); + signals.forEach((signal) => { + process.removeAllListeners(signal); + }); + process.stdin.removeAllListeners('end'); + testServer.close(done); + }); + + it.each(signals)('should close and exit on %s', (signal, done) => { + process.emit(signal); + + setTimeout(() => { + expect(killSpy.mock.calls.length).toEqual(1); + expect(exitSpy.mock.calls.length).toEqual(1); + done(); + }, 1000); + }); + + it('should close and exit on stdin end', (done) => { + process.stdin.emit('end'); + + setTimeout(() => { + expect(killSpy.mock.calls.length).toEqual(1); + expect(exitSpy.mock.calls.length).toEqual(1); + done(); + }, 1000); + }); +}); diff --git a/test/server/utils/__snapshots__/createConfig.test.js.snap b/test/server/utils/__snapshots__/createConfig.test.js.snap deleted file mode 100644 index d91ef15d33..0000000000 --- a/test/server/utils/__snapshots__/createConfig.test.js.snap +++ /dev/null @@ -1,732 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`createConfig bonjour option (devServer config) 1`] = ` -Object { - "bonjour": true, - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig bonjour option 1`] = ` -Object { - "bonjour": true, - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig client.logging option (in devServer config) 1`] = ` -Object { - "client": Object { - "logging": "none", - }, - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig clientLogging option 1`] = ` -Object { - "client": Object { - "logging": "none", - }, - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig clientLogging option overrides devServer config 1`] = ` -Object { - "client": Object { - "logging": "none", - }, - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig compress option (in devServer config) 1`] = ` -Object { - "compress": true, - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig compress option 1`] = ` -Object { - "compress": true, - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig firewall option (boolean false) 1`] = ` -Object { - "dev": Object {}, - "firewall": false, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig firewall option (boolean in devServer config) 1`] = ` -Object { - "dev": Object {}, - "firewall": true, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig firewall option (boolean true) 1`] = ` -Object { - "dev": Object {}, - "firewall": true, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig firewall option (empty string) 1`] = ` -Object { - "dev": Object {}, - "firewall": true, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig firewall option (string array in devServer config) 1`] = ` -Object { - "dev": Object {}, - "firewall": Array [ - ".host.com", - "host2.com", - ], - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig firewall option (string array) 1`] = ` -Object { - "dev": Object {}, - "firewall": Array [ - ".host.com", - "host2.com", - ], - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig historyApiFallback option (in devServer config) 1`] = ` -Object { - "dev": Object {}, - "historyApiFallback": true, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig historyApiFallback option 1`] = ` -Object { - "dev": Object {}, - "historyApiFallback": true, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig host option (devServer config) 1`] = ` -Object { - "dev": Object {}, - "host": "example.dev", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig host option (localhost) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig host option (null) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig host option (specify for CLI and devServer config) 1`] = ` -Object { - "dev": Object {}, - "host": "other.dev", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig host option (undefined) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig host option 1`] = ` -Object { - "dev": Object {}, - "host": "example.dev", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig hot only option (in devServer config) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": "only", - "port": 8080, -} -`; - -exports[`createConfig hot only option 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": "only", - "port": 8080, -} -`; - -exports[`createConfig hot option (false) (in devServer config) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": false, - "port": 8080, -} -`; - -exports[`createConfig hot option (false) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": false, - "port": 8080, -} -`; - -exports[`createConfig hot option (true) (in devServer config) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig hot option (true) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig http2 option (in devServer config) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "http2": true, - "https": true, - "port": 8080, -} -`; - -exports[`createConfig http2 option 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "http2": true, - "https": true, - "port": 8080, -} -`; - -exports[`createConfig https option (in devServer config) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "https": true, - "port": 8080, -} -`; - -exports[`createConfig https option (in devServer config) 2`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "https": Object { - "ca": Object { - "data": Array [], - "type": "Buffer", - }, - }, - "port": 8080, -} -`; - -exports[`createConfig https option 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "https": true, - "port": 8080, -} -`; - -exports[`createConfig https option 2`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "https": true, - "port": 8080, -} -`; - -exports[`createConfig info option 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig liveReload option 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "liveReload": false, - "port": 8080, -} -`; - -exports[`createConfig mimeTypes option - with force 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "mimeTypes": Object { - "force": true, - "typeMap": Object { - "text/html": Array [ - "phtml", - ], - }, - }, - "port": 8080, -} -`; - -exports[`createConfig mimeTypes option 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "mimeTypes": Object { - "text/html": Array [ - "phtml", - ], - }, - "port": 8080, -} -`; - -exports[`createConfig onListening option 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "onListening": [Function], - "port": 8080, -} -`; - -exports[`createConfig open option (boolean) (in devServer config) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "open": true, - "openPage": "", - "port": 8080, -} -`; - -exports[`createConfig open option (boolean) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "open": true, - "openPage": "", - "port": 8080, -} -`; - -exports[`createConfig open option (object) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "open": Object { - "app": Array [ - "Google Chrome", - "--incognito", - ], - }, - "openPage": "", - "port": 8080, -} -`; - -exports[`createConfig open option (string) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "open": "Google Chrome", - "openPage": "", - "port": 8080, -} -`; - -exports[`createConfig openPage multiple option (in devServer config) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "open": true, - "openPage": Array [ - "/different/page", - "/different/page2", - ], - "port": 8080, -} -`; - -exports[`createConfig openPage option (in devServer config) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "open": true, - "openPage": "/different/page", - "port": 8080, -} -`; - -exports[`createConfig openPage option 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "open": true, - "openPage": Array [ - "/different/page", - ], - "port": 8080, -} -`; - -exports[`createConfig overlay option (in devServer config) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "overlay": true, - "port": 8080, -} -`; - -exports[`createConfig overlay option 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "overlay": true, - "port": 8080, -} -`; - -exports[`createConfig port option (difference) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 7070, -} -`; - -exports[`createConfig port option (same) (null) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": null, -} -`; - -exports[`createConfig port option (same) (string) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": "9090", -} -`; - -exports[`createConfig port option (same) (undefined) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": undefined, -} -`; - -exports[`createConfig port option (same) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 9090, -} -`; - -exports[`createConfig profile option 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, - "profile": "profile", -} -`; - -exports[`createConfig progress option (devServer config) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, - "progress": true, -} -`; - -exports[`createConfig progress option 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, - "progress": true, -} -`; - -exports[`createConfig public option (devServer config) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, - "public": true, -} -`; - -exports[`createConfig public option 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, - "public": true, -} -`; - -exports[`createConfig publicPath option (not specify) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig publicPath option (path in devServer option) 1`] = ` -Object { - "dev": Object { - "publicPath": "/assets/", - }, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig publicPath option (path in output option) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig publicPath option (path without starting slash in output option) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig publicPath option (url in devServer option) 1`] = ` -Object { - "dev": Object { - "publicPath": "http://localhost:8080/assets/", - }, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig publicPath option (url in output option) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig simple 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, -} -`; - -exports[`createConfig static option (boolean) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, - "static": false, -} -`; - -exports[`createConfig static option (in devServer config) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, - "static": true, -} -`; - -exports[`createConfig static option (list of strings) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, - "static": Array [ - "assets1", - "assets2", - ], -} -`; - -exports[`createConfig static option (string) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, - "static": Array [ - "assets", - ], -} -`; - -exports[`createConfig useLocalIp option (in devServer config) 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, - "useLocalIp": true, -} -`; - -exports[`createConfig useLocalIp option 1`] = ` -Object { - "dev": Object {}, - "host": "localhost", - "hot": true, - "port": 8080, - "useLocalIp": true, -} -`; diff --git a/test/server/utils/createConfig.test.js b/test/server/utils/createConfig.test.js deleted file mode 100644 index 26e919f0eb..0000000000 --- a/test/server/utils/createConfig.test.js +++ /dev/null @@ -1,820 +0,0 @@ -'use strict'; - -const path = require('path'); -const createConfig = require('../../../lib/utils/createConfig'); -const webpackConfig = require('./../../fixtures/schema/webpack.config.simple'); - -const argv = { - port: 8080, - // Can be `--no-hot` in CLI (undocumented) - hot: true, -}; - -describe('createConfig', () => { - it('simple', () => { - const config = createConfig(webpackConfig, Object.assign({}, argv), { - port: 8080, - }); - - expect(config).toMatchSnapshot(); - }); - - it('bonjour option', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { - bonjour: true, - }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('bonjour option (devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { devServer: { bonjour: true } }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('host option', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { - host: 'example.dev', - }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('host option (localhost)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { - host: 'localhost', - }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('host option (undefined)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { - // eslint-disable-next-line no-undefined - host: undefined, - }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('host option (null)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { - // eslint-disable-next-line no-undefined - host: null, - }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('host option (devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { devServer: { host: 'example.dev' } }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('host option (specify for CLI and devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { devServer: { host: 'example.dev' } }), - Object.assign({}, argv, { host: 'other.dev' }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('public option', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { - public: true, - }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('public option (devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { public: true }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('progress option', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { - progress: true, - }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('progress option (devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { progress: true }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('publicPath option (not specify)', () => { - const config = createConfig(webpackConfig, argv, { port: 8080 }); - - expect(config).toMatchSnapshot(); - }); - - it('publicPath option (path in devServer option)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { - dev: { - publicPath: '/assets/', - }, - }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('publicPath option (url in devServer option)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { - dev: { - publicPath: 'http://localhost:8080/assets/', - }, - }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('publicPath option (url in output option)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - output: { publicPath: 'http://localhost:8080/assets/' }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('publicPath option (path in output option)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - output: { publicPath: '/assets/' }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('publicPath option (path without starting slash in output option)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - output: { publicPath: 'assets/' }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('hot option (true)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { hot: true }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('hot option (false)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { hot: false }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('hot option (true) (in devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { hot: true }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('hot option (false) (in devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { hot: false }, - }), - // eslint-disable-next-line no-undefined - Object.assign({}, argv, { hot: undefined }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('hot only option', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { hotOnly: true }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('hot only option (in devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { hot: 'only' }, - }), - // eslint-disable-next-line no-undefined - Object.assign({}, argv, { hot: undefined }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('clientLogging option', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { clientLogging: 'none' }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('client.logging option (in devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { - client: { - logging: 'none', - }, - }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('clientLogging option overrides devServer config', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { - client: { - logging: 'verbose', - }, - }, - }), - Object.assign({}, argv, { clientLogging: 'none' }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('static option (string)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { static: 'assets' }), - { port: 8080 } - ); - - config.static = config.static.map((staticDir) => - path.relative(process.cwd(), staticDir) - ); - - expect(config).toMatchSnapshot(); - }); - - it('static option (list of strings)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { static: ['assets1', 'assets2'] }), - { port: 8080 } - ); - - config.static = config.static.map((staticDir) => - path.relative(process.cwd(), staticDir) - ); - - expect(config).toMatchSnapshot(); - }); - - it('static option (boolean)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { static: false }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('static option (in devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { static: true }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('info option', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { info: false }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('mimeTypes option', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { mimeTypes: { 'text/html': ['phtml'] } }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('mimeTypes option - with force', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { - mimeTypes: { typeMap: { 'text/html': ['phtml'] }, force: true }, - }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('https option', () => { - const config1 = createConfig( - webpackConfig, - Object.assign({}, argv, { https: true }), - { port: 8080 } - ); - - expect(config1).toMatchSnapshot(); - - const config2 = createConfig( - webpackConfig, - Object.assign({}, argv, { - https: { ca: Buffer.from('') }, - }), - { port: 8080 } - ); - - expect(config2).toMatchSnapshot(); - }); - - it('https option (in devServer config)', () => { - const config1 = createConfig( - Object.assign({}, webpackConfig, { - devServer: { https: true }, - }), - argv, - { port: 8080 } - ); - - expect(config1).toMatchSnapshot(); - - const config2 = createConfig( - Object.assign({}, webpackConfig, { - devServer: { https: { ca: Buffer.from('') } }, - }), - argv, - { port: 8080 } - ); - - expect(config2).toMatchSnapshot(); - }); - - it('http2 option', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { https: true, http2: true }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('http2 option (in devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { https: true, http2: true }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('historyApiFallback option', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { historyApiFallback: true }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('historyApiFallback option (in devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { historyApiFallback: true }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('compress option', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { compress: true }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('compress option (in devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { compress: true }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('firewall option (empty string)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { firewall: '' }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('firewall option (boolean true)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { firewall: true }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('firewall option (boolean false)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { firewall: false }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('firewall option (string array)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { firewall: ['.host.com', 'host2.com'] }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('firewall option (boolean in devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { firewall: true }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('firewall option (string array in devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { firewall: ['.host.com', 'host2.com'] }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('open option (boolean)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { open: true }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('open option (boolean) (in devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { open: true }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('open option (string)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { open: 'Google Chrome' }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('open option (object)', () => { - const config = createConfig( - webpackConfig, - { - ...argv, - open: { - app: ['Google Chrome', '--incognito'], - }, - }, - { port: 8080 } - ); - expect(config).toMatchSnapshot(); - }); - - it('openPage option', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { openPage: '/different/page' }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('openPage option (in devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { open: true, openPage: '/different/page' }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('openPage multiple option (in devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { - open: true, - openPage: ['/different/page', '/different/page2'], - }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('useLocalIp option', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { useLocalIp: true }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('useLocalIp option (in devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { useLocalIp: true }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('port option (same)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { port: 9090 }), - { port: 9090 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('port option (same) (string)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { port: '9090' }), - { port: '9090' } - ); - - expect(config).toMatchSnapshot(); - }); - - it('port option (same) (null)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { port: null }), - { port: null } - ); - - expect(config).toMatchSnapshot(); - }); - - it('port option (same) (undefined)', () => { - const config = createConfig( - webpackConfig, - // eslint-disable-next-line no-undefined - Object.assign({}, argv, { port: undefined }), - // eslint-disable-next-line no-undefined - { port: undefined } - ); - - expect(config).toMatchSnapshot(); - }); - - it('port option (difference)', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { port: 7070 }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('onListening option', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { onListening: () => {} }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('overlay option', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { - overlay: true, - }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('overlay option (in devServer config)', () => { - const config = createConfig( - Object.assign({}, webpackConfig, { - devServer: { overlay: true }, - }), - argv, - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('liveReload option', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { - liveReload: false, - }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); - - it('profile option', () => { - const config = createConfig( - webpackConfig, - Object.assign({}, argv, { - profile: 'profile', - }), - { port: 8080 } - ); - - expect(config).toMatchSnapshot(); - }); -}); diff --git a/test/server/utils/setupExitSignals.test.js b/test/server/utils/setupExitSignals.test.js index 8c40b49429..3c68b86f11 100644 --- a/test/server/utils/setupExitSignals.test.js +++ b/test/server/utils/setupExitSignals.test.js @@ -5,10 +5,14 @@ const setupExitSignals = require('../../../lib/utils/setupExitSignals'); describe('setupExitSignals', () => { let server; let exitSpy; + let stdinResumeSpy; const signals = ['SIGINT', 'SIGTERM']; beforeAll(() => { exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {}); + stdinResumeSpy = jest + .spyOn(process.stdin, 'resume') + .mockImplementation(() => {}); server = { close: jest.fn((callback) => { callback(); @@ -22,46 +26,59 @@ describe('setupExitSignals', () => { signals.forEach((signal) => { process.removeAllListeners(signal); }); + process.stdin.removeAllListeners('end'); }); signals.forEach((signal) => { - it(`should exit process (${signal}, serverData never defined`, (done) => { - // eslint-disable-next-line no-undefined - setupExitSignals(undefined); + it(`should close server, then exit process (${signal})`, (done) => { + const serverSetupExitSignals = Object.assign( + { + options: { + setupExitSignals: true, + }, + }, + server + ); + setupExitSignals(serverSetupExitSignals); process.emit(signal); - setTimeout(() => { - expect(exitSpy.mock.calls.length).toEqual(1); - done(); - }, 1000); - }); - it(`should exit process (${signal}, server never defined)`, (done) => { - setupExitSignals({ - server: null, - }); - process.emit(signal); setTimeout(() => { + expect(server.close.mock.calls.length).toEqual(1); expect(exitSpy.mock.calls.length).toEqual(1); done(); }, 1000); }); + }); - it(`should close server, then exit process (${signal}, server defined before signal)`, (done) => { - const serverData = { - server: null, - }; - setupExitSignals(serverData); + it(`should resume stdin if stdin: true`, () => { + const serverStdin = Object.assign( + { + options: { + stdin: true, + }, + }, + server + ); + setupExitSignals(serverStdin); + expect(stdinResumeSpy.mock.calls.length).toEqual(1); + }); - setTimeout(() => { - serverData.server = server; - process.emit(signal); - }, 500); + it(`should close server, then exit process (stdin end)`, (done) => { + const serverStdin = Object.assign( + { + options: { + stdin: true, + }, + }, + server + ); + setupExitSignals(serverStdin); + process.stdin.emit('end'); - setTimeout(() => { - expect(server.close.mock.calls.length).toEqual(1); - expect(exitSpy.mock.calls.length).toEqual(1); - done(); - }, 1500); - }); + setTimeout(() => { + expect(server.close.mock.calls.length).toEqual(1); + expect(exitSpy.mock.calls.length).toEqual(1); + done(); + }, 1000); }); }); diff --git a/tsconfig.json b/tsconfig.json index 141d025c4d..f6f0c266b1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,5 +12,5 @@ "types": ["node"], "esModuleInterop": true }, - "include": ["lib/utils/DevServerPlugin.js"] + "include": ["bin/webpack-dev-server.js", "lib/utils/DevServerPlugin.js"] }