There is a proposal for introducing C# style async-await. I know Babel.js transpiles ES6 to ES5, but is there any way to make it transpile async-await to ES5?
Babel v6
As of Babel v6, Babel doesn't contain any transformers itself anymore. You have to explicitly specify any feature you want to transform.
Presets - non ES2015 environment
The quickest way to get this working is to use presets which already contain the set of plugins needed to transform ES2015 and newer proposals. For async, you will need the es2015 and es2017 presets and the runtime plugin (don't forget to install babel-runtime as described in the documentation):
{
"presets": [
"es2015",
"es2017"
],
"plugins": [
"transform-runtime"
]
}
Presets - ES2015 environment
If you run the code in an environment that supports ES2015 (more specifically, generators and Promises), then all you need is the es2017 preset:
{
"presets": [
"es2017"
]
}
Custom
To only transform the async functions, you will need the following plugins.
syntax-async-functions is needed in any every case to be able to parse async functions
In order to run the async function, you either need to use
transform-async-to-generator: Converts the async function into a generator. This will use Babel's own "co-routine" implementation.
transform-async-to-module-method: Also converts the async function to a generator, but passes it to the module and method specified in the configuration instead of Babel's own method. This allows you to use external libraries such as bluebird.
If your code runs in an environment that supports generators, then there is nothing left to do. However, if the target environment does not support generators, you will also have to transform the generator. This is done via the transform-regenerator transform. This transform depends on runtime functions, so you will also need Babel's transform-runtime transform (+ the babel-runtime package).
Examples:
Async to generator
{
"plugins": [
"syntax-async-functions",
"transform-async-to-generator"
]
}
Async to module method
{
"plugins": [
"syntax-async-functions",
["transform-async-to-module-method", {
"module": "bluebird",
"method": "coroutine"
}]
]
}
Async to generator + regenerator
{
"plugins": [
"syntax-async-functions",
"transform-async-to-generator",
"transform-regenerator",
"transform-runtime"
]
}
Babel v4 and older
Yes, you have to enable the experimental transformers. Babel uses regenerator.
Usage
$ babel --experimental
babel.transform("code", { experimental: true });
This solution may have changed since (Feb 25 Felix Kling) or perhaps there is still more than one way to use async await.
What worked for us was to run Babel like so
$ npm install babel-runtime
$ babel inputES7.js -o outputES5.js --optional runtime
I got this working as-of today by doing an additional npm install babel-preset-stage-0 and using it like
var babel = require("babel-core");
var transpiled = babel.transform(code, { "presets": ["stage-0"] });
See
http://babeljs.io/docs/usage/options/#options
http://babeljs.io/docs/plugins/preset-stage-0/
Perhaps even more up-to-date now; just put the babel stuff in a separate file:
'use strict';
require('babel/register'); // Imports babel - auto transpiles the other stuff
require('./app'); // this is es6 - gets transpiled
See my code at how-can-i-use-es2016-es7-async-await-in-my-acceptance-tests-for-a-koa-js-app for some more details.
The approved answer seems to be outdated now. The experimental flag has been deprecated in favor of stage.
http://babeljs.io/blog/2015/03/31/5.0.0/#experimental-option
Usage
$ babel --stage 0
babel.transform("code", { stage: 0 });
Stage 0
es7.classProperties
es7.comprehensions
Stage 1
es7.asyncFunctions
es7.decorators
es7.exportExtensions
es7.objectRestSpread
Stage 2 (Stage 2 and above are enabled by default)
es7.exponentiationOperator
Related
I added bundleconfig.json to ASP.NET Core application. It has the following structure:
[
{
"outputFileName": "wwwroot/js/main.min.js",
"inputFiles": [
"wwwroot/js/scripts/first.js",
"wwwroot/js/scripts/second.js"
],
"minify": {
"enabled": true,
"renameLocals": true
},
"sourceMap": false
}
]
Both scripts has been minified and merged into main.min.js. But after minification all async modifiers has been removed from result script.
Function such as
async function foo() {
await /* some promise */;
}
have been turned into:
function foo() {await /*some promise*/;}
How do I avoid removing async modifier?
I'v reproduced the issue and tried to minify a simple js file that using ES6 specifications and later.
Test.js
async function foo() {
await bar();
}
async function bar() {
for (var i = 0; i < 10; i++) { // do some work
}
}
Then i tried to minify the file with Bundler and Minifier tool then this error thrown:
This means Bundler and Minifier doesn't support ES6 specifications and later.
For confirmation i started searching about this issue in the Github and i found these same behaviors
Crash on ES6 arrow functions in source files
minify es6 js file without turning them to es5
Where BundleMinifier currently is usefull (and where not)
I can surely claim that this is The Transpilers Issue
Transpilers, or source-to-source compilers, are tools that read source
code written in one programming language, and produce the equivalent
code in another language.
The most common and widely use one is TypeScript
TypeScript in some cases Transpiles ES6 and later to ES5
For example: if you set Target to ES6 and ES2015 it Transpiles to ES5. However, if You Target to ES2020 does NOT Transpile your code.
At The End
BundlerMinifier uses NUglify that perform javascript code
minification So There is NO way minifying ES6 and later codes by
using Bundler and Minifier. Unless, The Author decides to support it.
You are encountering The Transpile Issue (ex:ES6 to ES5).
Bundler & Minifier doesn't remove unknown keywords like async but thrown error
Is there a way to use the TypeScript compiler only to remove type annotations, but not transpiling async functions? Something like a { target: 'esInfinite' } option? The reason is: There are browsers that already support async functions, so I wish to have a build target where those functions are not affected.
example input:
async function foo(a : number) : Promise<void> {}
example output:
async function foo(a) {}
In your tsconfig.json, change your target to ES2017, then it will preserve the async/await.
{
"compilerOptions": {
.....
"target": "ES2017",
.....
}
}
DO make sure your run-time supports it natively!!!
PS: as of Apr 2018, AWS Lambda now supports Nodejs 8. You should be able to use the above config for it.
This feature was already requested here. Targeting es2016 and es2017 should be available in the Community milestone and in TypeScript 2.1.
I am using an ES6 rest parameter in the simplest possible way:
function func(a, ...b) {
for (var i = 0; i < b.length; i += 2) {
console.log(b[i], b[i + 1]);
}
}
I've installed babel-preset-es2015, and my .babelrc file simply has:
{
"presets": ["es2015"]
}
Strangely, the resulting output is:
"use strict";
function func(a) {
for (var i = 0; i < arguments.length; i += 2) {
console.log(arguments[i], arguments[i + 1]);
}
}
I filed a bug at https://phabricator.babeljs.io/T7000, but I'm extremely concerned since this is the second time I've had really basic ES6 transpile incorrectly using Babel 6. Is my setup broken? Is Babel 6 documented somewhere as unstable/alpha? Or is this highly popular module really just that bad? Are there any alternate ES6 transpilers out there written with compiler-grade rigor and comprehensive test suites that would be safe to use?
The transform you're looking for is http://babeljs.io/docs/plugins/transform-object-rest-spread/, which is not part of babel-preset-es2015, but is part of babel-preset-stage-2, or you can install it by itself.
Here's what I discovered.
A preset is a collection of transform plugins, all versioned with ^. It seems that as these transform versions are bumped, new version of the preset are not published. It's not a good source for a curated set of plugins that are known to work together. So in my case, despite the fact that I had the latest babel-preset-es2015, and a new version of that didn't exist, it didn't mean I had all the latest plugins as well.
Forcing newer versions of the constituent plugins fixed the issue for me. The preset version tells you nothing useful apparently. If you don't have a direct dependency on the plugins themselves, the only way to force them to update is to force a reinstall of the preset.
I had the same ES6 rest parameter problem as Jacob had. I followed yaycmyk's advice and did npm install babel-preset-stage-2 and then changed my gulp file from
pipe(babel({ presets: ['es2015'] })) to
pipe(babel({ presets: ['es2015', 'stage-2'] })) and on my merry way I went.
I'm new to Gulp and the concept of task runners. I am wanting to write some javascript using es6 and have gulp run it through jscs, jshint and finally use babel to convert it to es5.
The part I'm confused about is the order I should have these tasks in my gulp pipeline. If I run jshint first I get warnings about how I can't use let and arrow functions. However, if I convert my code using babel first the babel output then fails validation as well.
What I'm looking for is a correct way of ordering my gulp task so it validates and converts my code to es5.
This is my current gulp task.
gulp.task('js-validation', function() {
$.util.log('**Starting js validation**');
return gulp
.src(config.alljs)
.pipe($.print())
.pipe($.jshint())
.pipe($.jscs())
.pipe($.babel())
.pipe($.jshint.reporter('jshint-stylish', {verbose: true}))
.pipe($.jshint.reporter('fail'))
.pipe(gulp.dest(config.temp));
});
This work for me:
.pipe(jshint({
esnext: true
}))
First, if possible, consider moving to ESLint; I'm not saying that because it's a subjective opinion, I'm saying that because it's modular and supports ES6, and even React+JSX if that's where you want to go with it.
You aren't going to have a lot of luck with JSHint, yet, if ES6 is where you're going.
If/when I'm wrong, please let me know, but I believe they have yet to replace their parser, to support all of ES6, and unless you're going to include the entirety of the browser polyfill+library in the pipeline (just for sake of having no missing methods, for validate to work), you may well be at a loss, here.
With ESLint in place, you could use the following config options (in .eslintrc, in the package.json, et cetera), to get ES6 support:
{
"env": {
"browser": true,
"node": true,
"es6": true
},
"ecmaFeatures": {
"modules": true,
"jsx": true
}
}
Of course, if you don't need node globals, JSX or ES6 modules, feel free to rip those out.
The one other caveat there is that ESLint has no support for ES7 (ES2016), yet (but will, when it's standardized).
So array/generator comprehensions, async/await, trailing commas in function argument lists, et cetera, are not supported and will cause explosions.
There is a babel-eslint version of eslint which will validate these, if that's your requirement.
You can put that in place by installing "babel-eslint" and then in your eslint config, setting { "parser": "babel-eslint" } to the root object, along with all of your other config preferences.
But typically, you would lint the code that you are putting into the system, pre-compile, using ESLint and Babel:
// ...
.pipe( eslint() )
.pipe( babel() )
// ...
To lint the source code (rather then the compiled code) you have to call the linter before babel, so the order is correct.
However, you have to use a linter that really understands ES6. With JSHint, you have to set the esnext option, but I'm not sure whether it supports all ES6 features. I recommend to have a look at eslint with babel-eslint instead.
Instead of JSHint, you can use ESLint, which will have support for numerous ES6 functions:
http://eslint.org/docs/user-guide/configuring
You are correct that you want your linting to occur prior to transpilation, also.
gulp.task('jshint', function () {
gulp.src('js/**/*.js')
.pipe(cache('jshint'))
.pipe(jshint({esnext:true}))
.pipe(jshint.reporter('default'));
});
.pipe(jshint({esnext:true}))
You have the correct order, but as suggested from other answers to use ESLint. You should also have a function to handle errors when linting. Here is my gulpfile.js (not a perfect example, but it's working for me):
const gulp = require("gulp"),
babel = require("gulp-babel"),
eslint = require("gulp-eslint");
gulp.task("babel", () => {
gulp.src("src/*.js")
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.failAfterError())
.on("error", onError) // handle error for eslint
.pipe(babel())
.on("error", onError) // handle error for babel
.pipe(gulp.dest("dist"));
});
gulp.task("watch", () => {
process.chdir(process.env.INIT_CWD);
gulp.watch("src/*.js", ["babel"]);
});
// ignore if error is from babel, eslint error message is enough
if (err.plugin != "gulp-babel" && err.message) {
console.log("Message: ", err.message);
}
I am trying to run Karma-babel-preprocessor and a straight forward ES6 generator:
//require('babel/polyfill');
describe("how Generators work", function() {
it("will allow generator functions", function() {
/*function * numbers() {
yield 1;
yield 2;
yield 3;
};*/
let numbers = {
[Symbol.iterator]:function*(){
yield 1;
yield 2;
yield 3;
}
}
let sum = 0;
for(n of numbers){
sum += n;
}
expect(sum).toBe(6);
});
});
From this I generated my test files (ES6 => ES5) with babel:
babel src --watch --out-dir tests
Then I run karma start I get error:
ReferenceError: regeneratorRuntime is not defined".
Relevant bits in karma.conf.js:
// list of files / patterns to load in the browser
files: [
'test-main.js',
{pattern: 'tests/*.js', included: true}
],
// list of files to exclude
exclude: [
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'src/*.js': ['babel']
},
'babelPreprocessor': {
options: {
sourceMap: 'inline'
},
filename: function(file) {
return file.originalPath.replace(/\.js$/, '.es5.js');
},
sourceFileName: function(file) {
return file.originalPath;
}
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
Full project on github
I am able to use many ES6 features including arrows. Just no go on Generators.
Node js Env - updated December 2015
This question has already been answered, please see accepted answer UNLESS running within NodeJS environment.
If like myself, you had the same error message: 'ReferenceError: regeneratorRuntime is not defined' but were running Babel within a NodeJS environment, then simply doing the following will likely solve your problem:
npm install babel-polyfill --save
Then insert the following require statement towards the top of the affected module to obtain required (generator) behaviour:
require("babel-polyfill");
This should be all you need, just importing the module adds required polyfill behaviour at runtime.
Similar to the post by arcseldon, I was running Babel within a NodeJS environment and getting the same error message 'ReferenceError: regeneratorRuntime is not defined'. While installing babel-polyfill does work, I went with #babel/plugin-transform-runtime instead.
#babel/plugin-transform-runtime
It needs to be installed in two ways ... first as a dev dependency:
npm install --save-dev #babel/plugin-transform-runtime
and second as a production dependency:
npm install --save #babel/runtime
And then there needs to be one simple addition to your .babelrc file:
{
"plugins": ["#babel/plugin-transform-runtime"]
}
These additions give ES6 authoring functionality without the ReferenceError.
While I'm taking a different approach** to using Karma with Babel in my project, I suspect you're having the same problem I was: the Babel polyfill is not being loaded, and so you're not getting the functionality it supports (including the custom regenerator runtime that Babel uses to make generators work).
One approach would be to find a way to include the polyfill, perhaps by feeding it to Karma via the files array:
files: [
'path/to/browser-polyfill.js', // edited: polyfill => browser-polyfill per P.Brian.Mackey's answer
...
An alternate approach may be to use Babel's runtime transformer [edit: on rereading the docs, this will not work unless you then browserify/webpack/etc. to process the require() calls created by the transformer]; per its docs,
The runtime optional transformer does three things:
Automatically requires babel-runtime/regenerator when you use generators/async functions.
Automatically requires babel-runtime/core-js and maps ES6 static methods and built-ins.
Removes the inline babel helpers and uses the module babel-runtime/helpers instead.
I have no experience with this, but I suspect you would do so by including the optional: ['runtime'] option from the Babel docs in your babelPreprocessor config, viz.:
'babelPreprocessor': {
options: {
optional: ['runtime'], // per http://babeljs.io/docs/usage/options/
sourceMap: 'inline'
},
...
(** I'm currently using jspm + jspm-karma + some config to get the Babel polyfill to load in SystemJS; ask if relevant and I'll expound.)
I modified karma.conf.js to add browser-polyfill as mentioned in the Docs Link:
files: [
'node_modules/babel/browser-polyfill.js',
'test-main.js',
{pattern: 'tests/*.js', included: true}
],
After this modification, the following unit test works in Karma:
describe("how Generators work", function() {
it("will allow generator functions", function() {
/*function* numbers(){
yield 1;
yield 2;
yield 3;
};*///Simplified syntax does not work
let numbers = {
[Symbol.iterator]:function*(){
yield 1;
yield 2;
yield 3;
}
}
let sum = 0;
for(let num of numbers){
sum += num;
}
expect(sum).toBe(6);
});
});
If you use React, adding polyfills from create-react-app worked for me.
yarn add --dev react-app-polyfill
Then add the following lines to webpack.config.js
entry: {
app: [
'react-app-polyfill/ie9', // Only if you want to support IE 9
'react-app-polyfill/stable',
'./src/index.jsx',
],
},
See more examples on the react-app-polyfill GitHub page.