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
refactor: replace is-my-json-valid with Ajv #8852
Conversation
Thanks for the pull request, @gajus! I took a look to make sure it's ready for merging and found some changes are needed:
Can you please update the pull request to address these? (More information can be found in our pull request guide.) |
@gajus, thanks for your PR! By analyzing the history of the files in this pull request, we identified @vitorbal, @not-an-aardvark and @gyandeeps to be potential reviewers. |
Thanks for the pull request, @epoberezkin! I took a look to make sure it's ready for merging and found some changes are needed:
Can you please update the pull request to address these? (More information can be found in our pull request guide.) |
Thanks for the pull request, @epoberezkin! I took a look to make sure it's ready for merging and found some changes are needed:
Can you please update the pull request to address these? (More information can be found in our pull request guide.) |
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.
I assume this is still WIP so I didn't do a full review- just noticed one tiny thing you might want to fix up in the next draft. Thanks!
lib/util/ajv.js
Outdated
}); | ||
|
||
ajv.addMetaSchema(metaSchema); | ||
/* eslint no-underscore-dangle: "off" */ |
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.
I believe this will disable the rule for the whole file. I think you might want to use // eslint-disable-next-line no-underscore-dangle
instead?
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.
thank you, I updated
Thanks for the pull request, @epoberezkin! I took a look to make sure it's ready for merging and found some changes are needed:
Can you please update the pull request to address these? (More information can be found in our pull request guide.) |
Thanks for the pull request, @epoberezkin! I took a look to make sure it's ready for merging and found some changes are needed:
Can you please update the pull request to address these? (More information can be found in our pull request guide.) |
Thanks for the pull request, @epoberezkin! I took a look to make sure it's ready for merging and found some changes are needed:
Can you please update the pull request to address these? (More information can be found in our pull request guide.) |
Thanks for the pull request, @epoberezkin! I took a look to make sure it's ready for merging and found some changes are needed:
Can you please update the pull request to address these? (More information can be found in our pull request guide.) |
LGTM |
1 similar comment
LGTM |
lib/rules/comma-dangle.js
Outdated
valueWithIgnore: { | ||
anyOf: [ | ||
{ | ||
$ref: "#/defs/value" |
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.
I haven't looked at this thoroughly yet, but as a quick question: Would this be a breaking change for rules? If some third-party rules have been relying on the (apparently nonstandard) $ref
property from is-my-json-valid
, it seems like that won't work anymore if $ref
is no longer supported in ajv.
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.
@not-an-aardvark this change only fixes the schema, it is not related to ajv. Ajv fully supports $ref (and it is standard), the changed schema also has $ref's. Should it be in a separate PR maybe? @gajus?
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.
Oh I see, I misunderstood what the change was here. I guess the more general question is: is this schema fix necessary, or would the PR still work without this schema fix? (I don't mind modifying the schema in general, but if it requires changes then that might indicate that switching to Ajv would risk breaking third-party rules.)
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.
@not-an-aardvark @gajus, now I understand the problem here. When you pass array as a rule schema (schema: [obj]
, where obj as a JSON schema) eslint internally converts it to a following JSON schema:
{
type: 'array',
items: [
obj
],
minItems: 0,
maxItems: 1
}
If there are definitions inside obj (like it is with comma-dangle) then their JSON pointers should be extended as well, otherwise they are not found. is-my-json-valid quietly ignores $ref's that are not found and ajv (by default) throws exception. This change to the schema fixes this issue. In addition there are "$refs" instead of "$ref" in several places, that are ignored by both is-my-json-valid and by ajv.
There may be some third party rules with similar incorrect usage of definitions not on the top level but inside item schema and these rules will start logging errors. Should it be a breaking change? I am not sure. These rules are not working correctly now so maybe it is ok if they have to be fixed. There are the following options:
- Leave things as they are (therefore breaking third-party rules with incorrect schemas)
- Change Ajv settings to log warnings when $ref's are not found instead of throwing exception, so things will work as they do now and all rules can be fixed separately (including comma-dangle).
- Change Ajv settings to keep things completely as they are, quietly ignoring $ref's that cannot be found.
- Improve eslint to correctly extend JSON pointers in the above scenario, so internally "#/defs/value" would become "#/items/0/defs/value". That's my least favourite option, as it can be confusing to debug and some third-party rules can still start breaking as currently they simply don't validate against these schemas and then they start, so if something was invalid it would start breaking.
I think option 2 is the safest, but let me know what you think.
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.
That makes sense, thanks for explaining. I agree that option 2 seems safest, if it's not too difficult a change. Maybe we can switch to option 1 in a future major release. Since a core rule had this problem but was otherwise working fine, it might not be safe to assume that the third-party rules with this problem would need to be fixed anyway.
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.
Thank you.
Option 2 is a very simple change, I will do it. And I will move fix for comma-dangle in a separate PR.
Option 4 could be a good change for the next major version as users probably expect that all items are validated separately and therefore use $ref's, expecting them to work, rather than ignored. So instead of saying "you can't use $ref if your rule schema is an array" or "you have to understand internal mechanics of eslint to use $ref in such cases" we can just make things work as users expect, either by validating items separately (as users think eslint does) or by transforming schemas correctly taking $ref's into account.
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 is done, fix to comma-dangle and docs update on $ref usage are in #8864
This PR replaces the validation functionality. @epoberezkin needs to have a look into how to handle the error message formatting.
LGTM |
1 similar comment
LGTM |
@@ -181,10 +186,10 @@ function formatErrors(errors) { | |||
* @returns {void} | |||
*/ | |||
function validateConfigSchema(config, source) { | |||
const validator = schemaValidator(configSchema, { verbose: true }); | |||
validateSchema = validateSchema || ajv.compile(configSchema); |
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.
Personally I'd put this function
to block code with validateSchema
variable like:
{
let validateSchema;
/**
* ...
*/
function validateConfigSchema(...) {
validateSchema = validateSchema || ajv.compile(configSchema);
// ...
}
}
It would help to read this part of code as well as help v8 to reduce variables count in context.
Or at least just put it closer to this function.
p.s. Not a blocker.
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.
@zxqfox is it better to make a commit now or make a separate PR after this one merged?
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.
I'm not sure we ever need that. It's just my personal preferences. Prob no need to do that at all ;-)
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 can be an eslint rule for that ;)
//------------------------------------------------------------------------------ | ||
|
||
const Ajv = require("ajv"), | ||
metaSchema = require("ajv/lib/refs/json-schema-draft-04.json"); |
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 already draft-06, hope you know.
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.
Ajv supports draft-06 by default - this code is needed as the schemas for all rules would be draft-04 and there are changes in draft-06 that would break some of them. So I thought that migrating to draft-06 would require a major version change as some third party rules will need to change their schemas.
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.
Ah, right. Forgot about we checking options for rules with JSON schemas. Sorry and thank you.
UPD: btw, do we want to (or can we) use "$schema": "http://json-schema.org/draft-04/schema#",
field in our rules?
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.
Thank you!
I'm not sure this actually needs to be approved by the TSC since it's just an internal change, but it seems like there was some confusion about whether it needed TSC approval, so I'll add it to the agenda for tomorrow's meeting just in case. TSC Summary: This PR proposes switching the internal schema validator to use TSC Question: Should we accept this proposal? |
This was accepted in today's TSC meeting. |
Thanks for contributing! |
Thank you for accepting this promptly. |
What is the purpose of this pull request? (put an "X" next to item)
[ ] Documentation update
[ ] Bug fix (template)
[ ] New rule (template)
[ ] Changes an existing rule (template)
[ ] Add autofixing to a rule
[ ] Add a CLI option
[X] Add something to the core
[ ] Other, please explain:
What changes did you make? (Give an overview)
Replace the ESLint rule validation library from is-my-json-valid to Ajv.
Is there anything you'd like reviewers to focus on?
WIP. Will ping when ready for review.
Motivation
Ajv has more contributors, more downloads, more regular releases. https://npmcompare.com/compare/ajv,is-my-json-valid summarizes it quite well.
From the technical perspective, the Ajv is the fastest and the most standard compliant (https://github.com/epoberezkin/ajv#performance) implementation of a JSON validator.
It is being used in webpack. I am using it in https://www.npmjs.com/package/table, https://www.npmjs.com/package/isomorphic-webpack, etc.
Finally, Ajv was originally planned to be used for ESLint (#8295 (comment)). The current error message formatting is a bit hacky as a result. Ajv (optionally) provides a custom error message abstraction via https://github.com/epoberezkin/ajv-errors.