-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
fix(eslint-plugin): fix schemas across several rules and add schema tests #6894
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
Conversation
Thanks for the PR, @domdomegg! typescript-eslint is a 100% community driven project, and we are incredibly grateful that you are contributing to that community. The core maintainers work on this in their personal time, so please understand that it may not be possible for them to review your work immediately. Thanks again! 🙏 Please, if you or your company is finding typescript-eslint valuable, help us sustain the project by sponsoring it transparently on https://opencollective.com/typescript-eslint. |
✅ Deploy Preview for typescript-eslint ready!
To edit notification comments on pull requests, go to your Netlify site settings. |
@@ -124,7 +126,6 @@ export default util.createRule<Options, MessageIds>({ | |||
'The array type expected for readonly cases. If omitted, the value for `default` will be used.', | |||
}, | |||
}, | |||
type: '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.
Moved this up to the top for consistency with most of the other rules I saw.
exceptAfterOverload: { | ||
type: 'boolean', | ||
default: true, | ||
const schema = Object.values( |
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.
Before, we had:
{ "0": { /* schema A */ }, "1": { /* schema B */ } }
AJV is actually not able to interpret this correctly, and no validation actually occurs.
This change fixes it to:
[{ /* schema A */ }, { /* schema B */ }]
Using Object.values here should be safe. The baseRule.meta.schema is an array itself, so will be spread in order (guaranteed by ECMA spec). deepMerge will merge the keys in the order of the first object. Object.values will take the values in order, so we'll get back the array in the right order.
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.
[Praise] Nifty trick to use Object.values
to convert the string index style object to an array!
@@ -51,7 +51,6 @@ export default util.createRule<Options, MessageIds>({ | |||
'public readonly', | |||
], | |||
}, | |||
minItems: 1, |
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.
Removed this constraint so the default is able to be validated against the schema.
Alternative could be to exclude this from the schema validation list, or have an 'override' schema for testing against the schema.
I don't think this matters for actual usage, and I think it's not harmful: in fact it might be useful in some configs to be able to specify some allowed things by default, and then specify none to be allowed in an override config explicitly to be clear about why it's there.
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.
Sorry, I don't follow - why is this minItems
not good? It's a correct restriction, no?
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 this PR, the main reason I did it is beacuse it would prefer the default options from validating against the schema itself, and this is a simple way of getting around that.
But also I think semantically it makes sense that it should accept an array of nothing for the exceptions allowed, meaning that no parameter properties are allowed.
@@ -27,18 +27,18 @@ const allowTypeImportsOptionSchema = { | |||
const schemaForMergeArrayOfStringsOrObjects = { | |||
items: { | |||
anyOf: [ | |||
{}, |
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.
These seem incorrect and unnecessary. I think the intention was for this to merge with the base anyOf
items, but deepMerge doesn't handle array items: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/utils/src/eslint-utils/deepMerge.ts
Although thinking about this now, I'm not certain the updated rule is 100% correct, would appreciate careful eyes on it. Might need to refactor how this extends from the base.
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.
Have redone this. It's fairly horrible how it indexes into the baseRule - happy to take feedback on better approaches.
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.
Yeah seems like you're certainly following the standard practice: https://github.com/eslint/eslint/blob/1fea2797801a82a2718814c83dad641dab092bcc/lib/rules/no-restricted-imports.js#L19.
You might want to assert const baseSchema = baseRule.meta.schema as SomeTypeDescribingWhatWeKnow
, since this is making an assumption on the base rule's schema type.
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.
Good idea, have done this.
properties: { | ||
allow: { | ||
type: 'array', | ||
items: { | ||
$ref: '#/$defs/modifier', | ||
}, | ||
minItems: 1, |
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.
Same minItems reasoning as before
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main #6894 +/- ##
=======================================
Coverage 87.37% 87.37%
=======================================
Files 386 386
Lines 13192 13201 +9
Branches 3867 3867
=======================================
+ Hits 11526 11534 +8
- Misses 1300 1301 +1
Partials 366 366
Flags with carried forward coverage won't be shown. Click here to find out more.
|
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.
Really digging this direction in general - especially with the added test coverage! Thanks for sending!
Gave a general / high-level review, but since generated-rule-docs.ts
is breaking, I'll wait to get more detailed. Hopefully getting that to work isn't too tricky? Explicitly marking this PR as draft till then - happy to re-review once it is (or give input if getting it to work is unclear). Cheers!
exceptAfterOverload: { | ||
type: 'boolean', | ||
default: true, | ||
const schema = Object.values( |
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.
[Praise] Nifty trick to use Object.values
to convert the string index style object to an array!
@JoshuaKGoldberg thanks for the quick review! I think I've got everything working now and have addressed the initial feedback, so it's ready for a more detailed review. |
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.
LGTM, thanks! Since @bradzacher has been looking in this area, I'll defer to Brad's second opinion for a review.
Co-authored-by: Josh Goldberg ✨ <git@joshuakgoldberg.com>
What I'll get you to do is rebase this on top of #6939 (which includes #6899 that's already landed in v6). So if you rebase this on my branch, we'll be able to merge your awesome validation tests into v6 and we can release it all together! Thanks so much for the work you did here - it was a good set of pointers for me to look for in the generated types when writing the schema gen tooling 😄 |
Sounds good! I'll wait for #6939 to be merged first, then rebase on top of v6 as soon as that's done. Just tried rebasing this and it was quite a pain, so probably easiest just to recreate the PR once things are more stable in this area :) |
PRs are both merged so feel free to rebase on v6 at your leisure :) |
Closing in favour of #6947 |
PR Checklist
simple-array
should be disallowed by the schema #6852, Bug: [array-type] Does not validate shape of config #6892Overview
This PR became a lot bigger than I was expecting, and was much more fiddly than I expected too. If we want, we can take d4ce93b on its own, and the rest of the changes as a separate PR - but then it won't have tests to enforce that the schema stays fixed.
### Specific array-type problem
The ultimate problem was that we were using
prefixItems
rather thanitems
. This was a problem because eslint uses an old version of ajv (v6) and and old version of the JSON schema spec (v4). See eslint/eslint#13888 (comment) and https://github.com/eslint/eslint/blob/9d1b8fc60cc31f12618e58c10a2669506b7ce9bf/lib/shared/ajv.js#L12As noted on https://json-schema.org/understanding-json-schema/reference/array.html#tuple-validation v4 of the spec didn't actually have
prefixItems
, and instead theitems
value was assumed to be prefixItems if it was an array of schemas:This in turn I think made the schema fail silently, and it just was okay validating any old random value you'd pass in.
I've fixed this by changing from
prefixItems
toitems
.Adding tests
Actually figuring this out was pretty difficult. To help me get there, I wrote some tests that validated the schemas generally against the same options as eslint.
This helped find other problems with array-type, which was that you could provide any arbitrary arguments as it did not have additionalProperties disabled.
Expanding this across all the rules, using their defaultOptions as sample config (which isn't a perfect solution, but at least is easy and works in every case except 2), managed to catch a few other badly configured rules. I've addressed these problems, applying the same fixes. I've left some comments in the PR to explain changes and why they were necessary too.
The tests are by no means perfect. They don't cover every edge case, and it's very possible to have nested objects with additional arbitrary properties that shouldn't be there. But they are a step in the right direction and what I had time to implement right now :)
Note on adding ajv dependency
This is added as a devDependency, as its only used in tests. Additionally, as eslint uses the same version this doesn't actually add a real dependency, it just makes it explicit - so it doesn't slow down installs etc.