-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expose the partial Babel config for callers to load and mutate. #7472
Conversation
cc @benjamn @KyleAMathews since I've talked to you two about this a little |
Build successful! You can test your changes in the REPL here: https://babeljs.io/repl/build/7145/ |
Looks great! /cc @jquense |
Neat! |
@@ -123,6 +123,78 @@ const parsedAst = babylon.parse(sourceCode, { allowReturnOutsideFunction: true } | |||
const { code, map, ast } = babel.transformFromAstSync(parsedAst, sourceCode, options); | |||
``` | |||
|
|||
## babel.parse(code: string, [options?](#options): Object) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this return the AST or something else? Maybe we can add return types so I don't have to look up the code to see what this does. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I'll add a note. Just realized we didn't document it when we actually landed that PR.
* `name: string | void` - The name that the user gave the plugin instance, e.g. `plugins: [ ['env', {}, 'my-env'] ]` | ||
* `file: Object | void` - Information about the plugin's file, if Babel knows it. | ||
* `request: string` - The file that the user requested, e.g. `"@babel/env"` | ||
* `resolved: string` - The full path of the resolved file, e.g. `"/tmp/node_modules/@babel/preset-env/lib/index.js"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome, so in theory babel-loader can add every single plugin/preset as dependency (if coming from a file) so that webpack watch will trigger a change when one of them changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potentially, but since the presets aren't flattened, there are other plugins that aren't included here, so it wouldn't cover everything. Also since they are CommonJS, they'll still be cached by the module loader unless something also cleared that. I mostly exposed this since we already knew it and it seemed helpful for users. I'm figuring this will be good because they we can dump out a user's config in a human-readable format mostly.
* `babelrc: string | void` - The path of the `.babelrc` file, if there was one. | ||
* `babelignore: string | void` - The path of the `.babelignore` file, if there was one. | ||
* `options: ValidatedOptions` - The partially resolved options, which can be manipulated and passed back to Babel again. | ||
* `plugins: Array<ConfigItem>` - See below. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
presets are not resolved to their plugins in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If they are not resolved (which I guess might be useful for some use cases) is there a way to "resolve" presets to plugins?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is babel.loadOptions
to get the fully flattened options/plugin list, but honestly I don't think manipulating that list is a good idea since it means that you're potentially changing the behavior of a preset that wouldn't otherwise be changeable.
|
||
export type { PartialConfig }; | ||
|
||
class PartialConfig { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there any value in this being a class?
also I havent ever understood the point of hiding "private" fields under public getters - would love to hear out the reasoning behind this structure, always seemed to me that it complicates a simple thing for no reason, especially that those getters do not have any additiona logic behind them
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly because I wasn't sure if we might want to add other helper methods and stuff on here in the future, but you're totally right it isn't required right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For instance I thought we might want a .hasFilesystemConfig()
to do this.babelrc !== undefined || this.babelConfig !== undefined
once we add the babel.config.js
file.
This seems super useful for other tools 👏 |
that callers will take the config's `.options`, manipulate it as then see fit | ||
and pass it back to Babel again. | ||
|
||
* `babelrc: string | void` - The path of the `.babelrc` file, if there was one. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does loadPartialConfig
only load the first .babelrc
or it also loads its parents (when needed)? Also, what about babel.config.js
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It basically does everything to know what the configured plugins and presets will be, without executing them, so that includes loading all config files, and any options passed in programmatically, and merging them all together into a single config object. It just skips actually calling the plugin/presets function, and flattening the presets themselves.
e69413a
to
74ab279
Compare
Alright, I updated the I also updated the |
Sorry to capture this thread, but I'm a little confused what it the idiomatic way to load a config with the Node API. So the README for babel.transform(code, options, function(err, result) {
result; // => { code, map, ast }
});
We have Say I have a const opts = { filename: './src/foo.js' };
const { OptionManager } = require('babel-core');
console.log(new OptionManager().init(opts).plugins.length); // 0
const { loadOptions } = require('@babel/core');
// loadOptions(opts) same as (new require('@babel/core').OptionManager().init(opts))
console.log(loadOptions(opts).plugins.length); // 23 It looks like the plugins are only resolved in Babel 7 (and only when I give it a Is the idiomatic way to load a config (e.g. is this return value of Should I use Thank you :) Update Or is the use case to make in-place changes to existing configs before the transformation (e.g. like here)? |
This PR exposes
.loadPartialConfig
as a way to load the Babel configuration up to the point where the plugins and presets are all known, but have not yet loaded. It also exposes the path of the.babelrc
and.babelignore
files that Babel found, since a lot of tooling likes to toggle behavior based on whether the user has created a config file or not.The goal of this PR is to expose enough of Babel's config that we can avoid tooling feeling like they have to implement it themselves. I think this achieves that, but if there are cases where that isn't the case I'd love to hear them.
See the README in this PR for more information, but the core of this PR is that it exposes a new first-class instance type
ConfigItem
that is allowed inplugins
andpresets
lists, and the results of.loadPartialConfig
ensures that each plugin and preset is aConfigItem
. From the README:Each
ConfigItem
exposes all of the information Babel knows. The fields are:value: {} | Function
- The resolved value of the plugin.options: {} | void
- The options object passed to the plugin.dirname: string
- The path that the options are relative to.name: string | void
- The name that the user gave the plugin instance, e.g.plugins: [ ['env', {}, 'my-env'] ]
file: Object | void
- Information about the plugin's file, if Babel knows it.request: string
- The file that the user requested, e.g."@babel/env"
resolved: string
- The full path of the resolved file, e.g."/tmp/node_modules/@babel/preset-env/lib/index.js"
Beyond that, tooling can manipulate the config however they'd like before passing the config back off to Babel again.