Skip to content
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

"You forgot to add 'mini-css-extract-plugin' plugin" #167

Open
jalovatt opened this issue Apr 3, 2021 · 92 comments
Open

"You forgot to add 'mini-css-extract-plugin' plugin" #167

jalovatt opened this issue Apr 3, 2021 · 92 comments

Comments

@jalovatt
Copy link

jalovatt commented Apr 3, 2021

Webpack 5 fails as soon as I smp.wrap() my config, with the following error:

ERROR in ..../Error.scss
Module build failed (from ../../node_modules/mini-css-extract-plugin/dist/loader.js):
Error: You forgot to add 'mini-css-extract-plugin' plugin (i.e. `{ plugins: [new MiniCssExtractPlugin()] }`), please read https://github.com/webpack-contrib/mini-css-extract-plugin#getting-started
    at Object.pitch (.../node_modules/mini-css-extract-plugin/dist/loader.js:50:14)

The error stems from a missing symbol, which is defined when the plugin module loads, and assigned in a compilation hook:

// mini-css-extract-plugin/dist/loader.js@50
  if (!this[_index.pluginSymbol]) {
    callback(new Error("You forgot to add 'mini-css-extract-plugin' plugin (i.e. `{ plugins: [new MiniCssExtractPlugin()] }`), please read https://github.com/webpack-contrib/mini-css-extract-plugin#getting-started"));
    return;
  }


// mini-css-extract-plugin/index.js@362
    compiler.hooks.compilation.tap(pluginName, compilation => {
      const normalModuleHook = typeof NormalModule.getCompilationHooks !== 'undefined' ? NormalModule.getCompilationHooks(compilation).loader : compilation.hooks.normalModuleLoader;
      normalModuleHook.tap(pluginName, loaderContext => {
        // eslint-disable-next-line no-param-reassign
        loaderContext[pluginSymbol] = true;
      });
    });

However, with smp.wrap() the inner callback above is never run and the symbol is never added to the loader's context.

Cheers. :)

Versions

webpack: 5.21.0
mini-css-extract-plugin: 1.3.9
speed-measure-webpack-plugin: 1.5.0

Config

plugins: [
  ...
  MiniCssExtractPlugin {
    _sortedModulesCache: WeakMap { <items unknown> },
    options: {
      filename: [Function: filename],
      ignoreOrder: false,
      chunkFilename: 'chunk-[name].[contenthash].css'
    },
    runtimeOptions: {
      insert: undefined,
      linkType: 'text/css',
      attributes: undefined
    }
  },
  ...
  SpeedMeasurePlugin {
    options: {},
    timeEventData: {},
    smpPluginAdded: true,
    wrap: [Function: bound wrap],
    getOutput: [Function: bound getOutput],
    addTimeEvent: [Function: bound addTimeEvent],
    apply: [Function: bound apply],
    provideLoaderTiming: [Function: bound provideLoaderTiming],
    generateLoadersBuildComparison: [Function: bound generateLoadersBuildComparison]
  }
],
module: {
  rules: [
    ...
    {
      test: /\.css$/,
      include: /node_modules/,
      use: [
        '.../node_modules/mini-css-extract-plugin/dist/loader.js',
        '.../node_modules/css-loader/dist/cjs.js'
      ]
    },
    {
      test: /\.(sass|scss|css)$/,
      exclude: /node_modules/,
      use: [
        '.../node_modules/mini-css-extract-plugin/dist/loader.js',
        {
          loader: '.../node_modules/css-loader/dist/cjs.js',
          options: { importLoaders: 1 }
        },
        {
          loader: '.../node_modules/sass-loader/dist/cjs.js',
          options: {
            implementation: {
              run_: [Function],
              render: [Function],
              renderSync: [Function],
              info: 'dart-sass\t1.23.0\t(Sass Compiler)\t[Dart]\n' +
                'dart2js\t2.5.1\t(Dart Compiler)\t[Dart]',
              types: [Object]
            },
            sassOptions: { fiber: false }
          }
        }
      ]
    }
  ]
}
@467057463
Copy link

this same issues.

@DunnJM
Copy link

DunnJM commented Apr 6, 2021

Same here

@CKGrafico
Copy link

+1

@vitalishapovalov
Copy link

same issue

@doyakovlev
Copy link

+1

@moyongshi
Copy link

support webpack 5 ?

@nikkii94
Copy link

any update on this?

@getSpidd
Copy link

Same question

@zhouyupeng
Copy link

+1

@mbalc
Copy link

mbalc commented May 11, 2021

any updates there?

@Ken-ding
Copy link

+1

2 similar comments
@hardk-ngu
Copy link

+1

@humingxian
Copy link

+1

@gentlecoder
Copy link

any solution?

@xikou1314
Copy link

+1

3 similar comments
@andrei10k
Copy link

+1

@2heal1
Copy link

2heal1 commented Jun 6, 2021

+1

@McDaddy
Copy link

McDaddy commented Jun 7, 2021

+1

@D-Rosado
Copy link

D-Rosado commented Jun 7, 2021

Still happening with

 "webpack": "5.38.1",
 "mini-css-extract-plugin": "1.6.0",

I have the feeling this is an issue with the 'mini-css-extract-plugin' as this happens even without the 'speed-measure-webpack-plugin' installed.

@zinge
Copy link

zinge commented Jun 7, 2021

+1

1 similar comment
@wangjing013
Copy link

+1

@taejs
Copy link

taejs commented Jun 17, 2021

I just downgraded mini-css-extract-plugin version to 1.3.6
https://github.com/webpack-contrib/mini-css-extract-plugin/releases/tag/v1.3.7
1.3.7 version cause this error.

@dejurin
Copy link

dejurin commented Jun 28, 2021

Not work with SpeedMeasurePlugin.
I was deleted SpeedMeasurePlugin from my config and mini-css-extract-plugin work's fine

"dependencies": {
     "format-money-js": "^1.4.3",
     "selectivity": "^3.1.0",
     "spectre.css": "^0.5.9",
     "zepto-webpack": "^1.2.1"
 },
 "devDependencies": {
     "case-sensitive-paths-webpack-plugin": "^2.4.0",
     "circular-dependency-plugin": "^5.2.2",
     "css-loader": "^5.2.6",
     "expose-loader": "^3.0.0",
     "imports-loader": "^3.0.0",
     "inspectpack": "^4.7.1",
     "mini-css-extract-plugin": "^1.6.1",
     "node-sass": "^6.0.0",
     "purecss-sass": "^2.0.5",
     "sass-loader": "^11.1.1",
     "speed-measure-webpack-plugin": "^1.5.0",
     "style-loader": "^2.0.0",
     "unused-files-webpack-plugin": "^3.4.0",
     "webpack": "^5.37.1",
     "webpack-cli": "^4.7.0"
 }```
 

@mayunyi
Copy link

mayunyi commented Jul 1, 2021

same issue

@mayunyi
Copy link

mayunyi commented Jul 1, 2021

image
image
image
Remove smp.wrap function run ok

@lanzhi-lee
Copy link

lanzhi-lee commented Jul 13, 2021

same issue, apart from lowering the version of mini-css-extract-plugin to 1.3.6, or remove smp.wrap(),

is there no other better solution that make smp and mini-css-extract-plugin use the latest version at the same time?

@lvxiaodi1234
Copy link

same issue

@kryptonat
Copy link

+1

@CPatchane
Copy link

CPatchane commented Nov 17, 2022

For those already using MiniCssExtractPlugin but can't use the workaround above because you'll get it twice in your plugins list, a workaround can be to extract your plugin definition before, then put it back after the wrapping:

const cssPluginIndex = webpackConfig.plugins.findIndex(
  (e) => e.constructor.name === "MiniCssExtractPlugin"
)
const cssPlugin = webpackConfig.plugins[cssPluginIndex]
const configToExport = smp.wrap(webpackConfig)
configToExport.plugins[cssPluginIndex] = cssPlugin
module.exports = configToExport

Didn't get the issue anymore after that and the css build is still measured 🚀

@roland-at-handshake
Copy link

@CPatchane - this worked for me as well. Thanks for the snippet!

@ShuiRuTian
Copy link

Hi guys, I am writing a successor of this plugin: https://github.com/ShuiRuTian/time-analytics-webpack-plugin.

You could have a try with it. I believe it's providing a better experience for webpack 5.

And I believe it also resolve this strange issue, no more strange hacks.

It's in an early period(the API is not frozen and maybe buggy), but it's working for me.

Any feedback is welcomed!

@propkitty
Copy link

At this point, this issue seriously needs to be fixed. It's one of the most popular CSS plugins, and the fact that almost two years after the issue of this problem existing, this hasn't been fixed? Is this not being maintained anymore?

@Banner-Keith
Copy link

At this point, this issue seriously needs to be fixed. It's one of the most popular CSS plugins, and the fact that almost two years after the issue of this problem existing, this hasn't been fixed? Is this not being maintained anymore?

First of all, why do you think giving @ShuiRuTian a thumbs down for trying to solve this on their own is helpful? And secondly, the person who maintains this repo is likely doing this on their own time, not getting paid for this, and this has a workaround. Many maintainers have stopped sharing their work altogether when demanding people harass them about problems but are unwilling to help out. If this is so important to you, open a PR.

@zedfight
Copy link

Remove MiniCssExtractPlugin.loader in loader

@Menzor-future
Copy link

对于那些已经在使用但无法使用上述解决方法的人,因为您将在插件列表中获得两次,解决方法可以是在之前提取插件定义,然后在包装后将其放回:MiniCssExtractPlugin

const cssPluginIndex = webpackConfig.plugins.findIndex(
  (e) => e.constructor.name === "MiniCssExtractPlugin"
)
const cssPlugin = webpackConfig.plugins[cssPluginIndex]
const configToExport = smp.wrap(webpackConfig)
configToExport.plugins[cssPluginIndex] = cssPlugin
module.exports = configToExport

在那之后就没有再遇到这个问题了,并且仍然测量了css构建🚀

感谢,我使用上面高赞的重新push方法后报错,显示“MiniCssExtractPlugin”已经注册。于是用您的替代法成功了,太棒了!
Thanks, I reported an error after using the repush method with the highest number of likes above, saying that "MiniCssExtractPlugin" is already registered. So I switched to your alternative and succeeded, great!

@ppya0812
Copy link

"mini-css-extract-plugin": "^1.3.6",
"webpack": "^5.21.1",

can success build

@MaximeCheramy
Copy link

Probably related: this also breaks https://github.com/jantimon/favicons-webpack-plugin : the alterAssetTags hook isn't called when HtmlWebpackPlugin is wrapped.

@ShuiRuTian
Copy link

I still suggest to try this package https://github.com/ShuiRuTian/time-analytics-webpack-plugin (written by me :p)

speed-measure-webpack-plugin is awesome, however, it's almost dead and not got updated for about 2 years.

And for now, it might cause perf issue and introduce subtle bugs, which should all be resolved("hacked" is more proper) in my new package.


If you are interested in the details, here we go.

Fact 1: Webpack and plugins use a lot of WeakMap internally as cache or state store.
Fact 2: to measure the time, we need to proxy the methods, so that we could record the start/end time and do some other stuffs. Not so surprised, speed-measure-webpack-plugin chooses Proxy to do this.

However, this introduces a subtle issue

// `foo` is an object, the content is not important for us.
const foo = {};

// `proxyFoo` is a proxy,the second parameter is pretty powerful, but we also not care it here.
const proxyFoo = new Proxy(foo,{});

proxyFoo === foo; // false

The reference is not equal now, it's natural, right?

However, this is a really bad news, because foo stands for the thing which would used as the key of WeakMap.

If the WeakMap is used as cache, we need to calculate the value again for each plugin(because it creates proxy for each ). And if the proxy object is not cached, we will always need to calculate the value, because the proxy object will always be a differnet one. In this situation, it would make whole thing slower.

If the WeakMap is used as a state store, then everything might be wrong. In this situation, it might cause subtle bug.

@cxybd
Copy link

cxybd commented Apr 7, 2023

module.exports = function (env) {

const isProduction = env.production
const tempConfig = isProduction ? productionConfig : developmentConfig
const configWithTimeMeasures = new SpeedMeasurePlugin().wrap(merge(commonConfig(isProduction), tempConfig));
configWithTimeMeasures.plugins.push(new MiniCssExtractPlugin({
filename: "css/[name].css",
chunkFilename: "css/[name]_chunk.css",
}));
return configWithTimeMeasures
}
Provious cgs are exported as a function
Like these,it works successfully!

@nathanredblur
Copy link

nathanredblur commented May 17, 2023

I have this error related to to mini-css when I use this plugin

TypeError: Cannot read properties of undefined (reading 'serialization')
    at MiniCssExtractPlugin.apply (/Users/pepito-perez/getaround/getaround-web/node_modules/mini-css-extract-plugin/dist/index.js:641:20)
    at WrappedPlugin.apply (/Users/pepito-perez/getaround/getaround-web/node_modules/speed-measure-webpack5-plugin/WrappedPlugin/index.js:290:29)
    at createCompiler (/Users/pepito-perez/getaround/getaround-web/node_modules/webpack/lib/webpack.js:73:12)
    at create (/Users/pepito-perez/getaround/getaround-web/node_modules/webpack/lib/webpack.js:134:16)
    at webpack (/Users/pepito-perez/getaround/getaround-web/node_modules/webpack/lib/webpack.js:158:32)
    at WebpackCLI.f [as webpack] (/Users/pepito-perez/getaround/getaround-web/node_modules/webpack/lib/index.js:64:16)
    at WebpackCLI.createCompiler (/Users/pepito-perez/getaround/getaround-web/node_modules/webpack-cli/lib/webpack-cli.js:1789:29)
    at async Command.<anonymous> (/Users/pepito-perez/getaround/getaround-web/node_modules/@webpack-cli/serve/lib/index.js:105:30)
    at async Promise.all (index 1)
    at async Command.<anonymous> (/Users/pepito-perez/getaround/getaround-web/node_modules/webpack-cli/lib/webpack-cli.js:1372:13)

any idea, workaround?

@nathanredblur
Copy link

nathanredblur commented May 17, 2023

I end doing this:

const speedMeasure = (config) => {
  const searchPlugin = (name) =>
    config.plugins.findIndex((e) => e.constructor.name === name)
  
  const reactRefreshPluginIndex = searchPlugin("ReactRefreshPlugin")
  const cssPluginIndex = searchPlugin("MiniCssExtractPlugin")

  const cssPlugin = config.plugins[cssPluginIndex]
  const reactRefreshPlugin = config.plugins[reactRefreshPluginIndex]
  
  const configToExport = smp.wrap(config)

  configToExport.plugins[cssPluginIndex] = cssPlugin
  if (reactRefreshPluginIndex !== -1)
    configToExport.plugins[reactRefreshPluginIndex] = reactRefreshPlugin
    
  return configToExport;
}

return isProfiling ? speedMeasure(config) : config;

@lonelam
Copy link

lonelam commented May 27, 2023

It seems that in NormalModule of the webpack, the hooks of Compilation instance are maintained by the WeakMap.get(compilation) call, the SMP plugin replaced the compilation object with a proxy for all other plugins, so the static call NormalModule.getCompilationHooks for plugins always returns wrong hooks. This PR seems have resolved the problem. I'll try it.

@lonelam
Copy link

lonelam commented May 28, 2023

well, after inspecting the issues, they are just incompatible because MiniCssExtractPlugin just pass configs between loader and plugin by the compiler object, which is replaced by a compiler object in this plugin. So I just use new webpack.ProgressPlugin({ profile: true }) instead.

@Zclhlmgqzc
Copy link

Like this

config => {
  const searchPlugin = name =>
    config.plugins.findIndex(p => p.constructor.name === name)
  const omitPlugin = index => config.plugins.splice(index, 1)[0]
  const omitPlugins = plugins => {
    plugins = plugins.reduce((result, name) => {
      const index = searchPlugin(name)
      if (index >= 0) {
        result.push({ index, plugin: omitPlugin(index) })
      }

      return result
    }, [])

    return () => {
      plugins.forEach(({ index, plugin }) => {
        config.plugins.splice(index, 0, plugin)
      })
    }
  }

  const restorePlugin = omitPlugins(['Plugin1', 'Plugin2'])

  const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')

  new SpeedMeasurePlugin().wrap(config)

  restorePlugin()
  
  return config
}

@aldoprogrammer
Copy link

I got same issue, and as it was saying to read the guidline here, then i follow it by installing using the command pnpm add -D mini-css-extract-plugin, then the problem is gone.

@FrancescoSaverioZuppichini

same

@adjfks
Copy link

adjfks commented Sep 1, 2023

the same

@goamn
Copy link

goamn commented Nov 9, 2023

kMap is used as cache, we need to calculate the value again for each plugin(because it creates proxy for each ). And if the proxy object is not cached, we will always n

@ShuiRuTian I tried your alternative plugin. It's quite good and no bugs. However it seems really inaccurate and confusing. There are multiple things with time results. Does it mean they are all running in parallel? Or there is some issue with the time keeping? My results:

┌── time-analytics-webpack-plugin
│ Webpack compile takes 120710.3568 ms
├── Plugins
│ Plugin TerserPlugin takes 20769.5217 ms
│ Plugin ESLintWebpackPlugin takes 2779.2995 ms
│ Plugin CssMinimizerPlugin takes 1555.3495 ms
│ Plugin HtmlWebpackPlugin takes 171.7745 ms
│ Plugin WebpackManifestPlugin takes 26.3319 ms
│ Plugin MiniCssExtractPlugin takes 7.8416 ms
│ Plugin DefinePlugin takes 3.4362 ms
│ Plugin ForkTsCheckerWebpackPlugin takes 0.9461 ms
│ Plugin InlineChunkHtmlPlugin takes 0.4624 ms
│ Plugin ModuleNotFoundPlugin takes 0.1330 ms
│ Plugin InterpolateHtmlPlugin takes 0.0762 ms
│ All plugins take 25315.1726 ms
├── Loaders
│ Loader babel-loader takes 2147845.5583 ms
│ Loader source-map-loader takes 180303.8255 ms
│ Loader mini-css-extract-plugin takes 116059.0275 ms
│ Loader @svgr/webpack takes 38772.6433 ms
│ Loader postcss-loader takes 452.6405 ms
│ Loader css-loader takes 5.7927 ms
│ Loader file-loader takes 5.6811 ms
│ All loaders take 2483445.1689 ms

@ShuiRuTian
Copy link

Hi @goamn , thanks for using it.

The alternative plugin records the 2 time points, the starting and ending of a loader function. In fact, there would be a thrown error if the plugin does not receive paired time points of a loader execution. So, we could say we always receive the time points we want.

As you said, the time are "inaccurate and confusing", it looks like so at the first glance. It might be indeed a bug. But, it might be surprisingly easy to do things in parallel in JS, many thanks to event-loop.

Here is an example for an async loader:

  1. At time point T1, a loader is started and we record the time.
  2. The loader wants to read file, so it creates a promise P1, and gives the control back to webpack.
  3. Webpack runs some loader(maybe same, not important) for another file, also, we record the starting time point T2.
  4. The second loader also wants to read a file, creates promise.
  5. We assume there is no more work, so the event loop enters idle status
  6. P1 and P2 are all resolved after a long time.
  7. The first loader does the remaining work and ends at time point T3.
  8. Similarly, the second loader ends at time point T4.

The time is almost duplicated for twice here.

And babel-loader is indeed an async-loader, I think it's what happened here.

Do you have any suggestions on this? I know there are some hooks might be helpful: https://nodejs.org/api/async_context.html and https://nodejs.org/api/async_hooks.html. But they are Experimental, which is suggested to be avoided.

@OleksandrKucherenko
Copy link

type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;
type IndexedPlugin = { index: number; plugin: Flatten<webpack.Configuration["plugins"]> };

const omitPlugins = (configuration: webpack.Configuration, ...names: string[]): IndexedPlugin[] => {
  const { plugins } = configuration;
  if (!plugins) throw new Error("Plugins are not defined.");

  const extracted = plugins
    .map((plugin, index) => (names.includes(plugin?.constructor.name ?? "<skip>") ? { index, plugin } : null))
    .filter(Boolean) as IndexedPlugin[];

  return extracted ?? [];
};

const recoverPlugins = (configuration: webpack.Configuration, ...plugins: IndexedPlugin[]) => {
  const { plugins: originalPlugins } = configuration;
  if (!originalPlugins) throw new Error("Plugins are not defined.");

  plugins.forEach(({ index, plugin }) => {
    originalPlugins[index] = plugin;
  });
};

const withSmpMeasuring = (configuration: webpack.Configuration) => {
  // FIXME (olku): this is a hack/workaround to get the SMP plugin work.
  // ref: https://github.com/stephencookdev/speed-measure-webpack-plugin/issues/167
  const excludes = omitPlugins(configuration, "MiniCssExtractPlugin");

  const smp = new SpeedMeasurePlugin({ disable: !process.env.MEASURE });
  const withSmpMeasuring = smp.wrap(configuration);

  recoverPlugins(withSmpMeasuring, ...excludes);

  return withSmpMeasuring;
};
// usage
return withSmpMeasuring(webpackConfiguration);

@loieee
Copy link

loieee commented Mar 13, 2024

anyone can resolved it?

@MaximeCheramy
Copy link

As explained above, you can wrap as a workaround but it's not perfect. This project is dead, stop using it.

@Clarkkkk
Copy link

Maybe try Rsdoctor? It is a tool for build analysis made by the Rspack team, and supports both Webpack 5 and Rspack.

@2heal1
Copy link

2heal1 commented Mar 13, 2024

Maybe try Rsdoctor? It is a tool for build analysis made by the Rspack team, and supports both Webpack 5 and Rspack.

yes, it can help to analyze the build

@tidalu
Copy link

tidalu commented Mar 19, 2024

Even i am not using webpack , still getting this error, does anyone know or have this issue?

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

No branches or pull requests