Thanks to visit codestin.com
Credit goes to github.com

Skip to content

RFC: Making it easier to use (and extend) base eslint rules within this plugin #510

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

Closed
bradzacher opened this issue May 10, 2019 · 4 comments

Comments

@bradzacher
Copy link
Member

I have a crazy idea. It's probably a stupid idea, but it's an idea none the less..

The idea

The idea is that we should consider exporting every single base eslint rule from the plugin.
Or alternately (as @j-f1 suggested, and I kind of like), create a new plugin (@typescript-eslint/eslint-plugin-eslint-compat or similar).

Why would I suggest this?

People keep reporting issues with base eslint rules.
The chance of fixing them upstream is more often than not going to be pretty low because they (rightfully so) don't want typescript specific code within eslint - they only want their rules to handle the "true" ESTree representation.

This means that if we want to patch a rule, we have to create a rule extension within the plugin - either by copying the entire rule source and patching it, or by manipulating the AST before it reaches the base rule's code.

This works well, and we've got a number of rules which do this. The problem comes with adoption after that. Once we release a rule extension, we then need to promote to users that the new extension exists, so that they can correct their config to turn on our rule, and turn off the base rule.
This is a huge problem for users that use pre-defined config sets, because they often don't know that they have the "broken" rule turned on until it breaks for them, then they have to investigate our documentation to see if an extension exists, then investigate the config to find the existing setting value, and then make the switch.

Solutions

We can export every single base eslint rule without having to worry about copying code or fiddling with the rules by essentially doing this:

import eslintRules from 'eslint/lib/built-in-rules-index';
import ourRuleExtensions from './rules';

const exports = {
    ...Object.keys(eslintRules).reduce((acc, k) => {
      acc[`@typescript-eslint/${k}`] = eslintRules[k];
      return acc;
    }, {}),
    // override base rules with our extensions
    ...ourRuleExtensions,
}

The important bit is that last spread - we override the base rule implementations with the custom extended implementations.

Doing this would mean the following:

Positives:

  • We can bugfix eslint rules with only a minor version bump in most cases.
  • Releasing a new rule extension would be transparent to the end user - they are already using, say @typescript-eslint/new-cap, so if we patch it, they have to make zero config changes.

Negatives:

  • Makes it much harder to configure and work with other eslint configs (read on...).

Helper

This would be clunky as heck for end users who use existing community configs.
They would have to do some manual coding or copy pasting to use an existing config with our plugin. This obviously is NOT AN OPTION.

So we would have to provide a config helper function to make this work seamlessly.
This has already been proposed and thought about by the community and maintainers (i.e. #301).

The idea would be that we would provide a config to a helper function, and it would translate the resolved config so it prefixes the base rules where appropriate:

// .eslintrc.js
const { configHelper } = require('@typescript-eslint/eslint-plugin');

module.exports = typescriptEslintConfigHelper({
  extends: ["prettier"],
  rules: {
    semi: "error"
  }
})

which translates the config

{
  "rules": {
    // rules from prettier
    "@typescript-eslint/array-bracket-newline": "off",
    "@typescript-eslint/array-bracket-spacing": "off",
    "@typescript-eslint/array-element-newline": "off",
    "@typescript-eslint/arrow-parens": "off",
    // ... etc ....

    // rules from config
    "@typescript-eslint/semi": "error",
  }
}

The great thing about providing a helper is that it now requires literally zero effort for a user to use this plugin with something like airbnb's config

We could also design the helper (or helperS?) so that we can make it easier for users to work with mixed (js/ts) codebases - by ensuring that typescript specific rules (such as explicit-function-return don't get turned on for JS files).

@bradzacher bradzacher added the RFC label May 10, 2019
@bradzacher
Copy link
Member Author

bradzacher commented May 10, 2019

cc @typescript-eslint/core-team and @typescript-eslint/eslint-team
Reply here rather than the slack messages so we can centralise discussion.

Would love some input from the eslint peeps - is this a terrible idea? would this work well? Would you hate it because people might report issues upstream?

@mysticatea
Copy link
Member

Hi. Thank you very much for working on this problem.

  • Personally, I'm fine if we fix the bugs of core rules for TypeScript. Just a... I wanted the formal spec document of TypeScript ESTree AST to share it with the team. The AST spec is grounds of the review.
  • I have recognized some plugins (typescript, babel, and vue) want to replace core rules (or want to define the recommended rule that depends on the user's config; e.g. if user's config has semi rule then the recommended rule has plugin/semi rule with the same option).
    So I'm happy if we can discuss to make the feature to define the recommended rule that depends on the user's config on https://github.com/eslint/rfcs.
  • Please don't depend on internal files of eslint package (E.g. import eslintRules from 'eslint/lib/built-in-rules-index';). Internal files don't a part of public API, so we may change the file structure without notice, without major bump. In fact, currently lib/built-in-rules-index is a ReadonlyMap object and the file will be moved to lib/rules/index soon.
    If you want to get all core rules, please use public API: const {Linter} = require("eslint"); new Linter().getRules().

@bradzacher
Copy link
Member Author

Please don't depend on internal files of eslint package

That was just a quick-and-dirty example to get my point across. We would definitely use the proper avenues in the final version! :)

I wanted the formal spec document of TypeScript ESTree AST to share it with the team

Just so I'm on the same page with what you mean - In this instance, you would want us to codify all of the changes we've made in TS-ESTree as a markdown document in the ESTree spec repo?

I have recognized some plugins (typescript, babel, and vue) want to replace core rules (or want to define the recommended rule that depends on the user's config

This would be the second best solution. Only because there are so many variables with this that could make an RFC + implementation take a long time.

I think nailing down a spec doc, and fixing things directly in core would be the best solution.

@mysticatea
Copy link
Member

I'm OK if the formal spec is the .d.ts file with enough comments about how the node type and properties work. For examples of the comments,

  • What case does the parser make each node?
  • What case does the parser set a property value?
  • What are differences from the ESTree spec if a node exists in ESTree spec? When do the differences appear?

Such a document would be helpful for contributors as well.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants