Skip to content
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

Merged
merged 8 commits into from Jul 7, 2017

Conversation

gajus
Copy link
Contributor

@gajus gajus commented Jul 1, 2017

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.

@eslintbot
Copy link

Thanks for the pull request, @gajus! I took a look to make sure it's ready for merging and found some changes are needed:

  • The commit summary needs to begin with a tag (such as Fix: or Update:). Please check out our guide for how to properly format your commit summary and update it on this pull request.

Can you please update the pull request to address these?

(More information can be found in our pull request guide.)

@mention-bot
Copy link

@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.

@eslintbot
Copy link

Thanks for the pull request, @epoberezkin! I took a look to make sure it's ready for merging and found some changes are needed:

  • The commit summary needs to begin with a tag (such as Fix: or Update:). Please check out our guide for how to properly format your commit summary and update it on this pull request.

Can you please update the pull request to address these?

(More information can be found in our pull request guide.)

@jsf-clabot
Copy link

jsf-clabot commented Jul 1, 2017

CLA assistant check
All committers have signed the CLA.

@eslintbot
Copy link

Thanks for the pull request, @epoberezkin! I took a look to make sure it's ready for merging and found some changes are needed:

  • The commit summary needs to begin with a tag (such as Fix: or Update:). Please check out our guide for how to properly format your commit summary and update it on this pull request.

Can you please update the pull request to address these?

(More information can be found in our pull request guide.)

Copy link
Member

@platinumazure platinumazure left a 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" */
Copy link
Member

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-dangleinstead?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you, I updated

@eslintbot
Copy link

Thanks for the pull request, @epoberezkin! I took a look to make sure it's ready for merging and found some changes are needed:

  • The commit summary needs to begin with a tag (such as Fix: or Update:). Please check out our guide for how to properly format your commit summary and update it on this pull request.

Can you please update the pull request to address these?

(More information can be found in our pull request guide.)

@eslintbot
Copy link

Thanks for the pull request, @epoberezkin! I took a look to make sure it's ready for merging and found some changes are needed:

  • The commit summary needs to begin with a tag (such as Fix: or Update:). Please check out our guide for how to properly format your commit summary and update it on this pull request.

Can you please update the pull request to address these?

(More information can be found in our pull request guide.)

@eslintbot
Copy link

Thanks for the pull request, @epoberezkin! I took a look to make sure it's ready for merging and found some changes are needed:

  • The commit summary needs to begin with a tag (such as Fix: or Update:). Please check out our guide for how to properly format your commit summary and update it on this pull request.

Can you please update the pull request to address these?

(More information can be found in our pull request guide.)

@eslintbot
Copy link

Thanks for the pull request, @epoberezkin! I took a look to make sure it's ready for merging and found some changes are needed:

  • The commit summary needs to begin with a tag (such as Fix: or Update:). Please check out our guide for how to properly format your commit summary and update it on this pull request.

Can you please update the pull request to address these?

(More information can be found in our pull request guide.)

@eslintbot
Copy link

LGTM

1 similar comment
@eslintbot
Copy link

LGTM

valueWithIgnore: {
anyOf: [
{
$ref: "#/defs/value"
Copy link
Member

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.

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?

Copy link
Member

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.)

Copy link

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:

  1. Leave things as they are (therefore breaking third-party rules with incorrect schemas)
  2. 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).
  3. Change Ajv settings to keep things completely as they are, quietly ignoring $ref's that cannot be found.
  4. 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.

Copy link
Member

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.

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.

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

@not-an-aardvark not-an-aardvark added chore This change is not user-facing core Relates to ESLint's core APIs and features evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion labels Jul 2, 2017
@gajus gajus changed the title refactor (WIP): replace is-my-json-valid with Ajv refactor: replace is-my-json-valid with Ajv Jul 2, 2017
@eslintbot
Copy link

LGTM

1 similar comment
@gajus
Copy link
Contributor Author

gajus commented Jul 2, 2017

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);
Copy link

@qfox qfox Jul 5, 2017

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.

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?

Copy link

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 ;-)

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");
Copy link

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.

Copy link

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.

Copy link

@qfox qfox Jul 5, 2017

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?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@not-an-aardvark not-an-aardvark added the tsc agenda This issue will be discussed by ESLint's TSC at the next meeting label Jul 6, 2017
@not-an-aardvark
Copy link
Member

not-an-aardvark commented Jul 6, 2017

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 ajv rather than is-my-json-valid.

TSC Question: Should we accept this proposal?

@not-an-aardvark not-an-aardvark added accepted There is consensus among the team that this change meets the criteria for inclusion and removed evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion tsc agenda This issue will be discussed by ESLint's TSC at the next meeting labels Jul 7, 2017
@not-an-aardvark
Copy link
Member

This was accepted in today's TSC meeting.

@not-an-aardvark not-an-aardvark merged commit 72f22eb into eslint:master Jul 7, 2017
@not-an-aardvark
Copy link
Member

Thanks for contributing!

@gajus
Copy link
Contributor Author

gajus commented Jul 7, 2017

Thank you for accepting this promptly.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion archived due to age This issue has been archived; please open a new issue for any further discussion chore This change is not user-facing core Relates to ESLint's core APIs and features
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants