diff --git a/.babelrc b/.babelrc
new file mode 100644
index 0000000000..002b4aa0d5
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,3 @@
+{
+ "presets": ["env"]
+}
diff --git a/.editorconfig b/.editorconfig
index 636843bbd9..45946eaa25 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,5 +1,13 @@
-root = true
+# editorconfig.org
-[*.js]
-indent_style=tab
-trim_trailing_whitespace=true
\ No newline at end of file
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+insert_final_newline = false
+trim_trailing_whitespace = false
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000000..d05df27c6b
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1 @@
+web_modules
diff --git a/.eslintrc b/.eslintrc
index a973dd4901..a7f05e00fb 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -1,28 +1,19 @@
{
- "env": {
- "node": true
- },
- "plugins": [
- "nodeca"
- ],
- "rules": {
- "strict": 0,
- "camelcase": 0,
- "curly": 0,
- "indent": [0, "tab"],
- "nodeca/indent": [2, "tabs", 1],
- "eol-last": 2,
- "no-shadow": 0,
- "no-redeclare": 2,
- "no-extra-bind": 2,
- "no-empty": 0,
- "no-process-exit": 2,
- "no-underscore-dangle": 0,
- "no-use-before-define": 0,
- "no-unused-vars": 0,
- "consistent-return": 0,
- "no-inner-declarations": 2,
- "no-loop-func": 2,
- "space-before-function-paren": [2, "never"]
- }
+ "extends": "webpack",
+ "globals": {
+ "document": true,
+ "window": true
+ },
+ "parserOptions": {
+ "sourceType": "script"
+ },
+ "rules": {
+ "comma-dangle": ["error", "never"],
+ "consistent-return": "off",
+ "no-param-reassign": "off",
+ "no-underscore-dangle": "off",
+ "prefer-destructuring": ["error", {"object": false, "array": false}],
+ "prefer-rest-params": "off",
+ "strict": ["error", "safe"]
+ }
}
diff --git a/.gitattributes b/.gitattributes
index 37d514472a..ff5566febb 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,2 +1,2 @@
* text=auto
-bin/* eol=lf
\ No newline at end of file
+bin/* eol=lf
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 0000000000..38c940c95e
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @evilebottnawi @michael-ciniawsky
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000000..b847fac37f
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,53 @@
+
+
+* Operating System:
+* Node Version:
+* NPM Version:
+* webpack Version:
+* webpack-dev-server Version:
+
+
+
+- [ ] This is a **bug**
+- [ ] This is a **modification** request
+
+### Code
+
+
+
+```js
+ // webpack.config.js
+```
+
+```js
+ // additional code, remove if not needed.
+```
+
+### Expected Behavior
+
+### Actual Behavior
+
+### For Bugs; How can we reproduce the behavior?
+
+### For Features; What is the motivation and/or use-case for the feature?
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000000..a5e876276c
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,39 @@
+
+
+- [ ] This is a **bugfix**
+- [ ] This is a **feature**
+- [ ] This is a **code refactor**
+- [ ] This is a **test update**
+- [ ] This is a **docs update**
+- [ ] This is a **metadata update**
+
+### For Bugs and Features; did you add new tests?
+
+
+
+### Motivation / Use-Case
+
+
+
+### Breaking Changes
+
+
+
+### Additional Info
diff --git a/.gitignore b/.gitignore
index 88531067fc..efa0cc7443 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,11 @@
+.DS_Store
+
+npm-debug.log
+
+.idea
+.nyc_output
+
+client
+coverage
+ssl/*.pem
node_modules
-/client/live.bundle.js
-/client/index.bundle.js
\ No newline at end of file
diff --git a/.jsbeautifyrc b/.jsbeautifyrc
deleted file mode 100644
index 79b0498467..0000000000
--- a/.jsbeautifyrc
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "js": {
- "allowed_file_extensions": ["js", "json", "jshintrc", "jsbeautifyrc"],
- "brace_style": "collapse",
- "break_chained_methods": false,
- "e4x": true,
- "eval_code": false,
- "end_with_newline": true,
- "indent_char": "\t",
- "indent_level": 0,
- "indent_size": 1,
- "indent_with_tabs": true,
- "jslint_happy": false,
- "jslint_happy_align_switch_case": true,
- "space_after_anon_function": false,
- "keep_array_indentation": false,
- "keep_function_indentation": false,
- "max_preserve_newlines": 2,
- "preserve_newlines": true,
- "space_before_conditional": false,
- "space_in_paren": false,
- "unescape_strings": false,
- "wrap_line_length": 0
- }
-}
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 25e15288ec..03d29e8404 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,40 @@
-sudo: false
language: node_js
-node_js:
- - node
-script: npm run travis
+
+jobs:
+ fast_finish: true
+ include:
+ - stage: Lint
+ env: SCRIPT=lint
+ node_js: 'stable'
+ - &linux
+ stage: Test (Linux)
+ env: SCRIPT=test
+ node_js: 'stable'
+ - <<: *linux
+ node_js: 'lts/*'
+ - <<: *linux
+ node_js: 6
+ - &osx
+ stage: Test (MacOS)
+ os: 'osx'
+ env: SCRIPT=test
+ # Node 10 instead of 'stable' since Node 11 segfaults
+ # https://github.com/webpack/webpack-dev-server/pull/1588
+ node_js: 10
+ - <<: *osx
+ node_js: 'lts/*'
+ - <<: *osx
+ node_js: 6
+
+install:
+ - npm i -g npm@latest
+ - npm i
+
+script: npm run $SCRIPT
+
+after_success:
+ - npm i codecov
+ - $(npm bin)/codecov
+
+notifications:
+ email: false
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000000..21f5986b71
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,66 @@
+# Change Log
+
+All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+
+## [3.1.11](https://github.com/webpack/webpack-dev-server/compare/v3.1.10...v3.1.11) (2018-12-21)
+
+
+### Bug Fixes
+
+* **bin/options:** correct check for color support (`options.color`) ([#1555](https://github.com/webpack/webpack-dev-server/issues/1555)) ([55398b5](https://github.com/webpack/webpack-dev-server/commit/55398b5))
+* **package:** update `spdy` v3.4.1...4.0.0 (assertion error) ([#1491](https://github.com/webpack/webpack-dev-server/issues/1491)) ([#1563](https://github.com/webpack/webpack-dev-server/issues/1563)) ([7a3a257](https://github.com/webpack/webpack-dev-server/commit/7a3a257))
+* **Server:** correct `node` version checks ([#1543](https://github.com/webpack/webpack-dev-server/issues/1543)) ([927a2b3](https://github.com/webpack/webpack-dev-server/commit/927a2b3))
+* **Server:** mime type for wasm in contentBase directory ([#1575](https://github.com/webpack/webpack-dev-server/issues/1575)) ([#1580](https://github.com/webpack/webpack-dev-server/issues/1580)) ([fadae5d](https://github.com/webpack/webpack-dev-server/commit/fadae5d))
+* add url for compatibility with webpack@5 ([#1598](https://github.com/webpack/webpack-dev-server/issues/1598)) ([#1599](https://github.com/webpack/webpack-dev-server/issues/1599)) ([68dd49a](https://github.com/webpack/webpack-dev-server/commit/68dd49a))
+* check origin header for websocket connection ([#1603](https://github.com/webpack/webpack-dev-server/issues/1603)) ([b3217ca](https://github.com/webpack/webpack-dev-server/commit/b3217ca))
+
+
+
+
+## [3.1.10](https://github.com/webpack/webpack-dev-server/compare/v3.1.9...v3.1.10) (2018-10-23)
+
+
+### Bug Fixes
+
+* **options:** add `writeToDisk` option to schema ([#1520](https://github.com/webpack/webpack-dev-server/issues/1520)) ([d2f4902](https://github.com/webpack/webpack-dev-server/commit/d2f4902))
+* **package:** update `sockjs-client` v1.1.5...1.3.0 (`url-parse` vulnerability) ([#1537](https://github.com/webpack/webpack-dev-server/issues/1537)) ([e719959](https://github.com/webpack/webpack-dev-server/commit/e719959))
+* **Server:** set `tls.DEFAULT_ECDH_CURVE` to `'auto'` ([#1531](https://github.com/webpack/webpack-dev-server/issues/1531)) ([c12def3](https://github.com/webpack/webpack-dev-server/commit/c12def3))
+
+
+
+
+## [3.1.9](https://github.com/webpack/webpack-dev-server/compare/v3.1.8...v3.1.9) (2018-09-24)
+
+
+
+
+## [3.1.8](https://github.com/webpack/webpack-dev-server/compare/v3.1.7...v3.1.8) (2018-09-06)
+
+
+### Bug Fixes
+
+* **package:** `yargs` security vulnerability (`dependencies`) ([#1492](https://github.com/webpack/webpack-dev-server/issues/1492)) ([8fb67c9](https://github.com/webpack/webpack-dev-server/commit/8fb67c9))
+* **utils/createLogger:** ensure `quiet` always takes precedence (`options.quiet`) ([#1486](https://github.com/webpack/webpack-dev-server/issues/1486)) ([7a6ca47](https://github.com/webpack/webpack-dev-server/commit/7a6ca47))
+
+
+
+
+## [3.1.7](https://github.com/webpack/webpack-dev-server/compare/v3.1.6...v3.1.7) (2018-08-29)
+
+
+### Bug Fixes
+
+* **Server:** don't use `spdy` on `node >= v10.0.0` ([#1451](https://github.com/webpack/webpack-dev-server/issues/1451)) ([8ab9eb6](https://github.com/webpack/webpack-dev-server/commit/8ab9eb6))
+
+
+
+
+## [3.1.6](https://github.com/webpack/webpack-dev-server/compare/v3.1.5...v3.1.6) (2018-08-26)
+
+
+### Bug Fixes
+
+* **bin:** handle `process` signals correctly when the server isn't ready yet ([#1432](https://github.com/webpack/webpack-dev-server/issues/1432)) ([334c3a5](https://github.com/webpack/webpack-dev-server/commit/334c3a5))
+* **examples/cli:** correct template path in `open-page` example ([#1401](https://github.com/webpack/webpack-dev-server/issues/1401)) ([df30727](https://github.com/webpack/webpack-dev-server/commit/df30727))
+* **schema:** allow the `output` filename to be a `{Function}` ([#1409](https://github.com/webpack/webpack-dev-server/issues/1409)) ([e2220c4](https://github.com/webpack/webpack-dev-server/commit/e2220c4))
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000000..443e5e5d79
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,57 @@
+# Contributing to webpack-dev-server
+
+Do you use webpack-dev-server and want to help us out? Thanks!
+
+Please review this document before contributing.
+
+Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should show respect in addressing your issue or assessing patches and features.
+
+## Core Ideas
+
+- There are hooks to add your own features, so we should not add less-common features.
+- The workflow should be to start webpack-dev-server as a separate process, next to the "normal" server and to request the script from this server or to proxy from dev-server to "normal" server (because webpack blocks the event queue too much while compiling which can affect "normal" server).
+- A user should not try to implement stuff that accesses the webpack filesystem. This lead to bugs (the middleware does it while blocking requests until the compilation has finished, the blocking is important).
+- It should be a development only tool. Compiling in production is bad, one should precompile and deliver the compiled assets.
+- Processing options and stats display is delegated to webpack, so webpack-dev-server/middleware should not do much with it. This also helps us to keep up-to-date with webpack updates.
+- The communication library (`SockJS`) should not be exposed to the user.
+
+## Submitting a Pull Request
+
+Good pull requests, such as patches, improvements, and new features, are a fantastic help. They should remain focused in scope and not contain unrelated commits.
+
+It is advised to first create an issue (if there is not one already) before making a pull request. This way the maintainers can first discuss with you if they agree and it also helps with providing some context.
+
+Run the relevant [examples](https://github.com/webpack/webpack-dev-server/tree/master/examples) to see if all functionality still works. When introducing new functionality, also add an example. This helps the maintainers to understand it and check if it still works.
+
+## Setting Up a Local Copy
+
+1. Clone the repo with `git clone https://github.com/webpack/webpack-dev-server`.
+
+2. Run `npm install` in the root `webpack-dev-server` folder.
+
+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 prepublish` after it. This will recompile the files.
+
+## Testing a Pull Request
+
+Pull requests often need some real-world testing.
+
+1. In your `package.json`, change the line with `webpack-dev-server` to:
+
+ ```json
+ "webpack-dev-server": "github:webpack/webpack-dev-server#pull//head"
+ ```
+
+ `` is the ID of the pull request.
+
+2. Run `npm install`.
+
+3. Go to the `webpack-dev-server` module (`cd node_modules/webpack-dev-server`), and run `npm run prepublish`.
+
+The pull request is now ready to be tested.
+
+
+------------
+
+*Many thanks to [create-react-app](https://github.com/facebookincubator/create-react-app/blob/master/CONTRIBUTING.md) for the inspiration with this contributing guide*
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000..8c11fc7289
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright JS Foundation and other contributors
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
index 0faf650428..9a83f5d6de 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,169 @@
+
+
+[![npm][npm]][npm-url]
+[![node][node]][node-url]
+[![deps][deps]][deps-url]
+[![tests][tests]][tests-url]
+[![coverage][cover]][cover-url]
+[![chat][chat]][chat-url]
+
# webpack-dev-server
-**THIS SERVER SHOULD BE USED FOR DEVELOPMENT ONLY!**
+Use [webpack](https://webpack.js.org) with a development server that provides
+live reloading. This should be used for **development only**.
-**DO NOT USE IT IN PRODUCTION!**
+It uses [webpack-dev-middleware][middleware-url] under the hood, which provides
+fast in-memory access to the webpack assets.
-It's a live reloading server for [webpack](http://webpack.github.io).
+## Getting Started
-# [Documentation](http://webpack.github.io/docs/webpack-dev-server.html)
+First things first, install the module:
-## Inspiration
+```console
+npm install webpack-dev-server --save-dev
+```
-This project is heavily inspired by [peerigon/nof5](https://github.com/peerigon/nof5).
+_Note: While you can install and run webpack-dev-server globally, we recommend
+installing it locally. webpack-dev-server will always use a local installation
+over a global one._
+
+## Usage
+
+There are two main, recommended methods of using the module:
+
+### With the CLI
+
+The easiest way to use it is with the CLI. In the directory where your
+`webpack.config.js` is, run:
+
+```console
+node_modules/.bin/webpack-dev-server
+```
+
+### With NPM Scripts
+
+NPM package.json scripts are a convenient and useful means to run locally installed
+binaries without having to be concerned about their full paths. Simply define a
+script as such:
+
+```json
+"scripts": {
+ "start:dev": "webpack-dev-server"
+}
+```
+
+And run the following in your terminal/console:
+
+```console
+npm run start:dev
+```
+
+NPM will automagically reference the binary in `node_modules` for you, and
+execute the file or command.
+
+### The Result
+
+Either method will start a server instance and begin listening for connections
+from `localhost` on port `8080`.
+
+webpack-dev-server is configured by default to support live-reload of files as
+you edit your assets while the server is running.
+
+See [**the documentation**][docs-url] for more use cases and options.
+
+## Browser Support
+
+While `webpack-dev-server` transpiles the client (browser) scripts to an ES5
+state, the project only officially supports the _last two versions of major
+browsers_. We simply don't have the resources to support every whacky
+browser out there.
+
+If you find an bug with an obscure / old browser, we would actively welcome a
+Pull Request to resolve the bug.
+
+## Support
+
+We do our best to keep Issues in the repository focused on bugs, features, and
+needed modifications to the code for the module. Because of that, we ask users
+with general support, "how-to", or "why isn't this working" questions to try one
+of the other support channels that are available.
+
+Your first-stop-shop for support for webpack-dev-server should by the excellent
+[documentation][docs-url] for the module. If you see an opportunity for improvement
+of those docs, please head over to the [webpack.js.org repo][wjo-url] and open a
+pull request.
+
+From there, we encourage users to visit the [webpack Gitter chat][chat-url] and
+talk to the fine folks there. If your quest for answers comes up dry in chat,
+head over to [StackOverflow][stack-url] and do a quick search or open a new
+question. Remember; It's always much easier to answer questions that include your
+`webpack.config.js` and relevant files!
+
+If you're twitter-savvy you can tweet [#webpack][hash-url] with your question
+and someone should be able to reach out and lend a hand.
+
+If you have discovered a :bug:, have a feature suggestion, or would like to see
+a modification, please feel free to create an issue on Github. _Note: The issue
+template isn't optional, so please be sure not to remove it, and please fill it
+out completely._
## Contributing
-The client scripts are built with `npm run-script prepublish`.
+We welcome your contributions! Please have a read of [CONTRIBUTING.md](CONTRIBUTING.md) for more information on how to get involved.
+
+## Maintainers
+
+
+
+## Attribution
+
+This project is heavily inspired by [peerigon/nof5](https://github.com/peerigon/nof5).
## License
-Copyright 2012-2014 Tobias Koppers
+#### [MIT](./LICENSE)
+
+
+[npm]: https://img.shields.io/npm/v/webpack-dev-server.svg
+[npm-url]: https://npmjs.com/package/webpack-dev-server
+
+[node]: https://img.shields.io/node/v/webpack-dev-server.svg
+[node-url]: https://nodejs.org
+
+[deps]: https://david-dm.org/webpack/webpack-dev-server.svg
+[deps-url]: https://david-dm.org/webpack/webpack-dev-server
+
+[tests]: http://img.shields.io/travis/webpack/webpack-dev-server.svg
+[tests-url]: https://travis-ci.org/webpack/webpack-dev-server
+
+[cover]: https://codecov.io/gh/webpack/webpack-dev-server/branch/master/graph/badge.svg
+[cover-url]: https://codecov.io/gh/webpack/webpack-dev-server
+
+[chat]: https://badges.gitter.im/webpack/webpack.svg
+[chat-url]: https://gitter.im/webpack/webpack
-[MIT](http://www.opensource.org/licenses/mit-license.php)
+[docs-url]: https://webpack.js.org/configuration/dev-server/#devserver
+[hash-url]: https://twitter.com/search?q=webpack
+[middleware-url]: https://github.com/webpack/webpack-dev-middleware
+[stack-url]: https://stackoverflow.com/questions/tagged/webpack-dev-server
+[uglify-url]: https://github.com/webpack-contrib/uglifyjs-webpack-plugin
+[wjo-url]: https://github.com/webpack/webpack.js.org
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000000..7a4a975609
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,27 @@
+build: 'off'
+
+init:
+ - git config --global core.autocrlf input
+
+environment:
+ matrix:
+ - nodejs_version: '10'
+ webpack_version: latest
+ - nodejs_version: '8'
+ webpack_version: latest
+ - nodejs_version: '6'
+ webpack_version: latest
+
+matrix:
+ fast_finish: true
+
+install:
+ - ps: Install-Product node $env:nodejs_version x64
+ - npm i -g npm@latest
+ - npm i
+
+before_test:
+ - cmd: npm i webpack@%webpack_version%
+
+test_script:
+ - cmd: npm t
diff --git a/bin/options.js b/bin/options.js
new file mode 100644
index 0000000000..03e4d9fd74
--- /dev/null
+++ b/bin/options.js
@@ -0,0 +1,166 @@
+'use strict';
+
+/* eslint-disable
+ global-require,
+ multiline-ternary,
+ space-before-function-paren
+*/
+const ADVANCED_GROUP = 'Advanced options:';
+const DISPLAY_GROUP = 'Stats options:';
+const SSL_GROUP = 'SSL options:';
+const CONNECTION_GROUP = 'Connection options:';
+const RESPONSE_GROUP = 'Response options:';
+const BASIC_GROUP = 'Basic options:';
+
+const options = {
+ bonjour: {
+ type: 'boolean',
+ describe: 'Broadcasts the server via ZeroConf networking on start'
+ },
+ lazy: {
+ type: 'boolean',
+ describe: 'Lazy'
+ },
+ inline: {
+ type: 'boolean',
+ default: true,
+ describe: 'Inline mode (set to false to disable including client scripts like livereload)'
+ },
+ progress: {
+ type: 'boolean',
+ describe: 'Print compilation progress in percentage',
+ group: BASIC_GROUP
+ },
+ 'hot-only': {
+ type: 'boolean',
+ describe: 'Do not refresh page if HMR fails',
+ group: ADVANCED_GROUP
+ },
+ stdin: {
+ type: 'boolean',
+ describe: 'close when stdin ends'
+ },
+ open: {
+ type: 'string',
+ describe: 'Open the default browser, or optionally specify a browser name'
+ },
+ useLocalIp: {
+ type: 'boolean',
+ describe: 'Open default browser with local IP'
+ },
+ 'open-page': {
+ type: 'string',
+ describe: 'Open default browser with the specified page',
+ requiresArg: true
+ },
+ color: {
+ type: 'boolean',
+ alias: 'colors',
+ default: function supportsColor() {
+ // Use `require('supports-color').stdout` for supports-color >= 5.0.0.
+ // See https://github.com/webpack/webpack-dev-server/pull/1555.
+ return require('supports-color').stdout;
+ },
+ group: DISPLAY_GROUP,
+ describe: 'Enables/Disables colors on the console'
+ },
+ info: {
+ type: 'boolean',
+ group: DISPLAY_GROUP,
+ default: true,
+ describe: 'Info'
+ },
+ quiet: {
+ type: 'boolean',
+ group: DISPLAY_GROUP,
+ describe: 'Quiet'
+ },
+ 'client-log-level': {
+ type: 'string',
+ group: DISPLAY_GROUP,
+ default: 'info',
+ describe: 'Log level in the browser (info, warning, error or none)'
+ },
+ https: {
+ type: 'boolean',
+ group: SSL_GROUP,
+ describe: 'HTTPS'
+ },
+ key: {
+ type: 'string',
+ describe: 'Path to a SSL key.',
+ group: SSL_GROUP
+ },
+ cert: {
+ type: 'string',
+ describe: 'Path to a SSL certificate.',
+ group: SSL_GROUP
+ },
+ cacert: {
+ type: 'string',
+ describe: 'Path to a SSL CA certificate.',
+ group: SSL_GROUP
+ },
+ pfx: {
+ type: 'string',
+ describe: 'Path to a SSL pfx file.',
+ group: SSL_GROUP
+ },
+ 'pfx-passphrase': {
+ type: 'string',
+ describe: 'Passphrase for pfx file.',
+ group: SSL_GROUP
+ },
+ 'content-base': {
+ type: 'string',
+ describe: 'A directory or URL to serve HTML content from.',
+ group: RESPONSE_GROUP
+ },
+ 'watch-content-base': {
+ type: 'boolean',
+ describe: 'Enable live-reloading of the content-base.',
+ group: RESPONSE_GROUP
+ },
+ 'history-api-fallback': {
+ type: 'boolean',
+ describe: 'Fallback to /index.html for Single Page Applications.',
+ group: RESPONSE_GROUP
+ },
+ compress: {
+ type: 'boolean',
+ describe: 'Enable gzip compression',
+ group: RESPONSE_GROUP
+ },
+ port: {
+ describe: 'The port',
+ group: CONNECTION_GROUP
+ },
+ 'disable-host-check': {
+ type: 'boolean',
+ describe: 'Will not check the host',
+ group: CONNECTION_GROUP
+ },
+ socket: {
+ type: 'String',
+ describe: 'Socket to listen',
+ group: CONNECTION_GROUP
+ },
+ public: {
+ type: 'string',
+ describe: 'The public hostname/ip address of the server',
+ group: CONNECTION_GROUP
+ },
+ host: {
+ type: 'string',
+ default: 'localhost',
+ describe: 'The hostname/ip address the server will bind to',
+ group: CONNECTION_GROUP
+ },
+ 'allowed-hosts': {
+ type: 'string',
+ describe: 'A comma-delimited string of hosts that are allowed to access the dev server',
+ group: CONNECTION_GROUP
+ }
+};
+
+module.exports = options;
diff --git a/bin/utils.js b/bin/utils.js
new file mode 100644
index 0000000000..21873a1509
--- /dev/null
+++ b/bin/utils.js
@@ -0,0 +1,114 @@
+'use strict';
+
+/* eslint-disable
+ no-shadow,
+ global-require,
+ multiline-ternary,
+ array-bracket-spacing,
+ space-before-function-paren
+*/
+const open = require('opn');
+
+const colors = {
+ info (useColor, msg) {
+ if (useColor) {
+ // Make text blue and bold, so it *pops*
+ return `\u001b[1m\u001b[34m${msg}\u001b[39m\u001b[22m`;
+ }
+
+ return msg;
+ },
+ error (useColor, msg) {
+ if (useColor) {
+ // Make text red and bold, so it *pops*
+ return `\u001b[1m\u001b[31m${msg}\u001b[39m\u001b[22m`;
+ }
+
+ return msg;
+ }
+};
+
+// eslint-disable-next-line
+const defaultTo = (value, def) => {
+ return value == null ? def : value;
+};
+
+function version () {
+ return `webpack-dev-server ${require('../package.json').version}\n` +
+ `webpack ${require('webpack/package.json').version}`;
+}
+
+function status (uri, options, log, useColor) {
+ const contentBase = Array.isArray(options.contentBase)
+ ? options.contentBase.join(', ')
+ : options.contentBase;
+
+ if (options.socket) {
+ log.info(`Listening to socket at ${colors.info(useColor, options.socket)}`);
+ } else {
+ log.info(`Project is running at ${colors.info(useColor, uri)}`);
+ }
+
+ log.info(
+ `webpack output is served from ${colors.info(useColor, options.publicPath)}`
+ );
+
+ if (contentBase) {
+ log.info(
+ `Content not from webpack is served from ${colors.info(useColor, contentBase)}`
+ );
+ }
+
+ if (options.historyApiFallback) {
+ log.info(
+ `404s will fallback to ${colors.info(useColor, options.historyApiFallback.index || '/index.html')}`
+ );
+ }
+
+ if (options.bonjour) {
+ log.info(
+ 'Broadcasting "http" with subtype of "webpack" via ZeroConf DNS (Bonjour)'
+ );
+ }
+
+ if (options.open) {
+ let openOptions = {};
+ let openMessage = 'Unable to open browser';
+
+ if (typeof options.open === 'string') {
+ openOptions = { app: options.open };
+ openMessage += `: ${options.open}`;
+ }
+
+ open(uri + (options.openPage || ''), openOptions).catch(() => {
+ log.warn(
+ `${openMessage}. If you are running in a headless environment, please do not use the --open flag`
+ );
+ });
+ }
+}
+
+function bonjour (options) {
+ const bonjour = require('bonjour')();
+
+ bonjour.publish({
+ name: 'Webpack Dev Server',
+ port: options.port,
+ type: 'http',
+ subtypes: [ 'webpack' ]
+ });
+
+ process.on('exit', () => {
+ bonjour.unpublishAll(() => {
+ bonjour.destroy();
+ });
+ });
+}
+
+module.exports = {
+ status,
+ colors,
+ version,
+ bonjour,
+ defaultTo
+};
diff --git a/bin/webpack-dev-server.js b/bin/webpack-dev-server.js
index 9e3e281342..d58daf2954 100755
--- a/bin/webpack-dev-server.js
+++ b/bin/webpack-dev-server.js
@@ -1,199 +1,424 @@
#!/usr/bin/env node
-var path = require("path");
-var url = require("url");
-var open = require("open");
-var fs = require("fs");
-
-// Local version replaces global one
-try {
- var localWebpackDevServer = require.resolve(path.join(process.cwd(), "node_modules", "webpack-dev-server", "bin", "webpack-dev-server.js"));
- if(__filename !== localWebpackDevServer) {
- return require(localWebpackDevServer);
- }
-} catch(e) {}
-
-var Server = require("../lib/Server");
-var webpack = require("webpack");
-
-var optimist = require("optimist")
-
-.usage("webpack-dev-server " + require("../package.json").version + "\n" +
- "Usage: http://webpack.github.io/docs/webpack-dev-server.html")
-
-.boolean("lazy").describe("lazy")
-
-.boolean("stdin").describe("stdin", "close when stdin ends")
-
-.boolean("info").describe("info").default("info", true)
-
-.boolean("quiet").describe("quiet")
-
-.boolean("inline").describe("inline", "Inline the webpack-dev-server logic into the bundle.")
-
-.boolean("https").describe("https")
-
-.string("key").describe("key", "Path to a SSL key.")
-
-.string("cert").describe("cert", "Path to a SSL certificate.")
-
-.string("cacert").describe("cacert", "Path to a SSL CA certificate.")
-
-.string("content-base").describe("content-base", "A directory or URL to serve HTML content from.")
-
-.string("content-base-target").describe("content-base-target", "Proxy requests to this target.")
-
-.boolean("history-api-fallback").describe("history-api-fallback", "Fallback to /index.html for Single Page Applications.")
-
-.boolean("compress").describe("compress", "enable gzip compression")
-
-.boolean("open").describe("open", "Open default browser")
-
-.describe("port", "The port").default("port", 8080)
-
-.describe("public", "The public hostname/ip address of the server")
-
-.describe("host", "The hostname/ip address the server will bind to").default("host", "localhost");
-
-require("webpack/bin/config-optimist")(optimist);
-
-var argv = optimist.argv;
-
-var wpOpt = require("webpack/bin/convert-argv")(optimist, argv, {
- outputFilename: "/bundle.js"
+'use strict';
+
+/* eslint-disable
+ import/order,
+ import/no-extraneous-dependencies,
+ global-require,
+ no-shadow,
+ no-console,
+ multiline-ternary,
+ arrow-parens,
+ array-bracket-spacing,
+ space-before-function-paren
+*/
+const debug = require('debug')('webpack-dev-server');
+
+const fs = require('fs');
+const net = require('net');
+const path = require('path');
+
+const portfinder = require('portfinder');
+const importLocal = require('import-local');
+
+const yargs = require('yargs');
+const webpack = require('webpack');
+
+const options = require('./options');
+
+const {
+ colors,
+ status,
+ version,
+ bonjour,
+ defaultTo
+} = require('./utils');
+
+const Server = require('../lib/Server');
+
+const addEntries = require('../lib/utils/addEntries');
+const createDomain = require('../lib/utils/createDomain');
+const createLogger = require('../lib/utils/createLogger');
+
+let server;
+
+const signals = [ 'SIGINT', 'SIGTERM' ];
+
+signals.forEach((signal) => {
+ process.on(signal, () => {
+ if (server) {
+ server.close(() => {
+ // eslint-disable-next-line no-process-exit
+ process.exit();
+ });
+ } else {
+ // eslint-disable-next-line no-process-exit
+ process.exit();
+ }
+ });
});
-var firstWpOpt = Array.isArray(wpOpt) ? wpOpt[0] : wpOpt;
-var options = wpOpt.devServer || firstWpOpt.devServer || {};
+// Prefer the local installation of webpack-dev-server
+if (importLocal(__filename)) {
+ debug('Using local install of webpack-dev-server');
-if(argv.host !== "localhost" || !options.host)
- options.host = argv.host;
-
-if(argv.public)
- options.public = argv.public;
-
-if(argv.port !== 8080 || !options.port)
- options.port = argv.port;
-
-if(!options.publicPath) {
- options.publicPath = firstWpOpt.output && firstWpOpt.output.publicPath || "";
- if(!/^(https?:)?\/\//.test(options.publicPath) && options.publicPath[0] !== "/")
- options.publicPath = "/" + options.publicPath;
-}
-
-if(!options.outputPath)
- options.outputPath = "/";
-if(!options.filename)
- options.filename = firstWpOpt.output && firstWpOpt.output.filename;
-[].concat(wpOpt).forEach(function(wpOpt) {
- wpOpt.output.path = "/";
-});
-
-if(!options.watchOptions)
- options.watchOptions = firstWpOpt.watchOptions;
-
-if(argv["stdin"]) {
- process.stdin.on('end', function() {
- process.exit(0);
- });
- process.stdin.resume();
-}
-
-if(!options.watchDelay && !options.watchOptions) // TODO remove in next major version
- options.watchDelay = firstWpOpt.watchDelay;
-
-if(!options.hot)
- options.hot = argv["hot"];
-
-if(argv["content-base"]) {
- options.contentBase = argv["content-base"];
- if(/^[0-9]$/.test(options.contentBase))
- options.contentBase = +options.contentBase;
- else if(!/^(https?:)?\/\//.test(options.contentBase))
- options.contentBase = path.resolve(options.contentBase);
-} else if(argv["content-base-target"]) {
- options.contentBase = {
- target: argv["content-base-target"]
- };
-} else if(!options.contentBase) {
- options.contentBase = process.cwd();
+ return;
}
-if(!options.stats) {
- options.stats = {
- cached: false,
- cachedAssets: 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');
+
+ process.exitCode = 1;
}
-if(typeof options.stats === "object" && typeof options.stats.colors === "undefined")
- options.stats.colors = require("supports-color");
-
-if(argv["lazy"])
- options.lazy = true;
+yargs.usage(
+ `${version()}\nUsage: https://webpack.js.org/configuration/dev-server/`
+);
-if(!argv["info"])
- options.noInfo = true;
+require('webpack-cli/bin/config-yargs')(yargs);
+// It is important that this is done after the webpack yargs config,
+// so it overrides webpack's version info.
+yargs.version(version());
+yargs.options(options);
-if(argv["quiet"])
- options.quiet = true;
+const argv = yargs.argv;
-if(argv["https"])
- options.https = true;
-
-if(argv["cert"])
- options.cert = fs.readFileSync(path.resolve(argv["cert"]));
-
-if(argv["key"])
- options.key = fs.readFileSync(path.resolve(argv["key"]));
-
-if(argv["cacert"])
- options.cacert = fs.readFileSync(path.resolve(argv["cacert"]));
-
-if(argv["inline"])
- options.inline = true;
-
-if(argv["history-api-fallback"])
- options.historyApiFallback = true;
-
-if(argv["compress"])
- options.compress = true;
-
-if(argv["open"])
- options.open = true;
-
-var protocol = options.https ? "https" : "http";
-
-if(options.inline) {
- var devClient = [require.resolve("../client/") + "?" + protocol + "://" + (options.public || (options.host + ":" + options.port))];
+const config = require('webpack-cli/bin/convert-argv')(yargs, argv, {
+ outputFilename: '/bundle.js'
+});
+// Taken out of yargs because we must know if
+// it wasn't given by the user, in which case
+// we should use portfinder.
+const DEFAULT_PORT = 8080;
+
+function processOptions (config) {
+ // processOptions {Promise}
+ if (typeof config.then === 'function') {
+ config.then(processOptions).catch((err) => {
+ console.error(err.stack || err);
+ // eslint-disable-next-line no-process-exit
+ process.exit();
+ });
+
+ return;
+ }
+
+ const firstWpOpt = Array.isArray(config)
+ ? config[0]
+ : config;
+
+ const options = config.devServer || firstWpOpt.devServer || {};
+
+ if (argv.bonjour) {
+ options.bonjour = true;
+ }
+
+ if (argv.host !== 'localhost' || !options.host) {
+ options.host = argv.host;
+ }
+
+ if (argv['allowed-hosts']) {
+ options.allowedHosts = argv['allowed-hosts'].split(',');
+ }
+
+ if (argv.public) {
+ options.public = argv.public;
+ }
+
+ if (argv.socket) {
+ options.socket = argv.socket;
+ }
+
+ if (argv.progress) {
+ options.progress = argv.progress;
+ }
+
+ if (!options.publicPath) {
+ // eslint-disable-next-line
+ options.publicPath = firstWpOpt.output && firstWpOpt.output.publicPath || '';
+
+ if (
+ !/^(https?:)?\/\//.test(options.publicPath) &&
+ options.publicPath[0] !== '/'
+ ) {
+ options.publicPath = `/${options.publicPath}`;
+ }
+ }
+
+ if (!options.filename) {
+ options.filename = firstWpOpt.output && firstWpOpt.output.filename;
+ }
+
+ if (!options.watchOptions) {
+ options.watchOptions = firstWpOpt.watchOptions;
+ }
+
+ if (argv.stdin) {
+ process.stdin.on('end', () => {
+ // eslint-disable-next-line no-process-exit
+ process.exit(0);
+ });
+
+ process.stdin.resume();
+ }
+
+ if (!options.hot) {
+ options.hot = argv.hot;
+ }
+
+ if (!options.hotOnly) {
+ options.hotOnly = argv['hot-only'];
+ }
+
+ if (!options.clientLogLevel) {
+ options.clientLogLevel = argv['client-log-level'];
+ }
+
+ // eslint-disable-next-line
+ if (options.contentBase === undefined) {
+ if (argv['content-base']) {
+ options.contentBase = argv['content-base'];
+
+ if (Array.isArray(options.contentBase)) {
+ options.contentBase = options.contentBase.map((p) => path.resolve(p));
+ } else if (/^[0-9]$/.test(options.contentBase)) {
+ options.contentBase = +options.contentBase;
+ } else if (!/^(https?:)?\/\//.test(options.contentBase)) {
+ options.contentBase = path.resolve(options.contentBase);
+ }
+ // It is possible to disable the contentBase by using
+ // `--no-content-base`, which results in arg["content-base"] = false
+ } else if (argv['content-base'] === false) {
+ options.contentBase = false;
+ }
+ }
+
+ if (argv['watch-content-base']) {
+ options.watchContentBase = true;
+ }
+
+ if (!options.stats) {
+ options.stats = {
+ cached: false,
+ cachedAssets: false
+ };
+ }
+
+ if (
+ typeof options.stats === 'object' &&
+ typeof options.stats.colors === 'undefined'
+ ) {
+ options.stats = Object.assign(
+ {},
+ options.stats,
+ { colors: argv.color }
+ );
+ }
+
+ if (argv.lazy) {
+ options.lazy = true;
+ }
+
+ if (!argv.info) {
+ options.noInfo = true;
+ }
+
+ if (argv.quiet) {
+ options.quiet = true;
+ }
+
+ if (argv.https) {
+ options.https = true;
+ }
+
+ if (argv.cert) {
+ options.cert = fs.readFileSync(
+ path.resolve(argv.cert)
+ );
+ }
+
+ if (argv.key) {
+ options.key = fs.readFileSync(
+ path.resolve(argv.key)
+ );
+ }
+
+ if (argv.cacert) {
+ options.ca = fs.readFileSync(
+ path.resolve(argv.cacert)
+ );
+ }
+
+ if (argv.pfx) {
+ options.pfx = fs.readFileSync(
+ path.resolve(argv.pfx)
+ );
+ }
+
+ if (argv['pfx-passphrase']) {
+ options.pfxPassphrase = argv['pfx-passphrase'];
+ }
+
+ if (argv.inline === false) {
+ options.inline = false;
+ }
+
+ if (argv['history-api-fallback']) {
+ options.historyApiFallback = true;
+ }
+
+ if (argv.compress) {
+ options.compress = true;
+ }
+
+ if (argv['disable-host-check']) {
+ options.disableHostCheck = true;
+ }
+
+ if (argv['open-page']) {
+ options.open = true;
+ options.openPage = argv['open-page'];
+ }
+
+ 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 === DEFAULT_PORT
+ ? defaultTo(options.port, argv.port)
+ : defaultTo(argv.port, options.port);
+
+ if (options.port != null) {
+ startDevServer(config, options);
+
+ return;
+ }
+
+ portfinder.basePort = DEFAULT_PORT;
+
+ portfinder.getPort((err, port) => {
+ if (err) {
+ throw err;
+ }
+
+ options.port = port;
+
+ startDevServer(config, options);
+ });
+}
- if(options.hot)
- devClient.push("webpack/hot/dev-server");
- [].concat(wpOpt).forEach(function(wpOpt) {
- if(typeof wpOpt.entry === "object" && !Array.isArray(wpOpt.entry)) {
- Object.keys(wpOpt.entry).forEach(function(key) {
- wpOpt.entry[key] = devClient.concat(wpOpt.entry[key]);
- });
- } else {
- wpOpt.entry = devClient.concat(wpOpt.entry);
- }
- });
+function startDevServer(config, options) {
+ const log = createLogger(options);
+
+ addEntries(config, options);
+
+ let compiler;
+
+ try {
+ compiler = webpack(config);
+ } catch (err) {
+ if (err instanceof webpack.WebpackOptionsValidationError) {
+ log.error(colors.error(options.stats.colors, err.message));
+ // eslint-disable-next-line no-process-exit
+ process.exit(1);
+ }
+
+ throw err;
+ }
+
+ if (options.progress) {
+ new webpack.ProgressPlugin({
+ profile: argv.profile
+ }).apply(compiler);
+ }
+
+ const suffix = (options.inline !== false || options.lazy === true ? '/' : '/webpack-dev-server/');
+
+ try {
+ server = new Server(compiler, options, log);
+ } catch (err) {
+ if (err.name === 'ValidationError') {
+ log.error(colors.error(options.stats.colors, err.message));
+ // eslint-disable-next-line no-process-exit
+ process.exit(1);
+ }
+
+ throw err;
+ }
+
+ if (options.socket) {
+ server.listeningApp.on('error', (e) => {
+ if (e.code === 'EADDRINUSE') {
+ const clientSocket = new net.Socket();
+
+ clientSocket.on('error', (err) => {
+ if (err.code === 'ECONNREFUSED') {
+ // No other server listening on this socket so it can be safely removed
+ fs.unlinkSync(options.socket);
+
+ server.listen(options.socket, options.host, (error) => {
+ if (error) {
+ throw error;
+ }
+ });
+ }
+ });
+
+ clientSocket.connect({ path: options.socket }, () => {
+ throw new Error('This socket is already used');
+ });
+ }
+ });
+
+ server.listen(options.socket, options.host, (err) => {
+ if (err) {
+ throw err;
+ }
+ // chmod 666 (rw rw rw)
+ const READ_WRITE = 438;
+
+ fs.chmod(options.socket, READ_WRITE, (err) => {
+ if (err) {
+ throw err;
+ }
+
+ const uri = createDomain(options, server.listeningApp) + suffix;
+
+ status(uri, options, log, argv.color);
+ });
+ });
+ } else {
+ server.listen(options.port, options.host, (err) => {
+ if (err) {
+ throw err;
+ }
+
+ if (options.bonjour) {
+ bonjour(options);
+ }
+
+ const uri = createDomain(options, server.listeningApp) + suffix;
+
+ status(uri, options, log, argv.color);
+ });
+ }
}
-new Server(webpack(wpOpt), options).listen(options.port, options.host, function(err) {
- var uri = protocol + "://" + options.host + ":" + options.port + "/";
- if(!options.inline)
- uri += "webpack-dev-server/";
-
- if(err) throw err;
- console.log(uri);
- console.log("webpack result is served from " + options.publicPath);
- if(typeof options.contentBase === "object")
- console.log("requests are proxied to " + options.contentBase.target);
- else
- console.log("content is served from " + options.contentBase);
- if(options.historyApiFallback)
- console.log("404s will fallback to %s", options.historyApiFallback.index || "/index.html");
- if(options.open)
- open(uri);
-});
+processOptions(config);
diff --git a/client-src/default/index.js b/client-src/default/index.js
new file mode 100644
index 0000000000..db102fd7f6
--- /dev/null
+++ b/client-src/default/index.js
@@ -0,0 +1,244 @@
+'use strict';
+
+/* global __resourceQuery WorkerGlobalScope self */
+/* eslint prefer-destructuring: off */
+
+const url = require('url');
+const stripAnsi = require('strip-ansi');
+const log = require('loglevel').getLogger('webpack-dev-server');
+const socket = require('./socket');
+const overlay = require('./overlay');
+
+function getCurrentScriptSource() {
+ // `document.currentScript` is the most accurate way to find the current script,
+ // but is not supported in all browsers.
+ if (document.currentScript) { return document.currentScript.getAttribute('src'); }
+ // Fall back to getting all scripts in the document.
+ const scriptElements = document.scripts || [];
+ const currentScript = scriptElements[scriptElements.length - 1];
+ if (currentScript) { return currentScript.getAttribute('src'); }
+ // Fail as there was no script to use.
+ throw new Error('[WDS] Failed to get current script source.');
+}
+
+let urlParts;
+let hotReload = true;
+if (typeof window !== 'undefined') {
+ const qs = window.location.search.toLowerCase();
+ hotReload = qs.indexOf('hotreload=false') === -1;
+}
+if (typeof __resourceQuery === 'string' && __resourceQuery) {
+ // If this bundle is inlined, use the resource query to get the correct url.
+ urlParts = url.parse(__resourceQuery.substr(1));
+} else {
+ // Else, get the url from the
-
-
-
-
-
-
-