@@ -12,6 +12,7 @@ var autoprefixer = require('autoprefixer');
1212var webpack = require ( 'webpack' ) ;
1313var HtmlWebpackPlugin = require ( 'html-webpack-plugin' ) ;
1414var ExtractTextPlugin = require ( 'extract-text-webpack-plugin' ) ;
15+ var WebpackMd5Hash = require ( 'webpack-md5-hash' ) ;
1516var url = require ( 'url' ) ;
1617var paths = require ( './paths' ) ;
1718
@@ -25,7 +26,13 @@ if (!publicPath.endsWith('/')) {
2526module . exports = {
2627 bail : true ,
2728 devtool : 'source-map' ,
28- entry : path . join ( paths . appSrc , 'index' ) ,
29+ entry : {
30+ // Split app into the main and vendor bundles.
31+ // Vendor will contain React by default but we might want to
32+ // dynamically choose to add something else from node_modules.
33+ main : path . join ( paths . appSrc , 'index' ) ,
34+ vendor : [ 'react' , 'react-dom' ] ,
35+ } ,
2936 output : {
3037 path : paths . appBuild ,
3138 filename : '[name].[chunkhash:8].js' ,
@@ -92,10 +99,53 @@ module.exports = {
9299 return [ autoprefixer ] ;
93100 } ,
94101 plugins : [
102+ // Create two common chunks:
103+ // - "vendor" chunk is declared in "entry" above.
104+ // - "manifest" is a built-in Webpack chunk with its runtime.
105+ // This would enable us to enable long-term caching.
106+ // The tricky part is that "manifest" will contain the filenames
107+ // of the other chunks. This is why we want to embed it into HTML.
108+ // Otherwise, changes in any chunk will cause manifest to change,
109+ // and thus if manifest isn't separated, its parent will also change.
110+ // Relevant discussion: https://github.com/webpack/webpack/issues/1315
111+ new webpack . optimize . CommonsChunkPlugin ( {
112+ names : [ 'vendor' , 'manifest' ]
113+ } ) ,
114+ // Fixes deterministic hashes so that changes to CSS don't invalidate JS.
115+ new WebpackMd5Hash ( ) ,
116+ // Avoids extra request for the manifest that contains webpack runtime
117+ // by embedding it right into HTML as an inline <script>.
118+ function embedManifestIntoHTML ( ) {
119+ this . plugin ( 'compilation' , ( compilation ) => {
120+ compilation . plugin ( 'html-webpack-plugin-before-html-processing' , ( data , cb ) => {
121+ var manifestSource ;
122+ // Find the manifest chunk generated by webpack,
123+ // grab its source, and embed it into generated HTML.
124+ Object . keys ( compilation . assets ) . forEach ( key => {
125+ if ( key . indexOf ( 'manifest.' ) !== 0 ) {
126+ return ;
127+ }
128+ var children = compilation . assets [ key ] . children ;
129+ if ( ! children || ! children [ 0 ] ) {
130+ return ;
131+ }
132+ delete compilation . assets [ key ] ;
133+ manifestSource = children [ 0 ] . _value ;
134+ } ) ;
135+ data . html = data . html . replace (
136+ '</body>' ,
137+ '<script>' + manifestSource + '</script></body>'
138+ ) ;
139+ cb ( )
140+ } )
141+ } )
142+ } ,
95143 new HtmlWebpackPlugin ( {
96144 inject : true ,
97145 template : paths . appHtml ,
98146 favicon : paths . appFavicon ,
147+ chunksSortMode : 'dependency' , // Ensure vendor chunk comes first.
148+ excludeChunks : [ 'manifest' ] , // We embedded it earlier.
99149 minify : {
100150 removeComments : true ,
101151 collapseWhitespace : true ,
0 commit comments