Search code examples
webpack-2bundling-and-minificationextract-text-plugincommonschunkpluginextracttextwebpackplugin

CSS not extracted for common chunk with extract-text-webpack-plugin


Not sure if this is a bug or just my setup, I am using the CommonsChunkPlugin to get a separate common.js for three entry points in my project, and using the extract-text-webpack-plugin to get the css files. My entry points are app, login and register. I am able to get:

app.js
app.vendor.js
login.js
register.js
common.js

For css:

app.css 
register.css
login.css

I can't seem to get common.css generated. All the css gets crammed into a single app.css file.

My project is setup based on the vuetify webpack template: https://github.com/vuetifyjs/webpack-advanced

Here is my config:

3 entry points:

module.exports = {
  entry: {
    app: './src/main.js',
    login: './src/Login/login.js',
    register: './src/Register/register.js'
 },

The plugins - I have HtmlWebpackPlugin for each entry point (showing only one):

new HtmlWebpackPlugin({
  filename: process.env.NODE_ENV === 'testing'
    ? 'index.html'
    : config.build.index,
  template: 'index.html',
  inject: false,
  hash: true,
  minify: {
    removeComments: true,
    collapseWhitespace: true,
    removeAttributeQuotes: false,
    minifyJS: true
  },
  chunksSortMode: appChunkOrder
}),

Common chunks:

new webpack.optimize.CommonsChunkPlugin({
  name: 'app.vendor',
  chunks: ['app'],
  minChunks: isVendor,
}),    
new webpack.optimize.CommonsChunkPlugin({
  name: 'login.vendor',
  chunks: ['login'],
  minChunks: isVendor,
}),
new webpack.optimize.CommonsChunkPlugin({
  name: 'register.vendor',
  chunks: ['register'],
  minChunks: isVendor,
}),    
// Extract chunks common to both app and login
new webpack.optimize.CommonsChunkPlugin({
  name: 'common',
  chunks: ['app.vendor', 'login.vendor', 'register.vendor', 'app', 'login', 'register'],
  minChunks: (module, count) => count >= 2 && isVendor(module),
}),

The full config:

plugins: [
new webpack.DefinePlugin({'process.env': env }),
new webpack.optimize.UglifyJsPlugin({
  compress: {
    warnings: false
  },
  sourceMap: true
}),
// extract css into its own file
new ExtractTextPlugin({
  filename: utils.assetsPath('css/[name].[contenthash].css')
}),
new OptimizeCSSPlugin({
  cssProcessorOptions: {
    safe: true
  }
}),
// generate Html index files for 3 entries:
new HtmlWebpackPlugin({
  filename: process.env.NODE_ENV === 'testing'
    ? 'index.html'
    : config.build.index,
  template: 'index.html',
  inject: false,
  hash: true,
  minify: {
    removeComments: true,
    collapseWhitespace: true,
    removeAttributeQuotes: false,
    minifyJS: true
  },
  chunksSortMode: appChunkOrder
}),
new HtmlWebpackPlugin({
  filename: process.env.NODE_ENV === 'testing'
    ? 'src/Login/login.html'
    : config.build.login,
  template: 'src/Login/login.html',
  inject: false,
  hash: true,
  minify: false,
  chunksSortMode: loginChunkOrder
}),
new HtmlWebpackPlugin({
  filename: process.env.NODE_ENV === 'testing'
    ? 'src/Register/register.html'
    : config.build.register,
  template: 'src/Register/register.html',
  inject: false,
  hash: true,
  minify: false,
  chunksSortMode: registerChunkOrder
}),    
// Chunks:
new webpack.optimize.CommonsChunkPlugin({
  name: 'app.vendor',
  chunks: ['app'],
  minChunks: isVendor,
}),    
new webpack.optimize.CommonsChunkPlugin({
  name: 'login.vendor',
  chunks: ['login'],
  minChunks: isVendor,
}),
new webpack.optimize.CommonsChunkPlugin({
  name: 'register.vendor',
  chunks: ['register'],
  minChunks: isVendor,
}),    
// Extract chunks common to both app and login
new webpack.optimize.CommonsChunkPlugin({
  name: 'common',
  chunks: ['app.vendor', 'login.vendor', 'register.vendor', 'app', 'login', 'register'],
  minChunks: (module, count) => count >= 2 && isVendor(module),
}),
// copy custom static assets
new CopyWebpackPlugin([
  {
    from: path.resolve(__dirname, '../static'),
    to: config.build.assetsSubDirectory,
    ignore: ['.*']
  }
])

Any help appreciated!


Solution

  • I have a very similar setup and managed to get it working. For me, the crucial part was to import common styles in .js files instead of in .scss files.

    Here's my project in a nutshell:

    common/
      main.scss    <-- common styles
    app/
      index.js
      main.scss    <-- styles specific for app
    admin/
      index.js
      main.scss    <-- styles specific for admin
    registration/
      index.js
      main.scss    <-- styles specific for registration
    base.html
    webpack.config.js
    

    app/index.js:

    import '../common/main'   // <-- this did the trick!
    import './main'
    
    // some js...
    

    Both admin/index.js and registration/index.js are very similar.


    webpack.config.js (only important parts, using some ES6 syntax):

    const entries = {
      app: 'app/index.js',
      admin: 'admin/index.js'
      registration: 'registration/index.js',
    };
    
    const commonChunks = [
      'vendor',
      'common',
    ];
    
    function isVendor(module) {
      if (typeof module.context !== 'string') {
        return false;
      }
      return module.context.indexOf('node_modules') !== -1;
    }
    
    export default {
      entry: {...entries},
    
      // ...
    
      plugins: [
        new webpack.optimize.CommonsChunkPlugin({
          name: 'common',
          filename: '[name].bundle.js',
          minChunks: 2
        }),
    
        new webpack.optimize.CommonsChunkPlugin({
          name: 'vendor',
          filename: '[name].bundle.js',
          chunks: [...Object.keys(entries), 'common'],
          minChunks: function(module) {
            return isVendor(module);
          }
        }),
    
        new ExtractTextPlugin({
          filename: '[name].bundle.css'
        }),
    
        new HtmlWebpackPlugin({
          template: path.resolve(__dirname, 'base.html'),
          filename: 'app.html',
          chunks: [...commonChunks, 'app'],
          chunksSortMode: 'manual'
        }),
    
        new HtmlWebpackPlugin({
          template: path.resolve(__dirname, 'base.html'),
          filename: 'admin.html',
          chunks: [...commonChunks, 'admin'],
          chunksSortMode: 'manual'
        }),
    
        new HtmlWebpackPlugin({
          template: path.resolve(__dirname, 'base.html'),
          filename: 'registration.html',
          chunks: [...commonChunks, 'registration'],
          chunksSortMode: 'manual'
        })
      ]
    };
    

    My initial approach was to import common/main.scss in other .scss files, e.g.

    app/main.scss:

    @import '../common/main'
    
    // styles specific for app
    

    but that didn't work and I came up with the solution above.

    Also, adding chunksSortMode: 'manual' was required in my case as the HtmlWebpackPlugin was sometimes including scripts in the wrong order.