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

feat!: Set default schema: [], drop support for function-style rules #17792

Merged
merged 21 commits into from Dec 27, 2023

Conversation

mdjermanovic
Copy link
Member

@mdjermanovic mdjermanovic commented Nov 25, 2023

Prerequisites checklist

What is the purpose of this pull request? (put an "X" next to an item)

[ ] Documentation update
[ ] Bug fix (template)
[ ] New rule (template)
[ ] Changes an existing rule (template)
[ ] Add autofix to a rule
[ ] Add a CLI option
[x] Add something to the core
[ ] Other, please explain:

Fixes #14709

Implements RFC Require schemas and object-style rules

This includes changes in the @eslint/eslintrc package: eslint/eslintrc#139

What changes did you make? (Give an overview)

Drop support for function-style rules:

  • Removed all places where function-style rules were being converted to object-style. Function-style rules will be passed in their original form to the rest of the code and eventually crash in the Linter.
  • In Linter, I added the following check right before running the rule's create() method:
    if (!rule || typeof rule !== "object" || typeof rule.create !== "function") then throw an error.
    This is to show a message that is more descriptive than TypeError: rule.create is not a function.
  • Since rule testers always wrap rules, I added the same check to rule testers.
  • Removed all the content from custom-rules-deprecated dost, just left a link to custom-rules, and added a note that the function style is no longer supported.
  • Removed function-style removal warnings from RuleTester, and the check that function-style rules cannot produce fixes from both rule testers.

Schema changes:

  • Updated all implementations of GetRuleOptionsSchema (flat config, eslintrc, and the one used by RuleTester) to work as follows:
    • Top-level schema property is ignored.
    • meta.schema:
      • Can be omitted, in which case this function returns a schema that disallows any options so that ESLint throws an error if any options are passed. Effectively the same as schema: []. schema: undefined is treated the same as omitted.
      • If specified, must be an array, object, or false. If any other value is specified (null, true, number...), this function throws an error.
      • Objects and arrays work the same as before.
      • For schema: false, this function returns null, meaning that options validation is disabled for the rule. The same as omitted (or falsy) before this change.
  • Changed eslintrc and flat config to throw a different error when options validation fails because of an invalid schema.
  • Changed Linter to recognize that error and rethrow it instead of creating a lint message for invalid inline config.
  • Changed both rule testers to check and throw an error for schema: {}.
  • Changed schema of the no-constructor-return rule, from {} (no-op) to [] (disallow passing options).
  • Removed missing schema warnings from RuleTester.
  • Updated custom-rules docs with the schema changes.

Is there anything you'd like reviewers to focus on?

  • Did I miss some places that should also be updated for either of these changes?
  • Function objects with a create method will also be disallowed. After this change, typeof rule must be "object".
  • Should the rules be validated (checked if the rule is an object with a create method) at some earlier point?

@eslint-github-bot eslint-github-bot bot added breaking This change is backwards-incompatible feature This change adds a new feature to ESLint labels Nov 25, 2023
Copy link

netlify bot commented Nov 25, 2023

Deploy Preview for docs-eslint canceled.

Name Link
🔨 Latest commit 27b4448
🔍 Latest deploy log https://app.netlify.com/sites/docs-eslint/deploys/658c66e74c13ec0008458bfb

@bmish
Copy link
Sponsor Member

bmish commented Nov 26, 2023

Thank you for picking this work up! I see you still have a few items on your todo list. My original draft PR may be helpful to reference: #16614

Copy link
Sponsor Member

Choose a reason for hiding this comment

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

In general, should we avoid deleting pages to avoid breaking links, and just add a placeholder page linking to the replacement page?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good idea, I've added back and updated this document in d14e2b5.

Copy link
Sponsor Member

Choose a reason for hiding this comment

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

It looks like we also could do a redirect like this: eslint/eslint.org#499

Discussion here: #17840 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

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

It looks like we also could do a redirect like this: eslint/eslint.org#499

Discussion here: #17840 (comment)

Maybe, but this is a slightly different situation so I'm not sure.

For configuration-files-new, its content is moved to configuration-files, so redirecting makes the most sense.

For custom-rules-deprecated, its content is not moved to custom-rules but deleted, so info that it no longer applies in ESLint 9+ might be more helpful.

);
}
}

/**
* Emit a deprecation warning if rule has options but is missing the "meta.schema" property
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Should be able to delete emitMissingSchemaWarning too.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I kept that for now to see if the tests for it will fail as expected after the schema default change (wip).

Copy link
Member Author

Choose a reason for hiding this comment

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

Done in 24144d4

@@ -32,18 +32,6 @@ describe("rules", () => {
assert.ok(rules.get(ruleId));
});

it("should return the rule as an object with a create() method if the rule was defined as a function", () => {
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

In my draft implementation, I updated this test to check that it throws: https://github.com/eslint/eslint/pull/16614/files#diff-0c938400b183b70017038162a127cef7bc753f98c6e2ccc97800bce0a0effcc8 But maybe you omitted this because it's tested in several other places?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I added a more general check (rule must be an object with a create method) in Linter. A test case where Rules#define is used (via Linter#defineRule) is here.

@mdjermanovic mdjermanovic changed the title feat!: Add default schema: [], drop support for function-style rules feat!: Set default schema: [], drop support for function-style rules Nov 29, 2023
@mdjermanovic
Copy link
Member Author

This is ready for review.

Copy link
Sponsor Member

@bmish bmish left a comment

Choose a reason for hiding this comment

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

Looks great! Thanks for picking up this work.

* At this point, `schema` can only be an object or `null`.
*/
if (schema && Object.keys(schema).length === 0) {
throw new Error(`\`schema: {}\` is a no-op${metaSchemaDescription}`);
Copy link
Sponsor Member

Choose a reason for hiding this comment

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

Disallowing schema: {} is most important since it's the most common, accidental, no-op schema.

I'm fine with this one check for now, but just want to ask if any other obvious/common no-op schemas come to mind? In the RFC, I mentioned schema: { type: "array" } as another no-op schema we could disallow, with the intention being to leave the door open to any others that we judge worth checking for. Obviously, we can only add update the banned no-op list during major versions.

Copy link
Member

Choose a reason for hiding this comment

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

is there a related discussion in ajv repo? it could benefit all the dependents (not just eslint) - if it could be supported by ajv.

Copy link
Sponsor Member

Choose a reason for hiding this comment

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

I did a brief search and didn't immediately find a relevant discussion in in ajv. But I have included a lot more context in this issue discussing a potential lint rule:

Overall, we should probably just allow any schema in ESLint except extremely common and obvious mistakes like {}, and consider linting to detect further possible problems.

Copy link
Member Author

Choose a reason for hiding this comment

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

Newer versions of Ajv have some built-in validations, but there were backwards compatibility issues when we tried to switch to Ajv 7+ (#13911). However, I believe schema: { type: "array" } would be considered valid by Ajv. It's just useless in ESLint because we're always passing arrays to these validations.

I agree that for now RuleTester should only check for the common and obvious mistake schema: {}.

Copy link

Hi everyone, it looks like we lost track of this pull request. Please review and see what the next steps are. This pull request will auto-close in 7 days without an update.

@github-actions github-actions bot added the Stale label Dec 10, 2023
@bmish bmish removed the Stale label Dec 10, 2023
@mdjermanovic mdjermanovic mentioned this pull request Dec 19, 2023
3 tasks
@mdjermanovic mdjermanovic added core Relates to ESLint's core APIs and features accepted There is consensus among the team that this change meets the criteria for inclusion labels Dec 19, 2023
@nzakas nzakas marked this pull request as ready for review December 20, 2023 23:10
@nzakas nzakas requested a review from a team as a code owner December 20, 2023 23:10
@nzakas
Copy link
Member

nzakas commented Dec 20, 2023

Looks like there are some merge conflicts here.

@mdjermanovic
Copy link
Member Author

I'll rebase when we merge a few more PRs that target the same files. This shouldn't be merged until we release @eslint/eslintrc (eslint/eslintrc#139) anyway.

@mdjermanovic
Copy link
Member Author

Merge conflicts are fixed now.

package.json Outdated Show resolved Hide resolved
@mdjermanovic
Copy link
Member Author

This is ready now. @nzakas would you like to take a look before merging?

Copy link
Member

@nzakas nzakas left a comment

Choose a reason for hiding this comment

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

LGTM. Please be sure to update the migration guide with info about this change.

@nzakas nzakas merged commit fb81b1c into main Dec 27, 2023
17 checks passed
@nzakas nzakas deleted the issue14709 branch December 27, 2023 18:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion breaking This change is backwards-incompatible core Relates to ESLint's core APIs and features feature This change adds a new feature to ESLint
Projects
Status: Complete
Development

Successfully merging this pull request may close these issues.

Change default rule schema to schema: [] and drop support for function-style rules
4 participants