Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(guides): rewrite of development.md #1405

Merged
merged 17 commits into from
Jul 15, 2017
Merged
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
301 changes: 186 additions & 115 deletions content/guides/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,182 +5,253 @@ contributors:
- SpaceK33z
- rafde
- fvgs
- TheDutchCoder
---

On this page we'll explain how to get started with developing and how to choose one of three tools to develop. It is assumed you already have a webpack configuration file.
T> This guide extends on code examples found in the [`Output Management`](/guides/output-management) guide.

W> Never use any of these tools in production. Ever.
If you've been following the guides, you should have a solid understanding of some of the webpack basics. Before we continue, let's look into setting up a development environment to make our lives a little easier.

W> The tools in this guide are __only meant for development__, please __avoid__ using them in production!!

## Adjusting Your Text Editor

Some text editors have a "safe write" feature and enable this by default. As a result, saving a file will not always result in a recompile.
## Using Source Maps

Each editor has a different way of disabling this. For the most common ones:
When webpack bundles your source code, it can become difficult to track down errors and warnings to their original location. For example, if you bundle three source files (`a.js`, `b.js`, and `c.js`) into one bundle (`bundle.js`) and one of the source files contains an error, the stack trace will simply point to `bundle.js`. This isn't always very helpful as you probably want to know exactly which source file the error came from.

* **Sublime Text 3** - Add `"atomic_save": false` to your user preferences.
* **IntelliJ** - use search in the preferences to find "safe write" and disable it.
* **Vim** - add `:set backupcopy=yes` in your settings.
* **WebStorm** - uncheck `Use "safe write"` in Preferences > Appearance & Behavior > System Settings
In order to make it easier to track down errors and warnings, JavaScript offers Source Maps, which maps your compiled code back to your original source code. If an error originates from `b.js`, the Source Map will tell you exactly that.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe link to somewhere else for an intro to source maps and rephrase slightly:

In order to make it easier to track down errors and warnings, JavaScript offers [source maps](http://blog.teamtreehouse.com/introduction-source-maps), which map your compiled code back to your original source code. If an error originates from `b.js`, the mapping will direct you to that file.

I would also lowercase "Source Maps" => "source maps" everywhere (except in titles).


There are a lot of [different options](/configuration/devtool) available when it comes to Source Maps, be sure to check them out so you can configure them to your needs.

## Source Maps
For this guide, let's use the `inline-source-map` option, which is good for illustrative purposes (though not for production):

When a JavaScript exception occurs, you'll often want to know what file and line is generating this error. Since webpack outputs files into one or more bundles, it can be inconvenient to trace the file.
__webpack.config.js__

**Source maps** intend to fix this problem. There are a lot of [different options](/configuration/devtool) - each with their own advantages and disadvantages. To get started, we'll use this one:
``` diff
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

```js
devtool: "cheap-eval-source-map"
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js'
},
+ devtool: 'inline-source-map',
plugins: [
new HtmlWebpackPlugin({
title: 'Development'
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
```

Now let's make sure we have something to debug, so let's create an error in our `print.js` file:

## Choosing a Tool

webpack can be used with **watch mode**. In this mode webpack will watch your files, and recompile when they change.
**webpack-dev-server** provides an easy to use development server with fast live reloading. If you already have a development server and want full flexibility, **webpack-dev-middleware** can be used as middleware.

webpack-dev-server and webpack-dev-middleware use in-memory compilation, meaning that the bundle will not be saved to disk. This makes compiling faster and results in less mess on your file system.

In most cases **you'll want to use webpack-dev-server**, since it's the easiest to get started with and offers much functionality out-of-the-box.


### webpack Watch Mode
__src/print.js__

webpack's watch mode watches files for changes. If any change is detected, it'll run the compilation again.

We also want a nice progress bar while it's compiling. Let's run the command:

```bash
webpack --progress --watch
``` diff
export default function printMe() {
- console.log('I get called from print.js!');
+ cosnole.log('I get called from print.js!');
}
```

Make a change in one of your files and hit save. You should see that it's recompiling.

Watch mode makes no assumptions about a server, so you will need to provide your own. An easy server to use is [`serve`](https://github.com/zeit/serve). After installing (`npm install --save-dev serve`), you can run it in the directory where the outputted files are:

```bash
`npm bin`/serve
Run an `npm run build`, it should compile to something like this:

``` bash
Hash: 7bf68ca15f1f2690e2d1
Version: webpack 3.1.0
Time: 1224ms
Asset Size Chunks Chunk Names
app.bundle.js 1.44 MB 0, 1 [emitted] [big] app
print.bundle.js 6.43 kB 1 [emitted] print
index.html 248 bytes [emitted]
[0] ./src/print.js 84 bytes {0} {1} [built]
[1] ./src/index.js 403 bytes {0} [built]
[3] (webpack)/buildin/global.js 509 bytes {0} [built]
[4] (webpack)/buildin/module.js 517 bytes {0} [built]
+ 1 hidden module
Child html-webpack-plugin for "index.html":
[2] (webpack)/buildin/global.js 509 bytes {0} [built]
[3] (webpack)/buildin/module.js 517 bytes {0} [built]
+ 2 hidden modules
```

You may find it more convenient to run `serve` using npm scripts. You can do so by first creating a `start` script in `package.json` as follows:
Now open the resulting `index.html` file in your browser. Click the button and look in your console where the error is displayed. The error should say something like this:

```json
...
"scripts": {
"start": "serve"
}
...
```
``` bash
Uncaught ReferenceError: cosnole is not defined
at HTMLButtonElement.printMe (print.js:2)
```

You can then start the server by running `npm start` from within your project directory. After each compilation, you will need to manually refresh your browser to see the changes.
We can see that the error also contains a reference to the file (`print.js`) and line number (2) where the error occurred. This is great, because now we know exactly where to look to fix the issue.

T> You may find the `--single` option useful for serving single page apps.

## Choosing a Development Tool

### Watch Mode with Chrome DevTools Workspaces
W> Some text editors have a "safe write" function that might interfere with some of the following tools. Read [`Adjusting Your text Editor`](#adjusting-your-text-editor) for a solution to these issue.

If you set up Chrome to [persist changes when saving from the _Sources_ panel](https://medium.com/@rafaelideleon/webpack-your-chrome-devtools-workspaces-cb9cca8d50da)
so you don't have to refresh the page, you will have to setup webpack to use
It quickly becomes a hassle to manually run `npm run build` everytime you want to compile your code.

```javascript
devtool: "inline-source-map"
```
There are a couple of different options available in webpack that help you automatically compile your code whenever it changes:

to continue editing and saving your changes from Chrome or source files.
1. webpack's Watch Mode
2. webpack-dev-server
3. webpack-dev-middlware
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

webpack-dev-middlware => webpack-dev-middleware


There are some _gotchas_ about using workspaces with watch:
In most cases, you probably would want to use `webpack-dev-server`, but let's explore all of the above options.

* Large chunks (such as a common chunk that is over 1MB) that are rebuilt could cause the page to blank, which will force you to refresh the browser.
* Smaller chunks will be faster to build than larger chunks since `inline-source-map` is slower due to having to base64 encode the original source code.

### Using Watch Mode

### webpack-dev-server
You can instruct webpack to "watch" all files within your dependency graph for changes. If one of these files is updated, the code will be recompiled so you don't have to run the full build manually.

webpack-dev-server provides you with a server and live reloading. This is easy to setup.
Let's add an npm script that will start webpack's Watch Mode:

To prepare, make sure you have a `index.html` file that points to your bundle. Assuming that `output.filename` is `bundle.js`:
__package.json__

```html
<script src="/bundle.js"></script>
``` diff
{
"name": "development",
"version": "1.0.0",
"description": "",
"main": "webpack.config.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
+ "watch": "webpack --watch",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"css-loader": "^0.28.4",
"csv-loader": "^2.1.1",
"file-loader": "^0.11.2",
"html-webpack-plugin": "^2.29.0",
"style-loader": "^0.18.2",
"webpack": "^3.0.0",
"xml-loader": "^1.2.1"
}
}
```

Start with installing `webpack-dev-server` from npm:
You can now run `npm run watch` from the command line to see that webpack compiles your code, but doesn't exit to the command line. This is because the script is still watching your files.

```bash
npm install --save-dev webpack-dev-server
```
Now, with webpack watching your files, let's remove the error we introduced earlier:

When it's done installing, you should be able to use `webpack-dev-server` like this:
__src/print.js__

```bash
webpack-dev-server --open
``` diff
export default function printMe() {
- cosnole.log('I get called from print.js!');
+ console.log('I get called from print.js!');
}
```

T> If your console says it can't find the command, try running `node_modules/.bin/webpack-dev-server`. Optimally you would add the command to your `package.json`, like this: `"scripts": { "start": "webpack-dev-server" }`.

The command above should automatically open your browser on `http://localhost:8080`.

Make a change in one of your files and hit save. You should see that the console is recompiling. After that's done, the page should be refreshed. If nothing happens in the console, you may need to fiddle with [`watchOptions`](/configuration/dev-server#devserver-watchoptions-).

Now you have live reloading working, you can take it even a step further: Hot Module Replacement. This is an interface that makes it possible to swap modules **without a page refresh**. See the [Hot Module Replacement guide](/guides/hot-module-replacement) for more information.
Now save your file and keep an eye on the terminal window. You should see webpack automatically recompile the changed module!

By default **inline mode** is used. This mode injects the client - needed for live reloading and showing build errors - in your bundle. With inline mode you will get build errors and warnings in your DevTools console.
The only downside is that you have to refresh your browser in order to see the changes. It would be much nicer if that would happen automatically as well, so let's try `webpack-dev-server` which will do exactly that.

webpack-dev-server can do many more things such as proxying requests to your backend server. For more configuration options, see the [**devServer documentation**](/configuration/dev-server).

### Using webpack-dev-server

### webpack-dev-middleware
The `webpack-dev-server` provides you with a simple web server and the ability to use live reloading. Let's set it up:

webpack-dev-middleware works for connect-based middleware stacks. This can be useful if you already have a Node.js server or if you want to have full control over the server.

The middleware will cause webpack to compile files in-memory. When a compilation is running, it will delay the request to a file until the compilation is done.

W> This is intended for advanced users. webpack-dev-server is much easier to use.
``` bash
npm install --save-dev webpack-dev-server
```

Start with installing the dependencies from npm:
Change your config file to tell the dev server where to look for files:

__webpack.config.js__

``` diff
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js'
},
devtool: 'inline-source-map',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sure you keep this in sync with whatever you decide above. Also it seems like this config changed a lot more (at least with the HtmlWebpackPlugin), maybe update the one above and be clear about which guide is the prequisite for this one.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is probably not synced yet with my examples, will try to do that :)

+ devServer: {
+ contentBase: './dist'
+ },
plugins: [
new HtmlWebpackPlugin({
title: 'Development'
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
```

```bash
npm install --save-dev express webpack-dev-middleware
This tells `webpack-dev-server` to serve the files from the `dist` directory on `localhost:8080`.

Let's add a script to easily run the dev server as well:

__package.json__

``` diff
{
"name": "asset-management",
"version": "1.0.0",
"description": "",
"main": "webpack.config.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "webpack --progress --watch",
+ "start": "webpack-dev-server --open"
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"css-loader": "^0.28.4",
"csv-loader": "^2.1.1",
"file-loader": "^0.11.2",
"html-webpack-plugin": "^2.29.0",
"style-loader": "^0.18.2",
"webpack": "^3.0.0",
"xml-loader": "^1.2.1"
}
}
```

After installing, you can use the middleware like this:
Now we can run `npm start` from the command line and we will see our browser automatically loading up our page. If you now change any of the source files and save them, the web server will automatically reload after the code has been compiled. Give it a try!

```js
var express = require("express");
var webpackDevMiddleware = require("webpack-dev-middleware");
var webpack = require("webpack");
var webpackConfig = require("./webpack.config");
The `webpack-dev-server` comes with many configurable options. Head over to the [`documentation`](/configuration/dev-server) to learn more.

var app = express();
var compiler = webpack(webpackConfig);
T> Now that your server is working, you might want to give [`Hot Module Replacement`](/guides/hot-module-replacement) a try!
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take "Hot Module Replacement" out of the backticks (``) as it's not code.


app.use(webpackDevMiddleware(compiler, {
publicPath: "/" // Same as `output.publicPath` in most cases.
}));

app.listen(3000, function () {
console.log("Listening on port 3000!");
});
```
### Using webpack-dev-middleware

Depending on what you've used in `output.publicPath` and `output.filename`, your bundle should now be available on `http://localhost:3000/bundle.js`.
?> This section is currently not working out of the box.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe update this as a call to action? e.g.

?> Familiar with `webpack-dev-middleware`? We need your help! Please submit a PR to fill in the missing instructions and example here. Make sure to keep it simple as this guide is intended for beginners.


By default, **watch mode** is used. It's also possible to use **lazy mode**, which will only recompile on a request to the entry point.

To compile only on a request to the entry `bundle.js`:
## Adjusting Your Text Editor

```js
app.use(webpackDevMiddleware(compiler, {
lazy: true,
filename: "bundle.js" // Same as `output.filename` in most cases.
}));
```
When using automatic compilation of your code, you could run into issues when saving your files. Some editors have a "safe write "feature that can potentially interfere with recompilation.

To disable this feature in some common editors, see the list below:

There are many more options you can use. For all configuration options, see the [**devServer documentation**](/configuration/dev-server).
* **Sublime Text 3** - Add `atomic_save: "false"` to your user preferences.
* **IntelliJ** - use search in the preferences to find "safe write" and disable it.
* **Vim** - add `:set backupcopy=yes` to your settings.
* **WebStorm** - uncheck Use `"safe write"` in `Preferences > Appearance & Behavior > System Settings`.


## References
## Conclusion

* [SurviveJS - Automatic Browser Refresh](http://survivejs.com/webpack/developing-with-webpack/automatic-browser-refresh/)
* [webpack your Chrome DevTools Workspaces](https://medium.com/@rafaelideleon/webpack-your-chrome-devtools-workspaces-cb9cca8d50da)
Now that you've learned how to automatically compile your and run a simple development server, you can check out the next guide, which will cover [`Hot Module Replacement`](/guides/hot-module-replacement).