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

Created on 15 May 2018  Â·  40Comments  Â·  Source: webpack-contrib/mini-css-extract-plugin

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?

3 (important) 3 (broken) bug

Most helpful comment

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.

All 40 comments

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.

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.

Sorry, I've realized that a link to documentation was missing in my original post, edited. That's quite important for understanding the problem.

Perhaps the issue with plural output files belongs to webpack itself, since its driven by optimization.splitChunks.cacheGroups config option, but the issue with "swallowing" the entrypoint of scripts is definitely coming from this plugin. If those are connected, than there's no better description

problems in runtime code which very hard to find and fix

Do you by chance know a repo with the aformentioned configuration in place? That could be a good starting point for investigation.

I don't have a public repo but I finally traced my problem with production builds in webpack-4 to this issue.

In development mode my optimization stanza is:

  optimization: {
    splitChunks: {
      chunks: 'all',
    },
    runtimeChunk: {
      name: 'manifest',
    },
  },

While in production mode I have

  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        styles: {
          name: 'styles',
          test: /\.css$/,
          chunks: 'all',
          enforce: true
        },
      },
    },
    runtimeChunk: {
      name: 'manifest',
    },
  },

Production properly spits out the correct singular bundled CSS file, however my entrypoint chunk no longer executes.

I played around with removing both instances of chunks: 'all' and this caused the entry to execute (however, obviously, all the styles are in separate files that I now have to chase down individually).

I also tried removing both chunks: 'all' as well as renaming the cacheGroup to "app" (which is my entrypoint's name) and the non-execution problem _came back_.

_Note: I've tested permutations with and without the runtime chunk stuff and it had no effect_
_Additionally: Removing the first chunks: 'all' immediately under the optimization and above cacheGroups has no effect_

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.

Please see https://github.com/dcousineau/mini-css-extract-plugin-reproduction for a demonstration of how, even though the styles.js is effectively empty it prevents the entrypoint from executing.

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)

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.

I would like to point out that this problem is doubly bad for javascript/css libraries trying to use mini-css-extract-plugin. Users of the library will have to not only import the library code and css, but also this generated style.js file or the library simply won't work.

Can't update to webpack 4 cus this bug

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

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!

+1

+1

Any updates?

+1

+1

+1

Please create minimum reproducible test repo

@evilebottnawi I suppose that's it

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

+1

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

It still fundamentally antithetical to bundling - all the styles for the
entire app loading with the all.

For us, thats a 100kb js file. Vs a few KiB if it were just the styles
needed for any given page.

Anthony Landis

CTO, Founder | Anywhere.com

mobile: 206-234-1018

On Wed, Sep 12, 2018 at 2:30 PM, James Diefenderfer <
[email protected]> wrote:

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

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/webpack-contrib/mini-css-extract-plugin/issues/147#issuecomment-420786698,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AACK9gjfJLlb0np0TfKcLdBhIoc3H1yTks5uaW7kgaJpZM4T_5Ca
.

+1

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 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'
    })
  ]
}

hi!
i have the same problem here. Any news ?

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

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 have the same problem but with Vue and I made a minimal reproduction repo for this bug: https://github.com/hirokiosame/test-build

is there any movement on this issue? IMO this is a major blocker from moving apps to webpack 4, or having webpack 4 apps do dynamic imports.

Try to search time on this, but sorry i am very very very busy, feel free to investigate :+1:

Please help me, I have a project in laravel-vuejs and i am extracting js chunks file through my webpack.config.js and my chunks are like 0.js,1.js,2.js etc and I want to extract the chunks of css or stylesheet how can i achieve this. please help me

Same problem here, any updates?
Can someone have an idea about how to build css in dev environment?

Code tu reproduce:
https://github.com/PrestaShop/PrestaShop/tree/develop/admin-dev/themes/new-theme

I confused about this problem for a long time, In my case two way can solve this.

  1. donnot extract css in 1 file, default code split looks good.
  2. just reserve the styles.js, an empty file affects little, I think.
    if use HtmlWebpackPlugin, style.js will auto insert into html.
    if you construct html by yourself, you can do this as well

In my case, it turned out to be an issue in my custom plugin. So, similarly, I'm assuming that this bug may be in html-webpack-plugin, and probably mini-css-extract-plugin is working fine. In my plugin, I was not going through all the files listed under compilation.chunks.files which caused the behavior described in this bug.

Just a gentle bump on this thread. This is a widely used, officially designated webpack plugin and a piece of functionality in the main README seems to have been broken for over a year. Perhaps a warning should be added to the documentation, or the relevant section should be removed.

It is fixed for webpack@5

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.

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

Was this page helpful?
0 / 5 - 0 ratings