-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
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
WIP: Validate rule options #2179
Conversation
In general approach looks pretty good to me. I would make a few changes, like, adding possible error message into schema as well as exposing a function on cli to dump all schemas for all rules, but all of that can be added later as an independent commit (this would allow us to automatically improve rules documentation as well as allow editors to get all possible configurations for rules). |
Also, one more thing, I'm not sure that would be possible, but it would be nice if the error message would point to the configuration file with line number (don't think it is, since it's inside the loop that processes files, so it will always point to the current file that is being validated). |
I think the idea is correct, just not sure about the implementation. The schema looks a bit complicated, which means we would need a schema validator, and that starts to get hairy. I'm wondering if we should use something like joi instead. @ilyavolodin I definitely don't want a separate validation step, it should be fine as part of the normal run, otherwise people will rarely be aware of errors. I'm not sure about the error representation either. The error could be in an eslintrc file or a JS file due to inline comment. We either need to be better about reporting where the error occurred or just throw the error and let ESLint crash. |
// test.js
/* eslint quotes: [3, "double"] */
var answer = 6 * 7; // config.json
{
"rules": {
"semi": [2, "laways"]
}
} $ eslint test.js
... error thrown
Error: test.js line 2:
Configuration for rule "quotes" is invalid:
Error level must be between 0 (off) and 2 (warning).
... stack trace $ eslint -c config.json test.js
... error thrown
Error: /Users/brandon/code/eslint/eslint/config.json:
Configuration for rule "semi" is invalid:
Value "laways" must be an enum value.
... stack trace $ eslint --rule 'eqeqeq: [2, "smart", "asdf"]' test.js
... error thrown
Error: CLI:
Configuration for rule "eqeqeq" is invalid:
Value "2,smart,asdf" has more items than allowed.
... stack trace
I looked into joi, but I found no way to programmatically inspect schemas. Right now we only need validation, but getting into configuration setup and fuzz testing, that becomes more important. I did, however, offload some of the schema creation onto the validator. Half of the schemas now become just an enum, module.exports.schema = [
{
"enum": ["always", "never"]
}
]; As for Travis, it's failing because of code coverage; I didn't break any logic. |
Here's my concern: representing data as JSON is a solved problem. If we're creating a new schema type, then we're also on the hook for creating something to validate that schema. I'd much prefer using an existing solution vs. creating something completely new. |
Just in case you change the format of schema to something else, please keep in mind I would like to add the following information to the schema later at some point. Here's the information that I would like to add: {
"description": "very short description of the rule",
"errors": ["list of all of the errors that can be reported by this rule"],
"options": [ { "always": "description", "never": "description"} ]
} Or something like that. This would allow us to enhance our documentation automatically, as well as provide a way to extract all of the options for all rules for integrations and things like web .eslintrc builder. |
@ilyavolodin we can always add extra properties to the rules themselves, this doesn't have to be part of the schema |
@btmills I love the idea to have the rules validated and it's great what you have prepared. |
@nzakas This is still using unmodified JSON schema. However, since much of each rule's schema was redundant, I had the rules just return the For example, [
{
"enum": ["single", "double"]
},
{
"enum": ["avoid-escape"]
}
] Wrapping it with {
"title": "quotes",
"description": "Specify whether double or single quotes should be used.",
"type": "array",
"items": [
{
"enum": [0, 1, 2],
"description": "Reporting level - 0 for off, 1 for warning, or 2 for error."
},
{
"enum": ["single", "double"],
"description": "Enforce single or double quotes."
},
{
"enum": ["avoid-escape"],
"description": "Avoid escaped quotes inside strings by allowing both quote types when necessary."
}
],
"minItems": 1,
"maxItems": 3
} Because there are valid schemas in both phases, we could validate them against JSON schema's schema in either place.
@ilyavolodin I annotated the above example with
@doberkofler Callbacks were actually the first thing I considered way back when I started working on fuzz testing, see #1568 (comment). However, as pointed out by @nzakas, fuzz testing and config validation would require two separate callbacks. We'd need another for some sort of autoconfiguration utility. That's why we need some standard data format that facilitates programmatic inspection and generation. From what I've seen, JSON schema seems to be that. |
@btmills Yes, that would work perfectly for auto configuration and auto documentation. One last thing that would be nice to add is some sort of way to mark default options (if there are any). |
Ah gotcha! Will have to take a closer look. |
@nzakas Any updates. Would love to get this into 1.0.0 |
|
@btmills That would work. |
Can we just use what's in eslint.json as the defaults? |
You are correct. I totally forgot about eslint.json. I can just use that for defaults. Are you fine with the rest of it? Can it be merged (after build breakage fixes)? |
source, ":\n", | ||
"\tConfiguration for rule \"", id, "\" is invalid:\n", | ||
"\tError level must be between 0 (off) and 2 (warning).\n" | ||
].join("")); |
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.
2 is error, not warning
Yeah, I think in general the approach is sound. I'd be interested in seeing the perf difference of running on this repo both with and without validation. |
I'm excited to see this happen. Is there any way I can help? |
29966e8
to
82e3194
Compare
I think this is almost done. Just a couple outstanding concerns:
Thoughts? |
Hmm, not too bad perf-wise. Can you explain more about the plugin problem? I think I get what you're saying, but I'm not entirely sure. |
@nzakas Since we want to report the location of invalid options, they have to be checked against the schema in {
"plugins": [
"react"
],
"rules": {
"react/display-name": 1
}
} @ilyavolodin Hmm from what I read I think #1971 is separate from the particular issue discussed above, though it would help plugin authors test schemas from |
The first part is at #2523. |
New: Add config validator (refs #2179)
New: Add rule schemas (refs #2179)
Can you open an issue for that discussion? I'd like to get out conversation back on issues and close this. |
Closing in favor of #2595. |
Breaking: Validate test options (refs eslint/eslint#2179)
This is a code-included, tests-passing request for bikeshedding that might address the discussions in #967 and (eventually) #1568. This is not ready for a line-by-line code review until higher-level concepts have been addressed and solidified.
With this approach, validation is optional. Rules opt in by exporting a
schema
object describing a JSON schema for the options the rule accepts. Most rules have very similar option formats, so this wasn't difficult at all.Example
$ eslint --reset --no-eslintrc -c config.json test.js test.js 0:0 error Invalid configuration: ["samrt"] must be an enum value eqeqeq ✖ 1 problem (1 error, 0 warnings)
Performance
So far, I've created schemas for all rules that are enabled by default in order to get a good picture of the performance impact, which, happily, turned out to be minor.
master
:This PR:
Note that these performance numbers are for linting a single file. When linting a whole directory, part of the validation cost is amortized over all files because is-my-json-valid uses code generation to create the validators, which are generated once, cached, and reused.
Configuration guidelines
These are some principles that we seem to prefer, but I'd like to consider formalizing them. These are just ones I've noticed or think make sense; feedback is requested.
All rules should have sensible defaults, such that they can be enabled and will do something without having to specify any options.
Example:
"quotes": 2
can assume double quotes since neither mode was specified.A rule whose primary option is a mode may specify that mode using a descriptive string as the first option.
Example:
"quotes": [2, "single"]
intuitively should enforce single quotes.All other (non-mode) options should be named properties of a single options object. This helps avoid confusion as to the order of parameters, and it allows some to be left as default while only specifying deviations from the defaults.
Example:
"semi-spacing": [2, { "before": false, "after": true }]
If we have validation, out of range values should fail validation. Previously, some rules,
comma-dangle
being one, considered unknown values to mean the default.Example:
"quotes": [2, "snigle"]
should fail, since clearly"single"
was intended, yet assuming the default of"double"
would lead to confusing behavior until the error was noticed. (This is a trivial example. Other, deeper options fail silently and are much harder to debug.)Some rules still support for deprecated options. For example,
no-mixed-spaces-and-tabs
also accepts a boolean value, though it's not documented, and a source code comment suggests adding a deprecation warning. Since this would be a breaking change, it could also be a good time to remove support for deprecated options concurrently, though I've not done so here.Thoughts?
Feedback requested