Transpiling to ES5 using webpack 4 and Babel - javascript

I'm still using webpack 4.28.4 for organizational reasons. We are trying to transpile some of our code to support ES5 using Babel 7, but our resulting bundle is throwing console errors when loaded in a browser (Chromium v44). We have seen some of the newer options available in webpack 5, which some of my colleagues have been able to use to resolve similar failures:
output.environment.arrowFunction = false
target = 'es5'
Is there some sort of equivalent approach for webpack 4? Do we just need to find the right combination of Babel plugins to fully convert to ES5?
The console error we get in Chromium 44 is always an "Uncaught SyntaxError: Unexpected token )" in some low level Dojo JavaScript file which we know is unchanged and should be a valid function.
Here's a cleaned up version of our webpack.config.js:
const path = require('path');
const webpack = require('webpack');
const BabelLoaderPlugin = require('babel-loader');
const BabelPreset = require('#babel/preset-env');
const makeConfig = ({
component_path,
js_module,
entryPoints = {"index": js_module + "/entryPoint",
"index-css": js_module + "/css/main"},
outputPublicPath = 'release/',
outputPath = path.resolve(component_path, 'release'),
dev_mode = 'production',
babel_chrome_level = "44",
} = {}) => {
return {
entry: entryPoints,
output: {
path: outputPath,
publicPath: outputPublicPath,
pathinfo: true,
filename: "bundle.[name].js"
},
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components|dojo)/,
use: {
loader: 'babel-loader',
query: {
babelrc: false,
presets: [
[BabelPreset,
{
"exclude": ["transform-regenerator"],
"targets": {
"chrome": babel_chrome_level
}
}]
],
plugins: [
require("#babel/plugin-transform-async-to-generator"),
require("#babel/plugin-transform-arrow-functions"),
require("#babel/plugin-transform-modules-commonjs")
]
}
}
},
]
},
plugins: [
...
],
};
}
module.exports = { makeConfig };

Did you try to hardcoded it to 44 instead of using babel_chrome_level?
In Chrome devtools, you might be able to get it to pretty-print the minified Dojo sources to better identify exactly which feature it's tripping over
The better fix for this is to find whatever numbskull in your organization has you stuck on a FIVE YEAR OLD version of a browser and smack them until they stop
Also it looks like you're explicitly excluding dojo from your Babel transform
I don't know much about Dojo -- I guess they made a totally new "modern" version at some point? -- but maybe you can pin to an older version of it that officially supports your version of Chrome?

Related

Webpack erroring on mini-css-extract-plugin loader

When I try to use the loader for mini-css-extract-plugin webpack returns the following error:
/node_modules/mini-css-extract-plugin/dist/loader.js:122
for (const asset of compilation.getAssets()) {
^
TypeError: compilation.getAssets(...) is not a function or its return value is not iterable
I am requiring the plugin and invoking the loader in my prod config file:
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const path = require("path");
const common = require("./webpack.common");
const merge = require("webpack-merge");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = merge(common, {
mode: "production",
output: {
filename: "[name].[contentHash].bundle.js",
path: path.resolve(__dirname, "dist")
},
plugins: [
new MiniCssExtractPlugin({filename: "[name].[contentHash].css"}),
new CleanWebpackPlugin()
],
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader, //3. Extract css into files
"css-loader", //2. Turns css into commonjs
"sass-loader" //1. Turns sass into css
],
},
],
},
});
I have it included in my devDependencies:
"mini-css-extract-plugin": "^1.3.6",
But still I receive the error. I haven't found anything in the [documentation][1] to indicate what could be happening. Is there something I'm overlooking with this code?
Why would methods from loader.js be getting flagged as 'not a function?'
[1]: https://www.npmjs.com/package/mini-css-extract-plugin
I understand getAssets is only available since webpack 4.40.0 https://webpack.js.org/api/compilation-object/#getassets
You could try:
Update webpack version to at least 4.40.0
Downgrade min-css-extract-plugin to 1.3.0
I tried the second one, and worked for me, but upgrading webpack should work too.

NuxtJS with Babel 7: still have spread operator in built files

I'm desperately trying to make my NuxtJS app work with IE11. I implemented babel config in many ways to build a compatible version but I still have spread operators in built pages files, as if Babel didn't transform Nuxt code.
Here is my config:
nuxt.config.js
const pkg = require('./package')
const path = require('path');
module.exports = {
mode: 'universal',
// ...
build: {
babel: {
babelrc: true
},
extend(config, ctx) {
config.resolve.modules.push(path.resolve(__dirname, 'assets'));
const svgRule = config.module.rules.find(rule => rule.test.test('.svg'));
svgRule.test = /\.(png|jpe?g|gif|webp)$/;
config.module.rules.push({
test: /\.svg$/,
loader: 'vue-svg-loader',
}, {
test: /\.js$/,
loader: 'babel-loader'
})
}
}
}
.babelrc
{
"presets": [["#babel/preset-env", { "modules": false }]],
"plugins": [
"#babel/transform-runtime",
"#babel/plugin-syntax-dynamic-import",
"#babel/plugin-transform-spread",
"#babel/plugin-proposal-object-rest-spread"
],
"env": {
"test": {
"presets": [["#babel/preset-env", { "targets": { "node": "current" } }]]
}
}
}
.browserslistrc
# Browsers that we support
>0.2%
not dead
not ie < 11
not op_mini all
Despite that config, I still see some spread operators used in Nuxt built pages, like the following generated by nuxt:
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["pages/events/_slug.pages/index"],{
/***/ "./assets/svg/events/market.svg":
/*!**************************************!*\
!*** ./assets/svg/events/market.svg ***!
\**************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony default export */ __webpack_exports__["default"] = ({
functional: true,
render(_h, _vm) {
const { _c, _v, data, children = [] } = _vm;
const {
class: classNames,
staticClass,
style,
staticStyle,
attrs = {},
...rest
} = data;
I searched from some time across different issues about NuxtJS and Babel, but Nuxt claims that it works with IE9 without extra Babel configuration, which is not the case here. I'd like to understand why the code is not transpiled the right way.
I ran into a similar issue: A Nuxt app wouldn't work in the Edge browser because of spread operators in #nuxtjs/axios and bootstrap-vue. I did find a solution.
The build property in nuxt.config.js should be defined as follows:
build: {
babel: {
babelrc: true,
configFile: './babel.config.js'
},
transpile: [ '#nuxtjs/axios', 'bootstrap-vue' ],
// Other config
}
The transpile property is key here. Internally, Nuxt defines an exclude for babel-loader that ignores everything in node_modules, unless it's in transpile.
Using babel.config.js also appears to matter, and the official Babel documentation says you should use it if you want to process node_modules.
babel.config.js:
module.exports = function (api) {
api.cache(true);
return {
sourceType: 'unambiguous',
presets: ['#nuxt/babel-preset-app'],
plugins: ['#babel/plugin-proposal-object-rest-spread']
};
}
You don't need include or exclude here, as it's taken care of by Nuxt, as noted.

webpack can convert js to es6?

I using webpack to bundle my node application.
I see the result in the bundle that webpack convert from const to var. This is mean that webpack convert my files to es5.
How can I tell webpack to convert to es6? (leave the const as it is and/or use import keyword for example)
app.js
import {test} from './some';
const x = 1;
console.log('test', test);
console.log('this should be const in the bundle, not var. ', x);
And the bundle is:
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _some__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./some */ "./some.js");
var x = 1;
console.log('test', _some__WEBPACK_IMPORTED_MODULE_0__["test"]);
console.log('this should be const in the bundle, not var. ', x);
/***/ }),
my webpack config:
const path = require('path');
module.exports = () => [
{
mode: 'development',
entry: path.resolve(__dirname, './app.js'),
output: {
path: path.resolve(__dirname, './dist')
},
devtool: 'source-map',
target: 'node',
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
}
]
}
}
];
You are using the #babel/preset-env without any options. That will transform the code to ES5 and the documentation says it's not really recommended to use it this way. The whole point of the "env" preset is that you give a target platform and it will automatically apply the transformations, which are needed for that platform. Passing "targets.node"-option value true or "current" will transform the code for the currently used node-version. Using the preset with this option has the additional advantage, that upgrading node.js will not require any changes in Babel configuration and less code will be transformed, if the new node.js supports more of the ES features used.
Support for ECMAScript modules in node.js is still experimental, but you can disable module transformation by passing false to "modules"-option.
options: {
presets: [[
'#babel/preset-env',
{
targets: {
node: "current"
},
modules: false
}
]]
}

Make webpack's library output compatible with babel6

Babel's 6th version changes the functioning of export default and in particular its relation with commonjs require.
To summarise, while until babel5, require('module') where giving the default export of the module, it now always returns the module object containing all of the exports of the module.
If one only wants the default, he/she must use require('module').default.
As explained here, there is very good reasons behind this and the aim of this question is not to break or hack this behaviour.
However, if one is building a library, he/she usually does not want to distribute a module but the export value of his library (e.g. a function, whatever module system is used internally).
This is well dealt with by webpack and the output.library configuration when using commonjs or AMD. Because prior babel's versions allowed the default export to be required with commonjs, babel was also compatible with this mechanism. However it is not the case anymore: the library now always provides an es6 module object.
Here is an example.
src/main.js
export default "my lib content";
webpack.config.js
var path = require("path");
var webpack = require("webpack");
module.exports = {
entry: {
lib: [ path.resolve(__dirname, "src/main.js") ],
},
output: {
path: path.join(__dirname, "dist"),
filename: "mylib-build.js",
library: 'myLib'
},
module: {
loaders: [
{
test: /\.js$/,
loader: "babel",
include: path.join(__dirname, "src"),
query: { presets: ['es2015'] }
}
]
}
};
test.html
<html>
<head></head>
<body>
<script src="dist/mylib-build.js"></script>
<!-- `myLib` will be attached to `window` -->
<script>
console.log(JSON.stringify(myLib)); // { default: "my lib content" }
</script>
</body>
</html>
This is a very simple example but I obviously want the export of mylib to be the string "my lib content" instead of { default: "my lib content" }.
One solution could be to create an export source file in commonjs to perform the transformation:
module.exports = require('./main').default;
However I find this solution quite poor. One should be able to solve it at the compilation level, without changing the source code.
Any idea?
Was just going at this my self. Whether one like to call it a workaround or solution, there seem to be a Babel plugin that "solve it".
Using the plugin babel-plugin-add-module-exports as referenced in https://stackoverflow.com/a/34778391/1592572
Example config
var webpackOptions = {
entry: {
Lib1: './src/Lib1.js',
Lib2: './src/Lib2.js'
},
output: {
filename: "Master.[name].js",
library: ["Master","[name]"],
libraryTarget: "var"
},
module: {
loaders: [
{
loader: 'babel',
query: {
presets: ['es2015'],
plugins: ["add-module-exports"]
}
}
]
}
};
This yields Master.Lib1 to be lib1 instead of Master.Lib1.default.
Webpack 2 now supports es6 modules which partially solves this issue. Migrating from webpack 1 to webpack 2 is relatively painless. One just needs to remember to disable babel's es6 module to commonjs conversion to make this work:
.babelrc
{
"presets": [
["es2015", {"modules": false}]
]
}
However, unfortunately, it does not work properly with export default (but an issue is opened, hopefully a solution will be released eventually).
EDIT
Good news! Webpack 3 supports the output.libraryExport option that can be used to directly expose the default export:
var path = require("path");
var webpack = require("webpack");
module.exports = {
entry: {
lib: [ path.resolve(__dirname, "src/main.js") ],
},
output: {
path: path.resolve(__dirname, "dist"),
filename: "mylib-build.js",
library: "myLib",
// Expose the default export.
libraryExport: "default"
},
module: {
loaders: [
{
test: /\.js$/,
loader: "babel",
include: path.resolve(__dirname, "src")
}
]
}
};
You can use this solution (this is more like workaround, but it allow you to keep your sources from change):
There is a loader called callback-loader. It allow you to change your sources in a build time by calling a callback and put a result instead of it. In other words you can turn all you require('module') into a require('module').default automatically in a build time.
Here is your config for it:
var webpackConfig = {
module: {
loaders: [
{ test: /\.js$/, exclude: /node_modules/, loader: 'callback' },
...
]
},
...
callbackLoader: {
require: function() {
return 'require("' + Array.prototype.join.call(arguments, ',') + '").default';
}
}
};

ES7 async await functions with babel-loader not working

I'm trying to run async await functions in JavaScript using the babel-loader of webpack. I'm using the following configuration:
{
name: 'client',
context: path.join(__dirname, 'src', 'static', 'scripts'),
entry: {
index: './index.js'
},
output: {
path: path.join(__dirname, 'src', 'static', 'bundles'),
filename: '[name].js'
},
module: {
loaders: [
{
test: /\.js$/,
loader: 'babel',
query: {
presets: ['es2015', 'stage-0']
}
}
]
},
resolve: {
root: path.join(__dirname),
fallback: path.join(__dirname, 'node_modules'),
modulesDirectories: ['node_modules'],
}
}
but it keeps pushing an error with the following message:
Module build failed: Error: ./src/static/scripts/index.js: Expected type "Identifier" with option {}
My index.js has this content:
console.log('hi from app');
async function hello() {
return Promise.resolve('hi')
}
async function conversation () {
const hi = await hello()
console.log(hi);
}
First, ensure you're using all of the latest versions of babel plug-ins:
npm i -D babel-loader babel-polyfill babel-preset-es2015 babel-preset-stage-0 babel-runtime
(I'm not sure if babel-runtime is actually required- YMMV)
In the entry of your webpack, use this:
entry: [
'babel-polyfill',
'./index.js'
]
You need the babel-polyfill adding to your entry script to enable "regeneratorRuntime", which is needed for async/await.
Then use the babel-loader in your module section (instead of babel)
module: {
loaders: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader',
query: {
presets: [
'es2015',
'stage-0'
]
}
}
]
That should fix it.
Note: If you're using Node on the server side, you'll need to add an explicit require('babel-polyfill'); to the top of your entry script to ensure any async/await syntax is recognised.
es2015 and stage-0 weren't enough to polyfill Node 5 - instead, use this in your .babelrc
{
"presets": [
"node5"
]
}
node5 is a collection of babel plug-ins I maintain to polyfill just those pieces of Node 5.x that are missing. It should mostly work with Node 4.x too (which, with minor variations, are now nearly identical in ES6 compatibility.)
You'll need to install it with NPM first:
npm i -D babel-preset-node5
It looks like you hit a bug. It seems to be solved but not released yet.
If you cannot wait until it is released, you can apply the patch corresponding to the commit that solves the issue.
One way to apply the patch:
Save the following diff into a file (visit.patch for example) in your package root folder. Then apply the patch with git apply visit.patch.
From 940b86dadbd0151c33c02e89f0b5ff61077c9214 Mon Sep 17 00:00:00 2001
From: Henry Zhu <hi#henryzoo.com>
Date: Thu, 5 Nov 2015 20:10:15 -0500
Subject: [PATCH] transform-regenerator: set node.id to an identifier if null -
fixes #2835
---
packages/babel-plugin-transform-regenerator/lib/visit.js | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/node_modules/babel-preset-es2015/node_modules/babel-plugin-transform-regenerator/lib/visit.js b/node_modules/babel-preset-es2015/node_modules/babel-plugin-transform-regenerator/lib/visit.js
index 0f68ffc..c4a0d2e 100644
--- a/node_modules/babel-preset-es2015/node_modules/babel-plugin-transform-regenerator/lib/visit.js
+++ b/node_modules/babel-preset-es2015/node_modules/babel-plugin-transform-regenerator/lib/visit.js
## -146,6 +146,10 ## function getOuterFnExpr(funPath) {
var node = funPath.node;
t.assertFunction(node);
+ if (!node.id) {
+ node.id = funPath.scope.parent.generateUidIdentifier("callee");
+ }
+
if (node.generator && // Non-generator functions don't need to be marked.
t.isFunctionDeclaration(node)) {
var pp = funPath.findParent(function (path) {
## -171,9 +175,7 ## function getOuterFnExpr(funPath) {
);
}
- return node.id || (
- node.id = funPath.scope.parent.generateUidIdentifier("callee")
- );
+ return node.id;
}
function getRuntimeMarkDecl(blockPath) {
Update:
It seems that the bug wasn't properly fixed and the commit has been reverted. Looks like it was due to another problem that has been addressed in this pull request.
For preset env on node v8.11, I'm having issue of async function simply never run, and no exception.
Then I refer to official documentation and adding target to .babelrc, now it works.
{
"presets": [
["env", {
"targets": {
"node": "current"
}
}]
]
}
Babel documentation

Categories

Resources