Cache plugins and presets based on their identity #6350
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This changes how Babel approaches caching and execution of plugins and presets.
In Babel 6
Plugins
The plugin functions in Babel 6 where only ever executed a single time. This was done for performance reasons because plugins do a bunch of work up front, like calling
template
and other stuff. The downside of this is that plugin options when used like:where only available during execution of the plugin itself in its handler functions. e.g.
Presets
The preset functions in Babel 6 run every time a file is compiled by Babel. This slows down the process of loading Babel's configuration.
With this PR
With this, up-front API of both plugins and presets is standardized as
so plugins may access their options up front during initialization time.
Another big benefit of this is that now plugin options can toggle plugin behavior more aggressively. If the options don't require a feature, you could for instance entirely omit the visitor functions for those nodes to potentially avoid extra work.
Plugins can also now toggle
inherits
blocks based on options, so if you had a syntax plugin that you only wanted to enable some of the time, it can now be easily toggled.What has changed to allow this
To achieve this, Babel needs to know how to cache a plugin or preset's execution result, and most importantly, when to consider a plugin or preset invalidated. With this, Babel's config loading has several layers of caching.
A give plugin/preset is tied to its location in the configuration. If you have a config like
the plugin construction function will be called twice, with each set of options.
If the
.babelrc
containing this config changes its mtime, each plugin will be re-executed. For.babelrc.js
files, this invalidation behavior will depend on the programmatic API defined for those in https://github.com/babel/babel/pull/5608/files#diff-2f4602ba055eb6ec98d6bac3161d43ffR67.Importantly, plugins and presets also have access to this programmatic API, the same way
.babelrc.js
files do, do if a preset depends on some kind of global state like an environmental variable, it can doto essentially say that the preset has two possible states, one with and one without a global
value
flag.Finally, in #6326 I talked briefly about caching for programmatic options, so that also comes into play here. Take a call like
the question is, how often does the initialization function for
es2015
get executed? In this case, the function will be executed twice, and because the plugins used in this preset depend on it as a parent configuration, all of those plugins have to be re-executed too.To address this in a way that should be workable for the 99% case, this PR makes it so that doing this will only execute the preset once.
because Babel can see that it was passed exactly the same object for
presets
both times, it will re-use all of the plugin and preset initialization that happened in the previous call to Babel.The only downside to this approach, I've called out in #6326 (comment), but I think realistically, it is an edge case that, if it is hit, is easily fixable by cloning the array instead of mutating it.