I'm having an issue with upgrading from Webpack 4 to Webpack 5, where Babel no longer seems to transpile code from one of my dependencies (async-mutex). I managed to strip it down to a minimal setup that demonstrates the problem:
package.json
{
"scripts": {
"build": "webpack --mode=production"
},
"devDependencies": {
"#babel/core": "~7.12.0",
"#babel/preset-env": "~7.12.0",
"async-mutex": "~0.2.0",
"babel-loader": "~8.2.0",
"webpack": "~5.10.0",
"webpack-cli": "~4.2.0"
},
"babel": {
"presets": [
"#babel/preset-env"
]
},
"browserslist": [
"Explorer >= 11"
]
}
webpack.config.js
module.exports = {
entry: {
bundle: './index.js',
},
module: {
rules: [
{
test: /\.m?js$/,
use: 'babel-loader',
},
],
},
};
index.js
import {Mutex} from 'async-mutex';
console.log(Mutex);
class MyClass {}
console.log(MyClass);
As per my browserslist, I need to support IE 11. After building this and inspecting the resulting dist/bundle.js I can see that the class MyClass was transpiled into a function, but the class Mutex was not transpiled, which obviously causes IE 11 to fail with a syntax error. It's as if Babel is using different settings to process the async-mutex package than it uses to process my index.js.
I found another question with an answer that suggests adding target: ['web', 'es5'], but that doesn't help and it also seems unnecessary, since Webpack is supposed to honor browserslist.
With Webpack 4 I did not have this issue, but I'm not sure if the problem is with my setup, with Webpack, with Babel or even with async-mutex.
Note aside: I'm aware that this minimal setup is lacking a Promise polyfill, but I omit it here because it seems irrelevant to the issue.
Babel configuration within package.json only applies within your specific package, not node_modules, so even though Babel is set up to process all files in your bundle, it's only been configured to perform transformations on your own package's files. See the Babel config file docs.
You need to create a babel.config.json instead, or you need to put the config directly into the Webpack config, so either
babel.config.json:
{
"presets": [
"#babel/preset-env"
]
}
OR
webpack.config.js:
module.exports = {
entry: {
bundle: './index.js',
},
module: {
rules: [
{
test: /\.m?js$/,
use: 'babel-loader',
options: {
"presets": [
"#babel/preset-env"
]
}
},
],
},
};
You'll need to include async-mutex in your webpack.config.js's module rule for babel. Once in awhile you'll come across a package that doesn't transform its ES6. Funny enough, the best way to include it in your transpilation is using exclude, like this:
module.exports = {
entry: {
bundle: './index.js',
},
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules\/(?!(async-mutex)\/).*/,
use: 'babel-loader',
},
],
},
};
This exclude rule says, "exclude all node_modules but async-mutex".
Related
I want to write a reusable UI component library and pack it with Webpack. However, when I import it in another project, where the babelrc has useBuiltIns: 'usage' set, the import will fail with an error:
"export 'default' (imported as 'Component') was not found in 'component'
This is part of my webpack configuration in library project:
output: {
path: path.resolve(process.cwd(), './dist'),
filename: 'component.js',
chunkFilename: '[id].js',
library: 'Component',
libraryTarget: 'commonjs2',
libraryExport: 'default'
},
...
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
Babel configuration in library project:
module.exports = {
presets: [
[
"env",
{
modules: false,
targets: {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}
],
"stage-2"
]
}
Babel configuration in the consuming project:
module.exports = {
presets: [
'#vue/app'
]
}
Where the useBuiltIns: 'usage' is implicitly set.
While the problem could be solve by either set useBuiltIns: false or scriptType: 'unambiguous' in the consuming project. But this is not what I want. Since my goal is to provide a reusable library and it is expected to be used in different projects. I cannot force all the consuming projects to do this.
Am I missing something here?
I've found the answer in the Vue.js forum: https://forum.vuejs.org/t/export-default-imported-as-components-was-not-found-in-mylib/63905
The problem was that I was adding the dependency with a local path, namely:
$ npm install ../component
And in this case, npm is creating a symlink in node_modules. It seems that some babel plugin doesn't really like symlinks.
After I've changed to use git:
$ npm install git+file://localhost/path/to/component
Everything works fine.
I'm having a hard time trying to configure Babel to transpile code that IE11 can understand, specifically arrow functions. Running npx webpack --mode=development with my configuration doesn't convert the arrow functions in my code: in the eval() statement in the generated code, I can see that all the instances have gone unconverted.
Unlike in the console output quoted in this question, there's no mention in mine of "Using targets" or "Using presets". Whether that's something to do with using npx webpack rather than npm run build I don't know.
Here's the Babel part of my package.json:
{
// name, version etc. snipped
"devDependencies": {
"#babel/core": "^7.1.2",
"#babel/plugin-transform-async-to-generator": "^7.1.0",
"#babel/plugin-transform-es2015-arrow-functions": "^6.22.0",
"#babel/plugin-transform-es2015-modules-commonjs": "^6.26.2",
"#babel/preset-env": "^7.1.0",
"ajv": "^6.5.4",
"copy-webpack-plugin": "^4.5.2",
"eslint-plugin-jest": "^21.24.1",
"jest": "^23.6.0",
"jest-dom": "^2.0.4",
"webpack": "^4.20.2",
"webpack-cli": "^3.1.2"
},
"babel": {
"presets": [
[
"#babel/preset-env",
{
"targets": {
"ie": "11"
}
}
]
],
"env": {
"development": {
"plugins": [
"transform-es2015-arrow-functions",
"transform-es2015-modules-commonjs"
]
},
"test": {
"plugins": [
"transform-es2015-arrow-functions",
"transform-es2015-modules-commonjs"
]
}
}
}
}
My webpack.config.js looks like:
const CopyWebpackPlugin = require("copy-webpack-plugin");
const path = require("path");
module.exports = {
entry: "./src/thing.js",
optimization: {
minimize: false
},
output: {
filename: "thing.js",
path: path.resolve(__dirname, "dist")
},
plugins: [
new CopyWebpackPlugin([
// snipped
])
]
};
I arrived at this point from reading other questions here about Babel configurations, and the babel-preset-env docs and also the very skimpy babel-plugin-transform-es2015-arrow-functions docs. The answers to this very similar question (no accepted answer) don't mention that plugin at all, and one suggests using a polyfill, which seems to involve loading a library in your actual code rather than at this stage?
I'm very new to working with Webpack in general and don't understand what the difference is between "env" (which gets mentioned in a lot of questions) and "#babel/preset-env". Or really what the former implies in general; if you click on "env" in the docs navigation, it takes you to the page for #babel/preset-env.
What am I doing wrong?
If you are using Webpack 5, you need to specify the features that you want to transpile in the ouput.environment configuration, as explained here: https://webpack.js.org/configuration/output/#outputenvironment.
output: {
// ... other configs
environment: {
arrowFunction: false,
bigIntLiteral: false,
const: false,
destructuring: false,
dynamicImport: false,
forOf: false,
module: false,
},
}
EDIT 08/10/22
While doing some refactoring of my webpack configuration, I figured out that the arrows had stopped transpiling (or maybe I didn't test extensively when giving the original answer).
Two things weren't setup correctly: the target key was missing, and the babel configuration had a wrong value.
For supporting legacy browsers, the target key needs to be set like this:
target: ['web', 'es5']
While in the Babel configuration, I had added 'not dead' in the browserslist configuration under targets in the Babel loader, so since IE11 is now technically dead, this configuration removed the trasnpilation of the arrow function.
So if you still need to transpile the arrow function, this would be the relevant part in the Babel configuration.
module: {
rules: [
{
test: /\.m?js$/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'#babel/preset-env',
{
useBuiltIns: 'entry',
targets: {
// be sure not to add 'not dead' here
browsers: ['> 1%', 'last 2 versions', 'Firefox ESR']
},
corejs: {
version: 3,
proposals: false,
},
},
],
],
plugins: [['#babel/plugin-transform-runtime', { corejs: 3 }]],
},
},
},
],
},
In addition to loganfsmyth's answer that solved the problem, I want to note for any other beginners reading this that I made life easier for myself afterward by moving the Babel configuration out of package.json into a .babelrc.
I also discovered that the plugins I needed, such as the one I mentioned above, babel-plugin-transform-es2015-arrow-functions, have newer versions with a different naming scheme - for that example, #babel/plugin-transform-arrow-functions. The documentation pages for the old versions don't mention that.
The module part of my webpack.config.js now looks like:
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
}
My .babelrc looks like:
{
"presets": [
[
"#babel/preset-env",
{
"targets": {
"ie": "11"
},
"useBuiltIns": "entry"
}
]
],
"plugins": [
"#babel/transform-async-to-generator",
"#babel/transform-arrow-functions",
"#babel/transform-modules-commonjs"
],
"env": {
"development": {},
"test": {},
"production": {}
}
}
Update 2021: As of Webpack version 5, it outputs ES6 code by default. If you need that not to happen, you need to add a configuration setting. See Giorgio Tempesta's answer.
Babel itself is a transformation library, but on its own it will not integrate into any specific tooling. To use Babel with Webpack, you'll want to install the babel-loader package and configure it in your Webpack config using something along the lines of
module: {
rules: [
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
}
}
]
}
I had the same problem. Turned out it wasn't all of my code that had arrow functions, but only one single library. I went inside my built bundle, and searched for code with arrow functions (=>). Then I searched and copied some unique-looking names around it and managed to find the source of it searching for it in all files within node_modules. In my case it turned out that the code with arrow functions came from fetch polyfill called unfetch. I'm not sure why it didn't get transpiled by the plugin, but I switched it to "whatwg-fetch" and it worked just fine - no arrow functions in bundle anymore. You could try the same technique to discover what causes it in your case.
2022 Update
If you have this problem now, it could be because browerslist treats IE 11 as dead since version 4.21.0. The solution to access IE 11 anyway is to add ie 11 either as the last entry in browserslist or at least after not dead.
Normally you don't see browserslist in the package.json, but if you use npm ls browserslist you can see which dependency uses browserslist and which version of it.
See also: https://github.com/browserslist/browserslist/issues/699
I'm using babel with webpack, I'm trying to make arrow functions work with Internet Explorer, but I can't get it working.
This is my package.json dev dependencies:
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-preset-env": "^1.7.0",
"webpack": "^3.12.0",
"webpack-cli": "^3.1.0"
}
and this is my webpack.config.js:
module.exports = {
entry: ['./chat.js'],
devtool: 'source-map',
output: {
path: path.resolve(__dirname, "dist"),
filename: "chat.js"
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}
]
}
};
I'm using the plugins with .babelrc:
{
"presets": ["env"],
"plugins": ["transform-class-properties"]
}
I don't know what I'm doing wrong or if I'm missing something but I get the following syntax error on Internet Explorer:
DF.fn = () => {
// Content
};
If you are using Babel 7, the behaviour of .babelrc has changed.
My advice is to drop .babelrc and put the config inside your webpack config.
Additionally you will need to either remove exclude: /node_modules/ from your config config, or add in some conditions for not excluding any libraries that use browser incompatible code (eg, arrow functions if you want to use IE).
Personally I removed the exclude altogether and noticed no degredation in speed or size.
For those using Webpack 5, you need to specify the features that you want to transpile in the ouput.environment configuration, as explained here: https://webpack.js.org/configuration/output/#outputenvironment.
I am using differential serving so I have set all the flags according to a modern variable (which is set to true only if building for recent browsers, which is the last 5 versions of Chrome and Firefox and the last 2 versions of Safari, Edge and iOS).
output: {
// ... other configs
environment: {
arrowFunction: modern,
bigIntLiteral: modern,
const: modern,
destructuring: modern,
dynamicImport: modern,
forOf: modern,
module: modern,
},
}
You have to specify the target browsers for your build. Based on that babel-preset-env decides which transforms need to be applied. Here's docs and example of config
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "safari >= 7"]
}
}]
]
}
And here are all possible ways to specify the browsers set.
I got a problem with transpiling imported modules from node_modules. Babel for some reason doesn't transpile imported module from node_modules, but transpile modules imported from src.
Here is an example repo: https://github.com/NikitaKA/babeltest
main.js
// result code contains const and let, but it shouldn't. :(
index.js
import qs from 'query-string; // not transpiled
import lib from './lib' // transpiled
const query = qs.parse(window.location.search);
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: "babel-loader"
}
}
]
}
};
.babelrc
{
"presets": [
["#babel/preset-env", {
"modules": false,
"targets": {
"chrome": 39
}
}],
["#babel/preset-stage-1", {
"modules": false,
"decoratorsLegacy": true,
"pipelineProposal": "minimal"
}]
],
"plugins": [
"transform-es2015-constants",
"#babel/plugin-transform-block-scoping",
"#babel/plugin-transform-runtime"
]
}
The solution in this case is to transpile the module again, this can be done by modifying the exclude property in your webpack config:
{
test: /\.js$/,
exclude: /node_modules\/(?!(es6-module|another-es6-module)\/).*/,
},
Modules es6-module and another-es6-module will no longer be ignored by webpack and will be transpiled with the rest of your source code.
See the GitHub issue on the webpack project.
Tested with webpack#3.12.0, babel-core#6.26.3 and babel-core#6.26.3
To expand upon my comments:
You really don't want to transpile all of node_modules – it'll take a long time, and most of the code there should already be ES5 (unless they're actually ES6 modules, in which case the ES6 entry point is announced as "module" in the package.json manifest).
query-string#6.x isn't, and it says so in its README:
This module targets Node.js 6 or later and the latest version of Chrome, Firefox, and Safari. If you want support for older browsers, use version 5: npm install query-string#5.
If you're using Vue, there is a simple solution. Just list your module in transpileDependencies:
vue.config.js
module.exports = {
publicPath: '',
outputDir: 'www',
transpileDependencies: [
'query-string'
],
}
Good afternoon,
This is the same issue I reported at webpack's github, but I suspect I might be the one doing something wrong, thus opening a question here.
I'm trying to configure webpack 2 with Babel, and one of the requirements is to transpile built-ins such as Symbol.
Despite that now working fine, when I try to use webpack and babel's transform-runtime, I'm unable to use exports *.
Input file (src/index.js):
export * from './secondFile'
secondFile.js:
export let TESTSYMBOL = Symbol('test');
export let TESTSYMBOL2 = Symbol('test2');
webpack.config.js (only copied the relevant part):
module: {
rules: [
{
test: /\.jsx?$/,
// Skip any files outside of `src` directory
include:path.resolve(__dirname, 'src'),
use: {
loader: 'babel-loader',
options: {
presets: ["es2015", "stage-3"],
plugins: ['transform-runtime']
}
}
}
]
}
script:
"webpack -d --config config/webpack.config.js"
Output file: gist
Exception:
Uncaught ReferenceError: exports is not defined - at Object.defineProperty(exports, "__esModule", {
Dev Dependencies:
"webpack": "2.6.1",
"webpack-dev-server": "2.4.5",
"webpack-notifier": "1.5.0"
"babel-cli": "6.24.1",
"babel-loader": "7.0.0",
"babel-plugin-transform-runtime": "6.23.0",
"babel-preset-es2015": "6.24.1",
"babel-preset-stage-3": "6.24.1"
Dependencies:
- "babel-runtime": "6.23.0"
Thanks for any help!
It seems that the problem is with the include. For some reason, I was unable to use path.resolve or path.join. The webpack documentation has such example.
If the webconfig is as follows, it works just fine:
module: {
rules: [
{
test: /\.js$/,
include: [
/src/
],
// or exclude: [/node_modules/],
use:
{
loader: 'babel-loader',
options: {
plugins: ['transform-runtime'],
presets: ['es2015', 'stage-3']
}
}
}
]
}
Either way, now there's a problem with exports not defined, which can be solved by setting modules to false in es2015 preset (thanks to Vanuan at Github for that suggestion):
presets: [['es2015', { modules: false }], 'stage-3'],
For IE or older browsers, I need to use es-shims - libraries which ports the ECMAScript specs to legacy JS engines.
These libs below may resolve your problem if added as the first imports on your index.html (or equivalent). Here's one for example:
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.7/es5-shim.min.js"></script>
See this link for every lib you may need:
ES-Shims