Allow more flexible file-based configuration while preventing .babelrcs from breaking things #7358
Conversation
I like this implementation. Just one thought: should Babel accept |
It might be confusing for Maybe there's a better name for this file? |
That's a fair point. I didn't bother because while I'm happy to run JSON5 on
Maybe, but at the end of the day I feel like there are only names that we'd want to encourage. In this case I was definitely mimicing Webpack, which I don't believe does hierarchical lookups. This PR also makes |
Is there a way to limit the number of config files we support? Or is that better than having another option in configs, or we are ok with this? Options:
also cc @babel/eslint regarding eslintrc lookup and root and this idea for babel |
Good question. At the end of the day, the hierarchical behavior of
Personally I think the majority of people don't need or care about configuring via hierarchy, so it'd be just as easy to recommend
I don't know, I feel like the logic there is identical to I see Babel's config as having a few pieces
|
I'm not sure exactly but this might break react native, because they transpile node_modules, although I'm not sure how they handle the babelconfig for this packages. We should maybe ask someone who is more into react native. |
@danez Fair point. The only question would be stopping |
cc @zertosh to ping someone on react-native then? Whatever we land, we'll really want to have good docs for the config behavior + and just in general (and probably some examples with folders/code)?
|
@yungsters spent some time in that area |
i feel like Or even, grant dev user the control to compose the strategy as part of the config. As well as exposing a configurable cache predicate. |
Why not just using cosmiconfig ? It has a defined set of config (names) which are supported, including
|
I agree. That's essentially the downside I called out in the PR description. The issue is that if we don't stop there, where do we stop? We could had yet another option to Babel, but that starts getting extreme. I explicitly want to stop doing "search all the way up" because that has caused users pain in the past, and the monorepo case seems to be the only one where it's potentially painful, and this PR presents an alternative.
That's essentially what we've already had until now, just implemented internally. I'm trying here to add a config file that doesn't cascade because cascading breaks for anyone who wants to symlink packages during local development, and for anyone looking to specific compilation configuration for packages that live inside |
I agree. That's essentially the downside I called out in the PR description. The issue is that if we don't stop there, where do we stop?
What pains ? :)
symlink => specify the absolute path to the config one intend to use ? |
What if the file being compiled isn't even in the working directory? e.g.
assuming
Most often its new/inexperienced users who create a It's note insanely common, but it exposes what I consider to be a pretty big flaw in hierarchical configs.
What I'm saying with that comment is that cosmicconfig doesn't add anything new functionality-wise. Adding it would not materially affect the objectives that this PR is trying to address, from what I can tell anyway. I'm also not familiar with how any of those projects handle cross-package compilation.
That's pretty much what this PR is adding with
To clarify, for ESLint a configuration is by-definition associated with a package itself. A package defines its own linting rules. Where that breaks down is that for Babel, in the case of node_modules, it is often some root application that wishes to configure the compilation rules. The fact that a given Say the user has
Because the node_modules has a |
I sort of feel like the find-up and hierarchical babelrc logic may be the source of this challenge.
Couldn't it be argued that this is not a case we can reliably support with auto find up behavior? Maybe, instead of a hierarchical babelrc we allow assignment of paths/patterns to a known babelrc location? Something like webpack's {
configs: [
{
include: [path.resolve('src')],
use: path.resolve('.babelrc') // or inline config
}
]
} Then, for something like babel cli - we would throw an error to use this strategy if we failed to find a babelrc up to or a particular level? |
Yeah, to me it feels like find-up only makes sense on an individual-package basis.
With this PR, you can get that by putting those rules into an
I'm not sure I follow what you mean by that. |
Dependens where the
If I would need to work with the file tree given in the example I would assume Where would the
Yeah, it's something one needs to learn and it can be annoying at first, but that's something one at some point definitely needs to understand imho :). Is
This is/can imho also true for transpiling in case the package author uses a
{
'src/index.js': {
babelrc: '../'
browserslist: '../'
},
'node_modules/dep/index.js'. {
babelrc: './node_modules/dep'
browserslist: '../../'
}
}
|
I think we all agree that non-standard language functionality should be compiled before publishing, but it is quite clear at this point that the community as a whole is going to adopt language features as they land in Node (LTS anyway). That means over time users looking to compile for browsers are going to need to run
You mean the package in
Automatic installation of packages isn't something I can see us ever wanting. If a config is so complicated that a user can't install it themselves, to me that points to an architectural problem in the config loading system itself. Avoiding that is a big part of why I'm doing this. Doing Going back to the example structure
First off, to clarify, what I'm talking about here is a user configuration where
Webpack will attempt to pass all JS files in a project. In this world, a user is conceptually trying to compile "some-app" with a set of dependencies. As far as they are concerned, they don't particularly care what configuration is in individual node_modules. They just want to say "compile all standards-compliant code to ESX" based on their own config in The core tenants of
I'm not sure I follow 100%, but I'd say being idealistic/naive is just what Babel has been doing up until now, and this is my attempt to fix that so our users have options for usecases that are quite painful currently, but are also becoming more and more common. |
Woops, missed these:
Babel does not merge multiple
I thought you wanted |
Could
Yep :), for the project side of affairs (as mentioned I initially thought dependencies should be individually 'transpilable' with their respective configs (as a requirement), but that's rightfully going in the opposite direction as it seems)
|
Meaning; if babel cli fails to find eslint approaches this with a concept of "overrides" - allowing you to assign linting rules to a particular list of paths/patterns. I feel like that hits close to home for this. They also have a concept of "root" - definitely something that could apply here as well. |
Change .babelrc to babel.config.js, as required to transform node_modules for browser support in bundles. See babel/babel#7358
See babel/babel#7358 SyntaxError: node_modules/react-native/Libraries/Utilities/Platform.ios.js: Unexpected token, expected "{" (35:17)
Temporary adding babelrc to root of monorepo due to next babel issues babel/babel#7701 babel/babel#6766 babel/babel#7358
I'm not opted into the beta version I don't think and presume this is released now? I think this has broken my build, I don't find the documentation of very helpful so far (particularly not in terms of what I need to change in my monorepo to get it working the way it did yesterday), and I didn't read super closely, but I think I'd find the behavior described here and in the documentation thoroughly undesirable and would vastly prefer cascading config that applies to sibling files like eslint's. |
@CH-RhyMoore add a module.exports = {
babelrcRoots: ["./packages/*"],
} Assuming you run commands from the root. If you run a command from within a package, add a module.exports = {
babelrcRoots: ["../*"],
}
The only thing that doesn't work is it won't transform files in node_modules. For that you have to target the module in webpack and specify your babel options. Something along these lines: {
test: /\.jsx?$/,
include: /node_modules\/module-name/,
use: [
{
loader: "babel-loader",
options: {
babelrc: false,
// customize your babel options below
presets: ["@babel/env", "@babel/react"],
plugins: [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-object-rest-spread",
],
},
},
],
} The reason for this is a given node module may use a different version of babel. Most node modules usually pre-compile, so it's not a common problem. |
Okay, that is simple enough. Thank you very, very much! Still, my 2 cents--eslint really has always had my favorite configuration experience. Personal preference plays a part here, but I've always found it very intuitive. |
I might have another issue going on at the same time, potentially with Webpack 4? Because that alone isn't fixing it. Scenario:
But searching that sent me back here. Oh well, I'll keep looking. |
It may have something to do with the Babel has an excellent Slack community. Someone there would likely know better than me. |
Thing is, we use project root
package with build script
|
FYI, getting a 523 at that Slack link. Was trying to peek at it in case I felt brave. |
The The Slack link: Looks like the host that does the redirect is down. Was working earlier today. I don't have the direct link. I copied it from the project README. EDIT: It's back up. |
In a monorepo where each package manages its own build, it should absolutely be possible. Why wouldn't it be? npm is quite capable of installing multiple versions of the same package when their semver ranges aren't compatible, Lerna is quite capable of not getting in the way of npm doing so, and Node.js is quite capable of resolving the version needed by files in a particular package. (We've been doing so without any issues for ~2 months. Our packages produce sufficiently different things that their builds aren't consistent enough to raise up to the project level at this point. We'll do that at some later point when we see which consistent patterns emerge.) My biggest issue wasn't Babel related directly at all, I don't believe. For some reason, the update to Webpack 4 we did 15 days ago suddenly began requiring us to install I think that issue is entirely about how Webpack (CLI only? *shrug*) does or doesn't run Babel on its own configuration file. So FYI for the future, if someone mentions the errors coming from their Webpack config file, it could be this issue. But by the time I finally found that, I'd already upgraded a half dozen or more dependencies in each package (times 20) to use Babel 7 in everything (and update plugins and then the tools that use the plugins to versions capable of handling it). We have updating on our roadmap for this month, so I here I am working late, because I thought I might as well continue, and I hope to god I'm close to finishing. I've had to change a multitude of other things to get this far in migrating from Babel 6 to 7 today, but, strangely, adding So it seems if you set up your monorepo like ours, you may not need to do anything about the new configuration behavior. Though it's possible it may yet arise as an issue. I haven't even gotten to Jest yet today since I'm still trying to get everything in the project building. ...later... Jest was fine as well with |
This comment has been minimized.
This comment has been minimized.
Sometimes we need to parse the files in the node_modules folder. So, I want you to provide a configurable place, not a fixed one. |
This comment has been minimized.
This comment has been minimized.
You just need to use babel.config.js and it will transpile node modules. |
This comment has been minimized.
This comment has been minimized.
According to your method, I compiled successfully. Thank you. |
Alright! Let's do this. This is my proposal for fixing the issues spelled out in #6766. This PR does the following:
When searching for
.babelrc
files for a given file, stop at package.jsonCurrently, Babel will look all the way up the directory hierarchy. This change prevents config files outside of a given package from potentially breaking your project's builds, which has been a problem occasionally. Also potentially frustrating for projects that integrate Babel, because they often allow users can optionally create .babelrc files, and if they haven't created one, but have one in a parent folder, it might find a bad config.
The downside here is that monorepos like
will fail to find the
.babelrc
. I think that is acceptable because there are alternatives:.babelrc
doing{ extends: "../../.babelrc" }
to explicitly load the config from a parent directory.Adds support for a
babel.config.js
file along the lines of what Webpack doesThis PR adds a
root
option (that defaults to the working directory), that defines the conceptual "root" of your project. This location is used to automatically search for the config filebabel.config.js
. Optionally instead the user can specifyconfigFile: "./foo.js"
or whatever they'd like.The file itself behaves just like a
.babelrc.js
file would, but is not loaded relative to the individual file being compiled, and thus has more flexibility in what files it applies to. For instance, if users are symlinking innode_modules
or something, it is currently impossible for a.babelrc
to configure their builds any way other than by hard-coding their config into webpack.config.js, which is not great.With our new support for
overrides
it's now easy for users to have a singlebabel.config.js
which could define the full build process for their project, when used alongsideenv
.Only search for a
.babelrc
for files inside theroot
(which defaults to cwd) (unless the user opts out)Specifically excluding
node_modules
folders. Given the path of the file being compiled, if the path relative to the root containsnode_modules
, do not attempt to search for.babelrc
files. This essentially has the effect of making.babelrc
files only useful by default in a "root" package, which is essentially how most people used them.This (potentially combined with the previous
babel.config.js
) allows users to safely dowithout risking loading a
.babelrc
from a node_modules that was not intented to apply to the current project. There are still other edge cases to compilingnode_modules
, but this was a particularly dangerous edge case, especially for things likebabel-register
that don't have a nicerules
list like Webpack does.To allow people to opt into
.babelrc
usage, potentially for local development, this PR allows Babel'sbabelrc
option to be an array, so users could dowhich would also attempt to load
.babelrc
files for anything within that package. Alternativelyif there was a specific package you wanted to load a config for, and were confident in its installation location.
What this PR does not do
Part of the reason I personally had so much back and forth on this issue is because it wasn't necessarily obvious what
root
should actually be responsible for. For instance, if Babel is passed a file that is not in theroot
, should it compile it at all? As is hopefully clear, my answer there was yes.One core thing that this PR does is that it leaves it up to the individual callers of Babel to decide what files should actually be passed to Babel in the first place. Alongside that, if you configure Babel with a custom
root
orconfigFile
orbabelrc
array, the user will have to decide if those need to be replicated between Babel callers, because they are top-level Babel options, not ones that can be included inside a config file (for obvious reasons).What is left
This PR has no tests, and an unoptimized implementation of a lot of stuff. It will need more work before it can land, but I want to post it now to get a feel for what everyone thinks.