This guide is part of The Complete Guide to ES6 with Babel 6 series. If you’re having trouble upgrading to Babel 6, start with Six Things You Need To Know About Babel 6.
The Babel CLI is great for compiling ES6 to ES5 on a file-by-file basis. However, when Babel encounters an import
statement, it outputs a require
call – which won’t get you very far in the browser.
To make our Babel output browser friendly, we’ll need to bundle it. My favourite tool for this is Webpack, and as it happens, Webpack has great Babel support through babel-loader
…
Installing packages
Before we start — if you’re currently using Babel 5, first remove its packages from your project’s package.json
file and then npm uninstall
them — babel
, babel-core
, babel-loader
, etc.
Build-time packages
Once you’re ready, start by installing the babel-core
and babel-loader
packages:
npm install babel-core babel-loader --save-dev
Next, you’ll need to install any presets and plugins you need. Start with babel-preset-es2015
– Babel’s collection of ES6 transforms. If you’re using JSX, you’ll also want babel-preset-react
. And if you want to play with fire, you can add babel-preset-stage-0
for access to ES7 decorators, async
/await
, etc.
# For ES6/ES2015 support
npm install babel-preset-es2015 --save-dev
# If you want to use JSX
npm install babel-preset-react --save-dev
# If you want to use experimental ES7 features
npm install babel-preset-stage-0 --save-dev
Runtime support
Babel can’t support all of ES6 with compilation alone — it also requires some runtime support. In particular, the new ES6 built-ins like Set, Map and Promise must be polyfilled, and Babel’s generator implementation also uses a number of runtime helpers. Given your app doesn’t have to share a JavaScript environment with other apps, you’ll be ok to use babel-polyfill
to handle this:
npm install babel-polyfill --save
Babel also bakes a number of smaller helpers directly into your compiled code. This is OK for single files, but when bundling with Webpack, repeated code will result in a heavier file size. It is possible to replace these helpers with calls to the babel-runtime
package by adding the transform-runtime
plugin:
npm install babel-runtime --save
npm install babel-plugin-transform-runtime --save-dev
Configuring babel-loader
This guide assumes you know the basics of Webpack. To get up to scratch, read Webpack Made Simple: Building ES6 & LESS with autorefresh.
With Webpack, running your JavaScript and JSX through Babel is a simple as adding a loaders
entry for babel-loader
to your webpack.config.js
:
module: {
loaders: [
{
loader: "babel-loader",
// Skip any files outside of your project's `src` directory
include: [
path.resolve(__dirname, "src"),
],
// Only run `.js` and `.jsx` files through Babel
test: /\.jsx?$/,
// Options to configure babel with
query: {
plugins: ['transform-runtime'],
presets: ['es2015', 'stage-0', 'react'],
}
},
]
}
In the above configuration, I’ve told Webpack to only apply Babel to files in my src
directory. This speeds up the build by making sure that Babel isn’t applied to your entire node_modules
directory – after all, NPM modules should be published as ES5. You could achieve the same effect with exclude
:
exclude: [
path.resolve(__dirname, "node_modules"),
],
The query
object contains the options passed to Babel. I’ve assumed that you’ve installed every package from the previous section. If you don’t want to extract Babel’s runtime helpers or don’t want React or experimental ES7 support, just leave out the appropriate string from query
.
.babelrc
Note that you could omit the presets
option passed to Webpack’s query
, and instead add a .babelrc
file:
{
"presets": ["es2015", "react"]
}
However, my recommendation is to keep the options to your build process in the build process configuration. It isn’t unheard of to want different configuration for tests, task runners, etc.
In Babel 5, the above query
object would have been equivalent to the following:
query: {
stage: 0,
externalHelpers: true,
},
The difference is that with Babel 5, you didn’t need to install packages for each of these options.
Entry point scripts
As discussed above, Babel requires some helper code to be run before your application. To achieve this, add the polyfill to the entry
section of your webpack.config.js
:
entry: [
// Set up an ES6-ish environment
'babel-polyfill',
// Add your application's scripts below
'./src/main',
],
In Babel 5, you needed two entry point scripts. Both were available in the babel-core
package:
'babel-core/external-helpers',
'babel-core/polyfill',
Examples
For a simple example of a Webpack-bundled app configured with Babel 6, see webpack-black-triangle.
For a more full-featured example of a React-based app bundled with Webpack, see the Unicorn Standard Starter Kit.
More ways to Babel
Now that you’ve got your app building with ES6, why not test it with ES6 too? And write your libraries with it. And write your tasks with it…
- Testing with Mocha and Babel’s
register
script - Transforming NPM Packages with the Babel 6 CLI
- Running tasks with
gulpfile.babel.js
Keeping up to date
So you’ve made Babel 6 work. But the JavaScript world moves fast — how long will it stay working?
If trying to keep up feel overwhelming, I’d love to help! Just subscribe to my free Newsletter to receive news and guides on the most important tools for people making small apps with React. And in return for your e-mail, you’ll immediately receive three bonus print-optimised PDF cheatsheets – on React (see preview), ES6 and JavaScript promises. All for free!
I will send you useful articles, cheatsheets and code.
One more thing – I love hearing your questions, offers and opinions. If you have something to say, leave a comment or send me an e-mail at james@jamesknelson.com. I’m looking forward to hearing from you!
I didn’t see ‘babel-runtime’ in your entry for your ‘unicorn-standard/starter-kit’ project.
wondering why not?
Thanks for mentioning this.
So it turns out that you need to have
babel-runtime
installed innode_modules
, but don’t need it inentry
. I’ll update the guide to mention this.Following the steps I made a async/await function in my entry file. But it pops out with this error:
“`
ERROR in ./~/babel-runtime/regenerator/runtime.js
Module not found: Error: Cannot resolve module ‘babel-runtime/helpers/typeof’ in /Users/AJ/Desktop/WebLab/node_modules/babel-runtime/regenerator
@ ./~/babel-runtime/regenerator/runtime.js 15:15-54
“`
Some help?
This seems to be a problem with the versions of Babel which have been released since I wrote the article (a whopping 3 days ago).
If you *need* async, you’re going to have a bit of trouble at the moment, because it doesn’t play nice with the latest version’s babel-plugin-transform-runtime, *or* with the register script. Unfortunately, the best course of action is probably to just wait for the Babel team to fix this.
Any update on this? I’m not able to transform generators into es5 even now!
Same, I figured I’d give babel 6 a few months to iron out the bugs but it’s still the most broken npm release I’ve ever seen.
Thanks James for this excellent guide 🙂
I’ve temporarily had to disable transform-runtime due to https://github.com/babel/babel/issues/2954
I’m then having an issue with IE8 which can’t handle the compiled code’s use of the word ‘default’
e.g. in the line:
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
Would this be resolved by having transform-runtime? Or is IE8 not supported? (Googling suggests it is supported, but I can’t find a solution to the issue I’m experiencing).
I doubt it would be solved by transform-runtime, somehow. But I’ve not had the misfortune of needing to target IE8, so I can’t help you, sorry.
try
babel-plugin-transform-es3-property-literals
Hello James,
I’ve followed the entire tutorial and still when I try to use decorators like this:
@connect((state) => {
return {
};
})
export default class Movie extends Component {
I get the following error in the terminal:
“Decorators are not supported yet in 6.x pending proposal update”
Thank you upfront.
That’s bizarre. I’m using decorators in a number of places, so I doubt they’re plain not supported. Possibly they’re not supported in combination with something else. Perhaps try looking in Babel’s GitHub issues?
Already went through Babel’s GitHub issues, no solution has yet fixed this.
Nevertheless, thank you for publishing great tutorials.
Have the same issue with decorators, but I use ‘stage-0’ and have used it before I decided to update my project deps.
Hey! First, thank you for your guide.
I’ve followed it and I get the following error in the terminal too:
“Decorators are not supported yet in 6.x pending proposal update”
You said “I’m using decorators in a number of places”.
How do you use decorators? For me it’s like that with Redux and React:
@connect(
state => ({
foo: state.foo,
})
)
class Indicator extends Component {
…
}
Thanks for your help.
Also found the same issue today.
Turns out I the decorators of mine which were working were in Babel 5 projects. An upgrade caused them to fail.
Babel 6 doesn’t support decorators yet. For more information, see this issue on the Babel 6 tracker
It seems that babel has internal issues with handling decorators – which does worry me personally – and it seems we must stick with 5.8 until babel 6 supports decorators.
It doesn’t have internal issues with them. It elected not to support them the old way because it doesn’t match with the new decorator spec…
https://phabricator.babeljs.io/T2645
Really helpful, thank you James.
Great writing.
I think you should add to your list:
1) Babel6 changes how export default xxx works. There was a hack in the babel5 that is removed in babel6 chat causes cold that was doing require(‘function_exported_as_default’)() to not work anymore. (let me know if you need more details on that).
2) that decorators were dropped in babel6, so e anyone using is out of luck with babel6 for now. (I’m working in a plugin to port decorators form 5 to 6, but in the mean time there is nothing.
Rafael I’d love more info on the exports default issue and how you’ve dealt with it. I’ve been using https://www.npmjs.com/package/babel-plugin-add-module-exports for my build code but unfortunately for my client code I test with karma and require plugin and these two plugins seem to conflict. Looks like I may have to go through all my client code and change syntax.
On another note generators have really strange behavior when using const and they will yell at you if you don’t. I added to phabricator issue here so please add on if you experience it as well https://phabricator.babeljs.io/T2843#68845
This whole Babel 6 upgrade has been an incredible nightmare:-((
Above is “rewire” plugin not “require” plugin
I am getting following error when I do npm run build
>npm run build
> @0.0.1 build
> babel-node tools/run build
build
[16:15:30] Starting ‘undefined’…
TypeError: object is not a function
at _callee$ (run.js:8:9)
at tryCatch ({path}\node_modules\babel-regenerator-runti
me\runtime.js:61:40)
at GeneratorFunctionPrototype.invoke [as _invoke] ({path}\node_modules\babel-regenerator-runtime\runtime.js:329:22)
at GeneratorFunctionPrototype.prototype.(anonymous function) [as next] ({path}\node_modules\babel-regenerator-runtime\runtime.js:94:21)
at step ({path}\tools\run.js:7:191)
at {path}\tools\run.js:7:423
at new Promise ({path}\node_modules\core-js\modules\es6.
promise.js:197:7)
at {path}\tools\run.js:7:99
at run (run.js:5:19)
at Object. (run.js:18:3)
hi,James
when i used babel-runtime to replace the helpers in my code like _extends ,_interopRequireDefault(obj) eg,but it does not work.what’s the reason?
this babel version i used:
“`
“babel-core”: “^6.10.4”,
“babel-eslint”: “^6.0.0”,
“babel-loader”: “^6.2.4”,
“babel-polyfill”: “^6.9.1”,
“babel-preset-es2015”: “^6.9.0”,
“babel-runtime”: “^6.6.1”,
“`
can you help me?thx
This was really helpful, thank you.
How to add css file loader?