Closed EdmundMai closed 6 years ago
@developit This is the same issue I was facing 3 weeks ago. As I mentioned on the issue there are 5 plugins that mode = production automatically adds I bet its one of those 5
Provides process.env.NODE_ENV with value production. Enables FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin and UglifyJsPlugin
Happens for me as well, in production mode, VNode
lacks properties defined via Object.defineProperty
, this is what I got:
{
"children": [...],
"attributes": {...},
"preactCompatUpgraded": true,
"preactCompatNormalized": true
}
There is no type of props there. Everything looks fine in dev mode.
Narrowed it down to UglifyJsPlugin
plugin. Turning it off helps, except bundle is size is much larger.
I've noticed that when minified preact-compat createElement
isn't called at all.
Setting unused : false
seems to be the temp solution at least for now.
I looked into this further and it seems (at least in preact.ems.js
) the initial VNode
class declaration gets mangled into the h()
function when run through the UglifyJSPlugin
with default options:
Before:
/** Virtual DOM Node */
function VNode() {}
// Skipped code
function h(nodeName, attributes) {
// Skipped code
var p = new VNode();
// Skipped code
return p;
}
After (interpreted by me):
/*** No class declaration here ***/
function h(nodeName, attributes) {
// Skipped code
// `function VNode() {}` becomes the below anonymous class
var p = new function() {};
// Skipped code
return p;
}
As a side-node, even renaming it to anything but still kept the in top-level scope would still work:
/** Virtual DOM Node */
function fooBar() {}
// Skipped code
function h(nodeName, attributes) {
// Skipped code
var p = new fooBar();
// Skipped code
return p;
}
So it seems the class declarations is being used as a global somewhere or there is some identity conditional that never evaluates to true
(since a new instance is created in the mangled form, instead of re-using the top-level declaration).
A work around for this is to use reduce_funcs
which stops the function getting mangled inside the h()
function. The above comment about unused: false
also works because again, it keeps the function in the global scope.
Here is a webpack config that can be merged into an existing config (I use WebPack merge to do this). It splits out preact
into its own chunk file so that only it gets applied the slightly differing Uglify options (this is because Uglify exclude/include options matches filenames after concatenation, not source chunk names). If you wanted to keep preact in your main bundle, then you can simply change the include
/exclude
options to the chunk with preact
in and get rid of the preact
cacheGroup
and the chunkFilename
setting.
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
output: {
// Add chunk name to filename so Uglify's include/exclude can target it.
chunkFilename: '[chunkhash].[name].js',
},
optimization: {
splitChunks: {
cacheGroups: {
// Create a separate chunk for preact to apply slightly different
// Uglify options on it.
preact: {
name: 'preact',
chunks: 'all',
minSize: 0,
test: /[\\/]preact[\\/]/,
priority: 99,
},
},
},
minimizer: [
// Prevent function reduction in preact for preact-compat to work.
new UglifyJsPlugin({
include: /preact\.js$/,
uglifyOptions: {
compress: {
reduce_funcs: false,
},
},
}),
// Normal uglifying for everything else.
new UglifyJsPlugin({
exclude: /preact\.js$/,
cache: true,
parallel: true,
}),
],
},
};
I did also had this problem where I was using preact in production. But found that I would need the reduce_vars
setting for UglifyJSPlugin to making react-router
work:
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
// and this config
optimization: {
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
compress: {
reduce_vars: false // see https://github.com/developit/preact/issues/961
}
}
})
]
}
And with another package redux-connect
there was a problem because it in the transpiled files was require babel-runtime/helpers/jsx
instead of react
var _jsx2 = require('babel-runtime/helpers/jsx');
I am no babel-expert but it looks to me like babel-runtime is a deprecated method to use, so I upgraded the package and after switching to this my project works fine with preact-compat and webpack 4
Sorry for the absence. I believe we may have merged a workaround for this Uglify bug in Preact itself, which will fix the issues described here. Sorry for the really difficult debugging!
Here's the PR to Preact: https://github.com/developit/preact/pull/1153
We'll release it as a patch update shortly. As mentioned by @gerhardsletten, a temporary workaround is to disable reduce_funcs
which contains the constructor miscategorization issue.
This is fixed in Preact 8.3.0. Update and enjoy!
For some reason, when I start the server with
cross-env NODE_ENV=production webpack --config='config/webpack.config.js' --mode production
it breaks and ReactDOM.render(...) returns
<undefined></undefined>
. Changing it to--mode development
fixes it.cross-env NODE_ENV=production webpack --config='config/webpack.config.js' --mode development
Webpack config: