Proposal: 'configurator' field in package.json
See original GitHub issuetl;dr
Add a field to package.json
that tells ESLint, Flow, other tools, where to look for configs.
Useful for zero-conf opinionated packages like create-react-app
and standard
.
// ESLint will look for react-scripts/config/.eslintrc
// Flow will look for react-scripts/config/.flowconfig
"configurator": "react-scripts"
As a community, we’ve mostly solved the problem of nested, extendable configuration. ESLint does this really well, Babel also does this.
However, I think that this project gaining 4k stars in 4 days proves that there is a market for non-configurable opinionated tools hiding dependencies from the user. I’d like to give props to standard for being one of the first tools demonstrating this is a viable model (it doesn’t matter whether I agree with its choices).
The problem I‘m starting to see is that many amazing tools, like ESLint and Flow, don’t currently play very well with this approach out of the box.
Problem
ESLint
ESLint is very flexible but if you want IDE integration, you need to tell it where your config is.
This is why our otherwise zeroconf package.json
now has to look like this:
{
"name": "my-app",
"version": "0.0.1",
"private": true,
"devDependencies": {
"react-scripts": "0.1.0"
},
"dependencies": {
"react": "^15.2.1",
"react-dom": "^15.2.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "./node_modules/react-scripts/config/eslint.js" // 😁
}
}
Yes, one extra field is not a high price, but it is already a bit frustrating that we have to hardcode ESLint there. We would like to treat it as an implementation detail, to the extent that it is reasonable. (e.g. eslint-ignore
comments are fine, but putting paths to configs into user’s package.json
is annoying)
Also, it doesn’t even solve the problem completely because ESLint will look for plugins relative to the project root, so any transitive plugin dependencies won’t be discovered with npm 2 (or in some cases, admittedly not likely with our tool, with npm 3). You can find the discussion about this in eslint/eslint#3458.
Flow
Flow poses a similar problem for us, although in a more unfortunate way. We can’t even tell Flow to look at a different config. When you run flow init
, it creates .flowconfig
in the project root, and that’s it. This is reasonable if the user plans to change it (which seems necessary for lib integrations), but we can’t even influence the default generated config in any way. And unfortunately, for a number of reasons, the default generated config won’t work for create-react-app
.
If you want Flow to work, you need to replace the config generated by flow init
with this one:
[libs]
./node_modules/fbjs/flow/lib
[options]
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable
module.name_mapper='^\(.*\)\.css$' -> 'react-scripts/config/flow/css'
module.name_mapper='^\(.*\)\.\(jpg\|png\|gif\|eot\|svg\|ttf\|woff\|woff2\|mp4\|webm\)$' -> 'react-scripts/config/flow/file'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
And we don’t want to do this automatically because:
- Many people don’t currently use Flow.
- Our philosophy is to not generate a ton of configs in the user folder.
- We want the config to be compatible with the current version of
react-scripts
, so if we generate Flow config before the person actually starts using Flow, by the time they start using it, their checks might fail because something changed on our side.
Solution?
I don’t know if there is a nice solution to this, but here is my proposal. This is how I want package.json
to look:
{
"name": "my-app",
"version": "0.0.1",
"private": true,
"devDependencies": {
"react-scripts": "0.1.0"
},
"dependencies": {
"react": "^15.2.1",
"react-dom": "^15.2.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"eject": "react-scripts eject"
},
"configurator": "react-scripts" // 😀
}
ESLint would then use react-scripts/config
as the base directory for its regular config/plugin search mechanism. So it would look for .eslintrc
, package.json
, etc, in that directory, and resolve plugin modules relative to it. Since ESLint wants to stay configurable, it will also look for overrides in the current folder (and use its regular override mechanism), but this would just add configurator
as a first-class citizen for providing configuration.
Similarly, Flow would look into that project when running flow init
, and if it finds .flowconfig
in react-scripts/config
, it would use that instead of its default config. This gives us a way to own the default configuration even if the user is on their own. In the future, Flow could support merging ${configurator}/config/.flowconfig
with local .flowconfig
—similar to how ESLint would work if it implemented this feature.
This would benefit an ecosystem by fostering development of opinionated presets of preconfigured tools. If you’re a tool, honor configurator
in package.json
and try reading configuration from ${configurator}/config
in addition to your normal lookup paths. If you’re a preset author, you can fork react-scripts
or any other configurator, make some changes to it, and keep it simple to adopt your preset: just swapping configurator
is all end users need to do.
What do you think? Am I missing something?
Issue Analytics
- State:
- Created 7 years ago
- Reactions:36
- Comments:20 (12 by maintainers)
Top GitHub Comments
The downside with https://github.com/facebookincubator/create-react-app/issues/215#issuecomment-235278535 is that every IDE/plugin would need to implement this. So, for each of N tools (ESLint, Flow), there would be M plugins (Atom, Sublime), and NxM integrations that would need to do it this way (ESLint for Atom, ESLint for Sublime, Flow for Atom, …).
Whereas if we do this on the tool level as suggested in https://github.com/facebookincubator/create-react-app/issues/215#issue-167602431, we only need the N tools to integrate this. IDEs already call the tools with no arguments and rely on the tools to know how to discover configuration.
There’s already a thousand ways that all those tools can figure out where to find the configuration, it’s really unclear to me that adding yet another one is the right solution. Also, adding a hardcoded path to the config that the tools can read means that anybody can also read it and it’s not a blackbox anymore.
Proposal instead:
Have the IDEs read package.json and if there’s a script called eslint, then use it instead of the global eslint.
Then, we would add an opaque script that calls eslint with --config=opaquepath/eslintrc