Foreword: None of this is really set in stone. Feel free to argue and discuss. This is kinda a long issue so I added some gifs, I hear developers love that kind of stuff. This post is also a bit rough, figured it's better to get it out there ASAP, I'll improve it as more feedback is collected. Thanks everyone for your (potential) participation!
What is 6.0 going to look like?
No internal transformers. None. Zero. Zilch.
This will be coupled with the removal of the following options:
whitelist
blacklist
optional
loose
stage
All of these options are redundant when you're manually specifying transformers.
This dramatically reduces complexity in the transformer options. Right now there's way too many ways to change the state of a transformer. This will reduce friction and make it easier to reason about what's actually enabled.
There's also a major problem with including very experimental syntax/transformers in core. If a big change was made to their semantics it would be a breaking change and would require a major version bump of Babel. In the interest of stability and being reactive to changes in the standardisation process, it makes sense to version them independently.
When?
idk whenever
Where will all the current builtin transformers go?
Into userland plugins.
Wont this make it harder to use 6.x like we use 5.x?
Nope! 6.0 will also introduce the concept of presets. These are modules distributed via npm that specify a set of options (plugins etc) that will be merged into your main configuration.
Recommended
This means that the current ES6 to ES5 configuration would be distributed on npm via the babel-preset-es2015
package. It would have the index.js
of:
module.exports = {
plugins: [
require("babel-plugin-arrow-functions"),
require("babel-plugin-classes"),
...
]
};
To use this you'd just specify the following in your .babelrc
:
{
"plugins": ["preset-es2015"]
}
Community-specific
These can be even more specific. For example, the React community may have an official preset called babel-preset-react
that will have the following index.js
:
module.exports = {
plugins: [
require("babel-plugin-jsx"), // enables jsx syntax
require("babel-plugin-flow"), // enables flow syntax
require("babel-plugin-react-jsx"), // transforms jsx to flow
require("babel-plugin-flow-comments"), // transforms type annotations to flow
]
};
Just like the es2015
preset, this could be enabled with the following .babelrc
:
{
"plugins": ["preset-es2015", "preset-react"]
}
Isn't this a dramatic shift in focus?
Nope, this is a part of the long term vision for Babel and everything has been leading up to this (cue dramatic music). I bought this up in #568 back in January where I said:
Ideally I'd like for 6to5 to be a more general JavaScript of the future transpiler. 6to5 already supports Flow/JSX as well as experimental ES7 features so the name isn't even representative of the project in it's current state although it is in how it's widely used.
A JavaScript transpiler based on future standards and specifications, whether ECMAScript or not, I think is an extremely noble goal. I think there's a large use case for 6to5 beyond just ES6. Having an interopable platform to base code transformations on I think is a very powerful idea. This may mean exposing a very feature-rich core that contains transforms or even exposing a public API.
What about syntax extensions?
I'll be holding out on allowing arbitrary syntax extensions for the timebeing. If we're going to do them at all, we're going to do them right. No point half assing a very critical and fragile piece such as syntax extension.
But if there's no syntax extensions then how would existing transformer that enable special syntax work?
So, the syntax would be implemented in the Babel parser Babylon, the "syntax plugins" would just toggle some parser options via an API. So the actual parser code would still live in Babylon. It's a neat solution until we support syntax extensions properly.
What else is coming?
Plugin options (#1833)
Plugin options are something that sorely needed.
Specifying options
These can be specified via a multidimensional array. For example, say you have a babel-plugin-pancakes
plugin, you could specify options to it like so:
.babelrc
{
"plugins": [["pancakes", { "syrup": "maple" }]]
}
But how would you specify options for plugins defined in a preset? One possible way is to specify them in the options to a preset, so if you had a preset like:
node_modules/babel-preset-breakfast/index.js
module.exports = {
plugins: [require("babel-plugin-pancakes")]
};
Then you could specify the options for babel-plugin-pancakes
like so:
.babelrc
{
"plugins": [["preset-breakfast", {
"pancakes": { "syrup": true }
}]]
}
Accessing them from a plugin
export default function ({ Plugin }) {
return new Plugin("pancakes", {
visitor: {
ObjectExpression(node, parent, scope, state) {
if (state.options.syrup !== "maple") {
throw this.errorWithNode("No objects allowed unless I get maple syrup >:(");
}
}
}
});
}
Swappable parser
Add a parser
option to allow use of another Babel AST compatible parser.
Plugin hooks (#1485)
There's currently no good way to perform initialisation of a plugin. Currently you can use the Program
visitor to kinda simulate this, but those callbacks are only called once the traversal has already started. The introduce of a init
and post
method to a Plugin will make this make nicer:
export default function ({ Plugin }) {
return new Plugin("pancakes", {
init(state) {
// initialise
},
post(state) {
// teardown
}
});
}
Way to provide the entire dependency graph to Babel
Another thing that I want to provide is a way to give Babel your entire dependency graph. This will allow cross file transformations. It'll allow us to catch more bugs (such as importing something from another module that wasn't exported).
This also allows us to simplify code (thus making it more performant) with access to other files. For example, we can drop the module interop output if we know that a file imported is ES6.
Minification plugins
I believe we can do minification better (if not equivalent) as UglifyJS. With the potential type information available this has the potential to lead to huge wins. I've already written quite a few plugins already and I believe with some time investment and the help of community plugins we can have a kickass minification pipeline.
Optimisation plugins
Babel plugins put a lot of potential for optimisations on the table. These optimisations fall into two categories:
Unreliable transformations
These transformations modify JavaScript semantics, they're pretty dangerous and fairly irresponsible. If you're a small devshop or independent developer it's probably way too risky to do this but if you're a largish tech company then you can weigh the risks and it may come out in favor of doing these sorts of transforms. The engine can't infer these kind of changes, you have to tell it that you don't care.
Want to assume all function expressions are constant value types and hoist them? Do it via a plugin.
Want to turn all function expressions that don't use this
or arguments
into arrow functions? Do it via a plugin. (Arrow functions are currently faster than normal functions in Firefox and likely all other browsers in the future)
Want to manually inline functions, removing the overhead of adding a frame to the call stack, potentially improving perf even if the function has been JIT inlined? Do it via a plugin.
Want to lazy load modules, assuming that the order they're imported doesn't matter as they're side effect free? Do it via a plugin.
You can kinda see how these assumptions can lead to fairly large wins. You just have to decide whether or not it's a good idea to do them.
Reliable transformations
These are transformations that can be done with 100% confidence. These can be performed without possibly breaking any code. With Flow integration and access to the dependency graph a lot of code can be statically inferrable.
But don't engines already do these? Different engines do different things. Some optimisations are too heavy for JITs to do but we have a bit more leeway since we're preprocessing offline. We also potentially have access to more information than engines (type annotations) and so can do some smarter things.
Debugging
Better debugging information will be absolutley necessary as Babel grows in complexity. A particular useful feature for userland would the ability to tell exactly what transforms are breaking or slowing down your build will be critical in this modulrisation.
Performance
5.x performance has improved drastically since 4.x. It could be better though. Performance will never stop being an issue.
If there's one part of Babel that is quite slow due to the overhead of JavaScript, there's the potential to provide two implementations of it. A fast version implemented via a native module that's used in Node and a pure JavaScript one that can be used in the browser. If parts of Babel get implemented as native modules there will always be a pure JavaScript version available.
See issue #1486 for related performance discussion.
Async API
Why?
This will allow an RPC with a long running server such as Flow. This will allow access to a large amount of type info. This will also allow IO to be done.
What about integrations that only allow a synchronous API?
A synchronous API will still be available for those limited integrations. If you attempt to use the synchronous API with plugins that have async visitors then an error will be thrown. Most of the systems that Babel has to integrate with are already async so this shouldn't be a concern for 90% of developers.
Anything else?
Probably forgetting something, I'll add more to this issue as I remember and feedback is collected.
cc everyone