ES7 async await functions with babel-loader not working - javascript

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

Related

How to exclude core-js using useBuiltIns: "usage"

Using babel 7.5.5, core-js 3.1.4 and webpack 4.38.0, how can I exclude core-js from transpiling?
I do not want to exclude node_modules altogether since I have libs that need transpiling
If I use exclude: /node_modules\/(core-js)/, a core-js module throws
TypeError: $ is not a function
This leaves me with two other options.
Use includes instead, include my src directory and every dependency that needs transpiling one by one
Use useBuiltIns: entry instead of usage, since in this case exclude: /node_modules/\(core-js)/ works, and import core.js at the top of main.js
Both of these options don't really seem like good solutions to me since usage is "no longer experimental" since 7.4.
Is there any way to make it work using usage? Is it a bug in either babel-loader or babel? Or is my configuration at fault?
This is my Webpack config:
const path = require('path');
const webpack = require('webpack');
module.exports = {
mode: 'production',
entry: {
main: ['./src/main'],
},
output: {
path: path.resolve(__dirname, './build/'),
publicPath: '/build/'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules\/(core-js)/,
use: {
loader: 'babel-loader'
},
},
{
test: require.resolve('jquery'),
use: [
{
loader: 'expose-loader',
options: 'jQuery'
},
{
loader: 'expose-loader',
options: '$'
}
]
}
]
},
plugins: [
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery'
})
],
};
This is my babel config:
module.exports = function (api) {
api.cache(true);
return {
presets: [
[
'#babel/preset-env',
{
corejs: {
version: 3,
},
useBuiltIns: 'usage',
}
]
],
};
};
You can reproduce the error with the following repository: https://github.com/tomm1996/usebuiltins-exclude-test
You need to exclude both core-js and webpack/buildin from the Babel transpilation.
You can use the folling exclude Regexes:
exclude : [
/\bcore-js\b/,
/\bwebpack\/buildin\b/
]
Here is also a complete babel-loader configuration with some useful comments:
{
module : {
rules : [{
test : /\.js$/,
// Some module should not be transpiled by Babel
// See https://github.com/zloirock/core-js/issues/743#issuecomment-572074215
exclude : [
/\bcore-js\b/,
/\bwebpack\/buildin\b/
],
loader : "babel-loader",
options : {
babelrc : false,
// Fixes "TypeError: __webpack_require__(...) is not a function"
// https://github.com/webpack/webpack/issues/9379#issuecomment-509628205
// https://babeljs.io/docs/en/options#sourcetype
sourceType : "unambiguous",
presets : [
["#babel/preset-env", {
// Webpack supports ES Modules out of the box and therefore doesn’t require
// import/export to be transpiled resulting in smaller builds, and better tree
// shaking. See https://webpack.js.org/guides/tree-shaking/#conclusion
modules : false,
// Adds specific imports for polyfills when they are used in each file.
// Take advantage of the fact that a bundler will load the polyfill only once.
useBuiltIns : "usage",
corejs : {
version : "3",
proposals : true
}
}]
]
}
}
}
}
See https://github.com/zloirock/core-js/issues/743#issuecomment-572074215
Edit: Also if you try to use #babel/plugin-transform-runtime:
plugins : [
// Require the Babel runtime as a separate module to avoid the duplication
// https://webpack.js.org/loaders/babel-loader/#babel-is-injecting-helpers-into-each-file-and-bloating-my-code
["#babel/plugin-transform-runtime", {
// Requires #babel/runtime-corejs3
// https://babeljs.io/blog/2019/03/19/7.4.0#migration-from-core-js-2
corejs : { version: 3, proposals: true }
}],
}
You may run into a similar error:
Uncaught TypeError: _typeof2 is not a function
at _typeof (typeof.js:8)
at eval (sockjs.js:123)
at Object.eval (sockjs.js:131)
at eval (sockjs.js:6565)
at Object../node_modules/sockjs-client/dist/sockjs.js (main.js:13790)
at __webpack_require__ (main.js:70)
at eval (webpack://PUBLIC_ENGINE/(:8000/webpack)-dev-server/client/clients/SockJSClient.js?:110:14)
at Object../node_modules/webpack-dev-server/client/clients/SockJSClient.js (main.js:13874)
at __webpack_require__ (main.js:70)
at eval (webpack://PUBLIC_ENGINE/(:8000/webpack)-dev-server/client/socket.js?:56:41)
This can be solved by excluding #babel/runtime-corejs3 from the transpilation:
exclude : [
/\bcore-js\b/,
/\bwebpack\/buildin\b/,
/#babel\/runtime-corejs3/
]

MQTT.js and Webpack - "WS is not a constructor"

I am trying to bundle one of our microservices which is using MQTT.js and I am struggling with really strange issue.
It is working fine without bundling, so ws is available in node_modules.
Stuff which I think matters:
error:
TypeError: WS is not a constructor
at WebSocketStream (dist/index.js:159329:16)
at createWebSocket (dist/index.js:147450:10)
at Object.buildBuilderBrowser (dist/index.js:147476:10)
at MqttClient.wrapper [as streamBuilder] (dist/index.js:147937:36)
at MqttClient._setupStream (dist/index.js:146471:22)
at new MqttClient (dist/index.js:146452:8)
at Function.connect (dist/index.js:147940:10)
webpack config:
const path = require('path');
const nodeExternals = require('webpack-node-externals');
const { NODE_ENV = 'production' } = process.env;
module.exports = {
entry: { index: './src/index.ts' },
mode: NODE_ENV,
target: 'node',
watch: NODE_ENV === 'development',
externals: [nodeExternals()],
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
},
resolve: {
extensions: ['.ts', '.js'],
},
node: {
__dirname: false,
},
module: {
rules: [
{
test: /\.ts$/,
use: [{ loader: 'ts-loader', options: { transpileOnly: true } }],
},
{
test: /(\.md|\.map)$/,
loader: 'null-loader',
},
],
},
};
Function where it happens:
createMqttClient(): MqttClient {
return mqtt.connect(this.mqttOptions.url, { ...this.mqttOptions.options });
}
The url is like: ssl://url-to-our-mqtt
Can anybody help please?
I also ran into same issue.
The problem for me was that I used
plugins: [
new webpack.NormalModuleReplacementPlugin(/^mqtt$/, "mqtt/dist/mqtt.js"),
],
in webpack.config.js order to fix the shebang error that comes with mqtt.js since it is a CLI tool.
Then instead I have used
{
test: [
/node_modules[/\\]mqtt[/\\]mqtt.js/,
/node_modules[/\\]mqtt[/\\]bin[/\\]sub.js/,
/node_modules[/\\]mqtt[/\\]bin[/\\]pub.js/,
],
loader: 'shebang-loader'
},
And my problem was fixed. Do you also use mqtt/dist/mqtt.js instead of mqtt in your imports or if you do something similar to mine, the shebang-loader rule I have posted above might solve your problem.
I experienced the same with Amazon aws-iot-device-sdk-js and Microsoft azure-iot-device-mqtt which both include mqtt.
The initial issue is the build error:
ERROR in ./node_modules/mqtt/mqtt.js Module parse failed: Unexpected character '#' (1:0)
This error is caused by the package mqtt. Three files (mqtt.js, pub.js and sub.js) contain a shebang line
#!/usr/bin/env node
The solution using module replacement suggested some places
plugins: [
new webpack.NormalModuleReplacementPlugin(/^mqtt$/, "mqtt/dist/mqtt.js"),
],
unfortunately changes the build error with the run time error
TypeError: WS is not a constructor
As mentioned in other answers, webpack can be configured (https://webpack.js.org/concepts/loaders/) to use the shebang loader (https://www.npmjs.com/package/shebang-loader)
TL;DR
Install shebang-loader
npm install shebang-loader --save
In webpack.config.js use the loader
module.exports = {
...
module: {
rules: [
{
test:
[
/.*mqtt\.js$/,
/.*sub\.js$/,
/.*pub\.js$/
],
use: 'shebang-loader'
}
]
}
}

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
}
]]
}

ERROR from UglifyJs: SyntaxError: Unexpected token: operator (>)

I'm getting an error when trying to run my webpack for production.
ERROR in js/main.21dbce548a76ffc14cfb.js from UglifyJs
SyntaxError: Unexpected token: operator (>) [./~/tmi.js/lib/utils.js:3,0][js/main.21dbce548a76ffc14cfb.js:3529,20]
utils.js:3,0 (which is the same as in my minified js) is:
// Return the second value if the first value is undefined..
get: (obj1, obj2) => { return typeof obj1 === "undefined" ? obj2 : obj1; },
So I assume from that the error is thrown because it's reading ES6 but it doesn't understand ES6? (The arrow function)
I don't see what's going wrong here, this is my webpack.config.js
// changed some loader syntax after reading
// https://webpack.js.org/how-to/upgrade-from-webpack-1/
const path = require(`path`);
const webpack = require(`webpack`);
const {UglifyJsPlugin} = webpack.optimize;
const CopyWebpackPlugin = require(`copy-webpack-plugin`);
const ExtractTextWebpackPlugin = require(`extract-text-webpack-plugin`);
const configHtmls = require(`webpack-config-htmls`)();
const extractCSS = new ExtractTextWebpackPlugin(`css/style.css`);
// change for production build on different server path
const publicPath = `/`;
// hard copy assets folder for:
// - srcset images (not loaded through html-loader )
// - json files (through fetch)
// - fonts via WebFontLoader
const copy = new CopyWebpackPlugin([{
from: `./src/assets`,
to: `assets`
}], {
ignore: [ `.DS_Store` ]
});
const config = {
entry: [
`./src/css/style.css`,
`./src/js/script.js`
],
resolve: {
// import files without extension import ... from './Test'
extensions: [`.js`, `.jsx`, `.css`]
},
output: {
path: path.join(__dirname, `server`, `public`),
filename: `js/[name].[hash].js`,
publicPath
},
devtool: `sourcemap`,
module: {
rules: [
{
test: /\.css$/,
loader: extractCSS.extract([
{
loader: `css`,
options: {
importLoaders: 1
}
},
{
loader: `postcss`
}
])
},
{
test: /\.html$/,
loader: `html`,
options: {
attrs: [
`audio:src`,
`img:src`,
`video:src`,
`source:srcset`
] // read src from video, img & audio tag
}
},
{
test: /\.(jsx?)$/,
exclude: /node_modules/,
use: [
{
loader: `babel`
},
{
loader: `eslint`,
options: {
fix: true
}
}
]
},
{
test: /\.(svg|png|jpe?g|gif|webp)$/,
loader: `url`,
options: {
limit: 1000, // inline if < 1 kb
context: `./src`,
name: `[path][name].[ext]`
}
},
{
test: /\.(mp3|mp4)$/,
loader: `file`,
options: {
context: `./src`,
name: `[path][name].[ext]`
}
}
]
},
plugins: [
extractCSS,
copy
]
};
if(process.env.NODE_ENV === `production`){
//image optimizing
config.module.rules.push({
test: /\.(svg|png|jpe?g|gif)$/,
loader: `image-webpack`,
enforce: `pre`
});
config.plugins = [
...config.plugins,
new UglifyJsPlugin({
sourceMap: true, // false returns errors.. -p + plugin conflict
comments: false
})
];
}
config.plugins = [...config.plugins, ...configHtmls.plugins];
module.exports = config;
OP's error is from UglifyJs, as is solved in the accepted answer, some people to this page may get the error from babel, in which case, fix it with: add "presets": ["es2015"] either to the options.presets section of babel-loader, or to .babelrc.
UglifyJs2 has a Harmony branch which accepts ES6 syntax to be minified. At this moment, you need to create a fork of webpack and point webpack to that fork.
I recently answered a couple of similar questions. Please have a look at #38387544 or #39064441 for detailed instructions.
In my case I was using webpack version 1.14
I got help from git ref
steps:
install yarn add uglifyes-webpack-plugin (and removed yarn remove uglifyjs-webpack-plugin)
then install yarn add uglify-js-es6
In webpack.config.js file change new webpack.optimize.UglifyJsPlugin to
new UglifyJsPlugin
then I was able to build. Thanks

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';
}
}
};

Categories

Resources