docs(webpack): update to webpack 2 (#3108)

* docs(webpack): update to webpack 2
This commit is contained in:
Jesús Rodríguez 2017-01-22 12:51:02 +01:00 committed by GitHub
parent 457412ae29
commit 9fb5e0ad24
8 changed files with 221 additions and 159 deletions

View File

@ -40,16 +40,16 @@
"@types/angular-sanitize": "^1.3.3", "@types/angular-sanitize": "^1.3.3",
"@types/jasmine": "~2.5.36", "@types/jasmine": "~2.5.36",
"@types/node": "^6.0.45", "@types/node": "^6.0.45",
"angular2-template-loader": "^0.4.0", "angular2-template-loader": "^0.6.0",
"awesome-typescript-loader": "^2.2.4", "awesome-typescript-loader": "^3.0.0-beta.18",
"babel-cli": "^6.16.0", "babel-cli": "^6.16.0",
"babel-preset-angular2": "^0.0.2", "babel-preset-angular2": "^0.0.2",
"babel-preset-es2015": "^6.16.0", "babel-preset-es2015": "^6.16.0",
"canonical-path": "0.0.2", "canonical-path": "0.0.2",
"concurrently": "^3.0.0", "concurrently": "^3.0.0",
"css-loader": "^0.25.0", "css-loader": "^0.26.1",
"extract-text-webpack-plugin": "^1.0.1", "extract-text-webpack-plugin": "2.0.0-beta.5",
"file-loader": "^0.8.5", "file-loader": "^0.9.0",
"html-loader": "^0.4.3", "html-loader": "^0.4.3",
"html-webpack-plugin": "^2.16.1", "html-webpack-plugin": "^2.16.1",
"http-server": "^0.9.0", "http-server": "^0.9.0",
@ -62,7 +62,7 @@
"karma-jasmine-html-reporter": "^0.2.2", "karma-jasmine-html-reporter": "^0.2.2",
"karma-phantomjs-launcher": "^1.0.2", "karma-phantomjs-launcher": "^1.0.2",
"karma-sourcemap-loader": "^0.3.7", "karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^1.8.0", "karma-webpack": "^2.0.1",
"lite-server": "^2.2.2", "lite-server": "^2.2.2",
"lodash": "^4.16.2", "lodash": "^4.16.2",
"null-loader": "^0.1.1", "null-loader": "^0.1.1",
@ -79,9 +79,9 @@
"ts-node": "^1.3.0", "ts-node": "^1.3.0",
"tslint": "^3.15.1", "tslint": "^3.15.1",
"typescript": "~2.0.10", "typescript": "~2.0.10",
"webpack": "^1.13.0", "webpack": "2.2.0",
"webpack-dev-server": "^1.14.1", "webpack-dev-server": "2.2.0-rc.0",
"webpack-merge": "^0.14.0" "webpack-merge": "^2.4.0"
}, },
"repository": {} "repository": {}
} }

View File

@ -32,14 +32,14 @@ output: {
// #enddocregion two-entries // #enddocregion two-entries
// #docregion loaders // #docregion loaders
loaders: [ rules: [
{ {
test: /\.ts$/ test: /\.ts$/
loaders: 'ts' loader: 'awesome-typescript-loader'
}, },
{ {
test: /\.css$/ test: /\.css$/
loaders: 'style!css' loaders: 'style-loader!css-loader'
} }
] ]
// #enddocregion loaders // #enddocregion loaders

View File

@ -15,34 +15,34 @@ module.exports = {
// #docregion resolve // #docregion resolve
resolve: { resolve: {
extensions: ['', '.ts', '.js'] extensions: ['.ts', '.js']
}, },
// #enddocregion resolve // #enddocregion resolve
// #docregion loaders // #docregion loaders
module: { module: {
loaders: [ rules: [
{ {
test: /\.ts$/, test: /\.ts$/,
loaders: ['awesome-typescript-loader', 'angular2-template-loader'] loaders: ['awesome-typescript-loader', 'angular2-template-loader']
}, },
{ {
test: /\.html$/, test: /\.html$/,
loader: 'html' loader: 'html-loader'
}, },
{ {
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/, test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'file?name=assets/[name].[hash].[ext]' loader: 'file-loader?name=assets/[name].[hash].[ext]'
}, },
{ {
test: /\.css$/, test: /\.css$/,
exclude: helpers.root('src', 'app'), exclude: helpers.root('src', 'app'),
loader: ExtractTextPlugin.extract('style', 'css?sourceMap') loader: ExtractTextPlugin.extract({ fallbackLoader: 'style-loader', loader: 'css-loader?sourceMap' })
}, },
{ {
test: /\.css$/, test: /\.css$/,
include: helpers.root('src', 'app'), include: helpers.root('src', 'app'),
loader: 'raw' loader: 'raw-loader'
} }
] ]
}, },
@ -50,6 +50,14 @@ module.exports = {
// #docregion plugins // #docregion plugins
plugins: [ plugins: [
// Workaround for angular/angular#11580
new webpack.ContextReplacementPlugin(
// The (\\|\/) piece accounts for path separators in *nix and Windows
/angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
helpers.root('./src'), // location of your src
{} // a map of your routes
),
new webpack.optimize.CommonsChunkPlugin({ new webpack.optimize.CommonsChunkPlugin({
name: ['app', 'vendor', 'polyfills'] name: ['app', 'vendor', 'polyfills']
}), }),

View File

@ -17,13 +17,8 @@ module.exports = webpackMerge(commonConfig, {
chunkFilename: '[id].[hash].chunk.js' chunkFilename: '[id].[hash].chunk.js'
}, },
htmlLoader: {
minimize: false // workaround for ng2
},
plugins: [ plugins: [
new webpack.NoErrorsPlugin(), new webpack.NoEmitOnErrorsPlugin(),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({ // https://github.com/angular/angular/issues/10618 new webpack.optimize.UglifyJsPlugin({ // https://github.com/angular/angular/issues/10618
mangle: { mangle: {
keep_fnames: true keep_fnames: true
@ -34,7 +29,13 @@ module.exports = webpackMerge(commonConfig, {
'process.env': { 'process.env': {
'ENV': JSON.stringify(ENV) 'ENV': JSON.stringify(ENV)
} }
}),
new webpack.LoaderOptionsPlugin({
htmlLoader: {
minimize: false // workaround for ng2
}
}) })
] ]
}); });
// #enddocregion // #enddocregion

View File

@ -1,39 +1,50 @@
// #docregion // #docregion
var webpack = require('webpack');
var helpers = require('./helpers'); var helpers = require('./helpers');
module.exports = { module.exports = {
devtool: 'inline-source-map', devtool: 'inline-source-map',
resolve: { resolve: {
extensions: ['', '.ts', '.js'] extensions: ['.ts', '.js']
}, },
module: { module: {
loaders: [ rules: [
{ {
test: /\.ts$/, test: /\.ts$/,
loaders: ['awesome-typescript-loader', 'angular2-template-loader'] loaders: ['awesome-typescript-loader', 'angular2-template-loader']
}, },
{ {
test: /\.html$/, test: /\.html$/,
loader: 'html' loader: 'html-loader'
}, },
{ {
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/, test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
loader: 'null' loader: 'null-loader'
}, },
{ {
test: /\.css$/, test: /\.css$/,
exclude: helpers.root('src', 'app'), exclude: helpers.root('src', 'app'),
loader: 'null' loader: 'null-loader'
}, },
{ {
test: /\.css$/, test: /\.css$/,
include: helpers.root('src', 'app'), include: helpers.root('src', 'app'),
loader: 'raw' loader: 'raw-loader'
} }
] ]
} },
plugins: [
new webpack.ContextReplacementPlugin(
// The (\\|\/) piece accounts for path separators in *nix and Windows
/angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
helpers.root('./src'), // location of your src
{} // a map of your routes
)
]
} }
// #enddocregion // #enddocregion

View File

@ -24,27 +24,27 @@
"devDependencies": { "devDependencies": {
"@types/node": "^6.0.45", "@types/node": "^6.0.45",
"@types/jasmine": "^2.5.35", "@types/jasmine": "^2.5.35",
"angular2-template-loader": "^0.4.0", "angular2-template-loader": "^0.6.0",
"awesome-typescript-loader": "^2.2.4", "awesome-typescript-loader": "^3.0.0-beta.18",
"css-loader": "^0.23.1", "css-loader": "^0.26.1",
"extract-text-webpack-plugin": "^1.0.1", "extract-text-webpack-plugin": "2.0.0-beta.5",
"file-loader": "^0.8.5", "file-loader": "^0.9.0",
"html-loader": "^0.4.3", "html-loader": "^0.4.3",
"html-webpack-plugin": "^2.15.0", "html-webpack-plugin": "^2.16.1",
"jasmine-core": "^2.4.1", "jasmine-core": "^2.4.1",
"karma": "^1.2.0", "karma": "^1.2.0",
"karma-jasmine": "^1.0.2", "karma-jasmine": "^1.0.2",
"karma-phantomjs-launcher": "^1.0.2", "karma-phantomjs-launcher": "^1.0.2",
"karma-sourcemap-loader": "^0.3.7", "karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^1.8.0", "karma-webpack": "^2.0.1",
"null-loader": "^0.1.1", "null-loader": "^0.1.1",
"phantomjs-prebuilt": "^2.1.7", "phantomjs-prebuilt": "^2.1.7",
"raw-loader": "^0.5.1", "raw-loader": "^0.5.1",
"rimraf": "^2.5.2", "rimraf": "^2.5.2",
"style-loader": "^0.13.1", "style-loader": "^0.13.1",
"typescript": "~2.0.10", "typescript": "~2.0.10",
"webpack": "^1.13.0", "webpack": "2.2.0",
"webpack-dev-server": "^1.14.1", "webpack-dev-server": "2.2.0-rc.0",
"webpack-merge": "^0.14.0" "webpack-merge": "^2.4.0"
} }
} }

View File

@ -5,11 +5,8 @@ require('zone.js/dist/zone');
if (process.env.ENV === 'production') { if (process.env.ENV === 'production') {
// Production // Production
} else { } else {
// Development // Development and test
Error['stackTraceLimit'] = Infinity; Error['stackTraceLimit'] = Infinity;
require('zone.js/dist/long-stack-trace-zone'); require('zone.js/dist/long-stack-trace-zone');
} }

View File

@ -1,12 +1,17 @@
include ../_util-fns include ../_util-fns
// The docs standard h4 style uppercases, making code terms unreadable. Override it.
style.
h4 {font-size: 17px !important; text-transform: none !important;}
.syntax { font-family: Consolas, 'Lucida Sans', Courier, sans-serif; color: black; font-size: 85%; }
:marked :marked
[**Webpack**](https://webpack.github.io/) is a popular module bundler, [**Webpack**](https://webpack.github.io/) is a popular module bundler,
a tool for bundling application source code in convenient _chunks_ a tool for bundling application source code in convenient _chunks_
and for loading that code from a server into a browser. and for loading that code from a server into a browser.
It's an excellent alternative to the *SystemJS* approach we use throughout the documentation. It's an excellent alternative to the *SystemJS* approach used elsewhere in the documentation.
In this guide we get a taste of Webpack and how to use it with Angular applications. This guide offers a taste of Webpack and explains how to use it with Angular applications.
<a id="top"></a> <a id="top"></a>
## Table of contents ## Table of contents
@ -40,17 +45,17 @@ include ../_util-fns
Webpack roams over your application source code, Webpack roams over your application source code,
looking for `import` statements, building a dependency graph, and emitting one (or more) _bundles_. looking for `import` statements, building a dependency graph, and emitting one (or more) _bundles_.
With plugin "loaders" Webpack can preprocess and minify different non-JavaScript files such as TypeScript, SASS, and LESS files. With plugins and rules, Webpack can preprocess and minify different non-JavaScript files such as TypeScript, SASS, and LESS files.
We determine what Webpack does and how it does it with a JavaScript configuration file, `webpack.config.js`. You determine what Webpack does and how it does it with a JavaScript configuration file, `webpack.config.js`.
a(id="entries-outputs") a(id="entries-outputs")
.l-main-section .l-main-section
:marked :marked
### Entries and outputs ### Entries and outputs
We feed Webpack with one or more *entry* files and let it find and incorporate the dependencies that radiate from those entries. You supply Webpack with one or more *entry* files and let it find and incorporate the dependencies that radiate from those entries.
In this example, we start from the application's root file, `src/app.ts`: The one entry point file in this example is the application's root file, `src/app.ts`:
+makeExample('webpack/ts-snippets/webpack.config.snippets.ts', 'one-entry', 'webpack.config.js (single entry)')(format=".") +makeExample('webpack/ts-snippets/webpack.config.snippets.ts', 'one-entry', 'webpack.config.js (single entry)')(format=".")
@ -60,38 +65,37 @@ a(id="entries-outputs")
+makeExample('webpack/ts-snippets/webpack.config.snippets.ts', 'app-example', 'src/app.ts')(format=".") +makeExample('webpack/ts-snippets/webpack.config.snippets.ts', 'app-example', 'src/app.ts')(format=".")
:marked :marked
Here it sees that we're importing *@angular/core* so it adds that to its dependency list for (potential) inclusion in the bundle. It sees that you're importing *@angular/core* so it adds that to its dependency list for (potential) inclusion in the bundle.
It opens *@angular/core* and follows _its_ network of `import` statements until it has built the complete dependency graph from `app.ts` down. It opens the *@angular/core* file and follows _its_ network of `import` statements until it has built the complete dependency graph from `app.ts` down.
Then it **outputs** these files to the `app.js` _bundle file_ designated in configuration: Then it **outputs** these files to the `app.js` _bundle file_ designated in configuration:
+makeExample('webpack/ts-snippets/webpack.config.snippets.ts', 'one-output', 'webpack.config.js (single output)')(format=".") +makeExample('webpack/ts-snippets/webpack.config.snippets.ts', 'one-output', 'webpack.config.js (single output)')(format=".")
:marked :marked
This `app.js` output bundle is a single JavaScript file that contains our application source and its dependencies. This `app.js` output bundle is a single JavaScript file that contains the application source and its dependencies.
We'll load it later with a &lt;script&gt; tag in our index.html. You'll load it later with a `<script>` tag in the `index.html`.
#### Multiple bundles #### Multiple bundles
We probably do not want one giant bundle of everything. You probably don't want one giant bundle of everything.
We'll likely prefer to separate our volatile application app code from comparatively stable vendor code modules. It's preferable to separate the volatile application app code from comparatively stable vendor code modules.
We change the configuration so that we have two entry points, `app.ts` and `vendor.ts`: Change the configuration so that it has two entry points, `app.ts` and `vendor.ts`:
+makeExample('webpack/ts-snippets/webpack.config.snippets.ts', 'two-entries','webpack.config.js (two entries)')(format=".") +makeExample('webpack/ts-snippets/webpack.config.snippets.ts', 'two-entries','webpack.config.js (two entries)')(format=".")
:marked :marked
Webpack constructs two separate dependency graphs Webpack constructs two separate dependency graphs
and emits *two* bundle files, one called `app.js` containing only our application code and and emits *two* bundle files, one called `app.js` containing only the application code and
another called `vendor.js` with all the vendor dependencies. another called `vendor.js` with all the vendor dependencies.
.l-sub-section .l-sub-section
:marked :marked
The `[name]` in the output name is a Webpack *placeholder* that is replaced with the entry names. The `[name]` in the output name is a *placeholder* that a Webpack plugin replaces with the entry names,
`app` and `vendor` respectively. `app` and `vendor`. Plugins are [covered later](#commons-chunk-plugin) in the guide.
We need a plugin to make this work; we'll [cover that later](#commons-chunk-plugin) in the chapter.
:marked :marked
We met `app.ts` earlier. We wrote `vendor.ts` such that it imports the vendor modules we need: To tell Webpack what belongs in the vendor bundle,
add a `vendor.ts` file that only imports the application's third-party modules:
+makeExample('webpack/ts/src/vendor.ts', null,'src/vendor.ts')(format=".") +makeExample('webpack/ts/src/vendor.ts', null,'src/vendor.ts')(format=".")
@ -101,9 +105,9 @@ a(id="loaders")
### Loaders ### Loaders
Webpack can bundle any kind of file: JavaScript, TypeScript, CSS, SASS, LESS, images, html, fonts, whatever. Webpack can bundle any kind of file: JavaScript, TypeScript, CSS, SASS, LESS, images, html, fonts, whatever.
Webpack itself doesn't know what to do with a non-JavaScript file. Webpack _itself_ only understands JavaScript files.
We teach it to process such files into JavaScript with *loaders*. Teach it to transform non-JavaScript file into their JavaScript equivalents with *loaders*.
Here we configure loaders for TypeScript and CSS: Configure loaders for TypeScript and CSS as follows.
+makeExample('webpack/ts-snippets/webpack.config.snippets.ts', 'loaders', 'webpack.config.js (two entries)')(format=".") +makeExample('webpack/ts-snippets/webpack.config.snippets.ts', 'loaders', 'webpack.config.js (two entries)')(format=".")
@ -118,7 +122,7 @@ a(id="loaders")
The first `import` file matches the `.ts` pattern so Webpack processes it with the `awesome-typescript-loader`. The first `import` file matches the `.ts` pattern so Webpack processes it with the `awesome-typescript-loader`.
The imported file doesn't match the second pattern so its loader is ignored. The imported file doesn't match the second pattern so its loader is ignored.
The second `import` matches the second `.css` pattern for which we have *two* loaders chained by the (!) character. The second `import` matches the second `.css` pattern for which you have *two* loaders chained by the (!) character.
Webpack applies chained loaders *right to left* so it applies Webpack applies chained loaders *right to left* so it applies
the `css` loader first (to flatten CSS `@import` and `url(...)` statements) and the `css` loader first (to flatten CSS `@import` and `url(...)` statements) and
then the `style` loader (to append the css inside *&lt;style&gt;* elements on the page). then the `style` loader (to append the css inside *&lt;style&gt;* elements on the page).
@ -129,7 +133,7 @@ a(id="plugins")
### Plugins ### Plugins
Webpack has a build pipeline with well-defined phases. Webpack has a build pipeline with well-defined phases.
We tap into that pipeline with plugins such as the `uglify` minification plugin: Tap into that pipeline with plugins such as the `uglify` minification plugin:
+makeExample('webpack/ts-snippets/webpack.config.snippets.ts', 'plugins')(format=".") +makeExample('webpack/ts-snippets/webpack.config.snippets.ts', 'plugins')(format=".")
@ -138,12 +142,12 @@ a(id="configure-webpack")
:marked :marked
## Configure Webpack ## Configure Webpack
After that brief orientation, we are ready to build our own Webpack configuration for Angular apps. After that brief orientation, you are ready to build your own Webpack configuration for Angular apps.
Begin by setting up the development environment. Begin by setting up the development environment.
Create a **new project folder** Create a **new project folder**
code-example(format=""). code-example(language="sh" class="code-shell").
mkdir angular-webpack mkdir angular-webpack
cd angular-webpack cd angular-webpack
@ -165,79 +169,114 @@ code-example(format="").
) )
.l-sub-section .l-sub-section
:marked :marked
Many of these files and much of their content should be familiar from other Angular documentation chapters. Many of these files should be familiar from other Angular documentation guides,
especially the [_Typescript configuration_](../guide/typescript-configuration.html) and
Learn about the `package.json` in the [npm packages](../guide/npm-packages.html) chapter. [_npm packages_](../guide/npm-packages.html) guides.
We require packages for Webpack use in addition to the ones listed in that chapter.
Webpack, the plugins, and the loaders are also installed as packages.
Learn about `tsconfig.json` in the [Typescript configuration](../guide/typescript-configuration.html) chapter. They are listed in the updated `packages.json`.
:marked :marked
Open a terminal/console window and install the *npm* packages with `npm install`. Open a terminal window and (re)install the *npm* packages
code-example(language="sh" class="code-shell").
npm install
a#polyfills
.l-main-section
:marked
### Polyfills
You'll need polyfills to run an Angular application in most browsers as explained
in the [_Browser Support_](browser-support.html) guide.
Polyfills should be bundled separately from the application and vendor bundles.
Add a `polyfills.ts` like this one to the `src/` folder.
+makeExample('webpack/ts/src/polyfills.ts', '', 'src/polyfills.ts')(format=".")
.callout.is-critical
header Loading polyfills
:marked
Load `zone.js` early within `polyfills.ts`, immediately after the other ES6 and metadata shims.
:marked
Because this bundle file will load first, `polyfills.ts` is also a good place to configure the browser environment
for production or development.
a(id="common-configuration") a(id="common-configuration")
.l-main-section .l-main-section
:marked :marked
### Common Configuration ### Common Configuration
We will define separate configurations for development, production, and test environments. Developers typically have separate configurations for development, production, and test environments.
All three have some configuration in common. All three have a lot of configuration in common.
We'll gather that common configuration in a separate file called `webpack.common.js`.
Gather the common configuration in a file called `webpack.common.js`.
Let's see the entire file and then walk through it a section at a time:
+makeExample('webpack/ts/config/webpack.common.js', null, 'config/webpack.common.js')(format=".") +makeExample('webpack/ts/config/webpack.common.js', null, 'config/webpack.common.js')(format=".")
:marked :marked
Webpack is a NodeJS-based tool so its configuration is a JavaScript _commonjs_ module file ### Inside _webpack.common.js_
that begins with `require` statements as such files do. Webpack is a NodeJS-based tool that reads configuration from a JavaScript _commonjs_ module file.
The configuration exports several objects, beginning with the *entries* described earlier: The configuration imports dependencies with `require` statements
and exports several objects as properties of a `module.exports` object.
* [`entries`](#common-entries) - the entry-point files that define the bundles.
* [`resolve`](#common-resolve) - how to resolve file names when they lack extensions.
* [`module.rules`](#common-rules) - `module` is an object with `rules` for deciding how files are loaded.
* [`plugins`](#common-plugins) - creates instances of the plugins.
a#common-entries
:marked
#### _entries_
The first export is the *entries* object, described above:
+makeExample('webpack/ts/config/webpack.common.js', 'entries', 'config/webpack.common.js')(format=".") +makeExample('webpack/ts/config/webpack.common.js', 'entries', 'config/webpack.common.js')(format=".")
:marked :marked
We are splitting our application into three bundles: This *entries* object defines the three bundles:
* polyfills - the standard polyfills we require to run Angular applications in most modern browsers.
* vendor - the vendor files we need: Angular, lodash, bootstrap.css...
* app - our application code.
.callout.is-critical
header Loading polyfills
:marked
Load Zone.js early, immediately after the other ES6 and metadata shims.
* polyfills - the polyfills needed to run Angular applications in most modern browsers.
* vendor - the third-party dependencies such as Angular, lodash, and bootstrap.css.
* app - the application code.
a#common-resolve
:marked :marked
Our app will `import` dozens if not hundreds of JavaScript and TypeScript files. #### _resolve_ extension-less imports
We _might_ write `import` statements with explicit extensions as in this example:
The app will `import` dozens if not hundreds of JavaScript and TypeScript files.
You could write `import` statements with explicit extensions like this example:
+makeExample('webpack/ts-snippets/webpack.config.snippets.ts', 'single-import')(format=".") +makeExample('webpack/ts-snippets/webpack.config.snippets.ts', 'single-import')(format=".")
:marked :marked
But most of our `import` statements won't mention the extension at all. But most `import` statements don't mention the extension at all.
So we tell Webpack to _resolve_ module file requests by looking for matching files with Tell Webpack to resolve extension-less file requests by looking for matching files with
`.ts` extension or `.js` extension (for regular JavaScript files and pre-compiled TypeScript files).
* an explicit extension (signified by the empty extension string, `''`) or
* `.js` extension (for regular JavaScript files and pre-compiled TypeScript files) or
* `.ts` extension.
+makeExample('webpack/ts/config/webpack.common.js', 'resolve', 'config/webpack.common.js')(format=".") +makeExample('webpack/ts/config/webpack.common.js', 'resolve', 'config/webpack.common.js')(format=".")
.l-sub-section .l-sub-section
:marked :marked
We could add `.css` and `.html` later if we want Webpack to resolve extension-less files with _those_ extension too. If Webpack should resolve extension-less files for styles and HTML,
add `.css` and `.html` to the list.
a#common-rules
:marked :marked
Next we specify the loaders: :marked
#### _module.rules_
Rules tell Webpack which loaders to use for each file (AKA _module_):
+makeExample('webpack/ts/config/webpack.common.js', 'loaders', 'config/webpack.common.js')(format=".") +makeExample('webpack/ts/config/webpack.common.js', 'loaders', 'config/webpack.common.js')(format=".")
:marked :marked
* awesome-typescript-loader - a loader to transpile our Typescript code to ES5, guided by the `tsconfig.json` file * awesome-typescript-loader - a loader to transpile the Typescript code to ES5, guided by the `tsconfig.json` file
* angular2-template-loader - loads angular components' template and styles * angular2-template-loader - loads angular components' template and styles
* html - for component templates * html - for component templates
* images/fonts - Images and fonts are bundled as well. * images/fonts - Images and fonts are bundled as well.
* css - The pattern matches application-wide styles; the second handles component-scoped styles (the ones specified in a component's `styleUrls` metadata property) * css - The pattern matches application-wide styles; the second handles component-scoped styles (the ones specified in a component's `styleUrls` metadata property)
.l-sub-section .l-sub-section
:marked :marked
The first pattern excludes `.css` files within the `/src/app` directories where our component-scoped styles sit. The first pattern excludes `.css` files within the `/src/app` directories where the component-scoped styles sit.
It includes only `.css` files located at or above `/src`; these are the application-wide styles. It includes only `.css` files located at or above `/src`; these are the application-wide styles.
The `ExtractTextPlugin` (described below) applies the `style` and `css` loaders to these files. The `ExtractTextPlugin` (described below) applies the `style` and `css` loaders to these files.
@ -246,10 +285,13 @@ a(id="common-configuration")
.l-sub-section .l-sub-section
:marked :marked
Multiple loaders can be also chained using the array notation. Multiple loaders can be chained using the array notation.
a#common-plugins
:marked :marked
Finally we add two plugins: :marked
#### _plugins_
Finally, create instances of three plugins:
+makeExample('webpack/ts/config/webpack.common.js', 'plugins', 'config/webpack.common.js')(format=".") +makeExample('webpack/ts/config/webpack.common.js', 'plugins', 'config/webpack.common.js')(format=".")
@ -257,23 +299,24 @@ a(id="commons-chunk-plugin")
:marked :marked
#### *CommonsChunkPlugin* #### *CommonsChunkPlugin*
We want the `app.js` bundle to contain only app code and the `vendor.js` bundle to contain only the vendor code. The `app.js` bundle should contain only application code. All vendor code belongs in the `vendor.js` bundle.
Our application code `imports` vendor code. Webpack is not smart enough to keep the vendor code out of the `app.js` bundle. Of course the application code `imports` vendor code.
We rely on the `CommonsChunkPlugin` to do that job. Webpack itself is not smart enough to keep the vendor code out of the `app.js` bundle.
The `CommonsChunkPlugin` does that job.
.l-sub-section .l-sub-section
:marked :marked
It identifies the hierarchy among three _chunks_: `app` -> `vendor` -> `polyfills`. The `CommonsChunkPlugin` identifies the hierarchy among three _chunks_: `app` -> `vendor` -> `polyfills`.
Where Webpack finds that `app` has shared dependencies with `vendor`, it removes them from `app`. Where Webpack finds that `app` has shared dependencies with `vendor`, it removes them from `app`.
It would do the same if `vendor` and `polyfills` had shared dependencies (which they don't). It would remove `polyfills` from `vendor` if they shared dependencies (which they don't).
a(id="html-webpack-plugin") a(id="html-webpack-plugin")
:marked :marked
#### *HtmlWebpackPlugin* #### *HtmlWebpackPlugin*
Webpack generates a number of js and css files. Webpack generates a number of js and css files.
We _could_ insert them into our `index.html` _manually_. That would be tedious and error-prone. You _could_ insert them into the `index.html` _manually_. That would be tedious and error-prone.
Webpack can inject those scripts and links for us with the `HtmlWebpackPlugin`. Webpack can inject those scripts and links for you with the `HtmlWebpackPlugin`.
a(id="environment-configuration") a(id="environment-configuration")
.l-main-section .l-main-section
@ -281,8 +324,8 @@ a(id="environment-configuration")
### Environment-specific configuration ### Environment-specific configuration
The `webpack.common.js` configuration file does most of the heavy lifting. The `webpack.common.js` configuration file does most of the heavy lifting.
We create separate, environment-specific configuration files that build on `webpack.common` Create separate, environment-specific configuration files that build on `webpack.common`
by merging into it the peculiarities particular to their target environments. by merging into it the peculiarities particular to the target environments.
These files tend to be short and simple. These files tend to be short and simple.
@ -291,29 +334,29 @@ a(id="development-configuration")
:marked :marked
### Development Configuration ### Development Configuration
Here is the development configuration file, `webpack.dev.js` Here is the `webpack.dev.js` development configuration file.
+makeExample('webpack/ts/config/webpack.dev.js', null, 'config/webpack.dev.js')(format=".") +makeExample('webpack/ts/config/webpack.dev.js', null, 'config/webpack.dev.js')(format=".")
:marked :marked
The development build relies on the Webpack development server which we configure near the bottom of the file. The development build relies on the Webpack development server, configured near the bottom of the file.
Although we tell Webpack to put output bundles in the `dist` folder, Although you tell Webpack to put output bundles in the `dist` folder,
the dev server keeps all bundles in memory; it doesn't write them to disk. the dev server keeps all bundles in memory; it doesn't write them to disk.
So we won't find any files in the `dist` folder (at least not any generated from `this development build`). You won't find any files in the `dist` folder (at least not any generated from `this development build`).
The `HtmlWebpackPlugin` (added in `webpack.common.js`) use the *publicPath* and the *filename* settings to generate The `HtmlWebpackPlugin` (added in `webpack.common.js`) use the *publicPath* and the *filename* settings to generate
appropriate &lt;script&gt; and &lt;link&gt; tags into the `index.html`. appropriate &lt;script&gt; and &lt;link&gt; tags into the `index.html`.
Our CSS are buried inside our Javascript bundles by default. The `ExtractTextPlugin` extracts them into The CSS styles are buried inside the Javascript bundles by default. The `ExtractTextPlugin` extracts them into
external `.css` files that the `HtmlWebpackPlugin` inscribes as &lt;link&gt; tags into the `index.html`. external `.css` files that the `HtmlWebpackPlugin` inscribes as &lt;link&gt; tags into the `index.html`.
Refer to the Webpack documentation for details on these and other configuration options in this file Refer to the Webpack documentation for details on these and other configuration options in this file
Grab the app code at the end of this guide and try: Grab the app code at the end of this guide and try:
code-example(format=""). code-example(language="sh" class="code-shell").
npm start npm start
a(id="production-configuration") a(id="production-configuration")
@ -326,29 +369,30 @@ a(id="production-configuration")
+makeExample('webpack/ts/config/webpack.prod.js', null, 'config/webpack.prod.js')(format=".") +makeExample('webpack/ts/config/webpack.prod.js', null, 'config/webpack.prod.js')(format=".")
:marked :marked
We don't use a development server. We're expected to deploy the application and its dependencies to a real production server. You'll deploy the application and its dependencies to a real production server.
You won't deploy the artifacts needed only in development.
This time the output bundle files are physically placed in the `dist` folder. Put the production output bundle files in the `dist` folder.
Webpack generates file names with cache-busting hash. Webpack generates file names with cache-busting hash.
Thanks to the `HtmlWebpackPlugin` we don't have to update the `index.html` file when the hashes changes. Thanks to the `HtmlWebpackPlugin`, you don't have to update the `index.html` file when the hashes changes.
There are additional plugins: There are additional plugins:
* **NoErrorsPlugin** - stops the build if there is any error. * **NoEmitOnErrorsPlugin** - stops the build if there is an error.
* **DedupePlugin** - detects identical (and nearly identical) files and removes them from the output.
* **UglifyJsPlugin** - minifies the bundles. * **UglifyJsPlugin** - minifies the bundles.
* **ExtractTextPlugin** - extracts embedded css as external files, adding cache-busting hash to the filename. * **ExtractTextPlugin** - extracts embedded css as external files, adding cache-busting hash to the filename.
* **DefinePlugin** - use to define environment variables that we can reference within our application. * **DefinePlugin** - use to define environment variables that you can reference within the application.
* **LoaderOptionsPlugins** - to override options of certain loaders.
Thanks to the *DefinePlugin* and the `ENV` variable defined at top, we can enable Angular production mode like this: Thanks to the *DefinePlugin* and the `ENV` variable defined at top, you can enable Angular production mode like this:
+makeExample('webpack/ts/src/main.ts', 'enable-prod')(format=".") +makeExample('webpack/ts/src/main.ts', 'enable-prod')(format=".")
:marked :marked
Grab the app code at the end of this guide and try: Grab the app code at the end of this guide and try:
code-example(format=""). code-example(language="sh" class="code-shell").
npm run build npm run build
a(id="test-configuration") a(id="test-configuration")
@ -356,25 +400,23 @@ a(id="test-configuration")
:marked :marked
### Test Configuration ### Test Configuration
We don't need much configuration to run unit tests. You don't need much configuration to run unit tests.
We don't need the loaders and plugins that we declared for our development and production builds. You don't need the loaders and plugins that you declared for your development and production builds.
We probably don't need to load and process the application-wide styles files for unit tests and doing so would slow us down; You probably don't need to load and process the application-wide styles files for unit tests and doing so would slow you down;
we'll use the `null` loader for those CSS. you'll use the `null` loader for those CSS files.
We could merge our test configuration into the `webpack.common` configuration and override the parts we don't want or need. You could merge the test configuration into the `webpack.common` configuration and override the parts you don't want or need.
But it might be simpler to start over with a completely fresh configuration. But it might be simpler to start over with a completely fresh configuration.
+makeExample('webpack/ts/config/webpack.test.js', null, 'config/webpack.test.js')(format=".") +makeExample('webpack/ts/config/webpack.test.js', null, 'config/webpack.test.js')(format=".")
:marked :marked
Here's our karma configuration: Reconfigure karma to use webpack to run the tests:
+makeExample('webpack/ts/config/karma.conf.js', null, 'config/karma.conf.js')(format=".") +makeExample('webpack/ts/config/karma.conf.js', null, 'config/karma.conf.js')(format=".")
:marked :marked
We're telling Karma to use webpack to run the tests. You don't precompile the TypeScript; Webpack transpiles the Typescript files on the fly, in memory, and feeds the emitted JS directly to Karma.
We don't precompile our TypeScript; Webpack transpiles our Typescript files on the fly, in memory, and feeds the emitted JS directly to Karma.
There are no temporary files on disk. There are no temporary files on disk.
The `karma-test-shim` tells Karma what files to pre-load and The `karma-test-shim` tells Karma what files to pre-load and
@ -383,23 +425,23 @@ a(id="test-configuration")
+makeExample('webpack/ts/config/karma-test-shim.js', null, 'config/karma-test-shim.js')(format=".") +makeExample('webpack/ts/config/karma-test-shim.js', null, 'config/karma-test-shim.js')(format=".")
:marked :marked
Notice that we do _not_ load our application code explicitly. Notice that you do _not_ load the application code explicitly.
We tell Webpack to find and load our test files (the files ending in `.spec.ts`). You tell Webpack to find and load the test files (the files ending in `.spec.ts`).
Each spec file imports all &mdash; and only &mdash; the application source code that it tests. Each spec file imports all &mdash; and only &mdash; the application source code that it tests.
Webpack loads just _those_ specific application files and ignores the other files that we aren't testing. Webpack loads just _those_ specific application files and ignores the other files that you aren't testing.
:marked :marked
Grab the app code at the end of this guide and try: Grab the app code at the end of this guide and try:
code-example(format=""). code-example(language="sh" class="code-shell").
npm test npm test
<a id="try"></a> <a id="try"></a>
:marked :marked
## Trying it out ## Trying it out
Here is the source code for a small application that we can bundle with the Here is the source code for a small application that bundles with the
Webpack techniques we learned in this chapter. Webpack techniques covered in this guide.
+makeTabs( +makeTabs(
`webpack/ts/src/index.html, `webpack/ts/src/index.html,
@ -431,13 +473,15 @@ p.
<a href="https://raw.githubusercontent.com/angular/angular.io/master/public/resources/images/logos/angular2/angular.png" target="_blank"> <a href="https://raw.githubusercontent.com/angular/angular.io/master/public/resources/images/logos/angular2/angular.png" target="_blank">
<img src="/resources/images/logos/angular2/angular.png" height="40px" title="download Angular logo"></a>. <img src="/resources/images/logos/angular2/angular.png" height="40px" title="download Angular logo"></a>.
a#bundle-ts
:marked
Here again are the TypeScript entry-point files that define the `polyfills` and `vendor` bundles.
+makeTabs( +makeTabs(
`webpack/ts/src/vendor.ts, `webpack/ts/src/polyfills.ts,
webpack/ts/src/polyfills.ts`, webpack/ts/src/vendor.ts`,
null, null,
`src/vendor.ts, `src/polyfills.ts,
src/polyfills.ts` src/vendor.ts`
) )
<a id="highlights"></a> <a id="highlights"></a>
:marked :marked
@ -450,7 +494,8 @@ p.
* The `AppComponent` itself has its own html template and css file. WebPack loads them with calls to `require()`. * The `AppComponent` itself has its own html template and css file. WebPack loads them with calls to `require()`.
Webpack stashes those component-scoped files in the `app.js` bundle too. Webpack stashes those component-scoped files in the `app.js` bundle too.
We don't see those calls in our source code; they're added behind the scenes by the `angular2-template-loader` plug-in. You don't see those calls in the source code;
they're added behind the scenes by the `angular2-template-loader` plug-in.
* The `vendor.ts` consists of vendor dependency `import` statements that drive the `vendor.js` bundle. * The `vendor.ts` consists of vendor dependency `import` statements that drive the `vendor.js` bundle.
The application imports these modules too; they'd be duplicated in the `app.js` bundle The application imports these modules too; they'd be duplicated in the `app.js` bundle
@ -460,9 +505,9 @@ p.
:marked :marked
## Conclusions ## Conclusions
We've learned just enough Webpack to configurate development, test and production builds You've learned just enough Webpack to configurate development, test and production builds
for a small Angular application. for a small Angular application.
_We could always do more_. Search the web for expert advice and expand your Webpack knowledge. _You could always do more_. Search the web for expert advice and expand your Webpack knowledge.
[Back to top](#top) [Back to top](#top)