135 README.md
@@ -1,4 +1,8 @@
# webpack-dev-server
<div align="center">
<a href="https://github.com/webpack/webpack">
<img width="200" height="200" src="https://webpack.js.org/assets/icon-square-big.svg">
</a>
</div>

[![npm][npm]][npm-url]
[![node][node]][node-url]
@@ -7,62 +11,134 @@
[![coverage][cover]][cover-url]
[![chat][chat]][chat-url]

<div align="center">
<a href="https://github.com/webpack/webpack">
<img width="200" height="200"
src="https://webpack.js.org/assets/icon-square-big.svg">
</a>
<h1>webpack Dev Server</h1>
</div>
# webpack-dev-server

Use [webpack](https://webpack.js.org) with a development server that provides live reloading. This 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**.

It uses [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) under the hood, which provides fast in-memory access to the webpack assets.
It uses [webpack-dev-middleware][middleware-url] under the hood, which provides
fast in-memory access to the webpack assets.

<h2 align="center">Install</h2>
## Getting Started

```
First things first, install the module:

```console
npm install webpack-dev-server --save-dev
```

<h2 align="center">Usage</h2>
_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._

The easiest way to use it is with the CLI. In the directory where your `webpack.config.js` is, run:
## 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
```

This will start a server, listening on connections from `localhost` on port `8080`.
### 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.

Now, when you change something in your assets, it should live-reload the files.
If you find an bug with an obscure / old browser, we would actively welcome a
Pull Request to resolve the bug.

See [**the documentation**](https://webpack.js.org/configuration/dev-server/#devserver) for more use cases and options.
## Support

<h2 align="center">Contributing</h2>
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.

We appreciate all help! Check out [CONTRIBUTING.md](CONTRIBUTING.md) for more information on how to help.
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.

<h2 align="center">Maintainers</h2>
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

We welcome your contributions! Please have a read of [CONTRIBUTING.md](CONTRIBUTING.md) for more information on how to get involved.

## Maintainers

<table>
<tbody>
<tr>
<td align="center">
<img width="150" height="150"
src="https://avatars.githubusercontent.com/SpaceK33z?v=3">
<img src="https://avatars.githubusercontent.com/SpaceK33z?v=4&s=150">
<br />
<a href="https://github.com/">Kees Kluskens</a>
<a href="https://github.com/SpaceK33z">Kees Kluskens</a>
</td>
<td align="center">
<img src="https://i.imgur.com/4v6pgxh.png">
<br />
<a href="https://github.com/shellscape">Andrew Powell</a>
</td>
</tr>
</tbody>
</table>

<h2 align="center">Inspiration</h2>
## Attribution

This project is heavily inspired by [peerigon/nof5](https://github.com/peerigon/nof5).

<h2 align="center">LICENSE</h2>
## License

#### [MIT](./LICENSE)

@@ -84,3 +160,10 @@ This project is heavily inspired by [peerigon/nof5](https://github.com/peerigon/

[chat]: https://badges.gitter.im/webpack/webpack.svg
[chat-url]: https://gitter.im/webpack/webpack

[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
@@ -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
@@ -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;
@@ -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
};

Large diffs are not rendered by default.

@@ -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 <script> this file was called with.
let scriptHost = getCurrentScriptSource();
// eslint-disable-next-line no-useless-escape
scriptHost = scriptHost.replace(/\/[^\/]+$/, '');
urlParts = url.parse((scriptHost || '/'), false, true);
}

if (!urlParts.port || urlParts.port === '0') {
urlParts.port = self.location.port;
}

let hot = false;
let initial = true;
let currentHash = '';
let useWarningOverlay = false;
let useErrorOverlay = false;
let useProgress = false;

const INFO = 'info';
const WARNING = 'warning';
const ERROR = 'error';
const NONE = 'none';

// Set the default log level
log.setDefaultLevel(INFO);

// Send messages to the outside, so plugins can consume it.
function sendMsg(type, data) {
if (
typeof self !== 'undefined' &&
(typeof WorkerGlobalScope === 'undefined' ||
!(self instanceof WorkerGlobalScope))
) {
self.postMessage({
type: `webpack${type}`,
data
}, '*');
}
}

const onSocketMsg = {
hot() {
hot = true;
log.info('[WDS] Hot Module Replacement enabled.');
},
invalid() {
log.info('[WDS] App updated. Recompiling...');
// fixes #1042. overlay doesn't clear if errors are fixed but warnings remain.
if (useWarningOverlay || useErrorOverlay) overlay.clear();
sendMsg('Invalid');
},
hash(hash) {
currentHash = hash;
},
'still-ok': function stillOk() {
log.info('[WDS] Nothing changed.');
if (useWarningOverlay || useErrorOverlay) overlay.clear();
sendMsg('StillOk');
},
'log-level': function logLevel(level) {
const hotCtx = require.context('webpack/hot', false, /^\.\/log$/);
if (hotCtx.keys().indexOf('./log') !== -1) {
hotCtx('./log').setLogLevel(level);
}
switch (level) {
case INFO:
case ERROR:
log.setLevel(level);
break;
case WARNING:
// loglevel's warning name is different from webpack's
log.setLevel('warn');
break;
case NONE:
log.disableAll();
break;
default:
log.error(`[WDS] Unknown clientLogLevel '${level}'`);
}
},
overlay(value) {
if (typeof document !== 'undefined') {
if (typeof (value) === 'boolean') {
useWarningOverlay = false;
useErrorOverlay = value;
} else if (value) {
useWarningOverlay = value.warnings;
useErrorOverlay = value.errors;
}
}
},
progress(progress) {
if (typeof document !== 'undefined') {
useProgress = progress;
}
},
'progress-update': function progressUpdate(data) {
if (useProgress) log.info(`[WDS] ${data.percent}% - ${data.msg}.`);
sendMsg('Progress', data);
},
ok() {
sendMsg('Ok');
if (useWarningOverlay || useErrorOverlay) overlay.clear();
if (initial) return initial = false; // eslint-disable-line no-return-assign
reloadApp();
},
'content-changed': function contentChanged() {
log.info('[WDS] Content base changed. Reloading...');
self.location.reload();
},
warnings(warnings) {
log.warn('[WDS] Warnings while compiling.');
const strippedWarnings = warnings.map(warning => stripAnsi(warning));
sendMsg('Warnings', strippedWarnings);
for (let i = 0; i < strippedWarnings.length; i++) { log.warn(strippedWarnings[i]); }
if (useWarningOverlay) overlay.showMessage(warnings);

if (initial) return initial = false; // eslint-disable-line no-return-assign
reloadApp();
},
errors(errors) {
log.error('[WDS] Errors while compiling. Reload prevented.');
const strippedErrors = errors.map(error => stripAnsi(error));
sendMsg('Errors', strippedErrors);
for (let i = 0; i < strippedErrors.length; i++) { log.error(strippedErrors[i]); }
if (useErrorOverlay) overlay.showMessage(errors);
initial = false;
},
error(error) {
log.error(error);
},
close() {
log.error('[WDS] Disconnected!');
sendMsg('Close');
}
};

let hostname = urlParts.hostname;
let protocol = urlParts.protocol;


// check ipv4 and ipv6 `all hostname`
if (hostname === '0.0.0.0' || hostname === '::') {
// why do we need this check?
// hostname n/a for file protocol (example, when using electron, ionic)
// see: https://github.com/webpack/webpack-dev-server/pull/384
// eslint-disable-next-line no-bitwise
if (self.location.hostname && !!~self.location.protocol.indexOf('http')) {
hostname = self.location.hostname;
}
}

// `hostname` can be empty when the script path is relative. In that case, specifying
// a protocol would result in an invalid URL.
// When https is used in the app, secure websockets are always necessary
// because the browser doesn't accept non-secure websockets.
if (hostname && (self.location.protocol === 'https:' || urlParts.hostname === '0.0.0.0')) {
protocol = self.location.protocol;
}

const socketUrl = url.format({
protocol,
auth: urlParts.auth,
hostname,
port: urlParts.port,
pathname: urlParts.path == null || urlParts.path === '/' ? '/sockjs-node' : urlParts.path
});

socket(socketUrl, onSocketMsg);

let isUnloading = false;
self.addEventListener('beforeunload', () => {
isUnloading = true;
});

function reloadApp() {
if (isUnloading || !hotReload) {
return;
}
if (hot) {
log.info('[WDS] App hot update...');
// eslint-disable-next-line global-require
const hotEmitter = require('webpack/hot/emitter');
hotEmitter.emit('webpackHotUpdate', currentHash);
if (typeof self !== 'undefined' && self.window) {
// broadcast update to window
self.postMessage(`webpackHotUpdate${currentHash}`, '*');
}
} else {
let rootWindow = self;
// use parent window for reload (in case we're in an iframe with no valid src)
const intervalId = self.setInterval(() => {
if (rootWindow.location.protocol !== 'about:') {
// reload immediately if protocol is valid
applyReload(rootWindow, intervalId);
} else {
rootWindow = rootWindow.parent;
if (rootWindow.parent === rootWindow) {
// if parent equals current window we've reached the root which would continue forever, so trigger a reload anyways
applyReload(rootWindow, intervalId);
}
}
});
}

function applyReload(rootWindow, intervalId) {
clearInterval(intervalId);
log.info('[WDS] App updated. Reloading...');
rootWindow.location.reload();
}
}
@@ -0,0 +1,129 @@
'use strict';

// The error overlay is inspired (and mostly copied) from Create React App (https://github.com/facebookincubator/create-react-app)
// They, in turn, got inspired by webpack-hot-middleware (https://github.com/glenjamin/webpack-hot-middleware).

const ansiHTML = require('ansi-html');
const Entities = require('html-entities').AllHtmlEntities;

const entities = new Entities();

const colors = {
reset: ['transparent', 'transparent'],
black: '181818',
red: 'E36049',
green: 'B3CB74',
yellow: 'FFD080',
blue: '7CAFC2',
magenta: '7FACCA',
cyan: 'C3C2EF',
lightgrey: 'EBE7E3',
darkgrey: '6D7891'
};
ansiHTML.setColors(colors);

function createOverlayIframe(onIframeLoad) {
const iframe = document.createElement('iframe');
iframe.id = 'webpack-dev-server-client-overlay';
iframe.src = 'about:blank';
iframe.style.position = 'fixed';
iframe.style.left = 0;
iframe.style.top = 0;
iframe.style.right = 0;
iframe.style.bottom = 0;
iframe.style.width = '100vw';
iframe.style.height = '100vh';
iframe.style.border = 'none';
iframe.style.zIndex = 9999999999;
iframe.onload = onIframeLoad;
return iframe;
}

function addOverlayDivTo(iframe) {
const div = iframe.contentDocument.createElement('div');
div.id = 'webpack-dev-server-client-overlay-div';
div.style.position = 'fixed';
div.style.boxSizing = 'border-box';
div.style.left = 0;
div.style.top = 0;
div.style.right = 0;
div.style.bottom = 0;
div.style.width = '100vw';
div.style.height = '100vh';
div.style.backgroundColor = 'rgba(0, 0, 0, 0.85)';
div.style.color = '#E8E8E8';
div.style.fontFamily = 'Menlo, Consolas, monospace';
div.style.fontSize = 'large';
div.style.padding = '2rem';
div.style.lineHeight = '1.2';
div.style.whiteSpace = 'pre-wrap';
div.style.overflow = 'auto';
iframe.contentDocument.body.appendChild(div);
return div;
}

let overlayIframe = null;
let overlayDiv = null;
let lastOnOverlayDivReady = null;

function ensureOverlayDivExists(onOverlayDivReady) {
if (overlayDiv) {
// Everything is ready, call the callback right away.
onOverlayDivReady(overlayDiv);
return;
}

// Creating an iframe may be asynchronous so we'll schedule the callback.
// In case of multiple calls, last callback wins.
lastOnOverlayDivReady = onOverlayDivReady;

if (overlayIframe) {
// We're already creating it.
return;
}

// Create iframe and, when it is ready, a div inside it.
overlayIframe = createOverlayIframe(() => {
overlayDiv = addOverlayDivTo(overlayIframe);
// Now we can talk!
lastOnOverlayDivReady(overlayDiv);
});

// Zalgo alert: onIframeLoad() will be called either synchronously
// or asynchronously depending on the browser.
// We delay adding it so `overlayIframe` is set when `onIframeLoad` fires.
document.body.appendChild(overlayIframe);
}

function showMessageOverlay(message) {
ensureOverlayDivExists((div) => {
// Make it look similar to our terminal.
div.innerHTML = `<span style="color: #${
colors.red
}">Failed to compile.</span><br><br>${
ansiHTML(entities.encode(message))}`;
});
}

function destroyErrorOverlay() {
if (!overlayDiv) {
// It is not there in the first place.
return;
}

// Clean up and reset internal state.
document.body.removeChild(overlayIframe);
overlayDiv = null;
overlayIframe = null;
lastOnOverlayDivReady = null;
}

// Successful compilation.
exports.clear = function handleSuccess() {
destroyErrorOverlay();
};

// Compilation with errors (e.g. syntax error or missing modules).
exports.showMessage = function handleMessage(messages) {
showMessageOverlay(messages[0]);
};
@@ -0,0 +1,42 @@
'use strict';

const SockJS = require('sockjs-client/dist/sockjs');

let retries = 0;
let sock = null;

const socket = function initSocket(url, handlers) {
sock = new SockJS(url);

sock.onopen = function onopen() {
retries = 0;
};

sock.onclose = function onclose() {
if (retries === 0) { handlers.close(); }

// Try to reconnect.
sock = null;

// After 10 retries stop trying, to prevent logspam.
if (retries <= 10) {
// Exponentially increase timeout to reconnect.
// Respectfully copied from the package `got`.
// eslint-disable-next-line no-mixed-operators, no-restricted-properties
const retryInMs = 1000 * Math.pow(2, retries) + Math.random() * 100;
retries += 1;

setTimeout(() => {
socket(url, handlers);
}, retryInMs);
}
};

sock.onmessage = function onmessage(e) {
// This assumes that all data sent via the websocket is JSON.
const msg = JSON.parse(e.data);
if (handlers[msg.type]) { handlers[msg.type](msg.data); }
};
};

module.exports = socket;
@@ -0,0 +1,18 @@
'use strict';

module.exports = {
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules|web_modules/,
use: [
{
loader: 'babel-loader'
}
]
}
]
}
};
@@ -0,0 +1,124 @@
'use strict';

/* eslint import/no-extraneous-dependencies: off, global-require: off */

const $ = require('jquery');
const stripAnsi = require('strip-ansi');
const socket = require('../default/socket');
require('./style.css');

let hot = false;
let currentHash = '';

$(() => {
$('body').html(require('./page.html'));
const status = $('#status');
const okness = $('#okness');
const $errors = $('#errors');
const iframe = $('#iframe');
const header = $('.header');

const contentPage = window.location.pathname.substr('/webpack-dev-server'.length) + window.location.search;

status.text('Connecting to sockjs server...');
$errors.hide();
iframe.hide();
header.css({
borderColor: '#96b5b4'
});

const onSocketMsg = {
hot() {
hot = true;
iframe.attr('src', contentPage + window.location.hash);
},
invalid() {
okness.text('');
status.text('App updated. Recompiling...');
header.css({
borderColor: '#96b5b4'
});
$errors.hide();
if (!hot) iframe.hide();
},
hash(hash) {
currentHash = hash;
},
'still-ok': function stillOk() {
okness.text('');
status.text('App ready.');
header.css({
borderColor: ''
});
$errors.hide();
if (!hot) iframe.show();
},
ok() {
okness.text('');
$errors.hide();
reloadApp();
},
warnings() {
okness.text('Warnings while compiling.');
$errors.hide();
reloadApp();
},
errors: function msgErrors(errors) {
status.text('App updated with errors. No reload!');
okness.text('Errors while compiling.');
$errors.text(`\n${stripAnsi(errors.join('\n\n\n'))}\n\n`);
header.css({
borderColor: '#ebcb8b'
});
$errors.show();
iframe.hide();
},
close() {
status.text('');
okness.text('Disconnected.');
$errors.text('\n\n\n Lost connection to webpack-dev-server.\n Please restart the server to reestablish connection...\n\n\n\n');
header.css({
borderColor: '#ebcb8b'
});
$errors.show();
iframe.hide();
}
};

socket('/sockjs-node', onSocketMsg);

iframe.on('load', () => {
status.text('App ready.');
header.css({
borderColor: ''
});
iframe.show();
});

function reloadApp() {
if (hot) {
status.text('App hot update.');
try {
iframe[0].contentWindow.postMessage(`webpackHotUpdate${currentHash}`, '*');
} catch (e) {
console.warn(e); // eslint-disable-line
}
iframe.show();
} else {
status.text('App updated. Reloading app...');
header.css({
borderColor: '#96b5b4'
});
try {
let old = `${iframe[0].contentWindow.location}`;
if (old.indexOf('about') === 0) old = null;
iframe.attr('src', old || (contentPage + window.location.hash));
if (old) {
iframe[0].contentWindow.location.reload();
}
} catch (e) {
iframe.attr('src', contentPage + window.location.hash);
}
}
}
});
File renamed without changes.
@@ -0,0 +1,7 @@
<div class="header">
<span id="okness"></span>
<span id="status"></span>
</div>
<pre id="errors"></pre>
<div id="warnings"></div>
<iframe id="iframe" src="javascript:;" allowfullscreen="allowfullscreen"></iframe>
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,38 @@
'use strict';

const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');

module.exports = {
mode: 'production',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules|web_modules/,
use: [
{
loader: 'babel-loader'
}
]
},
{
test: /\.html$/,
use: ['html-loader']
},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
},
plugins: [
new CopyPlugin([{
from: path.resolve(__dirname, 'live.html'),
to: path.resolve(__dirname, '../../client/live.html')
}])
]
};
@@ -0,0 +1,3 @@
'use strict';

module.exports = require('sockjs-client');
@@ -0,0 +1,9 @@
'use strict';

module.exports = {
mode: 'production',
output: {
library: 'SockJS',
libraryTarget: 'umd'
}
};

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 600"><title>icon-square-small</title><path fill="#FFF" d="M300 .1L565 150v299.9L300 599.8 35 449.9V150z"/><path fill="#8ED6FB" d="M517.7 439.5L308.8 557.8v-92L439 394.1l78.7 45.4zm14.3-12.9V179.4l-76.4 44.1v159l76.4 44.1zM81.5 439.5l208.9 118.2v-92l-130.2-71.6-78.7 45.4zm-14.3-12.9V179.4l76.4 44.1v159l-76.4 44.1zm8.9-263.2L290.4 42.2v89l-137.3 75.5-1.1.6-75.9-43.9zm446.9 0L308.8 42.2v89L446 206.8l1.1.6 75.9-44z"/><path fill="#1C78C0" d="M290.4 444.8L162 374.1V234.2l128.4 74.1v136.5zm18.4 0l128.4-70.6v-140l-128.4 74.1v136.5zM299.6 303zm-129-85l129-70.9L428.5 218l-128.9 74.4-129-74.4z"/></svg>
@@ -0,0 +1,26 @@
<!doctype html>
<html>
<head>
<title>WDS ▻ <%= htmlWebpackPlugin.options.title %></title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="/.assets/favicon.ico"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Code+Pro:400,600|Source+Sans+Pro:400,400i,500,600"/>
<link rel="stylesheet" href="/.assets/style.css"/>
</head>
<body>
<main>
<header>
<h1>
<img src="/.assets/icon-square.svg" style="width: 35px; height: 35px;"/>
webpack-dev-server
</h1>
</header>
<section>
<h2><%= htmlWebpackPlugin.options.title %></h2>
<div id="target"></div>
</section>
</main>
<script src="/bundle.js" type="text/javascript" charset="utf-8"></script>
</body>
</html>
@@ -0,0 +1,104 @@
@font-face {
font-family: 'Geomanist';
font-style: normal;
font-weight: 600;
src: url('assets/geomanist-medium.woff2') format('woff2'),
url('assets/geomanist-medium.woff') format('woff');
}

html,
body {
background-color: #f3f3f3;
color: #2B3A42;
font: 400 10px "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
height: 100%;
margin: 0;
padding: 0;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

a {
color: #2086d7;
text-decoration: none;
transition: color 250ms;
}

a:hover {
color: #1a6aab;
}

main {
width: 100%;
max-width: 94.2rem;
margin: 0px auto;
padding: 8rem 2.4rem;
}

h1 {
font-size: 3.4rem;
font-weight: 600;
line-height: 3.4rem;
margin-top: 0;
padding-top: 0;
}

h1 img {
height: 3.4rem;
width: 3.4rem;
vertical-align: middle;
}

h2 {
font-size: 2.8rem;
}

section {
padding: 0 0 0 4.2rem;
}

div, p, table {
font-size: 1.6rem;
}

code {
background-color: rgba(70, 94, 105, 0.06);
border-radius: 3px;
font-family: "Source Code Pro", Consolas, "Liberation Mono", Menlo, Courier, monospace;
font-size: 1.44rem;
margin: 0;
max-width: 100%;
line-height: initial;
overflow: auto;
padding: 2px 6px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
vertical-align: middle;
white-space: normal;
}

#target {
background: #DCF2FD;
border: 0.1rem solid #618ca0;
border-radius: 0.3rem;
color: #618ca0;
margin: 2rem 0;
padding: 2rem;
}

#target.warn {
background: #fcf8e3;
border: 0.1rem solid #8a6d3b;
color: #8a6d3b;
}

#target.pass {
background: #f2f9f4;
border: 0.1rem solid #4db277;
color: #4db277;
}

#target.fail {
background: #f2dede;
border: 0.1rem solid #a94442;
color: #a94442;
}
@@ -0,0 +1,5 @@
{
"rules": {
"no-console": "off"
}
}
@@ -1,9 +1,29 @@
# Examples

Each example showcases a feature. You can use this to learn how to use that feature, but also use it to test if something still works when making a Pull Request.
Each example showcases a particular feature of `webpack-dev-server`. You can use
these examples to learn how to use certain features, or as a means to test features
when working on a Pull Request.

An example should be as minimal as possible, and consists of:
An example should be as minimal as possible and consists of at least:

- The code that is necessary
- Instructions on how to run
- A description of what should happen
- An `app.js` file - the entry point for an example app.
- A `README.md` file containing information about, and how to run the example app.
- A description of what should happen when running the example.
- A `webpack.config.js` file containing the `webpack` configuration for the example app.

## API versus CLI

API examples can be found in the `api` directory. These examples demonstrate how
to access and run `webpack-dev-server` directly in your application / script.

CLI examples can be found in the `cli` directory. These examples demonstrate how
to run `webpack-dev-server` from the command line in your console / terminal.

## Notes

- Each example's `webpack` config is wrapped with `util.setup`; a helper function
that adds plugins and configuration needed by each example to render in a consistent
and visually pleasing way.
- Examples' `bundle.js` and `index.html` files are compiled and served from memory.
You won't actually see these files written to disk, but if you examine the `webpack`
output, you should see their file indicators.
@@ -0,0 +1,20 @@
# API: Custom Middleware

While it's recommended to run `webpack-dev-server` via the CLI, you may also
choose to start a server via the API. This example demonstrates using one of the
few custom middleware options; `before`.

```console
node server.js
```

## What Should Happen

1. Open `http://localhost:8080/` in your preferred browser.
2. You should see the text on the page itself change to read `Success!`.
3. In the console/terminal, you should see the following for each refresh in
the browser:
```
Using middleware for /
Using middleware for /bundle.js
```
@@ -0,0 +1,6 @@
'use strict';

const target = document.querySelector('#target');

target.classList.add('pass');
target.innerHTML = 'Success!';
@@ -0,0 +1,23 @@
'use strict';

const Webpack = require('webpack');
const WebpackDevServer = require('../../../lib/Server');
const webpackConfig = require('./webpack.config');

const compiler = Webpack(webpackConfig);
const devServerOptions = Object.assign({}, webpackConfig.devServer, {
stats: {
colors: true
},
before(app) {
app.use((req, res, next) => {
console.log(`Using middleware for ${req.url}`);
next();
});
}
});
const server = new WebpackDevServer(compiler, devServerOptions);

server.listen(8080, '127.0.0.1', () => {
console.log('Starting server on http://localhost:8080');
});
@@ -0,0 +1,13 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: ['./app.js', '../../../client/index.js?http://localhost:8080/'],
output: {
filename: 'bundle.js'
}
});
@@ -0,0 +1,17 @@
# API: Simple Server

While it's recommended to run `webpack-dev-server` via the CLI, you may also
choose to start a server via the API. This example starts a simple server setup.

```console
node server.js
```

## What should happen

1. Open `http://localhost:8080/` in your preferred browser.
2. You should see the text on the page itself change to read `Success!`.
3. In `app.js`, uncomment the code that results in an error and save. This error
should be visible in the console/terminal and in browser devtools.
4. In `app.js`, uncomment the code that results in a warning. This warning should
be visible in the console/terminal and in browser devtools.
@@ -0,0 +1,17 @@
'use strict';

const target = document.querySelector('#target');

target.classList.add('pass');
target.innerHTML = 'Success!';

// This results in a warning:
if (!window) {
// eslint-disable-next-line
require(`./${window}parseable.js`);
}

// This results in an error:
// if(!window) {
// require("test");
// }
@@ -0,0 +1,17 @@
'use strict';

const Webpack = require('webpack');
const WebpackDevServer = require('../../../lib/Server');
const webpackConfig = require('./webpack.config');

const compiler = Webpack(webpackConfig);
const devServerOptions = Object.assign({}, webpackConfig.devServer, {
stats: {
colors: true
}
});
const server = new WebpackDevServer(compiler, devServerOptions);

server.listen(8080, '127.0.0.1', () => {
console.log('Starting server on http://localhost:8080');
});
@@ -0,0 +1,13 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: ['./app.js', '../../../client/index.js?http://localhost:8080/'],
output: {
filename: 'bundle.js'
}
});

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

@@ -0,0 +1,15 @@
# Bonjour (ZeroConf)

The Bonjour capability broadcasts server information via ZeroConf when the Server
is started.

To run this example, run this command in your console or terminal:

```console
npm run webpack-dev-server -- --bonjour
```

## What Should Happen

A Zeroconf broadcast should occur, containing data with a type of `http` and a
subtype of `webpack`.
@@ -0,0 +1,5 @@
'use strict';

const target = document.querySelector('#target');

target.innerHTML = 'Please check your Zeroconf service.';
@@ -0,0 +1,10 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: './app.js'
});
@@ -0,0 +1,25 @@
# Gzip Compression

Website gzip compression makes it possible to reduce the file size of a file
to roughly 30% of its original size before the files are sent to the browser.

To run this example, run this command in your console or terminal:

```console
npm run webpack-dev-server -- --open --compress
```

## What should happen

1. The script should open `https://localhost:8080/`.
2. Files being sent to the browser from the `webpack` bundle should be gzipped.
3. Open the console in your browser's devtools and select the _Network_ tab.
4. Find `bundle.js`. The response headers should contain `Content-Encoding: gzip`.

## Notes

Some browsers, such as Chrome, won't show the `Content-Encoding: gzip` within
the _Response Headers_. This has been documented [here](https://github.com/expressjs/compression/issues/96).

To enable `Content-Encoding` for _Response Headers_ in Chrome, you can follow
[this tutorial](https://www.youtube.com/watch?v=47R6uv0RKCk).
@@ -0,0 +1,6 @@
'use strict';

const target = document.querySelector('#target');

target.classList.add('pass');
target.innerHTML = 'Success!';
@@ -0,0 +1,10 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: './app.js'
});
@@ -0,0 +1,25 @@
# CLI: Default State

This example demonstrates how to use `webpack-dev-server` in its default, inline
state.

```console
npm run webpack-dev-server -- --open
```

To run your app using an alternative config, use:

```console
npm run webpack-dev-server -- --open --config alternative.config.js
```

## What Should Happen

1. The script should open `http://localhost:8080/` in your default browser.
2. You should see the text on the page itself change to read `Success!`.
3. In `app.js` uncomment the code that results in an error and save.
4. This error should be visible in the console/terminal and in the browser's devtools.
5. In `app.js` uncomment the code that results in a warning. This warning should
be visible in the console/terminal and in the browser's devtools.
6. Try changing something in `style.less`. The browser should refresh, and the
change should be visible in the app.
@@ -0,0 +1,20 @@
'use strict';

require('./style.less');

const target = document.querySelector('#target');

target.classList.add('pass');
target.innerHTML = 'Success!';

const img = document.createElement('img');
img.src = '/svg.svg';
img.style = 'width: 200px;';

document.body.appendChild(img);

// This results in a warning:
// if(!window) require("./" + window + "parseable.js");

// This results in an error:
// if(!window) require("test");
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,27 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: './app.js',
module: {
rules: [
{
test: /\.less$/,
use: [
'style-loader',
'css-loader',
'less-loader'
]
},
{
test: /\.png$/,
loader: 'file-loader',
options: { prefix: 'img/' }
}
]
}
});
@@ -0,0 +1,20 @@
# CLI: History API Fallback Option

This option enables [History API Fallback](https://github.com/bripkens/connect-history-api-fallback)
support in `webpack-dev-server`, effectively asking the server to fallback to
`index.html` in the event that a requested resource cannot be found.

```shell
npm run webpack-dev-server -- --open --history-api-fallback
```

_Note: some URLs don't work by default. For example; if the url contains a dot.
Be sure to checkout the [connect-history-api-fallback](https://github.com/bripkens/connect-history-api-fallback)
options._

## What Should Happen

1. The script should open `http://0.0.0.0:8080/` in your default browser.
2. You should see text on the page that reads 'Current Path: /'.
3. Navigate to `http://localhost:8080/foo/bar`.
4. You should see text on the page that reads 'Current Path: /foo/background'.
@@ -0,0 +1,75 @@
'use strict';

/* global XMLHttpRequest */

const path = document.location.pathname;
const target = document.querySelector('#target');
const style = document.createElement('style');
const css = `table {
border-radius: 0.3rem;
border: 0.1rem solid #474747;
border-spacing: 0;
padding: 0;
width: 50%;
}
table td {
border-right: 0.1rem solid #474747;
padding: 0.5rem 1rem;
}
table tr td:last-child {
border-right: 0;
text-align: center;
}
table td.pass {
background: #f2f9f4;
color: #4db277;
}
table td.fail {
background: #f2dede;
color: #a94442;
}`;

style.appendChild(document.createTextNode(css));
document.head.appendChild(style);
target.innerHTML = `Current Path: <code>${path}</code>`;

document.addEventListener('DOMContentLoaded', () => {
if (document.querySelector('#files')) {
return;
}

const tests = [
{ url: '/', name: 'index', re: /^<!doctype html>/i },
{ url: '/test', name: 'non-existent path', re: /^<!doctype html>/i },
{ url: '/file.txt', name: 'existing path', re: /^file/ }
];
const table = document.createElement('table');
const tbody = document.createElement('tbody');

table.id = 'files';
table.appendChild(tbody);
target.parentNode.appendChild(table);

tests.forEach((test) => {
const tr = document.createElement('tr');
tbody.appendChild(tr);
check(test.url, test.re, (res) => {
tr.innerHTML = `<td>${test.name}</td>`;
tr.innerHTML += `<td><a href="${test.url}">${test.url}</a></td>`;
tr.innerHTML += `<td class="${res}">${res}</td>`;
});
});
}, true);

function check(url, re, cb) {
const xhr = new XMLHttpRequest();
xhr.addEventListener('load', () => {
cb(re.test(xhr.responseText) ? 'pass' : 'fail');
});
xhr.open('GET', url);
xhr.send();
}
File renamed without changes.
@@ -0,0 +1,15 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: './app.js',
devServer: {
historyApiFallback: {
disableDotRule: true
}
}
});
@@ -0,0 +1,30 @@
# Hot Module Reloading

Hot Module Replacement (HMR) exchanges, adds, or removes modules while an
application is running, without a full reload of the page.

To run this example, run this command in your console or terminal:

```console
npm run webpack-dev-server -- --open --hot
```

## What Should Happen

1. The script should open `http://localhost:8080/` in your default browser.
2. In your editor, open `example.js` and change any part of the `innerHTML` string.
3. Open the console in your browser's devtools.

In the devtools console you should see:

```
[WDS] App updated. Recompiling...
[WDS] App hot update...
[HMR] Checking for updates on the server...
[HMR] Updated modules:
[HMR] - ./example.js
[HMR] App is up to date.
```

You should also see the text on the page itself change to match your edits in
`example.js`.
@@ -0,0 +1,11 @@
'use strict';

require('./example');

if (module.hot) {
module.hot.accept((err) => {
if (err) {
console.error('Cannot apply HMR update.', err);
}
});
}
@@ -0,0 +1,5 @@
'use strict';

const target = document.querySelector('#target');

target.innerHTML = 'Modify and save <code>/examples/hmr/example.js</code> to update this element without reloading the page.';
@@ -0,0 +1,10 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: './app.js'
});
@@ -0,0 +1,24 @@
# CLI: Host and Port Options

You may choose to wish to change the host and port on which `webpack-dev-server`
will run. The `host` and `port` options allow for that.

## IPv4

```console
npm run webpack-dev-server -- --open --port 5000 --host 0.0.0.0
```

## IPv6

_This also works with IPv4_

```console
npm run webpack-dev-server -- --open --port 5000 --host ::
```

## What Should Happen

1. The script should open `http://0.0.0.0:5000/` if specifying the IPv4 option,
or `http://[::]:5000/` for IPv6, in your default browser.
2. You should see the text on the page itself change to read `Success!`.
@@ -0,0 +1,6 @@
'use strict';

const target = document.querySelector('#target');

target.classList.add('pass');
target.innerHTML = 'Success!';
@@ -0,0 +1,10 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: './app.js'
});
@@ -0,0 +1,48 @@
# CLI: Https Option

By default webpack-dev-server will generate a self-signed, 2048 bit, sha256 SSL
Certificate, which is used to enable https. The certificate will be located in the
`ssl` directory after the server is started for the first time. The generated
certificate is only good for 30 days, at which point it'll be regenerated.

We highly recommend creating and managing your own certificates. Please see the
following resources for doing so:

### MacOS
https://certsimple.com/blog/localhost-ssl-fix

### Windows 10
https://technet.microsoft.com/itpro/powershell/windows/pkiclient/new-selfsignedcertificate

### Windows 7
https://msdn.microsoft.com/en-us/library/aa386968.aspx

Example (the .pfx file generated the following way can be used without `--pfx-passphrase`):
```
makecert -r -pe -sky exchange -sv makecert.pvk makecert.cer
pvk2pfx -pvk makecert.pvk -spc makecert.cer -pfx makecert.pfx
```

## Getting Started

```shell
npm run webpack-dev-server -- --open --https
```

## Using Your Certificate

Options are available for using your own SSL Certificate in your preferred or
OS-required format.

Given the base command `npm run webpack-dev-server -- --open --https`, append
one of the following:

* (PEM Files) `--cert=../../ssl/server.pem --key=../../ssl/server.pem`
* (PFX and Passphrase) `--pfx=./test_cert.pfx --pfx-passphrase=sample`

## What To Expect

The script should open `https://localhost:8080/`in your default browser. If your
browser displays a warning about a non-trusted certificate, follow the procedure
for your browser of choice to continue. After doing so you should see "It's Working"
displayed on the page.
@@ -0,0 +1,6 @@
'use strict';

const target = document.querySelector('#target');

target.classList.add('pass');
target.innerHTML = 'Success!';
File renamed without changes.
@@ -0,0 +1,10 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: './app.js'
});
@@ -0,0 +1,19 @@
# CLI: Inline Iframe

The `--no-inline` option instructs `webpack-dev-server` to open the app in an
iframe. The page then contains the client script to connect to the server.

```shell
npm run webpack-dev-server -- --no-inline --open
```

## What Should Happen

1. The script should open `http://localhost:8080/webpack-dev-server/` in your
default browser.
2. There should be a bar at the top of the page that reads `App ready`.
3. In `app.js`, uncomment the code that results in an error and save.
4. The bar at the top should read `Errors while compiling. App updated with errors.
No reload!` along with a stack trace.
5. In `app.js`, uncomment the code that results in a warning. The bar at the top
should read `Warnings while compiling`.
@@ -1,6 +1,9 @@
require("./style.less");
'use strict';

document.write("It's working.");
const target = document.querySelector('#target');

target.classList.add('pass');
target.innerHTML = 'Success!';

// This results in a warning:
// if(!window) require("./" + window + "parseable.js");
@@ -0,0 +1,10 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: './app.js'
});
@@ -0,0 +1,19 @@
# CLI: Lazy Option

With the `lazy` option enabled, `webpack-dev-server` does **not** watch the
bundle files, nor does it automatically recompile them or refresh the browser.
Instead, it only compiles after you manually refresh the page.

```shell
npm run webpack-dev-server -- --open --lazy --no-inline
```

## What should happen

1. The script should open `http://localhost:8080/` in your default browser.
2. You should see the text on the page itself change to read `Success!`.
3. Change something in `app.js` and save.
4. You should not see any changes in the console/terminal output.
5. You should not see any changes in the browser.
6. Refresh the page.
7. You should see compilation in the console/terminal and changes in the browser.
@@ -1,6 +1,9 @@
require("./style.less");
'use strict';

document.write("It's working.");
const target = document.querySelector('#target');

target.classList.add('pass');
target.innerHTML = 'Success!';

// This results in a warning:
// if(!window) require("./" + window + "parseable.js");
@@ -0,0 +1,10 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: './app.js'
});
@@ -1,25 +1,31 @@
# Listen to socket
# CLI: Socket Option

Start dev server in socket mode:
To run the entire example, start the server with the following command:

```shell
node ../../bin/webpack-dev-server.js --socket ./webpack.sock
```console
npm run webpack-dev-server -- --socket ./webpack.sock
```

And then start node server that will use that socket:

```shell
```console
node check-socket.js
```

## What Should Happen

You should see this responses to this command:

```shell
```console
Successfully connected to socket, exiting
```

Common use-case for this feature is configuring upstream in nginx that points to webpack socket.
Listening to socket instead of port is much more convenient because if you have many applications you don't have to resolve port conflicts since each of them has own unique socket.
## Use Cases

A common use-case for this feature is configuring upstream in nginx that points
to webpack socket. Listening to socket instead of port is much more convenient
because if you have many applications you don't have to resolve port conflicts
since each of them has own unique socket.

Example of configuring upstream with Websockets support in nginx:

@@ -40,4 +46,5 @@ location /sockjs-node/ {
}
```

Replace `/webpack/` with your `publicPath` param value and path in `proxy_pass` to your actual proxy location.
Replace `/webpack/` with your `publicPath` param value and path in `proxy_pass`
to your actual proxy location.
@@ -0,0 +1,6 @@
'use strict';

const target = document.querySelector('#target');

target.classList.add('pass');
target.innerHTML = 'Success!';
@@ -0,0 +1,9 @@
'use strict';

const net = require('net');

const client = net.createConnection('./webpack.sock');
client.on('connect', () => {
console.log('Successfully connected to socket, exiting');
process.exit(1); // eslint-disable-line
});
@@ -0,0 +1,10 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: './app.js'
});
@@ -0,0 +1,15 @@
# CLI: Multi Compiler

`webpack-dev-server` should be able to compile multiple webpack configs.

```shell
npm run webpack-dev-server -- --open
```

## What Should Happen

1. The script should open `http://localhost:8080/` in your default browser.
2. You should see the text on the page itself change to read `Success!`.
3. In `app.js` write code containing a syntax error and save the file.
4. The page should now refresh and show a full screen error overlay, displaying
the syntax error.
@@ -0,0 +1,9 @@
'use strict';

const target = document.querySelector('#target');

target.classList.add('pass');
target.innerHTML = 'Success!';

// This results in an error:
// if(!window) require("test");
@@ -0,0 +1,10 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = [setup({
context: __dirname,
entry: './app.js'
})];
@@ -0,0 +1,10 @@
# CLI: Webpack `node` Option

```shell
npm run webpack-dev-server -- --open
```

## What should happen

1. The script should open `http://localhost:8080/` in your default browser.
2. You should see the text on the page itself change to read `Success!`.
@@ -0,0 +1,6 @@
'use strict';

const target = document.querySelector('#target');

target.classList.add('pass');
target.innerHTML = 'Success!';
@@ -0,0 +1,11 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: './app.js',
node: false
});
@@ -0,0 +1,14 @@
# CLI: Open Page Option

```console
npm run webpack-dev-server -- --open-page example.html#page1
```

Some applications may consist of multiple pages. During development it may
be useful to directly open a particular page. The page to open may be specified
as the argument to the `open-page` option.

## What Should Happen

The script should open `http://localhost:8080/` in your default browser.
You should see the text on the page itself change to read `Success!`.
@@ -0,0 +1,11 @@
'use strict';

const target = document.querySelector('#target');

if (window.location.href.endsWith('example.html#page1')) {
target.classList.add('pass');
target.innerHTML = 'Success!';
} else {
target.classList.add('fail');
target.innerHTML = 'Houston, we have a problem.';
}
@@ -0,0 +1,18 @@
'use strict';

const HtmlWebpackPlugin = require('html-webpack-plugin');
// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: './app.js',
plugins: [
new HtmlWebpackPlugin({
filename: 'example.html',
template: '../../.assets/layout.html',
title: 'Open Page / Example'
})
]
});
@@ -0,0 +1,18 @@
# CLI: Overlay Option

`webpack-dev-server` can display an overlay for warnings and errors.

```shell
npm run webpack-dev-server -- --open --overlay
```

_Note: To view additional options for the overlay visit
[https://webpack.js.org/configuration/dev-server/#devserver-overlay](https://webpack.js.org/configuration/dev-server/#devserver-overlay)._

## What Should Happen

1. The script should open `http://localhost:8080/` in your default browser.
2. You should see the text on the page itself change to read `Success!`.
3. In `app.js` write code containing a syntax error and save the file.
4. The page should now refresh and show a full screen error overlay, displaying
the syntax error.
@@ -0,0 +1,9 @@
'use strict';

const target = document.querySelector('#target');

target.classList.add('pass');
target.innerHTML = 'Success!';

// This results in an error:
// if(!window) require("test");
@@ -0,0 +1,10 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: './app.js'
});
@@ -0,0 +1,12 @@
# CLI: Progress Option

```shell
npm run webpack-dev-server -- --open
```

## What Should Happen

1. The script should open `http://localhost:8080/` in your default browser.
2. You should see the text on the page itself change to read `Success!`.
3. In `app.js` change the text and save.
4. You should see the compilation progress in the browser console.
@@ -0,0 +1,6 @@
'use strict';

const target = document.querySelector('#target');

target.classList.add('pass');
target.innerHTML = 'Success!';
@@ -0,0 +1,13 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: './app.js',
devServer: {
progress: true
}
});
@@ -0,0 +1,18 @@
# CLI: Public Option Protocol

```console
npm run webpack-dev-server
```

_NOTE: replace `<insert local ip>` with your local IP Address._

You're now able to explicitly define the protocol used with the `public` option
(have a look to the config provided in `webpack.config.js`).

## What Should Happen

The script should open `http://localhost:8080/` in your default browser.

You should see a failed attempt to establish a connection to `/sockjs-node`
via the explicitly defined `https://localhost:8080`. This fails of course since
we're not hosting https.
@@ -0,0 +1,5 @@
'use strict';

const target = document.querySelector('#target');

target.innerHTML = 'Please check the sockjs-info request in devtools, it should try to connect to the protocol + server defined in the public setting.';
@@ -4,6 +4,5 @@
<script src="bundle.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<h1>Example: https</h1>
</body>
</html>
@@ -0,0 +1,15 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: './app.js',
devServer: {
host: '0.0.0.0',
public: 'https://localhost:8080',
disableHostCheck: true
}
});
@@ -0,0 +1,21 @@
# CLI: Public Option

```console
npm run webpack-dev-server -- --open --host 0.0.0.0 --public <insert local ip>:8080
```

_NOTE: replace `<insert local ip>` with your local IP Address._

In order to make the server publicly accessible the client needs to know with
what host to connect to the server. If `--host 0.0.0.0` is given, the client
would try to connect to `0.0.0.0`. With the `--public` options it is possible to
override this.

## What Should Happen

1. The script should open `http://0.0.0.0:8080/` in your default browser.
2. You should see the text on the page itself change to read `Success!`.
3. Open the console in your browser's devtools.
4. Select the 'Network' tab.
5. Select the 'WS' or 'WebSockets' sub-tab.
6. Verify that the WebSocket is connecting to `<insert local ip>:8080`.
@@ -0,0 +1,6 @@
'use strict';

const target = document.querySelector('#target');

target.classList.add('pass');
target.innerHTML = 'Success!';
@@ -0,0 +1,10 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: './app.js'
});
@@ -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._
@@ -0,0 +1,5 @@
'use strict';

const target = document.querySelector('#target');

target.innerHTML = 'Press <code>CTL+D</code> on your keyboard to close the server.';
@@ -0,0 +1,6 @@
'use strict';

module.exports = {
context: __dirname,
entry: './app.js'
};
@@ -0,0 +1,42 @@
# CLI: Watch Content Base

## Watching a single directory

```console
npm run webpack-dev-server -- --content-base assets --watch-content-base --open
```

### What Should Happen

1. The script should open `http://localhost:8080/` in your default browser.
2. You should see the text on the page itself change to read `Success!`.
3. In your editor, edit `assets/index.html` and save your changes.
4. The app should reload in the browser.

## Watching an Array of Directories

```js
// webpack.conf.js
module.exports = {
/* ... */
devServer: {
contentBase: [
"assets",
"css",
]
}
}
```

```console
npm run webpack-dev-server -- --watch-content-base --open
```

### What Should Happen

1. The script should open `http://localhost:8080/` in your default browser.
2. You should see the text on the page itself change to read `Success!`.
3. In your editor, edit `assets/index.html` and save your changes.
4. The app should reload.
5. In your editor, edit `css/styles.css` and save your changes.
6. The app should reload.
@@ -0,0 +1,6 @@
'use strict';

const target = document.querySelector('#target');

target.classList.add('pass');
target.innerHTML = 'Success!';
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,16 @@
'use strict';

// our setup function adds behind-the-scenes bits to the config that all of our
// examples need
const { setup } = require('../../util');

module.exports = setup({
context: __dirname,
entry: './app.js',
devServer: {
contentBase: [
'assets',
'css'
]
}
});

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.