mini-css-extract-plugin: "Cannot read property 'pop' of undefined" with a common cache group

I ran into this after upgrading to 0.4.2.

ERROR in chunk common [initial]
common.css
Cannot read property 'pop' of undefined
TypeError: Cannot read property 'pop' of undefined
    at MiniCssExtractPlugin.renderContentAsset (.../node_modules/mini-css-extract-plugin/dist/index.js:343:44)
    at Object.render (.../node_modules/mini-css-extract-plugin/dist/index.js:175:32)
    at Compilation.createChunkAssets (.../node_modules/webpack/lib/Compilation.js:2358:29)
    at hooks.optimizeTree.callAsync.err (.../node_modules/webpack/lib/Compilation.js:1268:10)
    at AsyncSeriesHook.eval [as callAsync] (eval at create (.../node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:6:1)
    at AsyncSeriesHook.lazyCompileHook [as _callAsync] (.../node_modules/tapable/lib/Hook.js:35:21)
    at Compilation.seal (.../node_modules/webpack/lib/Compilation.js:1213:27)
    at hooks.make.callAsync.err (.../node_modules/webpack/lib/Compiler.js:547:17)
    at _done (eval at create (.../node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:9:1)
    at _err16 (eval at create (.../node_modules/tapable/lib/HookCodeFactory.js:24:12), <anonymous>:218:22)
    at _addModuleChain (.../node_modules/webpack/lib/Compilation.js:1064:12)
    at processModuleDependencies.err (.../node_modules/webpack/lib/Compilation.js:980:9)
    at _combinedTickCallback (internal/process/next_tick.js:131:7)
    at process._tickCallback (internal/process/next_tick.js:180:9)

common.css is set up like so:

optimization: {
  splitChunks: {
    cacheGroups: {
      common: {
        name: 'common',
        chunks: 'initial',
        minChunks: 3,
        priority: 10,
        reuseExistingChunk: true
      }
    }
  }
},

And has an explicit entry with the following:

common: [
  path.resolve(srcPath, 'vendor.scss'),
  path.resolve(srcPath, 'main.scss')
]

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 27
  • Comments: 41 (10 by maintainers)

Commits related to this issue

Most upvoted comments

Webpack is the biggest joke played on the JavaScript community so far.

Best avoid using an entrypoint as name for a cacheGroup. You can rewrite this config to this:

entry: {
    first: ['./common.css', './first.jsx'],
    second: ['./common.css', './second.jsx'],
  },
 optimization: {
    splitChunks: {
      cacheGroups: {
        common: {
          name: 'common',
          chunks: 'initial',
          minChunks: 2,
          priority: 10,
          reuseExistingChunk: true
        }
      }
    }
  },

btw. in webpack 5 there will be an error when using an entrypoint name as cacheGroup name.

@aganglada When somebody send a PR

When will this get resolved? I’m running into the same issue here.

Moreover , everything works fine when I add { enforce: true } in my splitChunks entries or remove { chunks: 'all' } from them

Ran into this today too.

webpack: 4.17.2
mini-css-extract-plugin: 0.4.2

Little more insight into what may be happening: screenshot from 2018-09-07 19-34-44 screenshot from 2018-09-07 18-13-22

bestMatch never gets a value because sortedModules is empty (note that in my case, the function input param modules in renderContentAsset has 3 items in it). This is because getModuleIndex2 returns undefined for every module that gets passed in (I verified this manually by running all the modules through it).

I was able to fix this by locally editing index.js and changing this if statement to always return false https://github.com/webpack-contrib/mini-css-extract-plugin/blob/dd141f2aacddbb30d9129a14425085b11f3cba8d/src/index.js#L397

So it looks like the fallback logic https://github.com/webpack-contrib/mini-css-extract-plugin/blob/dd141f2aacddbb30d9129a14425085b11f3cba8d/src/index.js#L480-L485 still works.

@OscarBarrett Found problem. Maybe related to other people.

entry: {
    first: './first.jsx',
    second: './second.jsx',
    common: './common.css'
  },
 optimization: {
    splitChunks: {
      cacheGroups: {
        common: {
          name: 'common',
          chunks: 'initial',
          minChunks: 2,
          priority: 10,
          reuseExistingChunk: true
        }
      }
    }
  },

You grouped modules to common chunk and use common: './common.css' entry. Just change name common entry to commonStyle

why this issue closed, it is not solved.

Ran into this issue myself and going off of what @ndisidore posted I think I see another issue at play, at least with my setup.

webpack.config.js

optimization: {
        minimize: true,
        minimizer: [
            new OptimizeCSSAssetsPlugin({})
        ],
        splitChunks: {
            cacheGroups: {
                mainStyles: {
                    name: 'main',
                    test: (m,c,entry = 'main') => m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
                    chunks: 'all',
                    enforce: true
                }
            }
        }
    }

The issue I am seeing revolves around this block in the index.js of the plugin.

      for (const list of modulesByChunkGroup) {
          // skip and remove already added modules
          while (list.length > 0 && usedModules.has(list[list.length - 1])) list.pop();

          // skip empty lists
          if (list.length !== 0) {
            const module = list[list.length - 1];
            const deps = moduleDependencies.get(module);
            // determine dependencies that are not yet included
            const failedDeps = Array.from(deps).filter(unusedModulesFilter);

            // store best match for fallback behavior
            if (!bestMatchDeps || bestMatchDeps.length > failedDeps.length) {
              bestMatch = list;
              bestMatchDeps = failedDeps;
            }
            if (failedDeps.length === 0) {
              // use this module and remove it from list
              usedModules.add(list.pop());
              success = true;
              break;
            }
          }
        }

Best avoid using an entrypoint as name for a cacheGroup. You can rewrite this config to this:

entry: {
    first: ['./common.css', './first.jsx'],
    second: ['./common.css', './second.jsx'],
  },
 optimization: {
    splitChunks: {
      cacheGroups: {
        common: {
          name: 'common',
          chunks: 'initial',
          minChunks: 2,
          priority: 10,
          reuseExistingChunk: true
        }
      }
    }
  },

btw. in webpack 5 there will be an error when using an entrypoint name as cacheGroup name.

hi @sokra , I also encountered the same problem. extract-text-webpack-plugin is all OK but only using [hash]… , and mini-css-extract-plugin throws the error: Cannot read property ‘pop’ of undefined . there are lots of scenarios using an entrypoint name as cacheGroup name, Why not support it and fix what @cshomo11 posted but remove it in webpack5? Looking forward to your reply, thanks.

Ran into this issue myself and going off of what @ndisidore posted I think I see another issue at play, at least with my setup.

webpack.config.js

optimization: {
        minimize: true,
        minimizer: [
            new OptimizeCSSAssetsPlugin({})
        ],
        splitChunks: {
            cacheGroups: {
                mainStyles: {
                    name: 'main',
                    test: (m,c,entry = 'main') => m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry,
                    chunks: 'all',
                    enforce: true
                }
            }
        }
    }

The issue I am seeing revolves around this block in the index.js of the plugin.

      for (const list of modulesByChunkGroup) {
          // skip and remove already added modules
          while (list.length > 0 && usedModules.has(list[list.length - 1])) list.pop();

          // skip empty lists
          if (list.length !== 0) {
            const module = list[list.length - 1];
            const deps = moduleDependencies.get(module);
            // determine dependencies that are not yet included
            const failedDeps = Array.from(deps).filter(unusedModulesFilter);

            // store best match for fallback behavior
            if (!bestMatchDeps || bestMatchDeps.length > failedDeps.length) {
              bestMatch = list;
              bestMatchDeps = failedDeps;
            }
            if (failedDeps.length === 0) {
              // use this module and remove it from list
              usedModules.add(list.pop());
              success = true;
              break;
            }
          }
        }

I’m finding that in some cases, all of the arrays in modulesByChunkGroup are empty. When this happens, bestMatch is never set and success=true is also never reached so the .pop error occurs. I’ve tried to do some empty array checks but that doesn’t seem to be the right way to tackle this issue. Hoping this will help track down the right fix for this issue.

I can totally help with a PR. May need a little direction on the correct way to do it vs the quick fix.

It looks like getModuleIndex2 is just a .get on the map of the chunkGroup. So essentially what we’re trying to do is see if the current module is in the given chunk group. Under what circumstances would this never be the case and are those expected?

Some more context around those specifics. screenshot from 2018-09-10 18-58-29

I don’t know why but add enforce: true does fix this for my vendors cache group.

Failed with version 9.0.5, no problem with version 9.0.7-canary.3.

Webpack is the biggest joke played on the JavaScript community so far.

As annoying as webpack issues can be, I don’t think that comment helps anyone @stephan-v.

Super ugly workaround to force the non-broken fallback logic without editing the code of this library:

class FixedMiniCssExtractPlugin extends MiniCssExtractPlugin {
  // This very awful workaround prevents a weird `<undefined>.pop()` in the plugin
  // that's caused by who-knows-what (NOT related to dynamic imports).
  // See this github issue for details:
  // https://github.com/webpack-contrib/mini-css-extract-plugin/issues/257
  renderContentAsset(compilation, chunk, modules, requestShortener) {
    const [chunkGroup] = chunk.groupsIterable;
    let rv;
    const getModuleIndex2 = chunkGroup.getModuleIndex2;
    try {
      chunkGroup.getModuleIndex2 = null;
      rv = super.renderContentAsset(compilation, chunk, modules, requestShortener);
    } finally {
      chunkGroup.getModuleIndex2 = getModuleIndex2;
    }
    return rv;
  }
}
webpack: 4.29.0
mini-css-extract-plugin: 0.5.0

Setup:

entry: {
      bootstrap: "./javascripts/bootstrap.js",
      .....
}
....
splitChunks: {
        cacheGroups: {
          ...
          bootstrap: {
            chunks: 'initial',
            name: 'bootstrap',
            test: 'bootstrap',
            enforce: true
          }
        }
      }

Error:

ERROR in chunk bootstrap [initial]
stylesheets/bootstrap.css
Cannot read property 'pop' of undefined

Rolled back to webpack 3.x.

@OscarBarrett Find problem. Maybe related to other people.

entry: {
    first: './first.jsx',
    second: './second.jsx',
    common: './common.css'
  },
 optimization: {
    splitChunks: {
      cacheGroups: {
        common: {
          name: 'common',
          chunks: 'initial',
          minChunks: 2,
          priority: 10,
          reuseExistingChunk: true
        }
      }
    }
  },

You grouped modules to common chunk and use common: './common.css' entry. Just change name common entry to commonStyle

What if I want to combine css content from entry and also splitChunks cachegroup? If we change entry to commonStyle, basically, it will create common.css and commonStyle.css. Instead, I want these two file contents to be merged to one file (common.css). How can we achieve that?