standard babel presets requirements - javascript

In order to setup webpack + babel + react, I was told to include following in .babelrc:
"presets": ["latest", "stage-0", "react"]
I want to understand: why should I use babel presets, what do they allow me to do (apart from babel itself)? That's one question. Hope that's not opinion-based (in terms of stackoverflow), it's about how babel works.
As far as I read in the docs, preset-latest combines preset-es2015 + preset-es2016 + preset-es2017. As far as I understand, these are officially accepted features of upcoming ES versions and latest is a shorthand for choosing not only ES2015, but all future versions at one shot. The specs won't change, so it's stable enough to use in production.
But how about stage-0, stage-1, stage-2, stage-3 - do they represent features that are still unofficial proposals of upcoming ECMAScript versions or does that stand for something else? Babel docs is not clear about that. That's second question.
And finally, what is the difference between a plugin and a preset?

...why should I use babel presets, what do they allow me to do...
A Babel preset conveniently defines a group of Babel plugins so that you don't have to explicitly declare you want to use each of them under "plugins" in your .babelrc (or wherever you declare your config).
Take a look at the source code of the es2016 preset and you'll see what I mean... it simply exports an array of plugins: https://github.com/babel/babel/blob/master/packages/babel-preset-es2016/src/index.js
...(apart from babel itself)?
Babel itself is an interface for its plugins. It utilises a sibling program, babylon, a fork of acorn, that provides plugins a particular way of parsing, inspecting, and then manipulating the source code of your program, in order to add the features you want according to the plugins you use.
And finally, what is the difference between a plugin and a preset?
As discussed, a preset itself does not contain features, rather a list of plugins. Together these typically represent some related group of functionality. For example, the stage-0 preset will contain all plugins that implement features of proposals which are at stage zero of the process of submission defined by TC39, ECMAScript's "governing body".
You might have noticed that a preset is a JavaScript file instead of JSON. This is because the list of plugins that a preset defines can be derived from a configuration. Take a look at the env preset, for example: https://github.com/babel/babel-preset-env/blob/master/src/index.js
But how about stage-0, stage-1, stage-2, stage-3 - do they represent features that are still unofficial proposals of upcoming ECMAScript versions or does that stand for something else?
There are no "official" proposals. A proposal can be submitted by anyone. But if what you mean by official is whether the proposal is being seriously considered, that is determined by 1) what stage it is at in the process and 2) general consideration by the community of its worth as a new feature. However you should always take proposals with a pinch of salt in terms of whether they will be accepted, even at the last stage, as we have experienced with Object#observe, which was dropped at the very last minute.

I also didn't understand why "modules": false and why there is an "env" setting and that env has its own preset configuration.
Finally, I found this article What's in our .babelrc? explains it well, e.g
Secondly, we set modules to false to ensure that import statements are
left as is (opposed to transpiling them to require). We're doing this
to give Webpack the ability to statically analyze our code to produce
more efficient bundles.
Lastly, we have an environment specific override for Jest, our testing
framework of choice. Since Jest is run in node, we need to transpile
our imports to requires, and target whatever node runtime we're
currently working in.

Related

Enable destructuring objects rest properties with Babel?

I noticed that Babel wasn't transforming this:
function({ param, ...rest }) {}
This syntax is already supported in the latest popular browsers. However, according to Mozilla (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment), it's not supported in browsers as new as Edge 79 (released this year).
I tried manually enabling some Babel plugins, but it didn't work:
#babel/plugin-transform-destructuring
#babel/plugin-proposal-object-rest-spread
#babel/plugin-syntax-object-rest-spread
#babel/plugin-transform-parameters
Is it possible to transform this syntax with Babel? I need to do testing in older browsers, but I'm getting SyntaxError: invalid property id because of this.
Sometimes babel needs to combine more than one plugins to transform something. In this case I think you would need to combine at least 2 plugins:
#babel/plugin-transform-destructuring
#babel/plugin-transform-parameters
npx babel /path/to/yourFile plugins=#babel/plugin-transform-destructuring,#babel/plugin-transform-parameters
To make life easier, I would suggest to use preset-env instead which supports official ES changes (since it includes all official plugins for you):
npx babel /path/to/yourFile presets=#babel/preset-env

What do these core-js packages do? (fix-re-wks, task, iter-define, etc)

I have a lot of core-js packages included by Babel:
Some of these are pretty straight forward, e.g. es6.promise is obviously a promise polyfill, _object-create is obviously a Object.create polyfill.
I'd like to know what some of the bigger packages do. Specifically:
_fix-re-wks
_task
_iter-define
_redefine
_microtask
_invoke
This would help because I can stub out unneeded packages. E.g. I stubbed out symbol because I don't need the symbol methods, just the symbol constructor.
core-js is a modular standard library for JavaScript. It includes polyfills for ECMAScript up to 2019: promises, symbols, collections, iterators, typed arrays, many other features, ECMAScript proposals, some cross-platform WHATWG / W3C features and proposals like URL. You can load only required features or use it without global namespace pollution.
The pieces that you asked about, specifically:
_fix-re-wks
_task
_iter-define
_redefine
_microtask
_invoke
are internal parts of core-js like helpers and utils. See the directory for those here:
https://github.com/zloirock/core-js/tree/master/packages/core-js/internals

How to correctly configure experimental ECMAScript module so import/export can be used in Node.js

Time is flying and now the Node.js ver.10.0.0 have been out for the public. While I'm working with my MERN stack, frequently I have to switch between ES6 import/export syntax for the front-end (React.JS) and the CommonJS module.exports/require syntax in my Node/Express server-side. And I'm really wish the writing style could be unified in import/export shortly without using Babel and get it from the native support.
A good news is since the last year I read James' post on Medium addressing in the difficulty of implementing the ES6 module system in Node.js, the experimental ECMAScript Modules is now in stability 1 for a while, which means it could be enabled by --experimental-modules flag.
However, when I'm trying to use import/export syntax on Node, it is never working. For example, I have try this:
import fs from 'fs';
console.log(typeof fs.readFile);
The above code will throw me error:
(function (exports, require, module, __filename, __dirname) { import fs from 'fs';
^^
SyntaxError: Unexpected identifier
I'm really sure I have enabled the experimental flag calling as $node --experimental-modules, so what should I really do in order to kick the ES6 import/export experimental module working on my Node local server? What am I missed?
Added:
The reason why I want to try this experimental feature is to have consistent coding style in both front and back. And because it is now available natively, so I want to get ready to it. Once it have been reach in stage 2, I then can adapted to import/export quickly and have less pain.
But obviously, Google (ref) and AirBnb(ref) have different view points upon if we should use import/export syntax or not in their code-style guide. And based on Google, I'm still surprising that the semantics of ES6 import/export is not yet finalized while ECMA2019 is on its way. I'm just wonder when I can really use import/export in my project, or what is really need to be finalized?
Updated:
After pointed out by Jaromanda X, if I changed my file name to .mjs then it works. Originally I thought is the module to be loaded have to be named in .mjs extension, but it seems I was wrong. However, if that is the case, which means I will need to renamed all my project file in .mjs... and that is not appealing. Just wonder if there is a way to use this feature it in the traditional .js file? What should I configure?
Right at this point ECMAScript Module is not yet finalized in node.js v10, so it is not recommended to use it in the production environment. Since the use of import/export is standardized in the ECMAScript specification, so no matter how, it is important for node to provide such supports for the community.
The major differences between require() and ECMAScript Module is how node is going to handle its cache. Unlike require(), so far ECMAScript Module have no such API to manipulate the module cache. Also, it is unknown if it would be directly supported in the normal *.js files without a loader, so stay tuned.
The direction right now is try to introduce a new *.mjs file to explicitly showing it used the standard JS module (in accordance with ES6), but if one read the doc closely you actually could see it is also possible to specify your own extension match, including the use of the more traditional *.js. However, it required more configurations and the use of --loader flag to load the extra configuration.
There are 6 different formats are supported, including the one for modules written in C++ (it can also be loaded by require()), which is very powerful. In short, to use *.js file extension, the key setting in the loader is to have:
const JS_EXTENSIONS = new Set(['.js', '.mjs']);
And the loader function will need to check as if the file to be loaded is in JS_EXTENSIONS data set. Here, the key in the loader function is that it should return an object that have two properties: url and format, i.e.:
return {
url: (...), // e.g. './x?n=1234'
format: 'esm', // there are 5 + 1 dynamic formatting
};
It would be recommended to named the loader as custom-loader.mjs, and use it as $node --experimental-modules --loader ./custom-loader.mjs since it was demonstrated in the official doc. Once you have configured the loader correctly (based on what you need), then you should be able to use import/export as if you may used in a front-end react app in your node project.

Using async/await in Node 6 with Babel

I'm trying to configure Babel for Node v6.9.2. I want to use async/await constructs.
Because I'm new to Babel and all Node infrastructure, I confused how to configure it properly:
What preset should I use? Node is already implemented most of the ES6 features. So I don't want Babel to transpile features already supported by Node 6.9.x (arrow functions, new import mechanism etc) for performance reasons.
What plugins should I include so I can use async/await? There I also confused, because after some researching I found several plugins: syntax-async-functions, transform-async-to-generator and some more.
Example of .babelrc will help.
Thanks
What preset should I use?
You don't need to use any preset. Presets are just a collection of plugins which makes it easier to use if you want to transpile a set of features (for instance all ES2015 with preset-es2015). But when you want to transpile only a selection of these features, you only include the corresponding plugins.
What plugins should I include so I can use async/await?
Because Node 6 supports generators, you can use transform-async-to-generator with the following .babelrc:
{
"plugins": ["transform-async-to-generator"]
}
And of course you would need to add plugins if you need to transpile more unsupported features.
Alternative babel-preset-env
babel-preset-env automatically determines what plugins you need for the specified environment. This will not include any plugins that are not necessary. To specify your current Node version you would use this .babelrc:
{
"presets": [
["env", {
"targets": {
"node": "current"
}
}]
]
}
Short answer
Use Babel preset for Node 6.x:
https://www.npmjs.com/package/babel-preset-node6
Long answer
To see what ES feature is supported in a given Node version, see:
http://node.green/
For async/await support in particular, see:
http://node.green/#ES2017-features-async-functions
If you use Node v7.x (the current version) then you can use the --harmony flag and use async/await natively without transpilation.
Node v8.x (available as nightly builds) doesn't even need the --harmony flag for that.
But note that Node doesn't support import/export - to know why see:
javascript - Why is there a spec for sync and async modules?
Exporting Node module from promise result

Using the whitelist option with Babel's external-helpers

I'm trying to use Rollup with Babel's external-helpers. It works, but it's dropping a bunch of babel helpers which I don't even need, for example asyncGenerator.
The docs show a whitelist option but I can't get it to work
rollup.rollup({
entry: 'src/buttonDropdown.es6',
plugins: [
babel({
presets: ['react', ['es2015', { modules: false }], 'stage-2'],
plugins: [['external-helpers', { whitelist: ['asyncGenerator'] }]]
})
]
})
The above has no effect: all Babel helpers are still dropped into my resulting bundle.
What is the correct way of using this feature, and is there a full list of which helpers' names the whitelist array takes?
Or is there some other Rollup plugin I should be using with Rollup to automatically "tree shake" the babel external helpers.
Problem
The babel-plugin-external-helpers plugin is not responsible for injecting those dependencies in the final bundle.
The only thing it controls is that how the generated code will access those functions. For example:
classCallCheck(this, Foo);
// or
babelHelpers.classCallCheck(this, Foo);
It is needed so all rollup-plugin-babel needs to do is to inject babelHelpers in every module.
The documentation is misleading, the whitelist options is not on the external-helpers plugin. It's on the completely separate module and command line tool called babel-external-helpers, which is actually responsible for generating babelHelpers.
It's rollup-plugin-babel what is injecting babelHelpers. And does it using a trick to modularize the final code. It calls babel-external-helpers to generate the helpers, and ignores the whitelist parameter. See my issue requesting to expose an option.
This approach is correct, because rollup will tree-shake the unused helper functions. However some of the helpers (like asyncGenerator) are written in a way that is hard to detect if the initialization has any side effects, thus preventing removal during tree-shaking.
Workaround
I forked rollup-plugin-babel and created a PR which exposes the whitelist option of building babelHelpers in the plugin's options. It can be used this way:
require("rollup").rollup({
entry: "./src/main.js",
plugins: [
require("rollup-plugin-babel")({
"presets": [["es2015", { "modules": false }]],
"plugins": ["external-helpers"],
"externalHelpersWhitelist": ['classCallCheck', 'inherits', 'possibleConstructorReturn']
})
]
}).then(bundle => {
var result = bundle.generate({
format: 'iife'
});
require("fs").writeFileSync("./dist/bundle.js", result.code);
}).then(null, err => console.error(err));
Note that I didn't publish distribution version on npm, you will have to clone the git repo and build it using rollup -c.
Solution
In my opinion the right solution would be to somehow detect or tell rollup that those exports are pure, so can be removed by tree shaking. I will start a discussion about it on github after doing some research.
As I have found in this particular issue in the GitHub page.
The Babel member Hzoo suggests that
Right now the intention of the preset is to allow people to use it without customization - if you want to modify it then you'll have to
just define plugins yourself or make your own preset.
But still if you want to exclude a specific plugin from the default preset then here are some steps.
As suggested by Krucher you can create a fork to the undesirable plugin in the following way
First one is by forking technique
"babel": {
"presets": [
"es2015"
],
"disablePlugins": [
"babel-plugin-transform-es2015-modules-commonjs"
]
}
But if two or more people want to include the es2015-with-commonjs then it would be a problem.For that you have to define your own preset or extend the preset of that module.
The second method would involve the tree-shaking as shown in this article done by Dr. Axel Rauschmayer.
According to the article webpack2 is used with the Babel6.
This helps in removal of the unwanted imports that might have been used anywhere in the project in two ways
First, all ES6 module files are combined into a single bundle file. In that file, exports that were not imported anywhere are not exported, anymore.
Second, the bundle is minified, while eliminating dead code. Therefore, entities that are neither exported nor used inside their modules do not appear in the minified bundle. Without the first step, dead code elimination would never remove exports (registering an export keeps it alive).
Other details can be found in the article.
Simple implemetation is referred as here.
The third method involves creating your own preset for the particular module.
Creating aplugin and greating your own preset can be implemented according to the documentation here
Also as an extra tip you should also use babel-plugin-transforn-runtime
If any of your modules have an external dependancy,the bundle as a whole will have the same external dependancy whether or not you actually used it which may have some side-effects.
There are also a lot of issues with tree shaking of rollup.js as seen in this article
Also as shown in the presets documentation
Enabled by default
These plugins have no effect anymore, as a newer babylon version enabled them by default
- async-functions (since babylon 6.9.1)
- exponentiation-operator (since babylon 6.9.1)
- trailing-function-commas (since babylon 6.9.1)**
Also the concept of whitelisting and blacklisting the plugins has benn brilliantly explained by loganfsmyth here in this thread.
you can pass a whitelist option to specify specific transformations to run, or a blacklist to specific transformations to disable.
You cannot blacklist specific plugins, but you may list only the plugins you want, excluding the ones you do not wish to run.
Update :
According to this article here is an important update -
"The --external-helpers option is now a plugin. To avoid repeated inclusion of Babel’s helper functions, you’ll now need to install and apply the babel-plugin-transform-runtime package, and then require the babel-runtime package within your code (yes, even if you’re using the polyfill)."
Hope this may solve your problem
Hope it may help you.

Categories

Resources