webpack-chain
webpack-chain copied to clipboard
Unhelpful error message when adding minimizer ("TypeError: Cannot read property '__expression' of undefined")
I want to explicitly add (in order to configure) the Terser Plugin as minimizer to a webpack config with webpack-chain
v6.0.0.
The code
const TerserPlugin = require('terser-webpack-plugin');
config.optimization
.minimizer('terser')
.use(TerserPlugin);
results in the error
TypeError: Cannot read property '__expression' of undefined
at Object.toConfig (/some/path/node_modules/webpack-chain/src/Plugin.js:56:38)
at clean.Object.assign.minimizer.minimizers.values.map.plugin (/some/path/node_modules/webpack-chain/src/Optimization.js:39:66)
at Array.map (<anonymous>)
at module.exports.toConfig (/some/path/node_modules/webpack-chain/src/Optimization.js:39:45)
at module.exports.toConfig (/some/path/node_modules/webpack-chain/src/Config.js:130:41)
Any idea what the problem might be?
Hi! Thank you for filing an issue.
I'm not able to reproduce using:
// package.json
{
"dependencies": {
"terser-webpack-plugin": "2.1.3",
"webpack": "4.41.1",
"webpack-chain": "6.0.0"
}
}
// test.js
const Config = require('webpack-chain');
const config = new Config();
const TerserPlugin = require('terser-webpack-plugin');
config.optimization
.minimizer('terser')
.use(TerserPlugin);
console.log(config.toConfig());
console.log(config.toString());
$ node --version
v12.11.1
$ node test.js
{ optimization: { minimizer: [ [TerserPlugin] ] } }
{
optimization: {
minimizer: [
/* config.optimization.minimizer('terser') */
new TerserPlugin()
]
}
}
Could you create a reduced testcase that shows the error?
I had same issue (with TerserPlugin). My problem was that I used vue-cli-plugin-nativescript-vue with vue-cli 4.x They have different interface usage(4 vs 6) within webpack-chain plugin.
The same problem has arisen
// Please ignore the above references
import Config from "webpack-chain";
import common from "./webpack.common";
import TerserPlugin from "terser-webpack-plugin";
const config = new Config();
config.mode("production");
config.optimization.minimizer("terser").use(TerserPlugin);
export default common.merge(config.toConfig()).toConfig();
package.json
{
"webpack": "^4.41.3",
"terser-webpack-plugin": "^2.3.0",
"webpack-chain": "^6.2.0",
}
error
const constructorName = plugin.__expression
^
TypeError: Cannot read property '__expression' of undefined
@bosens-China Hi! Please can you create a fully contained testcase that demonstrates the issue? And file as a new issue? I can't use the above, since webpack.common
is missing.
me too
My problem was that I used vue-cli-plugin-nativescript-vue with vue-cli 4.x They have different interface usage(4 vs 6) within webpack-chain plugin.
Thank you for this clue. Checking out that vue plugin, I can see it's still using the old webpack-chain <= 4 syntax for optimization.minimizer
:
https://github.com/nativescript-vue/vue-cli-plugin-nativescript-vue/blob/e13ae2c02b66d8ac68daddc27782374163be7f01/index.js#L327-L347
The solution is either to downgrade to webpack chain 4 (by downgrading Vue CLI to v3) or else fork that plugin and update it to be compatible with webpack 5+ that comes with newer Vue CLI.
This seems like a common enough problem that it would be worth adding some additional error checking to webpack-chain so we can display a more user-friendly error message (even though this is not a webpack-chain bug itself).
I've dug into this more yesterday/today. This error message is shown when the plugin configuration is incomplete, which can happen in a variety of ways.
Whilst this isn't a webpack-chain bug, showing unhelpful error messages is not ideal and we should add additional error checking at other locations, to make it easier to work out what is invalid about the configuration, so that users can fix it themselves.
Scenario 1
In its simplest form, this error message can be triggered like so:
const webpackChain = require('webpack-chain');
const config = new webpackChain();
config.plugin('name');
// We forgot to call .use() before .toConfig()!
// config.plugin('name').use(SomePlugin);
config.toConfig();
$ node test.js
.../node_modules/webpack-chain/src/Plugin.js:56
const constructorName = plugin.__expression
^
TypeError: Cannot read property '__expression' of undefined
The nature of webpack-chain's API for adding plugins, means it's a multi-part process, and if the .use()
is forgotten, then the plugin isn't defined so .toConfig()
will fail.
To handle this case, we could add a check inside Plugin's .toConfig()
that throws a more user-friendly Error in this case. However this error message would still have to be fairly generic, since at the time .toConfig()
is called, it's too late for us to know how we ended up in this state.
Scenario 2:
Using the legacy optimization.minimizer
syntax that was valid in webpack-chain 4:
config.optimization.minimizer([
new WebpackPluginFoo(),
new WebpackPluginBar(arg1, arg2),
]);
...rather than the new syntax from webpack-chain 5+:
config.optimization.minimizer('foo').use(WebpackPluginFoo);
config.optimization.minimizer('bar').use(WebpackPluginBar, [arg1, arg2]);
This can happen when eg using Vue CLI 4 with a Vue plugin that's not yet been made compatible with it. This case we can handle more specifically, by checking whether an array was passed to optimization.minimizer
and if so, displaying an error message that explains the syntax change. I've opened PR #226 for this.
Scenario 3:
Trying to pass a webpack-chain style configuration object into .merge()
even though it doesn't support it.
eg:
const webpackChain = require('webpack-chain');
const config1 = new webpackChain();
const config2 = new webpackChain();
class TestPlugin {}
config2.optimization.minimizer("terser").use(TestPlugin);
// This is not valid usage of `.merge()` and isn't expected to work!
config1.merge(config2.toConfig()).toConfig();
There are a few issues here:
- The docs for
.merge()
weren't correct foroptimization.minimizer
, which I've fixed in #224 -
.merge()
accepts a configuration object this is similar to but not identical to the webpack configuration schema, and whilst this is mentioned in the docs, it should really be made more obvious given it's counter-intuitive. I've opened #225 for this. - Given the counter-intuitive behaviour of
.merge()
we should really add some additional validation to try and warn people when they pass webpack-config objects to.merge()
without first transforming them.
Personally I'm not a big fan of .merge()
-- judging from comments above and ones I've seen in other issues, it feels like it's being used in cases where webpack-chain's API should be used directly instead. It's also missing functionality (eg #210). It also seems like if we added a true "clone webpack chain object" functionality (#212), then people might not need it?
v6.3.1 has just been released, which includes some improvements to the error message shown in some of the scenarios outlined above, which should make it easier to self-debug what's broken in project's usage of webpack-chain.
I still want to improve the error message for scenario 1 and scenario 3.3, so leaving this open for now :-)
i tried, it working when NODE_ENV === 'production' dist/js/app.js
no consoles
but when NODE_ENV !== 'production' dist/js/app.js
has console
chainWebpack: config => {
config.optimization
.minimizer('terser')
.tap(args => {
if (process.env.NODE_ENV === 'production') {
args[0].terserOptions.compress.drop_console = true
args[0].terserOptions.compress.drop_debugger = true
}
return args
})
}