This is most probably a bug in one of the build tools used by react-scripts, or some misconfiguration. I recently ran into unexpected functionality in production build. https://github.com/AriPerkkio/asus-merlin-simple-vpn-client-setup/issues/1
The code works just fine on development mode but breaks on production build. I've traced the bug into a switch-case block which produces incorrect results after transpile process. I could debug this further but don't have much time for this now. I've prepared a minimal reproduce branch for this, see below.
Yes, I've ran rm -rf node_modules/ yarn.lock.
Read it many times, great documentation!
Environment Info:
System:
OS: Linux 4.19 Debian GNU/Linux 9 (stretch) 9 (stretch)
CPU: (4) x64 06/8e
Binaries:
Node: 13.6.0 - /usr/bin/node
Yarn: 1.21.1 - /usr/bin/yarn
npm: 6.13.4 - /usr/bin/npm
Browsers:
Chrome: Not Found
Firefox: Not Found
npmPackages:
react: ^16.12.0 => 16.12.0
react-dom: ^16.12.0 => 16.12.0
react-scripts: 3.3.0 => 3.3.0
npmGlobalPackages:
create-react-app: Not Found
$ git clone https://github.com/AriPerkkio/asus-merlin-simple-vpn-client-setup.git
$ cd asus-merlin-simple-vpn-client-setup
$ git checkout transpile-bug
$ yarn # Install dependencies
$ yarn build # Builds minimal setup and runs prettier on it
# Check build/ui/static/js/main.<hash>.chunk.js
Input:
case 'B': {
const { key } = action;
const keys = state.keys.map(_key =>
_key.id === key.id ? key : _key
);
return { ...state, keys };
}
Unexpected output in build:
case 'B':
t.key;
var i = e.keys.map(function(e) {
return e.id, e.id, e;
});
return c({}, e, { keys: i });
Rename _key
case 'B': {
const { key } = action;
const keys = state.keys.map(_someKey =>
_someKey.id === key.id ? key : _someKey
);
return { ...state, keys };
}
Output is as expected:
case 'B':
var i = t.key,
u = e.keys.map(function(e) {
return e.id === i.id ? i : e;
});
return c({}, e, { keys: u });
See above.
See steps to reproduce.
This issue seems to be caused by babel-preset-react-app. Minimal repo without CRA https://github.com/AriPerkkio/babel-preset-react-app-transpile-bug.
I have the same issue.
babel-plugin-transform-react-remove-prop-types is used with removeImport: true option. By setting it to false the issue seems to be fixed. @oliviertassinari Any ideas what's going on here?
isEnvProduction && [
// Remove PropTypes from production build
require('babel-plugin-transform-react-remove-prop-types').default,
{
removeImport: true,
},
]
This setup is enough to reproduce this:
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "entry",
"corejs": 3,
"modules": false
}
]
],
"plugins": [
[
"babel-plugin-transform-react-remove-prop-types",
{ "removeImport": true }
]
]
}
Some more debugging details. The cause still seems to be babel-plugin-transform-react-remove-prop-types.
Babel output when removeImport is true.
case 'B': {
var _key = action.key;
var _keys = state.keys.map(function(_key) {
return _key.id === _key.id ? _key : _key;
});
return _objectSpread({}, state, {
keys: _keys,
});
}
Conflicting var key and map(function(_key) makes terser go crazy:
case 'B':
action.key;
var _keys = state.keys.map(function(_key) {
return _key.id, _key.id, _key;
});
return _objectSpread({}, state, { keys: _keys });
Output when removeImport is set false;
// Babel
case 'B': {
var _key2 = action.key;
var _keys = state.keys.map(function(_key) {
return _key.id === _key2.id ? _key2 : _key;
});
return _objectSpread({}, state, {
keys: _keys,
});
}
// Terser
case 'B':
var _key2 = action.key,
_keys = state.keys.map(function(_key) {
return _key.id === _key2.id ? _key2 : _key;
});
return _objectSpread({}, state, { keys: _keys });
More details. I've checked plugins from preset-env one-by-one and found incompatibility between @babel/plugin-transform-block-scoping and babel-plugin-transform-react-remove-prop-types.
This setup is enough for reproducing;
{
"plugins": [
"@babel/plugin-transform-block-scoping",
[
"babel-plugin-transform-react-remove-prop-types",
{ "removeImport": true }
]
]
}
I've been studying this using this playground repo.
As before, setting removeImport: false fixes the issue. Next I'll try to debug babel but I'm not familiar with AST and compilers.
@AriPerkkio if you need assistance or anything, feel free to ping us over at https://github.com/babel/babel and/or open an issue!
I found that scope.crawl() method used by babel-plugin-transform-react-remove-prop-types is causing this. By commenting it out the bug disappeared. I searched for other plugins using the same method and found babel-plugin-remove-debug. The issue can now be reproduced without babel-plugin-transform-react-remove-prop-types.
{
"plugins": [
"@babel/plugin-transform-block-scoping",
"babel-plugin-remove-debug"
]
}
Bug opened to babel: https://github.com/babel/babel/issues/11057
This issue has been automatically marked as stale because it has not had any recent activity. It will be closed in 5 days if no further activity occurs.
Last comments before the bot closes this issue.
This bug is still valid. If you encounter this try renaming your variables and check how the code transpiled from the minified bundle.
Unit tests and development environment cannot be trusted 100%. Integration and manual tests against production build are the only ways to validate the actual functionality.
Projects where react-scripts has been ejected can flip the removeImport flag - and hope that no other plugin will cause this in the future.