Reputation: 22923
My babel+webpack config works fine, but the resulting bundle isn't runnable in IE11 as it contains const
declarations. I thought having the es2015
preset was enough to fix this? Running $(npm bin)/babel test/some-es2015.js
produces strict ES5.1 code, so Babel seems to work, but the actual code that borks in IE11 is in modules imported from node_modules
.
When grepping for 'const '
in my resulting bundle I get certain lines like this (the eval is due to eval source mapping btw):
eval("\nObject.defineProperty(exports, \"__esModule\", { value: true });\nconst validator = __webpack_require__(/*! validator */ \"./node_modules/tcomb-additional-types/node_modules/validator/index.js\");\nconst t = __webpack_require__(/*! tcomb */ \"./node_modules/tcomb/index.js\");\nconst IP = t.refinement(t.String, validator.isIP);\nexports.IP = IP;\nexports.default = IP;\n//# sourceMappingURL=ip.js.map\n\n//# sourceURL=webpack:///./node_modules/tcomb-additional-types/lib/string/ip.js?");
The important part to note is the stuff such as const validator =
. This isn't ES5.1 syntax. My own code seems to have been transpiled to ES5 just fine. I can see this file in /node_modules/tcomb-additional-types/lib/string/ip.js
, where they use const
, so this isn't Babel adding const
s, but the source containing them. Most of the other packages are ES5.
So far, I have found that most const
s are from material-ui
and tcomb-additional-types
.
Babel .babelrc:
{
"compact": false,
"presets": [
"es2015",
"es2017"
],
"plugins": [
["transform-runtime", {
"polyfill": false,
"regenerator": true
}],
"transform-class-properties",
"transform-react-jsx",
"transform-object-rest-spread"
]
}
Webpack config:
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
/** @returns {String} an absolute path */
function toRoot(rootRelativeDir) {
return path.resolve(__dirname, '..', rootRelativeDir);
}
module.exports = {
entry: ['./src/app.js', './styles/flex.less'].map(toRoot),
output: {
filename: 'bundle.js',
path: toRoot('.webpack/dist')
},
resolve: {
extensions: ['.js', '.jsx'],
alias: {}
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
/* General options are read using .babelrc - only webpack loader specific here */
cacheDirectory: toRoot('.webpack/babel_cache')
}
}
]
}
]
},
plugins: [new CopyWebpackPlugin([toRoot('public')])]
};
Upvotes: 4
Views: 7788
Reputation: 22923
My underlying problem was that some Node packages are not written using ES5 syntax, and the Babel transforms did not transform them for some reason. This is a normal issue
Finding why this happened was pretty easy (@Vincent's answer helped); I had exclude: /node_modules/
in the config. Of course, removing this would "fix" the issue, but it would introduce new issues, as the exclude
is there for a reason, as you don't want Babel to process every file in there.
So what you want is this: selective filtering allowing some modules.
Trying to construct a regex that will allow a list of packages under node_modules, but restrict the rest is cumbersome and error prone. Thankfully the Webpack docs describe that the condition rules, of which exclude
is one, can be
Creating such a function is easy! So instead of having exclude: /node_modules
, I changed it to be exclude: excludeCondition
, where excludeCondition
is the following function:
function excludeCondition(path){
const nonEs5SyntaxPackages = [
'material-ui',
'tcomb-additional-types'
]
// DO transpile these packages
if (nonEs5SyntaxPackages.some( pkg => path.match(pkg))) {
return false;
}
// Ignore all other modules that are in node_modules
if (path.match(toRoot("node_modules"))) { return true; }
else return false;
}
This fixed my issue, as there is just a tiny number of packages using ES2015 syntax, so adding them to the allowlist is manageable.
Addendum
Since people ask about the toRoot()
, this is the verbatim code:
/** @returns {String} an absolute path */
function toRoot(rootRelativeDir) {
return path.resolve(__dirname, '..', rootRelativeDir);
}
Adapt to your own needs.
The fuller code:
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
/** @returns {String} an absolute path */
function toRoot(rootRelativeDir) {
return path.resolve(__dirname, '..', rootRelativeDir);
}
function excludeCondition(path) {
const nonEs2015Packages = ['tcomb-additional-types', 'material-ui'];
// DO transpile these packages
if (nonEs2015Packages.some(pkg => path.match(pkg))) {
return false;
}
// Ignore all other modules that are in node_modules
return Boolean(path.match(toRoot('node_modules')));
}
module.exports = {
entry: ['./src/app.js', './styles/custom.less', './styles/flex.less', './styles/rc_slider.less'].map(toRoot),
output: {
filename: 'bundle.js',
path: toRoot('.webpack/dist')
},
resolve: {
extensions: ['.js', '.jsx'],
alias: {}
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: excludeCondition,
use: [
{
loader: 'babel-loader',
options: {
/* General options are read using .babelrc - only webpack loader specific here */
cacheDirectory: toRoot('.webpack/babel_cache')
}
}
]
}
]
},
plugins: [new CopyWebpackPlugin([toRoot('public')])]
};
Upvotes: 5
Reputation: 63
I had a similar problem and I fixed it by renaming .babelrc.js
to babel.config.js
.
Apparently, .babelrc
has smaller scope than babel.config.js
, if you'd like to read more about that, check out this post:
When to use babel.config.js and .babelrc
Upvotes: 3
Reputation: 1663
The same problem happened to me as well. Some node modules don't provide browser support and target node versions that leverage newer ES syntax.
I came across that handy package that transpiles node modules code:
https://www.npmjs.com/package/babel-engine-plugin
It solved my problem regarding IE11 support, hope it helps
Upvotes: 1