mini-css-extract-plugin: Breaking JS entrypoint when extracting all css chunks into 1 file

Package version: 0.4.0 Node version: 6.8.0

So I’ve updated to webpack 4 and instantly run into extract-text-webpack-plugin incompatibility, and guys on stackoverflow insisted on switching in favor of this module. My use case is almost exactly as described in documentation with the difference being that there’s sass-loader in the end of chain as well. All the rest is non-essential, the error is reproducable even with “zero” config.

My project is React-based, with a set of components each having it’s own scss file, required inside each component individually, including the Root component - the one which is rendered to DOM inside my entry file (say /src/index.js, has require('index.scss')).

What happens on build stage is kinda in line with expectations - all the css got exported to style.css (despite it produces extra files like style.main.js for no obvious reason). The problem is that that my entrypoint javascript never got executed when the bundle is loaded. So there’s no warnings or errors, everything compiles smoothly, the files (main.js and style.css) are delivered to the browser, but the code from index.js is not executed, so I just stare at a blank html template.

In the end I switched back to extract-text-webpack-plugin@next cause I couldn’t overcome that issue. Any ideas what causes that?

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 46
  • Comments: 40 (7 by maintainers)

Commits related to this issue

Most upvoted comments

It is fixed for webpack@5

In attempting to create a minimal reproduction case, it appears if you don’t include the outputted styles.js bundle generated via the cacheGroup the application won’t execute at all. I was using the HTML Webpack Plugin which automatically inserted the styles.js as a script tag, when removing it the application ceases to function.

While this description is not 100% accurate, it feels like MiniCSSExtractPlugin is accidentally stealing ‘entrypoint’ responsibilities from the actual entry point and investing it in the very tiny styles.js file it generates in the process of extracting the styles imported.

Can’t update to webpack 4 cus this bug

@michael-ciniawsky Any timeline till when can we expect this to be fixed?

The workaround which works for me is removing, all the instances of chunks: "all".

Please create minimum reproducible test repo

I think the ideal behavior would be the app.js entry bundle would be capable of loading the styles.js bundle itself, or anything that comes from the import statement (e.g. classNames when using css modules) would get inlined or included in the app bundle itself (no generated styles.js bundle)

Feel free to investigate and send PR. Don’t use extract-text-webpack-plugin@next it is break long term cache, output very large bundle and can raise problems in runtime code which very hard to find and fix.

In case it helps, a “workaround” I’ve found is to inline the contents of the extra js file generated using html-webpack-plugin (got the idea from @octaharon’s suggestion). E.g:

Add the html-webpack-plugin to list of plugins in webpack config (you need to be using the alpha html-webpack-plugin version with webpack 4):

new HtmlWebpackPlugin({
    filename: "../../tmpl/my-css-prod.html",
    hash: false,
    inject: false,
    template: "my-css-tmpl.html"
})

Create a template that will inline the css of the extra js file e.g my-css-tmpl.html:

<% for (var css in htmlWebpackPlugin.files.css) { %>
  <link href="<%= htmlWebpackPlugin.files.css[css].path.substr(htmlWebpackPlugin.files.publicPath.length) %>" rel="stylesheet">
  <script type="text/javascript">
    <%=
        compilation.assets[htmlWebpackPlugin.files.css[css].path
        .substr(htmlWebpackPlugin.files.publicPath.length).match(/([a-z-]+)\.css/)[1] + ".js"]
        .source()
    %>
  </script>
<% } %>

I then import this generated html file into my head (in place of my manual css imports). The generated html file looks something like this:

<link href="../styles-a.css" rel="stylesheet">
<script type="text/javascript">
    (window.webpackJsonp=window.webpackJsonp||[]).push([[0],[]]);
</script>
<link href="../styles-b.css" rel="stylesheet">
<script type="text/javascript">
    (window.webpackJsonp=window.webpackJsonp||[]).push([[46],[]]);
</script>

With this the css and js loads correctly - however I’m still having issues getting the import order of my css correct but I’m leaving that for another day…

Hope it helps - this has been stumping me for awhile! Any other ideas - let me know!

I too was seeing an issue similar to this. Instead I was getting a third bundle created. For example if the bundle was named bundle.js I would see an output of 0.bundle.js, bundle.js, styles.css.

I’m not entirely sure if I have the same problem as you all but this is working for me to get a single extract CSS file and still have the entry JS execute. Notice there is no chunks: 'all' at all…

webpack.config.js

module.exports = {
  entry: './applications/responsive/browser.js',
  output: {
    path: path.resolve(__dirname, 'static'),
    publicPath: config.get('app.assetPath'),
    filename: path.join('generated', `${appName}-responsive.js`),
    chunkFilename: path.join(
      'generated',
      'chunks',
      `${appName}-[name]-${packageJson.version}.js`
    ),
  },
  devtool: 'inline-source-map',
  plugins: [
    new CleanWebpackPlugin(['static/generated']),
    new MiniCssExtractPlugin({
      filename: path.join('generated', `generetic.css`),
    }),
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: [/node_modules/],
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          'postcss-loader',
        ],
      },
      {
        test: /\.(woff|ttf|svg)$/,
        exclude: [/images/],
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[ext]',
              outputPath: 'generated/fonts/',
            },
          },
        ],
      },
      {
        test: /\.(gif|png|jpe?g|svg)$/i,
        exclude: [/fonts/],
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 8192,
              outputPath: 'generated/images/',
            }
          },
          'image-webpack-loader',
        ]
      }
    ],
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        styles: {
          name: 'generetic',
          test: /\.css$/,
          enforce: true,
        },
      },
    },
  },

browser.js (entry)

import './styles';
// ... now do JS app init stuff ...

styles.js (I manually created this in the root)

import '../../css/fonts.css';
import '../../css/global.css';
// ... add every CSS file required here

I have the same problem. Did someone figured this out?

Here two problems:

  1. some people forgot to add chunks on own html (yes you need include all generated chunks, even they are empty all content was extracted)
  2. in some cases html-webpack-plugin doesn’t include runtime code in html page (i think it should be already fixed, if not please open a new issue in html-webpack-plugin)
  3. https://github.com/webpack-contrib/mini-css-extract-plugin/issues/85 (fixed in webpack@5), it is allow to avoid empty chunks and unnecessary scripts with only extracted content

If somebody think what he/she still has a problem and it is related to this issue please open a new issue, here a lot of difference issues and it is hard to investigate.

If somebody wants to know why it is happens I can explain with comments

Thanks for the update @evilebottnawi

I’m dealing with a Vue build and it seems vue-loader is incompatible w/ Webpack 5 due to https://github.com/webpack/webpack/pull/9138, blocking me from confirming this is solved. I might investigate further when I have more time, but it seems like for Vue users we’ll have to wait till Webpack 5 is stable and a compatible vue-loader is released.

I am using multiple entry points so my solution may not be relevant to others.

For me including the styles chunk in the HtmlWebpackPlugin chunks list helped with the webpack bootstrap not happening on page load. Here is my code:

        new HtmlWebpackPlugin({
            chunks: ['styles', 'vendor', 'app'],
            inject: true,
            template: paths.appHtml,
            minify: {
                removeComments: true,
                collapseWhitespace: true,
                removeRedundantAttributes: true,
                useShortDoctype: true,
                removeEmptyAttributes: true,
                removeStyleLinkTypeAttributes: true,
                keepClosingSlash: true,
                minifyJS: true,
                minifyCSS: true,
                minifyURLs: true,
            },
        }), 
optimization: {
    splitChunks: {
        cacheGroups: {
            styles: {
                name: 'styles',
                test: /\.css$/,
                chunks: 'all',
                enforce: true
            }
        }
    }
},

I was able to work around this issue by using the rename-webpack-plugin.

I configured it to search for the extra “styles” bundle (with a unique HASH) and rename it to a static “styles.bundle.js” file that I can dependably include in my entry HTML (because it always has the same name).

Here’s an example

const RenameWebpackPlugin = require('rename-webpack-plugin')

module.exports = {
  entry: 'app.js',
  output: {
    path: __dirname + '/dist',
    filename: 'app.[chunkhash].js'
  },
  plugins: [
    new RenameWebpackPlugin({
        originNameReg: /styles\..*\.js/,
        targetName: 'styles.bundle.js'
    })
  ]
}

Ended up concatenating the contents of the extra JS file to our entry file in a post-build step which worked.

Good job with locating the repo, man! I support the idea of avoiding generating extra js files when we extract css only, that’s somewhat not semantical and user-friendly at all. That configuration, as it says in the documentation, should behave pretty much the same as the extract-text-plugin in this case. Which is, by the way, working perfectly for me so far in couple with optimize-css-assets-plugin. Other option would be auto-injecting the style.js into one of entries by providing entryName or something in plugin config, declarative style, or even detecting the ‘target’ setting of webpack itself.