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

New: Object support in extends, parser, plugins, and processor #54

Closed

Conversation

mysticatea
Copy link
Member

Rendered RFC

Summary

ESLint accepts objects in all of the extends, parser, plugins, and processor fields in configurations. It solves the longest standing issue #3458 "Support having plugins as dependencies in shareable config", with small breaking changes.

Related Issues

@mysticatea mysticatea added the Initial Commenting This RFC is in the initial feedback stage label May 12, 2020
@nzakas
Copy link
Member

nzakas commented May 12, 2020

👎 Given that we have already decided to do simple config, I don’t think we should be making any further changes to eslintrc. The more enhancements added, the tougher it becomes to get full compatibility with simple config.

@mysticatea
Copy link
Member Author

@nzakas I hope you read and evaluate rather than just negating.

The more enhancements added, the tougher it becomes to get full compatibility with simple config.

The enhancements of this RFC already exist in #9.

I think that we are seeing the same goal. The following items are what I want to do for the config system:

  1. Use require("...") (or ES modules in the future) in the userland to assemble configuration.
  2. Use a (top-level) array instead of the extends/overrides arrays (it improves how users configure multiple kind files).
  3. Deprecate env in favor of third-party packages and spread properties.
  4. Deprecate cascading config in favor of configurations with glob patterns.
  5. Add settings that are unrelated to files, such as formatter.

It will be very similar to #9. Just a difference is, I believe that we can do those in the current config system. This RFC does the 1. (I have plans to write RFCs for the other.)
Therefore, for persuasive, this RFC contains the concrete implementation. For example,

  • Update _loadExtends() method to support objects in extends.
  • Update _loadParser() method to support objects in parser.
  • Update _loadPlugins() and _loadExtendedPluginConfig() methods to support objects in plugins.
  • (About processor, it moved the responsibility of resolving processor into the config array from the user of the config array. Now there is resolveProcessor() function about that.)

It indicates the current codebase is well-structured and we need only small changes to support those. And the small change (rather than a complete replacement that has migration pain) can resolve our longest standing issue with no breaking changes.

And while discussion of #9, some concept (plugins/extends) revived. The difference between two config systems is going to smaller.

Copy link
Member

@kaicataldo kaicataldo left a comment

Choose a reason for hiding this comment

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

While I understand the desire to incrementally change our config system, I do feel strongly that it has gotten extremely complex and unwieldy.

A large number of issues we see from end users are about configuration loading, and despite the complexity involved in our tool, I think we have a problem of valuing flexibility over usability. I agree that flexibility is important, but I really think we should be optimizing for the "happy path" (majority use case), and I believe that requires a holistic approach, rather than a large number of incremental changes.

While the individual changes all make sense, we now have bolted on so many different CLI flags and extra features that I personally am supportive of pursuing a new approach to configuration, where we can design the feature set up front and have a better understanding of the usability of the integration of all the features.

Edit: Split up the wall of text into paragraphs so it's easier to read.

I think that we can do a softer way than the complete replacement because:

- While discussions, the `plugins` field revived and the `extends` field or similar thing is going to revive. It's going to be similar to the existing config system except `env` and cascading.
- That RFC has brought the config array concept and we have done huge refactoring with the concept. Currently, the config loading logic is well-structured and maintainable.
Copy link
Member

Choose a reason for hiding this comment

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

I would argue that our current configuration logic is actually quite complex and hard to understand. Maybe this is just a limitation of the amount of knowledge I can personally keep in my head, but I know that I often have to go back to the source code when I'm triaging issues. And if I'm having difficulties keeping track of how our configuration works, I think it's a safe bet that our end users are struggling as well.

Copy link
Member Author

Choose a reason for hiding this comment

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

This RFC is what reduces the complexity by the same approach as #7; it expects users to assemble configuration by JS code and well-known require(...) instead of ESLint's own resolving logic.

@bradzacher
Copy link

In support of @kaicataldo's comment, from the perspective of a large plugin maintainer.
The configuration complains I hear from user usually fall into a few categories:

  • misconfigured rule entirely due to not understanding how to configure eslint
    • usually things like not understanding why "<rulename>": { ruleOption: value } doesn't work
    • completely user error in them not actually reading docs - usually link to the eslint docs on configuring rules
      • side note and completely off-topic - why is the section on configuring rules 1/2 way down this page, and why is the configuration file section not the first section?
  • figuring out how to change config for different paths (or more importantly in our case, different file extensions)
    • I usually point them at the overrides config docs.
    • it's non-trivial to set this up and can quickly become hard to reason about what config is applying in what way
  • debugging why rule X is turned on for file Y
    • I usually point them at --print-config, which is a start for proving the rule is configured in a certain way.
    • Most of the time though it is non-trivial to figure out why the file's config is resolved to be what it is.
    • Figuring it out involves manually evaluating and tracing all of the overrides to figure out where the problem is
      • Sometimes also involves them manually exploring extends configs.
    • Often the users will just say "eff it", and use another overrides to "correct" the config.
      • This ofc makes it harder to debug the next configuration issue...

As an interesting aside, at facebook (because of the history of their linting tooling) the configuration is done not via an .eslintrc file, but instead by a configuration function which looks like:

function getConfig(path: string, contents: string): ESLintConfig {
  const isProjectX = match(path, '/foo/bar/X');
  const isProjectY = match(path, '/foo/bar/Y');
  // ...
  const isFlow = match(contents, '@flow');
  // ...
  const isTypeScript = match(path, '.ts$');

  const config = { parser: 'babel-eslint', rules: { ... } };

  if (isProjectX) {
    Object.assign(config.rules, { ... });
  }

  if (isProjectY) {
    Object.assign(config.rules, { ... });
  }

  // ...

  if (isTypeScript) {
    config.parser = '@typescript-eslint/parser';
    config.plugins.push('@typescript-eslint');
    Object.assign(config.rules, { ... });
  }

  return config;
}

The file itself is a bit of a mess (5k lines), but it is surprisingly easy to reason about the config for a specific file because ultimately it's all just code which can be debugged with any debugging tool.

@mysticatea
Copy link
Member Author

mysticatea commented May 26, 2020

@kaicataldo Which do you talk about the complexity of either userland or our code?


@bradzacher Thank you for your feedback!

  • misconfigured rule entirely due to not understanding how to configure eslint

Yes, I agree. I feel hard to understand our documentation, too.
I appreciate the Season of Docs in progress.

  • figuring out how to change config for different paths (or more importantly in our case, different file extensions)
  • debugging why rule X is turned on for file Y

As an interesting aside, at facebook (because of the history of their linting tooling) the configuration is done not via an .eslintrc file, but instead by a configuration function which looks like:

Interesting.
Both this RFC and #9 don't solve that problem because configuration files are tied project rather than each file. To solve it, probably we can split configuration for file iteration and for linting.

export const projectConfig = {
    includes: ["src/**/*"],
    excludes: ["node_modules/*"],
    formatter: "stylish",
    formatterOptions: {...},
    concurrency: "auto",
}

export function getConfigForFile(filePath) {
    // Assemble config for a file.
    return {...}
}

Also, for the current config system, we can improve --print-config to display where each rule came from. It's very easy.

@btmills
Copy link
Member

btmills commented Jul 16, 2020

Thank you all for the discussion in this thread and particularly @mysticatea for putting the RFC together! Now that we've decided on a direction to take configs going forward in #9, the TSC decided today to close the other config-related RFCs so we can focus on the new format. You can follow along with that implementation work at eslint/eslint#13481.

@btmills btmills closed this Jul 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Initial Commenting This RFC is in the initial feedback stage
Projects
None yet
5 participants