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

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

Open
octaharon opened this Issue May 15, 2018 · 28 comments

Comments

Projects
None yet
@octaharon

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

This comment has been minimized.

Show comment
Hide comment
@blakedietz

blakedietz May 15, 2018

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.

blakedietz commented May 15, 2018

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.

@evilebottnawi

This comment has been minimized.

Show comment
Hide comment
@evilebottnawi

evilebottnawi May 15, 2018

Member

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.

Member

evilebottnawi 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

This comment has been minimized.

Show comment
Hide comment
@octaharon

octaharon May 16, 2018

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.

octaharon commented May 16, 2018

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

This comment has been minimized.

Show comment
Hide comment
@dcousineau

dcousineau 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 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

This comment has been minimized.

Show comment
Hide comment
@dcousineau

dcousineau 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 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

This comment has been minimized.

Show comment
Hide comment
@dcousineau

dcousineau May 17, 2018

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 commented May 17, 2018

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

This comment has been minimized.

Show comment
Hide comment
@dcousineau

dcousineau May 17, 2018

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)

dcousineau commented May 17, 2018

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

This comment has been minimized.

Show comment
Hide comment
@octaharon

octaharon 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.

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

This comment has been minimized.

Show comment
Hide comment
@coltonw

coltonw 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.

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

This comment has been minimized.

Show comment
Hide comment
@rzcoder

rzcoder Jun 5, 2018

Can't update to webpack 4 cus this bug

rzcoder commented Jun 5, 2018

Can't update to webpack 4 cus this bug

@crocodilu

This comment has been minimized.

Show comment
Hide comment
@crocodilu

crocodilu Jul 26, 2018

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

crocodilu commented Jul 26, 2018

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

@melissa-hong

This comment has been minimized.

Show comment
Hide comment
@melissa-hong

melissa-hong 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!

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

This comment has been minimized.

Show comment
Hide comment
@thuanmb

thuanmb commented Aug 1, 2018

+1

@newmanw

This comment has been minimized.

Show comment
Hide comment
@newmanw

newmanw Aug 10, 2018

+1

Any updates?

newmanw commented Aug 10, 2018

+1

Any updates?

@flylixiaolong

This comment has been minimized.

Show comment
Hide comment
@flylixiaolong

flylixiaolong commented Aug 14, 2018

+1

2 similar comments
@lourenc

This comment has been minimized.

Show comment
Hide comment
@lourenc

lourenc commented Aug 17, 2018

+1

@tony-landis

This comment has been minimized.

Show comment
Hide comment
@tony-landis

tony-landis commented Aug 21, 2018

+1

@evilebottnawi

This comment has been minimized.

Show comment
Hide comment
@evilebottnawi

evilebottnawi Aug 22, 2018

Member

Please create minimum reproducible test repo

Member

evilebottnawi commented Aug 22, 2018

Please create minimum reproducible test repo

@octaharon

This comment has been minimized.

Show comment
Hide comment
@octaharon

octaharon commented Aug 22, 2018

@evilebottnawi I suppose that's it

@piyushmahen

This comment has been minimized.

Show comment
Hide comment
@piyushmahen

piyushmahen Aug 29, 2018

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

piyushmahen commented Aug 29, 2018

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

@isuttell

This comment has been minimized.

Show comment
Hide comment
@isuttell

isuttell commented Sep 12, 2018

+1

@jimmydief

This comment has been minimized.

Show comment
Hide comment
@jimmydief

jimmydief Sep 12, 2018

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

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

This comment has been minimized.

Show comment
Hide comment
@tony-landis

tony-landis Sep 12, 2018

tony-landis commented Sep 12, 2018

@fender

This comment has been minimized.

Show comment
Hide comment
@fender

fender commented Sep 14, 2018

+1

@fender

This comment has been minimized.

Show comment
Hide comment
@fender

fender 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

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

This comment has been minimized.

Show comment
Hide comment
@timothywisdom

timothywisdom Sep 19, 2018

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

timothywisdom commented Sep 19, 2018

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

This comment has been minimized.

Show comment
Hide comment
@CaptainYouz

CaptainYouz Oct 4, 2018

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

CaptainYouz commented Oct 4, 2018

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

@kryptonian41

This comment has been minimized.

Show comment
Hide comment
@kryptonian41

kryptonian41 Oct 15, 2018

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

kryptonian41 commented Oct 15, 2018

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

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