Using native ECMAScript modules in Babeljs config - javascript

I'm very, very confused about using babel config with native ECMAScript modules, and "type": "module" set in package.json. As far as I understand Babel docs (here, under "Supported file extensions"), its should be possible. But if I try with config like this:
const config = () => {
const presets = [
"#babel/preset-react",
[
"#babel/preset-env",
{
bugfixes: true,
useBuiltIns: "usage",
corejs: { version: "3.6", proposals: true },
},
],
];
const plugins = ["#babel/plugin-transform-runtime"];
return { presets, plugins };
};
export default config;
I get Error while loading config - You appear to be using a native ECMAScript module configuration file, which is only supported when running Babel asynchronously.
This is expected as the said docs states that "native ECMAScript modules are asynchronous". Alas, sprinkling the above config with async / await doesn't solve the problem. I'm running babel through parcel - is this a issue with parcel? Did I misunderstood the docs? I really appreciate if someone could clarify it for me.

I had a similar problem, and reading Babel site, I came to conclusion that whatever is using your babel config is not calling it asynchronously. In my case it was jest 26.
I got around the problem by changing the config to a json file - babel.config.json.
Other people have changed their config file to a commonjs file - babel.config.cjs, then you will need to change your config file to be commonjs, i.e. to use module.exports = {rest of your config}

I think the problem is that your package.json says you are using ES6 modules, but your Babel config is using module.exports which is CommonJS (not ES6 modules).
Rename babel.config.js to babel.config.cjs

Related

Where should I insert '#babel/polyfill' into?: main.js or each modules [duplicate]

I just started to use Babel to compile my ES6 javascript code into ES5. When I start to use Promises it looks like it's not working. The Babel website states support for promises via polyfills.
Without any luck, I tried to add:
require("babel/polyfill");
or
import * as p from "babel/polyfill";
With that I'll get the following error on my app bootstrapping:
Cannot find module 'babel/polyfill'
I searched for the module but it seems I'm missing some fundamental thing here. I also tried to add the old and good bluebird NPM but it looks like it's not working.
How to use the polyfills from Babel?
This changed a bit in babel v6.
From the docs:
The polyfill will emulate a full ES6 environment. This polyfill is automatically loaded when using babel-node.
Installation:
$ npm install babel-polyfill
Usage in Node / Browserify / Webpack:
To include the polyfill you need to require it at the top of the entry point to your application.
require("babel-polyfill");
Usage in Browser:
Available from the dist/polyfill.js file within a babel-polyfill npm release. This needs to be included before all your compiled Babel code. You can either prepend it to your compiled code or include it in a <script> before it.
NOTE: Do not require this via browserify etc, use babel-polyfill.
The Babel docs describe this pretty concisely:
Babel includes a polyfill that includes a custom regenerator runtime
and core.js.
This will emulate a full ES6 environment. This polyfill is
automatically loaded when using babel-node and babel/register.
Make sure you require it at the entry-point to your application, before anything else is called. If you're using a tool like webpack, that becomes pretty simple (you can tell webpack to include it in the bundle).
If you're using a tool like gulp-babel or babel-loader, you need to also install the babel package itself to use the polyfill.
Also note that for modules that affect the global scope (polyfills and the like), you can use a terse import to avoid having unused variables in your module:
import 'babel/polyfill';
For Babel version 7, if your are using #babel/preset-env, to include polyfill all you have to do is add a flag 'useBuiltIns' with the value of 'usage' in your babel configuration. There is no need to require or import polyfill at the entry point of your App.
With this flag specified, babel#7 will optimize and only include the polyfills you needs.
To use this flag, after installation:
npm install --save-dev #babel/core #babel/cli #babel/preset-env
npm install --save #babel/polyfill
Simply add the flag:
useBuiltIns: "usage"
to your babel configuration file called "babel.config.js" (also new to Babel#7), under the "#babel/env" section:
// file: babel.config.js
module.exports = () => {
const presets = [
[
"#babel/env",
{
targets: { /* your targeted browser */ },
useBuiltIns: "usage" // <-----------------*** add this
}
]
];
return { presets };
};
Reference:
usage#polyfill
babel-polyfill#usage-in-node-browserify-webpack
babel-preset-env#usebuiltins
Update Aug 2019:
With the release of Babel 7.4.0 (March 19, 2019) #babel/polyfill is deprecated. Instead of installing #babe/polyfill, you will install core-js:
npm install --save core-js#3
A new entry corejs is added to your babel.config.js
// file: babel.config.js
module.exports = () => {
const presets = [
[
"#babel/env",
{
targets: { /* your targeted browser */ },
useBuiltIns: "usage",
corejs: 3 // <----- specify version of corejs used
}
]
];
return { presets };
};
see example: https://github.com/ApolloTang/stackoverflow-eg--babel-v7.4.0-polyfill-w-core-v3
Reference:
7.4.0 Released: core-js 3, static private methods and partial
application
core-js#3, babel and a look into the future
If your package.json looks something like the following:
...
"devDependencies": {
"babel": "^6.5.2",
"babel-eslint": "^6.0.4",
"babel-polyfill": "^6.8.0",
"babel-preset-es2015": "^6.6.0",
"babelify": "^7.3.0",
...
And you get the Cannot find module 'babel/polyfill' error message, then you probably just need to change your import statement FROM:
import "babel/polyfill";
TO:
import "babel-polyfill";
And make sure it comes before any other import statement (not necessarily at the entry point of your application).
Reference: https://babeljs.io/docs/usage/polyfill/
First off, the obvious answer that no one has provided, you need to install Babel into your application:
npm install babel --save
(or babel-core if you instead want to require('babel-core/polyfill')).
Aside from that, I have a grunt task to transpile my es6 and jsx as a build step (i.e. I don't want to use babel/register, which is why I am trying to use babel/polyfill directly in the first place), so I'd like to put more emphasis on this part of #ssube's answer:
Make sure you require it at the entry-point to your application,
before anything else is called
I ran into some weird issue where I was trying to require babel/polyfill from some shared environment startup file and I got the error the user referenced - I think it might have had something to do with how babel orders imports versus requires but I'm unable to reproduce now. Anyway, moving import 'babel/polyfill' as the first line in both my client and server startup scripts fixed the problem.
Note that if you instead want to use require('babel/polyfill') I would make sure all your other module loader statements are also requires and not use imports - avoid mixing the two. In other words, if you have any import statements in your startup script, make import babel/polyfill the first line in your script rather than require('babel/polyfill').
babel-polyfill allows you to use the full set of ES6 features beyond
syntax changes. This includes features such as new built-in objects
like Promises and WeakMap, as well as new static methods like
Array.from or Object.assign.
Without babel-polyfill, babel only allows you to use features like
arrow functions, destructuring, default arguments, and other
syntax-specific features introduced in ES6.
https://www.quora.com/What-does-babel-polyfill-do
https://hackernoon.com/polyfills-everything-you-ever-wanted-to-know-or-maybe-a-bit-less-7c8de164e423
Like Babel says in the docs, for Babel > 7.4.0 the module #babel/polyfill is deprecated, so it's recommended to use directly core-js and regenerator-runtime libraries that before were included in #babel/polyfill.
So this worked for me:
npm install --save core-js#3.6.5
npm install regenerator-runtime
then add to the very top of your initial js file:
import 'core-js/stable';
import 'regenerator-runtime/runtime';

Webpack's UglifyJsPlugin throws error with Node modules containing let

This is the relevant code (I'm using Vue.js' Webpack official template):
.babelrc:
"presets": [
"babel-preset-es2015",
"babel-preset-stage-2",
]
webpack.prod.config.js
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
drop_console: shouldDropConsole
},
sourceMap: true
}),
This is the error I get when I do npm run build:
ERROR in static/js/vendor.a6271913414e87e123c2.js from UglifyJs
Unexpected token: name (_months)
[./node_modules/calendar-js/index.js:56,0][static/js/vendor.a6271913414e87e123c2.js:90602,6]
This is the offending line:
let _months = MONTHS;
(If I replace all the let's to vars the project is built without problems. And the const's don't seem to bother Webpack/UglifyJS.)
Do I need to configure something so that Webpack/UglifyJS build node modules containing let's? (The let's in my actual project don't give me problems.)
This could be because you might be using an older version of node which does not support es6 syntax.
let, const, arrow functions etc. are part of es6 syntax. To know more follow this link http://es6-features.org/
You might need the older version of node for your other projects so install nvm. NVM is a node version manager which will help you to switch between node versions easily. Follow the link for documentation and installation process https://github.com/creationix/nvm
Node v6+ supports ES6 syntax try upgrading to that.
UPDATE
On the comments of this answer, it's confirmed that it was not a version issue and got resolved by following this GitHub issue thread https://github.com/joeeames/WebpackFundamentalsCourse/issues/3.
Peace!

How is the global promise polyfill implemented in webpack v2?

I'd like to better understand the differences between how promises are implemented in webpack. Normally, blissful ignorance was enough to get by as I mostly develop apps, but I am definately a little confused in how to properly develop a plugin/tool/lib.
In creating apps the two following approaches never caused any issues; I guess mostly cause it didn't matter
webpack.config.js - using babel-polyfill as an entry point
module.exports = {
entry: {
foo: [
'core-js/fn/promise', <-- here
'./js/index.js'
]
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader'
}
]
}
}
Q: In this approach, since it's a polyfill it modifies the global Promise?
webpack config - shimming using webpacks provide plugin
module.exports = {
entry: './js/index.js',
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader'
}
]
},
plugins: [
new webpack.ProvidePlugin({
Promise: 'es6-promise' <-- here
})
]
};
Q: Does this mean that the Promise is a module only specific to the webpack bundling process? Does the transpiled ES5 code have a local copy or es6-promise? Does it patch the global Promise?
In regards to creating a jquery plugin/tool/lib which is using babel for transpilation...
webpack.config.js - using babel-plugin-transform-runtime
module.exports = {
entry: {
foo: [
'./js/start.js'
]
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader'
}
]
}
}
.babelrc
{
"presets": [ "es2015" ],
"plugins": ["transform-runtime"] <--here
}
start.js
require('babel-runtime/core-js/promise').default = require('es6-promise'); <--here
require('plugin');
Q: This aliases the es6-promise to the babel-runtime promise and is not global but only local to the tool?
Polyfill in webpack entry
entry: ['core-js/fn/promise', './index.js']
This has the same effect as if you imported it at the top of your entry point.
In this approach, since it's a polyfill it modifies the global Promise?
Yes, this polyfill changes the global Promise. Calling it a polyfill usually means that it patches the global built-ins, although this is not strictly adhered to. If they don't change existing APIs but only provide the functionality, they are sometimes called Ponyfills.
Webpack shimming with ProvidePlugin
new webpack.ProvidePlugin({
Promise: 'es6-promise'
})
The ProvidePlugin will import the configured module at the beginning of the module that uses it when the corresponding free variable is found. A free variable is an identifier that has not been declared in the current scope. Global variables are free variables in all local scopes.
Whenever a free Promise is encountered, webpack will add the following to the beginning of the module:
var Promise = require('es6-promise');
Does this mean that the Promise is a module only specific to the webpack bundling process?
That is correct, because ProvidePlugin is webpack specific and it's very unlikely that any other tool will respect any webpack settings.
Does the transpiled ES5 code have a local copy or es6-promise?
As with any other module, it is included once by webpack and all imports refer to that module.
Does it patch the global Promise?
It will only modify the global Promise if the imported module does it explicitly. The es6-promise you're using, does not patch the global by default as shown in Auto-polyfill.
Babel transform runtime
{
"plugins": ["transform-runtime"]
}
The babel-plugin-transform-runtime uses core-js to provide missing functionalities like Promise. As you will recall, I said that core-js modifies the global Promise. This is not true for this case, because babel uses the version that doesn't pollute the global namespace, which is in core-js/library as mentioned in the core-js README. For example:
const Promise = require('core-js/library/fn/promise');
Babel will import the core-js Promise and replace Promise with the imported variable. See also the example in babel-plugin-transform-runtime - core-js aliasing. This is essentially the same thing as webpack's ProvidePlugin except that babel does not bundle up the modules, so it's just adding the import.
This aliases the es6-promise to the babel-runtime promise and is not global but only local to the tool?
It is not global because it's just a module. Babel takes your JavaScript and outputs some other JavaScript where the configured features are transpiled to ES5. You will run or bundle the resulting JavaScript and it's practically the same as if you had written ES5 in the first place.
require('babel-runtime/core-js/promise').default = require('es6-promise');
With that you modify the export and therefore the modules will use es6-promise instead. But overwriting an export is not a good idea, especially since the imports of ES modules are immutable in the spec. Babel is currently not spec-compliant in that regard. For more details see Making transpiled ES modules more spec-compliant.
Which one should you use?
It depends on what you're doing. Apart from the difference of whether they change globals or not, you can choose whichever you prefer. For instance using babel's transform runtime allows you to use it with any tool that uses babel, not just webpack.
For a library
None.
Leave the polyfill to the application developer. But you might mention that it depends on a certain feature and when the user wants to use the library in an environment that doesn't support the feature, they have to polyfill it. It's also fairly reasonable to assume that Promises are widely supported and if an application targets older environments, they will very likely have polyfilled it already. Keep in mind that this doesn't mean that you shouldn't transpile new features / syntax. This is specifically for new functionality like Promise or String.prototype.trimLeft.
For a tool
That also depends on your definition of a tool. Let's assume a tool is a piece of software that is used by developers (e.g. webpack, eslint, etc.). In that case it is exactly the same as for any app, at the end of the day it's just another app but only targeting developers. Specifically speaking about command line tools, you should decide what minimum Node version you want to support and include anything that is needed for that, you can specify that in your package.json in the engines field.
For a plugin
Plugin is a very broad term and can be anything between a library and an app. For example a webpack plugin or loader should work as is, whereas a jQuery plugin will be part of a web app and you should treat it as a library (they should probably be called library instead of plugin). Generally you want to match the guidelines of whatever you're extending. Have a look at it and see what they are targeting. For example webpack currently supports Node verions >=4.3.0, so your plugin should too.

vscode dynamic es6 module import error

vscode does not seems to support es6 import when used as a function to load dynamic chucks.
import is actually supported by webpack 2 + babel.
There are no issue with normal es6 imports or when using async functions.
Eslint was also reporting an error until I integrated babel-eslint.
I am using vscode version 1.9.1
I tried using a jsconfig.json but it did not change anything
{
// See http://go.microsoft.com/fwlink/?LinkId=759670
// for the documentation about the jsconfig.json format
"compilerOptions": {
"target": "es2017"
},
"exclude": [
"node_modules",
"bower_components",
"jspm_packages",
"tmp",
"temp"
]
}
I don't think dynamic imports are part of ES6, and they aren't supported by TypeScript yet (which drives the JS language service) so I think you'll have to use require.
You can set "javascript.validate.enable": false in the preferences and use another syntax validation (for example https://github.com/flowtype/flow-for-vscode).
Dynamic imports are now supported by TypeScript (v2.4.x) and VSCode since June 2017 release (v1.14.x).

Babel / Rollup errors transpiling and bundling ES2017

I'm getting an error transpiling ES2017 JavaScript (specifically async/await functions) into ES5 using Rollup and Babel:
Error transforming \src\index.js with 'babel' plugin: Options
{"modules":false} passed to
\node_modules\babel-preset-es2017\lib\index.js which does not accept
options.
My .babelrc file:
{
"presets": [
"es2017"
],
"plugins": [
"transform-runtime"
],
"comments": false
}
Naturally, the error goes away if I change the presets from es2017 to es2015 and comment out the async/await code.
Note that while the application uses ES2017 features (i.e. async/await), it is published as an NPM package (as an ES6 (ES2015) module) that is later transpiled to generic ES5 (ES2009).
How do I get past this error and get my ES2017 happily transpiling to ES5?
It seems that the actual configuration you provided is:
{
"presets": [
"es2017", {modules: false}
],
"plugins": [
"transform-runtime"
],
"comments": false
}
You should remove the {modules: false} option for babel-preset-es2017, which accepts none, as it applies to babel-preset-es2015.
Target es2015 in your .babelrc file:
"presets": [
"es2015"
],
and preprocess the es2017 features by first adding the Rollup Async functions plugin, which:
replaces async functions with generator functions that can run in
modern browsers or in most versions of node.js during bundling
Add to your project:
npm install --save-dev rollup-plugin-async
And then insert async preprocessing to your rollup.config.js plugins array, before babel():
import async from 'rollup-plugin-async';
...
plugins: [
async(),
babel(babelrc())
],
The key insight was found in this Rollup Github issues thread, wherein #Victorystick confirms that (as of Oct 10, 2016):
Rollup currently only targets ES6, although it'll likely get extended
to ES7 shortly. For now, if you use >ES6 features, they'll need to be
transpiled for Rollup to process them
As async/await are likely to be standardized in the next version of ECMAScript, ES2017 (aka ES8), you'll need an extra plugin to process them before Babel runs.
NOTE:
YMMV, but I later received AsyncHelper not found run time Errors using rollup-plugin-async on Windows 7, so my solution was to (npm uninstall --save-dev rollup-plugin-async and) rollback use of async/await in favor of ES6 Promises throughout my codebase.

Categories