Skip to content

Breaking JS entrypoint when extracting all css chunks into 1 file #147

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
octaharon opened this issue May 15, 2018 · 40 comments
Closed

Breaking JS entrypoint when extracting all css chunks into 1 file #147

octaharon opened this issue May 15, 2018 · 40 comments

Comments

@octaharon
Copy link

octaharon commented May 15, 2018

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?

@blakedietz
Copy link

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.

@alexander-akait
Copy link
Member

alexander-akait commented May 15, 2018

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.

@octaharon
Copy link
Author

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.

@dcousineau
Copy link

dcousineau commented May 17, 2018

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

@dcousineau
Copy link

dcousineau commented May 17, 2018

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.

@dcousineau
Copy link

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.

@dcousineau
Copy link

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)

@octaharon
Copy link
Author

octaharon commented May 17, 2018

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.

@coltonw
Copy link

coltonw commented May 23, 2018

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.

@rzcoder
Copy link

rzcoder commented Jun 5, 2018

Can't update to webpack 4 cus this bug

@emilomir
Copy link

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

@melissa-hong
Copy link

melissa-hong commented Jul 26, 2018

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!

@thuanmb
Copy link

thuanmb commented Aug 1, 2018

+1

@newmanw
Copy link

newmanw commented Aug 10, 2018

+1

Any updates?

@flylixiaolong
Copy link

+1

2 similar comments
@lourenc
Copy link

lourenc commented Aug 17, 2018

+1

@tony-landis
Copy link

+1

@alexander-akait
Copy link
Member

Please create minimum reproducible test repo

@octaharon
Copy link
Author

@evilebottnawi I suppose that's it

@piyushmahen
Copy link

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

@isuttell
Copy link

+1

@jimmydief
Copy link

jimmydief commented Sep 12, 2018

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

@tony-landis
Copy link

tony-landis commented Sep 12, 2018 via email

@fender
Copy link

fender commented Sep 14, 2018

+1

@fender
Copy link

fender commented Sep 14, 2018

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

@timothywisdom
Copy link

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

@CaptainYouz
Copy link

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

@kryptonian41
Copy link

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

@viljark
Copy link

viljark commented Nov 16, 2018

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
            }
        }
    }
},

@privatenumber
Copy link
Contributor

I have the same problem but with Vue and I made a minimal reproduction repo for this bug: https://github.com/hirokiosame/test-build

@TerribleDev
Copy link

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.

@alexander-akait
Copy link
Member

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

rowanoulton added a commit to rowanoulton/mini-css-extract-plugin that referenced this issue Jan 15, 2019
rowanoulton added a commit to rowanoulton/mini-css-extract-plugin that referenced this issue Jan 16, 2019
rowanoulton added a commit to rowanoulton/mini-css-extract-plugin that referenced this issue Jan 16, 2019
@theshashiverma

This comment has been minimized.

@PierreRambaud
Copy link

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

@zhangdongming0607
Copy link

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

@webextensions
Copy link

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.

@JonathanTR
Copy link

JonathanTR commented Aug 23, 2019

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.

@alexander-akait
Copy link
Member

It is fixed for webpack@5

@privatenumber
Copy link
Contributor

Thanks for the update @evilebottnawi

I'm dealing with a Vue build and it seems vue-loader is incompatible w/ Webpack 5 due to webpack/webpack#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.

@alexander-akait
Copy link
Member

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. Single-file configuration emits JS assets in its own chunk group #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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests