├── .github
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── social-rfc-update.yml
├── README.md
├── designs
├── 2018-processors-improvements
│ └── README.md
├── 2018-simplified-package-loading
│ └── README.md
├── 2019-additional-lint-targets
│ └── README.md
├── 2019-allow-all-directives-in-line-comments
│ └── README.md
├── 2019-changing-base-path-in-config-files-that-cli-options-specify
│ └── README.md
├── 2019-config-simplification
│ └── README.md
├── 2019-config-tester
│ └── README.md
├── 2019-core-options
│ └── README.md
├── 2019-deprecating-personal-config
│ └── README.md
├── 2019-description-in-directive-comments
│ └── README.md
├── 2019-drop-node8
│ └── README.md
├── 2019-esm-compatibilty
│ └── README.md
├── 2019-expose-rules-to-formatters
│ └── readme.MD
├── 2019-move-to-async-api
│ └── README.md
├── 2019-pass-cwd-from-cli-engine
│ └── README.md
├── 2019-plugin-loading-improvement
│ └── README.md
├── 2019-plugin-root-path-flag
│ └── README.md
├── 2019-processor-shared-settings
│ └── README.md
├── 2019-recoverable-error-handling
│ └── README.md
├── 2019-rule-tester-improvements
│ └── README.md
├── 2019-suggestions
│ └── README.md
├── 2019-update-default-ignore-patterns
│ └── README.md
├── 2019-variable-definition-information-of-config-files
│ └── README.md
├── 2020-cache-contents-option
│ └── README.md
├── 2020-cwd-in-formatters
│ └── README.md
├── 2020-es-module-support
│ └── README.md
├── 2020-generic-ast-support
│ └── README.md
├── 2020-rule-tester-only
│ └── README.md
├── 2020-timing-list-size
│ └── README.md
├── 2021-break-on-parsing-errors
│ └── README.md
├── 2021-fixable-disable-directives
│ └── design.md
├── 2021-init-command-eslint-cli
│ └── README.md
├── 2021-package-exports
│ └── README.md
├── 2021-schema-object-rules
│ └── README.md
├── 2021-stricter-rule-test-validation
│ └── README.md
├── 2021-suppression-support
│ ├── ESLint.png
│ ├── README.md
│ ├── design_diagram.png
│ └── violation.png
├── 2022-community-eslint-org
│ └── README.md
├── 2022-docs-information-architecture-update
│ └── README.md
├── 2022-languages
│ └── README.md
├── 2022-suggestion-parse-errors
│ └── README.md
├── 2022-supress-ignored-file-warnings
│ └── README.md
├── 2022-unused-disable-directive-flexible-config
│ └── README.md
├── 2023-only-run-reporting-rules
│ └── README.md
├── 2023-rule-options-defaults
│ └── README.md
├── 2023-rule-performance-statistics
│ └── README.md
├── 2023-test-rule-errors
│ └── README.md
├── 2024-baseline-support
│ └── README.md
├── 2024-config-extends
│ └── README.md
├── 2024-config-lookup-from-file
│ └── README.md
├── 2024-deprecated-rule-metadata
│ └── README.md
├── 2024-hooks-for-test-cases
│ └── README.md
├── 2024-multithread-linting
│ └── README.md
├── 2024-repo-ecosystem-plugin-tests
│ └── README.md
├── 2024-report-unused-inline-configs
│ └── README.md
├── 2024-support-ts-config-files
│ └── README.md
└── 2025-base-path-in-config-objects
│ └── README.md
└── templates
└── design.md
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Summary
2 |
3 |
4 |
5 | ## Related Issues
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.github/workflows/social-rfc-update.yml:
--------------------------------------------------------------------------------
1 | name: Post RFC Social Update
2 | on:
3 | # Can't use pull_request because it won't have access to secrets
4 | pull_request_target:
5 | types: [labeled, closed]
6 | jobs:
7 | tweetStateChange:
8 | runs-on: ubuntu-latest
9 | if: github.event.action == 'labeled' && contains(github.event.label.name, 'Commenting')
10 | steps:
11 | - uses: actions/setup-node@v3
12 | with:
13 | node-version: '18.x'
14 | - run: npx @humanwhocodes/crosspost -t -b -m "The RFC '$TITLE' is now in the ${{ github.event.label.name }} phase.\n\n${{ github.event.pull_request.html_url }}"
15 | env:
16 | TITLE: ${{ github.event.pull_request.title }}
17 | TWITTER_API_CONSUMER_KEY: ${{ secrets.TWITTER_CONSUMER_KEY }}
18 | TWITTER_API_CONSUMER_SECRET: ${{ secrets.TWITTER_CONSUMER_SECRET }}
19 | TWITTER_ACCESS_TOKEN_KEY: ${{ secrets.TWITTER_ACCESS_TOKEN_KEY }}
20 | TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
21 | MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }}
22 | MASTODON_HOST: ${{ secrets.MASTODON_HOST }}
23 | BLUESKY_IDENTIFIER: ${{ vars.BLUESKY_IDENTIFIER }}
24 | BLUESKY_PASSWORD: ${{ secrets.BLUESKY_PASSWORD }}
25 | BLUESKY_HOST: ${{ vars.BLUESKY_HOST }}
26 |
27 | tweetMerge:
28 | runs-on: ubuntu-latest
29 | if: github.event.action == 'closed' && github.event.pull_request.merged && contains(github.event.pull_request.labels.*.name, 'Final Commenting')
30 | steps:
31 | - uses: actions/setup-node@v3
32 | with:
33 | node-version: '18.x'
34 | - run: npx @humanwhocodes/crosspost -t -b -m "The RFC '$TITLE' has been approved and merged!\n\n${{ github.event.pull_request.html_url }}"
35 | env:
36 | TITLE: ${{ github.event.pull_request.title }}
37 | TWITTER_API_CONSUMER_KEY: ${{ secrets.TWITTER_CONSUMER_KEY }}
38 | TWITTER_API_CONSUMER_SECRET: ${{ secrets.TWITTER_CONSUMER_SECRET }}
39 | TWITTER_ACCESS_TOKEN_KEY: ${{ secrets.TWITTER_ACCESS_TOKEN_KEY }}
40 | TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
41 | MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }}
42 | MASTODON_HOST: ${{ secrets.MASTODON_HOST }}
43 | BLUESKY_IDENTIFIER: ${{ vars.BLUESKY_IDENTIFIER }}
44 | BLUESKY_PASSWORD: ${{ secrets.BLUESKY_PASSWORD }}
45 | BLUESKY_HOST: ${{ vars.BLUESKY_HOST }}
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ESLint RFCs
2 |
3 | Many changes, including bug fixes and documentation improvements, can be
4 | implemented and reviewed via the normal GitHub pull request workflow.
5 |
6 | However, some changes are "substantial", and we ask that these be put
7 | through a bit of a design process and produce a consensus among the ESLint Technical Steering Committee (TSC).
8 |
9 | The "RFC" (request for comments) process is intended to provide a
10 | consistent and controlled path for new features to enter the project.
11 |
12 | ## When you need to follow this process
13 |
14 | You need to follow this process if you intend to make "substantial"
15 | changes to any part of the ESLint project or its documentation. What constitutes a
16 | "substantial" change is evolving based on community norms, but may
17 | include the following.
18 |
19 | * A new ESLint command line option.
20 | * A new feature of ESLint core.
21 | * A refactoring of existing ESLint core functionality.
22 | * Any breaking change.
23 |
24 | In addition to these, the TSC may request an RFC for any other change that it deems "substantial" based on the size or scope of the request.
25 |
26 | If you submit a pull request to implement a new feature without going
27 | through the RFC process, it may be closed with a polite request to
28 | submit an RFC first.
29 |
30 | ## Gathering feedback before submitting
31 |
32 | Before submitting an RFC, open an issue in the repository where you'd like to make the change (for example, to make a change to ESLint, open an issue in the [eslint/eslint](https://github.com/eslint/eslint) repository). The purpose of opening the issue is to gather feedback from the community and the ESLint team to ensure that there is enough interest to put together an RFC.
33 |
34 | Once the ESLint team has indicated that there is enough interest, you'll be asked to create an RFC to describe the change you'd like to make in more detail.
35 |
36 | **Note:** Please do not submit a pull request with your prototype or proof-of-concept. Instead, include a link to your fork or branch when you submit your RFC (see next section).
37 |
38 | ## How to submit an RFC to this repo
39 |
40 | To submit a new RFC, follow these steps:
41 |
42 | 1. [Fork](https://github.com/eslint/rfcs/fork) the RFC repo.
43 | 1. Create a directory inside of the `designs` directory. The directory name should begin with the year and include a meaningful description, such as `designs/2018-typescript-support`.
44 | 1. Copy the appropriate template file from the `templates` directory into the appropriate `designs` subdirectory (such as `designs/2018-typescript-support/README.md`). Be sure to name your file `README.md` so it is easily viewable in the GitHub interface.
45 | 1. If you want to include images in your RFC, place them in the same directory as the `README.md`.
46 | 1. Fill in the RFC. Please fill in every section in the template with as much detail as possible.
47 | 1. Submit a pull request to this repo with all of your files. This begins the approval process (detailed below).
48 | 1. RFCs that are accepted will be merged directly into this repo; RFCs that are not accepted will have their pull requests closed without merging.
49 |
50 | ## The RFC Approval Process
51 |
52 | When an RFC is submitted, it goes through the following process:
53 |
54 | 1. **Initial commenting period (21-90 days)** - the community and ESLint team are invited to provide feedback on the proposal. During this period, you should expect to update your RFC based on the feedback provided. Very few RFCs are ready for approval without edits, so this period is important for fine-tuning ideas and building consensus. (A PR in the initial commenting period has the **Initial Commenting** label applied.) The initial commenting period must last at least 21 days and not more than 90 days to allow the team and community enough time to comment. The TSC may decide to reject the RFC without promoting it to the final commenting period.
55 | 1. **Final commenting period (7 days)** - when all feedback has been addressed, the pull request author requests a final commenting period where ESLint TSC members provide their final feedback and either approve of the pull request or state their disagreement. (A PR in the final commenting period has the **Final Commenting** label applied.) ESLint TSC members are notified through GitHub when an RFC has passed into the final commenting period.
56 | 1. **Approval and Merge** - if the TSC reaches consensus on approving the RFC, the pull request will be merged. If consensus is not reached on the pull request then the RFC will be discussed at the next TSC meeting to determine whether or not to move forward. Approving and merging an RFC signifies that the TSC believes the change is directionally correct and wants to see it implemented in ESLint. An RFC does not need to be a final specification because we can make tweaks and consider edge cases during the implementation phase.
57 |
58 | **Note:** If two or more RFCs attempt to address the same problem or need, the TSC will consider all of the competing RFCs together to determine which RFC to approve. Alternately, the TSC may request that competing RFCs be merged in order to create a hybrid solution.
59 |
60 | ## The RFC Lifecycle
61 |
62 | Once an RFC is merged into this repo, then the authors may implement it and submit a pull request to the appropriate ESLint repo without opening an issue. Note that the implementation still needs to be reviewed separate from the RFC, so you should expect more feedback and iteration.
63 |
64 | If the RFC authors choose not to implement the RFC, then the RFC may be implemented by anyone. There is no guarantee that RFCs not implemented by their author will be implemented by the ESLint team.
65 |
66 | Changes to the design during implementation should be reflected by updating the related RFC. The goal is to have RFCs to look back on to understand the motivation and design of shipped ESLint features.
67 |
68 | ## Implementing an RFC
69 |
70 | The author of an RFC is not obligated to implement it. Of course, the
71 | RFC author (like any other developer) is welcome to post an
72 | implementation for review after the RFC has been accepted.
73 |
74 | When a pull request has implemented an RFC, the RFC should be updated with a link
75 | to the PR implementing it.
76 |
77 | **Thanks to the [Ember RFC process](https://github.com/emberjs/rfcs) for the inspiration for ESLint's RFC process.**
78 |
--------------------------------------------------------------------------------
/designs/2019-additional-lint-targets/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-05-12
2 | - RFC PR: https://github.com/eslint/rfcs/pull/20
3 | - Authors: Toru Nagashima ([@mysticatea](https://github.com/mysticatea))
4 |
5 | # Configuring Additional Lint Targets with `.eslintrc`
6 |
7 | ## Summary
8 |
9 | This proposal adds the ability to specify additional target files into configuration files. This enhancement will solve the pain that people have to use the `--ext` option with wanted file extensions even if they use plugins which support additional file types.
10 |
11 | ## Motivation
12 |
13 | People have to use the `--ext` option or glob patterns to check wanted files even if they use plugins which support additional file types.
14 |
15 | ```yml
16 | plugins:
17 | - markdown
18 | - html
19 | - "@typescript-eslint"
20 | - react
21 | - vue
22 | ```
23 |
24 | ```bash
25 | # ESLint checks only `*.js`.
26 | eslint src docs
27 |
28 | # Needs `--ext` option
29 | eslint src docs --ext .js,.md,.html,.ts,.jsx,.tsx,.vue
30 | ```
31 |
32 | However, using plugins which support additional file types is the intention that wants to check those files. The requirement of redundant CLI options is not reasonable.
33 |
34 | Per "[Related Discussions](#related-discussions)" section, this is very regularly requested; I want to configure additional target files with `.eslintrc`.
35 |
36 | ## Detailed Design
37 |
38 | This proposal enhances `overrides` property of the config file.
39 |
40 | - If a config file in a directory has `overrides` property, ESLint checks the files which are matched by any of override entries (i.e., `files`/`excludedFiles` criteria) additionally in the directory.
41 | - If any of `files` value of an override entry ended with `*`, this enhancement doesn't use the entry in order to avoid checking too many kinds of files.
42 | - This enhancement affects only the case where a directory path is provided on the CLI. If people provide glob patterns on the CLI, ESLint behaves the same as currently.
43 | - The `--ext` option precedences this enhancement. If the `--ext` option was given, this enhancement is disabled. So people can use the current behavior by `--ext .js`.
44 | - The ignoring configuration (`.eslintignore`) precedences this enhancement. If `.eslintignore` contains the additional target files, ESLint just ignores those as same as currently.
45 |
46 | The `overrides` property means that people intend to check those files. So this behavior is intuitive.
47 |
48 |
49 | 💡 Example:
50 |
51 | overrides:
52 | - files: "*.ts"
53 | parser: "@typescript-eslint/parser"
54 | - files: "tests/**/*"
55 | env: { mocha: true }
56 |
57 |
58 | With the above config, `eslint .` command will check `*.ts` files additionally. But the command doesn't check all files inside `tests` directory.
59 | |
60 |
61 | ### Code blocks
62 |
63 | If a code block that processors extracted has the virtual filename, ESLint filters the file extension of the virtual filename with `--ext` option. This enhancement affects to that check. ESLint lints the code block if the `overrides` matches the virtual filename.
64 |
65 | ### Legacy file extension processors
66 |
67 | This enhancement **doesn't** affect legacy file extension processors. This means that `plugins` property in config files never changes the kinds of target files.
68 |
69 | On the other hand, `extends` property in config files can change the kinds of target files by the `overrides` property in the shareable configs.
70 |
71 | ### Implementation
72 |
73 | - For files, we can add the check to [lib/cli-engine/file-enumerator.js#L410](https://github.com/eslint/eslint/blob/553795712892c8350b1780e947f65d3c019293a7/lib/cli-engine/file-enumerator.js#L410).
74 | - For code blocks, we can add the check to [lib/cli-engine/cli-engine.js#L248](https://github.com/eslint/eslint/blob/21f3131aa1636afa8e5c01053e0e870f968425b1/lib/cli-engine/cli-engine.js#L248).
75 |
76 | ## Documentation
77 |
78 | This enhancement needs migration guide because of a breaking change.
79 |
80 | - If your config contains `overrides` property, `eslint` command with directory paths lints the files which are matched automatically. This may increase errors of your command.
81 | If you don't want to add file types to check, please use glob patterns instead of directory paths or `--ext .js` option.
82 |
83 | This enhancement, so it needs to update some documents.
84 |
85 | - In the description of `--ext` CLI option, it should say that your config file may add file types automatically.
86 | - In the description of `overrides` property, it should say that the `overrides[].files` property adds target files automatically.
87 |
88 | ## Drawbacks
89 |
90 | - This is a breaking change.
91 | - Implicit behavior may be confusing people.
92 |
93 | ## Backwards Compatibility Analysis
94 |
95 | In the following situation, `eslint` command increases errors.
96 |
97 | - Their configuration has `overrides` property (with `files` property which doesn't end with `*`).
98 | - Their `eslint` command is using directory paths without `--ext` option.
99 |
100 | I guess that the impact is limited because people use `--ext` option or glob patterns if they use configuration which affects files other than `*.js`.
101 |
102 | ## Alternatives
103 |
104 | - To add `extensions` property that behaves as same as `--ext` option. Explicit reduces confusion. However, plugins and shareable configs cannot add the `extensions` property without the breaking change that drops old ESLint support.
105 |
106 | ## Open Questions
107 |
108 | -
109 |
110 | ## Frequently Asked Questions
111 |
112 | -
113 |
114 | ## Related Discussions
115 |
116 | - [eslint/eslint#801](https://github.com/eslint/eslint/issues/801) - Configurable Extension Filter
117 | - [eslint/eslint#1674](https://github.com/eslint/eslint/issues/1674) - Allow setting file extensions in .eslintrc
118 | - [eslint/eslint#2274](https://github.com/eslint/eslint/issues/2274) - Configure file extensions in .eslintrc
119 | - [eslint/eslint#2419](https://github.com/eslint/eslint/issues/2419) - Allow configuration of default JS file types
120 | - [eslint/eslint#7324](https://github.com/eslint/eslint/issues/7324) - File extensions in .eslintrc
121 | - [eslint/eslint#8399](https://github.com/eslint/eslint/issues/8399) - Add .jsx to the default --ext extensions list
122 | - [eslint/eslint#10828](https://github.com/eslint/eslint/issues/10828) - Support specifying extensions in the config
123 | - [eslint/eslint#11223](https://github.com/eslint/eslint/issues/11223) - Add --ext to `eslintrc` or other config files
124 |
--------------------------------------------------------------------------------
/designs/2019-allow-all-directives-in-line-comments/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-07-22
2 | - RFC PR: (leave this empty, to be filled in later)
3 | - Authors: Jordan Eldredge
4 |
5 | # Allow All Directives in Line Comments
6 |
7 | ## Summary
8 |
9 | ESLint currently only allows the following types of directives to be used inside of block comments (`/* Block Comment */`):
10 |
11 | * `exported`
12 | * `global(s)`
13 | * `eslint-(en|dis)able`
14 | * `eslint`
15 |
16 | Currently if a user adds one of these directives in a line comment (`// Line Comment`) the directive will be silently ignored. Which can be very confusing.
17 |
18 | I propose that we allow all directives to work in either type of comment.
19 |
20 | ## Motivation
21 |
22 | As a user of ESLint I occasionally wish to disable a rule, declare a valid global or add some other file level config. In doing so I often forget that these directives are only valid inside block level comments and add them as inline comments by mistake. When the comment fails to have its desired effect I consult the documentation to see what I did wrong. Did I remember the directive syntax or name incorrectly? On more than one occasion the problem has been that I used the wrong kind of comment.
23 |
24 | It would be nice if I didn't have to think about the comment type that I'm using.
25 |
26 | ## Detailed Design
27 |
28 | The function `getDirectiveComments` in [`linter.js`](https://github.com/eslint/eslint/blob/1fb362093a65b99456a11029967d9ee0c31fd697/lib/linter/linter.js#L263) currently does an `if (comment.type === "Block")` check before it looks for some types of directives.
29 |
30 | I believe we could simply remove that check.
31 |
32 | For `/* eslint-env */` directives which, by necessity, are parsed out of the file before an AST is avaliable, the regular expression in [linter.js](https://github.com/eslint/eslint/blob/fb08b7c9d28bc68864eb940e26df274059228b6a/lib/linter/linter.js#L406) would need to be updated to allow it to match line comments.
33 |
34 | ## Documentation
35 |
36 | We would need to update the [configuration](https://eslint.org/docs/user-guide/configuring#using-configuration-comments) documentation to remove the places where it states that some types of directives must use block comments. We would replace each of those comments with a note that "Before version _x.x_ this directive only worked in block comments" so that users using an older version of ESLint could figure out why their line comments were ignored.
37 |
38 | ## Drawbacks
39 |
40 | I don't see any drawbacks to this change.
41 |
42 | ## Backwards Compatibility Analysis
43 |
44 | While this change is backwards compatible in that it does not change any existing API contract, there is a possiblity that user's code bases will contain [accidental directives](#accidental-directives) (see below) which could potentially mean that upgrading to a version that contained this change would result in lint errors when it did not before.
45 |
46 | Therefore, we will consider this change a _breaking change_.
47 |
48 | ### Latent Directives
49 |
50 | Users may have added directives to their code using line comments and then never noticed that they had no effect. With this change, they will now begin functioning again. In some cases this may be welcome, since the user's original intention would finally be respected. In other cases the code may have changed since the user added the latent directive. In that case this change may cause an unwanted directive to be enabled.
51 |
52 | Anecdotally, I did an audit of a large swath of our code base and discovered only two directives which were being ignored due to their comment type. In both cases, enabling would be the correct behavior.
53 |
54 | ### Accidental Directives
55 |
56 | Users may also have written inline comments which were not intended as a directive, but parse as directives. I found one example in ESLint's own codebase:
57 |
58 | > `lineNumTokenBefore = 0; // global return at beginning of script`
59 |
60 | -- [newline-before-return.js:173](https://github.com/eslint/eslint/blob/02d7542cfd0c2e95c2222b1e9e38228f4c19df19/lib/rules/newline-before-return.js#L137)
61 |
62 | This would result in the following, somewhat confusing, error after upgrading to a version that contained this change:
63 |
64 | ```
65 | /Users/jeldredge/projects/eslint/lib/rules/newline-before-return.js
66 | 137:51 error 'return' is defined but never used no-unused-vars
67 | 137:58 error 'at' is defined but never used no-unused-vars
68 | 137:61 error 'beginning' is defined but never used no-unused-vars
69 | 137:71 error 'of' is defined but never used no-unused-vars
70 | 137:74 error 'script' is defined but never used no-unused-vars
71 |
72 | ✖ 5 problems (5 errors, 0 warnings)
73 | ```
74 |
75 | ## Alternatives
76 |
77 | An earlier revision of this RFC suggested reporting an error/warning when a directive that is only supported in a block comment was found in a line comment. See the following section for a discussion of why this may be a worth doing in addition to the changes detailed above.
78 |
79 | ## Optional Short Term Improvements
80 |
81 | Since code bases may contain line comments which were not intended as directives, but would be parsed as such after this change, (see [Accidental Directives](#accidental-directives) above) this change will be a breaking change and thus will need to wait for a major release. If the next major release is far off we could employ a short term fix of making line comment directives warnings which could be shipped in a minor release.
82 |
83 | This would have two benefits:
84 |
85 | 1. In the time between when the minor release ships and when the next major release ships, user would get explicit feedback when they accidentally write a line comment directive when ESLint is expecting a block comment directive rather than having it silently fail.
86 | 2. Accidental directives would be surfaced as errors in the minor version which means that those who adopt the minor release before the next major release, and resolved all new warnings would have a smooth upgrade experience.
87 |
88 | ### Implementation of Warnings
89 |
90 | If we decide to do the additional work of adding warnings in a minor version, here is how it could be implemented:
91 |
92 | The function `getDirectiveComments` in [`linter.js`](https://github.com/eslint/eslint/blob/1fb362093a65b99456a11029967d9ee0c31fd697/lib/linter/linter.js#L263) already has some examples of reporting problems with directive comments via the `createLintingProblem` function.
93 |
94 | Currently `getDirectiveComments` works by first handling `eslint-disable-(next-)line` directives, which are comment type agnostic, and then only looking for the other types of directives if the comment type is `Block`.
95 |
96 | Instead, after handling the `eslint-disable-(next-)line` directives, we could check to see if we are in an inline comment. If we are, we could add a problem with something like:
97 |
98 | ```JavaScript
99 | problems.push(createLintingProblem({
100 | ruleId: null,
101 | message: `The ${match[1]} directive is only allowed in block comments.`,
102 | loc: comment.loc
103 | }));
104 | ```
105 |
106 | ## Open Questions
107 |
108 | As far as I am aware, there are no open questions.
109 |
110 | ## Help Needed
111 |
112 | I expect that I could make the pull request myself without help.
113 |
114 | ## Related Discussions
115 |
116 | * [Warn when ESLint directives which expect to be used in block level comments are used in inline comments #12014](https://github.com/eslint/eslint/issues/12014)
--------------------------------------------------------------------------------
/designs/2019-changing-base-path-in-config-files-that-cli-options-specify/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-09-18
2 | - RFC PR: https://github.com/eslint/rfcs/pull/37
3 | - Authors: Toru Nagashima
4 |
5 | # Changing Base Path of `overrides` and `ignorePatterns` that CLI Options Specify
6 |
7 | ## Summary
8 |
9 | This RFC changes the base path of `overrides`, `ignorePatterns`, and `.eslintignore` from the directory which contains the config file to the current working directory if the config file was specified by CLI options `--config` or `--ignore-path`.
10 |
11 | ## Motivation
12 |
13 | Currently, the base path of `overrides`, `ignorePatterns`, and `.eslintignore` is the directory which contains the config file even if the config file was specified by CLI options `--config` or `--ignore-path`.
14 |
15 | ```bash
16 | # the paths of 'overrides' and 'ignorePatterns' are relative to 'node_modules/my-config/'
17 | eslint lib --config node_modules/@me/my-config/.eslintrc.js
18 | # the paths in `.eslintignore` are relative to 'node_modules/my-config/'
19 | eslint lib --ignore-path node_modules/@me/my-config/.eslintignore
20 | ```
21 |
22 | This is a barrier to use `--config`/`--ignore-path` option with shared files. If we change the base path to the current working directory, it will resolve this problem.
23 |
24 | ### .gitignore and core.excludesFile
25 |
26 | `.eslintignore` has been designed as similar stuff to `.gitignore`. Therefore, it's better if the feature around `.eslintignore` is similar to `.gitignore`.
27 |
28 | Git doesn't have `--ignore-path`-like CLI option, but has `core.excludesFile` setting to specify additional ignore file.
29 |
30 | ```bash
31 | # Use './config/ignore' file as '.gitignore'.
32 | git config core.excludesFile config/ignore
33 | ```
34 |
35 | In this case, the base path of the paths in `config/ignore` is the repository root. For example, `/node_modules` in `config/ignore` is `./node_modules` rather than `./config/ignore/node_modules`. This behavior is different from our `--ignore-path` option.
36 |
37 | ## Detailed Design
38 |
39 | It changes the base path of the following patterns to the current working directory:
40 |
41 | - In the file which is specified by the `--config` option:
42 | - `overrides[i].files`
43 | - `overrides[i].excludedFiles`
44 | - `ignorePatterns`
45 | - In the file which is specified by the `--ignore-path` option:
46 | - file patterns.
47 |
48 | ```bash
49 | # the paths of 'overrides' and 'ignorePatterns' are relative to './'
50 | eslint lib --config node_modules/@me/my-config/.eslintrc.js
51 | # the paths in `.eslintignore` are relative to './'
52 | eslint lib --ignore-path node_modules/@me/my-config/.eslintignore
53 | ```
54 |
55 | This RFC *doesn't* change the resolving logic of `extends`, `parser`, and `plugins`.
56 |
57 | ### Implementation
58 |
59 | It modifies [`createCLIConfigArray()` function](https://github.com/eslint/eslint/blob/869f96aa87c4f990f54e1eeccb0e3f7dbd66e6c2/lib/cli-engine/cascading-config-array-factory.js#L133-L165). In the function, it modifies the elements of the config arrays which are created from the `ignorePath` (corresponds to `--ignore-path`) and the `specificConfigPath` (corresponds to `--config`). It modifies `element.criteria.basePath` (corresponds to `overrides`) and `element.ignorePattern.basePath` (corresponds to `ignorePatterns` and `.eslintignore`) to `cwd`.
60 |
61 | ## Documentation
62 |
63 | This change needs the migration guide because of a breaking change.
64 |
65 | This change needs to update the following documents:
66 |
67 | - the "[Configuration Based on Glob Patterns » Relative glob patterns](https://github.com/eslint/eslint/blob/869f96aa87c4f990f54e1eeccb0e3f7dbd66e6c2/docs/user-guide/configuring.md#relative-glob-patterns)" section.
68 | - the "[Ignoring Files and Directories » `ignorePatterns` in config files](https://github.com/eslint/eslint/blob/869f96aa87c4f990f54e1eeccb0e3f7dbd66e6c2/docs/user-guide/configuring.md#ignorepatterns-in-config-files)" section.
69 | - the "[Ignoring Files and Directories » `.eslintignore`](https://github.com/eslint/eslint/blob/869f96aa87c4f990f54e1eeccb0e3f7dbd66e6c2/docs/user-guide/configuring.md#eslintignore)" section.
70 | - As a side note, this section is outdated a bit. It said "Paths are relative to `.eslintignore` location or the current working directory.", but it has been changed since ESLint 4.0.0. Currently, it's "Paths are relative to `.eslintignore` location."
71 |
72 | ## Drawbacks
73 |
74 | This is a breaking change. It can break the current workflow.
75 |
76 | For example, if a user runs ESLint on variety directories with the same config by `--config` and `--ignore-path`, the workflow will be broken. The user has to change the path to target files instead of the working directory.
77 |
78 | ## Backwards Compatibility Analysis
79 |
80 | This is a breaking change.
81 |
82 | The behavior of `--config` and `--ignore-path` options will be changed.
83 |
84 | ## Alternatives
85 |
86 | ### Prior Arts
87 |
88 | - [`.gitignore` and `core.excludesFile`](#gitignore-and-coreexcludesfile) is similar to this RFC's behavior.
89 |
90 | ## Related Discussions
91 |
92 | - https://github.com/eslint/eslint/issues/6759
93 | - https://github.com/eslint/eslint/issues/11558
94 | - https://github.com/eslint/eslint/issues/12278
95 |
--------------------------------------------------------------------------------
/designs/2019-config-tester/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-06-14
2 | - RFC PR: https://github.com/eslint/rfcs/pull/27
3 | - Authors: Toru Nagashima <https://github.com/mysticatea>
4 |
5 | # Config Tester
6 |
7 | ## Summary
8 |
9 | Providing `ConfigTester` API that tests shareable configs and plugin's preset configs.
10 |
11 | ## Motivation
12 |
13 | Currently, we don't provide stuff to test shareable configs. This means the community doesn't have an easy way to check if their config is good or not.
14 |
15 | - Is the config structure valid?
16 | - Does every rule exist?
17 | - Does every rule have valid options?
18 | - Aren't there any deprecated rules?
19 | - Are there plugin settings of configured rules?
20 | - Will this config work after publish?
21 | - Does `package.json` have parsers, plugins, and extended configs?
22 |
23 | ## Detailed Design
24 |
25 | This RFC adds `ConfigTester` API to check configs.
26 |
27 | ```js
28 | const { ConfigTester } = require("eslint")
29 |
30 | // Instantiate the tester.
31 | const tester = new ConfigTester()
32 | const options = {
33 | ignoreDeprecatedRules: false,
34 | ignoreDisabledUnknownRules: false,
35 | ignoreMissingDependencies: false,
36 | ignoreRulesMissingFromConfig: false,
37 | }
38 |
39 | // Verify a shareable config (a path to the target file).
40 | tester.runOnConfigFile("index.js", options)
41 | tester.runOnConfigFile("es5.js", options)
42 | tester.runOnConfigFile("es2015.js", options)
43 |
44 | // Or verify plugin's preset configs (a config name in the plugin).
45 | tester.runOnPluginConfig("base", options)
46 | tester.runOnPluginConfig("recommended", options)
47 | tester.runOnPluginConfig("opinionated", options)
48 | ```
49 |
50 | ### § `ConfigTester(projectRoot)` constructor
51 |
52 | Instantiate a new `ConfigTester` instance.
53 |
54 | #### Parameters
55 |
56 | The constructor has an optional parameter.
57 |
58 | Name | Description
59 | :----|:-----------
60 | `projectRoot` | Default is `process.cwd()`. The path to the project root. The root should contain `package.json`.
61 |
62 | #### Behavior
63 |
64 | The tester reads `` `${projectRoot}/package.json` `` to use in `run()` method.
65 |
66 |
69 |
70 | ### § `tester.runOnConfigFile(filePath, options)` method / `tester.runOnPluginConfig(configName, options)` method
71 |
72 | Validates a config data.
73 |
74 | #### Parameters
75 |
76 | Name | Description
77 | :----|:-----------
78 | `filePath` | Required. This is a path to a file (relative from `projectRoot`).
79 | `configName` | Required. This is a config name of the plugin. If it cannot load the entry file (`main` field in `${projectRoot}/package.json` or `${projectRoot}/index.js`), it throws `MODULE_NOT_FOUND_ERROR`.
80 | `options.ignoreDeprecatedRules` | Default is `false`. If `true` then the tester ignores deprecated rules.
81 | `options.ignoreDisabledUnknownRules` | Default is `false`. If `true` then the tester ignores unknown rules if the rule was configured as `0` (`"off"`).
82 | `options.ignoreRulesMissingFromConfig` | Default is `false`. If `true` then the tester ignores missing rules. The missing rules mean the rules that ESLint or a plugin defined but not configured.
83 | `options.ignoreMissingDependencies` | Default is `false`. If `true` then the tester ignores wrong dependency definition (`dependencies`/`peerDependencies`).
84 |
85 | #### Behavior
86 |
87 | Similarly to `RuleTester`, `ConfigTester` defines tests by `describe` and `it` global variables. Then it does:
88 |
89 | 1. Validate the config object has the valid scheme with `validateConfigSchema()`.
90 |
93 | 1. Validate the config content with `validateConfigArray()`.
94 |
97 | 1. Report non-existence rules.
98 | - Because `validateConfigArray(configArray)` ignores non-existence rules.
99 | - Configured plugin's rules are in `configArray.pluginRules`.
100 | - If `ignoreDisabledUnknownRules` option was `true` and non-existence rule's severity was `"off"`, the tester ignores it.
101 |
104 | 1. Report deprecated rules.
105 | - If `ignoreDeprecatedRules` option was `true`, the tester skips this step.
106 | - If the rule severity was `"off"`, the tester ignores it.
107 | - Check `meta.deprecated` in both core rules and `configArray.pluginRules`.
108 |
111 | 1. Check whether the config configures all rules.
112 | - If `ignoreRulesMissingFromConfig` option was `true`, the tester skips this step.
113 | - This step lets people know about new rules.
114 |
117 | 1. Check whether `${options.projectRoot}/package.json` contains the configured parser, plugins, and shareable configs.
118 | - If `ignoreMissingDependencies` option was `true`, the tester skips this step.
119 | - If `parser` or `extends` were a file path except `node_modules/**`, the file should be published; check `.npmignore` and `package.json`'s `lib` field.
120 | - If `parser` or `extends` were a package or a file path to `node_modules/**`, the package should be in `dependencies` or `peerDependencies`.
121 | - `plugins` should be in `peerDependencies` or `name`.
122 |
125 |
126 | ## Documentation
127 |
128 | - [Node.js API](https://eslint.org/docs/developer-guide/nodejs-api) page should describe the new `ConfigTester` API.
129 | - [Creating a Shareable Config](https://eslint.org/docs/developer-guide/shareable-configs#creating-a-shareable-config) section should note the tester.
130 | - [Configs in Plugins](https://eslint.org/docs/developer-guide/working-with-plugins#configs-in-plugins) section should note the tester.
131 |
132 | ## Drawbacks
133 |
134 | - If people can write the config with no mistakes, this tester may not be needed.
135 |
136 | ## Backwards Compatibility Analysis
137 |
138 | - This is not a breaking change. It just adds a new API.
139 |
140 | ## Alternatives
141 |
142 | - https://www.npmjs.com/package/eslint-find-rules - we can check missing rules and deprecated rules with this package.
143 |
144 | ## Related Discussions
145 |
146 | - https://github.com/eslint/eslint/issues/10289
147 |
--------------------------------------------------------------------------------
/designs/2019-core-options/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-05-12
2 | - RFC PR: https://github.com/eslint/rfcs/pull/22
3 | - Authors: Toru Nagashima ([@mysticatea](https://github.com/mysticatea))
4 |
5 | # Configuring core options in Config Files
6 |
7 | ## Summary
8 |
9 | This RFC adds several properties into config files to configure core options. People can use config files (including shareable configs!) instead of CLI options in order to configure some linter behavior.
10 |
11 | ## Motivation
12 |
13 | We cannot configure some linter's behavior with config files, especially, shareable configs. It's convenient if we can configure those in shareable configs.
14 |
15 | ## Detailed Design
16 |
17 | This RFC adds four properties to config files.
18 |
19 | ```js
20 | // .eslintrc.js
21 | module.exports = {
22 | noInlineConfig: false, // Corresponds to --no-inline-config
23 | reportUnusedDisableDirectives: false, // Corresponds to --report-unused-disable-directives
24 | verifyOnRecoverableParsingErrors: false, // Corresponds to --verify-on-recoverable-parsing-errors
25 | ignorePatterns: [], // Corresponds to --ignore-pattern
26 |
27 | overrides: [
28 | {
29 | files: ["*.ts"],
30 | noInlineConfig: false,
31 | reportUnusedDisableDirectives: false,
32 | verifyOnRecoverableParsingErrors: false,
33 | // ignorePatterns: [], // Forbid this to avoid confusion with 'excludedFiles' property.
34 | },
35 | ],
36 | }
37 | ```
38 |
39 | ### § noInlineConfig
40 |
41 | That value can be a boolean value. Default is `false`.
42 |
43 | If `true` then it disables inline directive comments such as `/*eslint-disable*/`.
44 |
45 | If `noInlineConfig` is `true`, `--no-inline-config` was not given, and there are one or more directive comments, then ESLint reports each directive comment as a warning message (`severify=1`). For example, `"'eslint-disable' comment was ignored because your config file has 'noInlineConfig' setting."`. Therefore, end-users can know why directive comments didn't work.
46 |
47 |
48 | 💠Implementation:
49 | In Linter#_verifyWithoutProcessors method, the linter checks both providedConfig and filenameOrOptions to determine noInlineConfig option. The filenameOrOptions.allowInlineConfig precedences providedConfig.noInlineConfig .
50 | |
51 |
52 | ### § reportUnusedDisableDirectives
53 |
54 | That value can be a boolean value. Default is `false`.
55 |
56 | If `true` then it reports directive comments like `//eslint-disable-line` when no errors would have been reported on that line anyway.
57 |
58 | This option is different a bit from `--report-unused-disable-directives` CLI option. The `--report-unused-disable-directives` CLI option fails the linting with non-zero exit code (i.e., it's the same behavior as `severity=2`), but this `reportUnusedDisableDirectives` setting doesn't fail the linting (i.e., it's the same behavior as `severity=1`). Therefore, we cannot configure ESLint with `reportUnusedDisableDirectives` as failed by patch releases.
59 |
60 |
61 | 💠Implementation:
62 |
63 | Linter and CLIEngine have options.reportUnusedDisableDirectives . This RFC enhances these options to accept "off" , "warn" , and "error" . Existing false is the same as "off" and existing true is the same as "error" .
64 | - In
Linter#_verifyWithoutProcessors method, the linter checks both providedConfig and filenameOrOptions to determine reportUnusedDisableDirectives option. The filenameOrOptions.reportUnusedDisableDirectives precedences providedConfig.reportUnusedDisableDirectives .
65 |
66 | |
67 |
68 | ### § verifyOnRecoverableParsingErrors
69 |
70 | That value can be a boolean value. Default is `false`.
71 |
72 | If `true` then it runs rules even if recoverable errors existed. Then it shows both results.
73 |
74 |
75 | 💠Implementation:
76 | In Linter#_verifyWithoutProcessors method, the linter checks both providedConfig and filenameOrOptions to determine verifyOnRecoverableParsingErrors option. The filenameOrOptions.verifyOnRecoverableParsingErrors precedences providedConfig.verifyOnRecoverableParsingErrors .
77 | |
78 |
79 | ### § ignorePatterns
80 |
81 | That value can be an array of strings. Default is an empty array.
82 |
83 | This is very similar to `.eslintignore` file. Each value is a file pattern as same as each line of `.eslintignore` file. ESLint compares the path to source code files and the file pattern then it ignores the file if it was matched. The path to source code files is addressed as relative to the entry config file, as same as `files`/`excludedFiles` properties.
84 |
85 | ESLint concatenates all ignore patterns from all of `.eslintignore`, `--ignore-path`, `--ignore-pattern`, and `ignorePatterns`. If there are multiple `ignorePatterns` in a `ConfigArray`, all of them are concatenated. The order is:
86 |
87 | 1. The default ignoring. (I.e. `.*`, `node_modules/*`, and `bower_components/*`)
88 | 1. `ignorePatterns` in the appearance order in the config array.
89 | 1. `--ignore-path` or `.eslintignore`.
90 | 1. `--ignore-pattern`
91 |
92 | Negative patterns mean unignoring. For example, `!.*.js` makes ESLint checking JavaScript files which start with `.`. Negative patterns are used to override parent settings.
93 | Also, negative patterns is worthful for shareable configs of some platforms. For example, the config of VuePress can provide the configuration that unignores `.vuepress` directory.
94 |
95 | It disallows `ignorePatterns` property in `overrides` entries in order to avoid confusion with `excludedFiles`. And if a `ignorePatterns` property came from shareable configs in `overrides` entries, ESLint ignores it. This is the same behavior as `root` property.
96 |
97 | The `--no-ignore` CLI option disables `ignorePatterns` as well.
98 |
99 |
100 | 💠Implementation:
101 |
105 | |
106 |
107 | ### § Other options?
108 |
109 | - `extensions` - This RFC doesn't add `extensions` option that corresponds to `--ext` because [#20 "Configuring Additional Lint Targets with `.eslintrc`"](https://github.com/eslint/rfcs/pull/20) is the good successor of that.
110 | - `rulePaths` - This RFC doesn't add `rulePaths` option that corresponds to `--rulesdir` because [#14 (`localPlugins`)](https://github.com/eslint/rfcs/pull/20) is the good successor of that. Because the `rulePaths` doesn't have namespace, shareable configs should not be able to configure that. (Or but it may be useful for some plugins such as `@typescript-eslint/eslint-plugin` in order to replace core rules. I'd like to discuss the replacement way in another place.)
111 | - `format` - This RFC doesn't add `format` option that corresponds to `--format` because it doesn't fit cascading configs. It needs another mechanism.
112 | - `maxWarnings` - This RFC doesn't add `maxWarnings` option that corresponds to `--max-warnings` because it doesn't fit cascading configs. It needs another mechanism.
113 |
114 | ## Documentation
115 |
116 | - [Configuring ESLint](https://eslint.org/docs/user-guide/configuring) page should describe new top-level properties.
117 | - [`--no-ignore` document](https://eslint.org/docs/user-guide/command-line-interface#--no-ignore) should mention `ignorePatterns` setting.
118 |
119 | ## Drawbacks
120 |
121 | Nothing in particular.
122 |
123 | ## Backwards Compatibility Analysis
124 |
125 | No concerns. Currently, unknown top-level properties are a fatal error.
126 |
127 | ## Alternatives
128 |
129 | -
130 |
131 | ## Related Discussions
132 |
133 | - [eslint/eslint#3529](https://github.com/eslint/eslint/issues/3529) - Set ignore path in .eslintrc
134 | - [eslint/eslint#4261](https://github.com/eslint/eslint/issues/4261) - combine .eslintignore with .eslintrc?
135 | - [eslint/eslint#8824](https://github.com/eslint/eslint/issues/8824) - Allow config to ignore comments that disable rules inline
136 | - [eslint/eslint#9382](https://github.com/eslint/eslint/issues/9382) - Proposal: `reportUnusedDisableDirectives` in config files
137 | - [eslint/eslint#10341](https://github.com/eslint/eslint/issues/10341) - do not ignore files started with `.` by default
138 | - [eslint/eslint#10891](https://github.com/eslint/eslint/issues/10891) - Allow setting ignorePatterns in eslintrc
139 | - [eslint/eslint#11665](https://github.com/eslint/eslint/issues/11665) - Add top-level option for noInlineConfig or allowInlineConfig
140 |
--------------------------------------------------------------------------------
/designs/2019-deprecating-personal-config/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-07-16
2 | - RFC PR: https://github.com/eslint/rfcs/pull/32
3 | - Authors: Toru Nagashima ([@mysticatea](https://github.com/mysticatea))
4 |
5 | # Deprecating Personal Config
6 |
7 | ## Summary
8 |
9 | This RFC deprecates the personal config that is `.eslintrc` files on home directory.
10 |
11 | ## Motivation
12 |
13 | Since 6.0.0, ESLint doesn't load plugins/configs/parsers from the global installation even if it's not a part of a project such as the personal config. This is inconvenient for the personal config. But, in #28 discussion, we confirm that we don't want to use the global installation.
14 |
15 | We don't recommend to use global-installed ESLint. But the personal config has encouraged to use it.
16 |
17 | ## Detailed Design
18 |
19 | Three steps:
20 |
21 | 1. **Soft deprecation**: Update our documentation to announce to deprecate the personal config on the next minor release.
22 | 2. **Hard deprecation**: Add the deprecation warning of the personal config on ESLint 7.0.0.
23 | - ESLint shows a warning when it loaded the personal config.
24 | - ESLint shows a warning when it ignored the personal config (Currently, it ignores `.eslintrc` files on home directory if a project directory is in home directory and the project has `.eslintrc` files.)
25 | 3. **Removal**: Remove the personal config functionality on ESLint 8.0.0.
26 | - ESLint no longer loads the personal config even if the project config was not found.
27 | - ESLint no longer ignores the config files on home directory even if the project config was found.
28 |
29 | ## Documentation
30 |
31 | - Remove the personal config from [Configuring ESLint](https://eslint.org/docs/user-guide/configuring) page.
32 | - Announce on the release notes.
33 |
34 | ## Drawbacks
35 |
36 | It will make people inconvenient to lint standalone files.
37 |
38 | ## Backwards Compatibility Analysis
39 |
40 | Yes, this is a breaking change.
41 |
42 | To lint standalone files, people have to have a dummy project directory such as `sandbox`.
43 |
44 | ## Alternatives
45 |
46 | - https://github.com/eslint/rfcs/pull/28
47 |
48 | ## Related Discussions
49 |
50 | - https://github.com/eslint/rfcs/pull/7
51 | - https://github.com/eslint/rfcs/pull/28
52 | - https://github.com/eslint/eslint/issues/11914
53 |
--------------------------------------------------------------------------------
/designs/2019-description-in-directive-comments/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-07-20
2 | - RFC PR: https://github.com/eslint/rfcs/pull/33
3 | - Authors: Toru Nagashima ([@mysticatea](https://github.com/mysticatea))
4 |
5 | # Description in directive comments
6 |
7 | ## Summary
8 |
9 | This RFC adds the description support into directive comments such as `/*eslint-disable*/`. For example, `/* eslint-disable no-new -- this class has a side-effect in the constructor and it's a library's. */`.
10 |
11 | ## Motivation
12 |
13 | Directive comments make exceptions for static analysis. The explanation of why you made the exception here helps to maintain the code. However, ESLint doesn't have a comfortable way to write such an explanation.
14 |
15 | ## Detailed Design
16 |
17 | ESLint ignores the part preceded by `\s-{2,}\s` in directive comments.
18 |
19 | ```js
20 | /* eslint eqeqeq: off -- this part is ignored. */
21 | /* eslint-disable -- this part is ignored. */
22 | /* eslint-enable -- this part is ignored. */
23 | // eslint-disable-line -- this part is ignored.
24 | // eslint-disable-next-line -- this part is ignored.
25 | /* global foo -- this part is ignored. */
26 | /* globals foo, bar -- this part is ignored. */
27 | /* exported foo, bar -- this part is ignored. */
28 |
29 | "Longer is OK."
30 | // eslint-disable-line -------- this part is ignored.
31 |
32 | "Multiline."
33 | /* eslint-disable
34 | a-rule,
35 | another-rule
36 | --------
37 | this part is ignored.
38 | this part is ignored. */
39 |
40 | "Not affect to '--' which is not surrounded by spaces."
41 | /* eslint spaced-comment: [error, { exceptions: ["--"] }] */
42 | ```
43 |
44 | ### Implementation
45 |
46 | ```js
47 | // Use only the part before the first `--`.
48 | const [value] = comment.value.split(/\s-{2,}\s/u)
49 | ```
50 |
51 | ## Documentation
52 |
53 | - The "[Disabling Rules with Inline Comments](https://eslint.org/docs/user-guide/configuring#disabling-rules-with-inline-comments)" section should describe `--`.
54 |
55 | ## Drawbacks
56 |
57 | If a complex directive comment has `\s-{2,}\s` in the body, this feature breaks it.
58 |
59 | ## Backwards Compatibility Analysis
60 |
61 | This is a breaking change technically, but I believe the effect is very limited.
62 |
63 | ## Alternatives
64 |
65 | - Another comment around directive comments.
66 | ```js
67 | /* explanation */
68 | /* eslint-disable */
69 | f()
70 | ```
71 | In some cases, it's challenging to write another comment near directive comments. For example, there is `//eslint-disable-line` comment on a long line.
72 |
73 | ### Prior Arts
74 |
75 | - [istanbul](https://github.com/istanbuljs/istanbuljs)
76 | > ```
77 | > /* istanbul ignore [non-word] [optional-docs] */
78 | > ```
79 | >
80 | > https://github.com/gotwarlost/istanbul/blob/master/ignoring-code-for-coverage.md
81 | - [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer)
82 | > ```
83 | > // phpcs:disable PEAR,Squiz.Arrays -- this isn't our code
84 | > ```
85 | >
86 | > https://github.com/squizlabs/PHP_CodeSniffer/wiki/Advanced-Usage#ignoring-parts-of-a-file
87 | - [TSLint](https://github.com/palantir/tslint) doesn't have this feature, but there is a discussion: https://github.com/palantir/tslint/issues/1484.
88 |
89 | ## Related Discussions
90 |
91 | - https://github.com/eslint/eslint/issues/11806
92 |
--------------------------------------------------------------------------------
/designs/2019-drop-node8/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-10-13
2 | - RFC PR: https://github.com/eslint/rfcs/pull/44
3 | - Authors: Toru Nagashima ([@mysticatea](https://github.com/mysticatea))
4 |
5 | # Drop supports for Node.js 8.x and 11.x
6 |
7 | ## Summary
8 |
9 | This RFC drops supports for Node.js 8.x and 11.x because of end-of-life. (See [nodejs/release](https://github.com/nodejs/release#readme) to check the release schedule of Node.js)
10 |
11 | ## Motivation
12 |
13 | We get the capability to use new features and syntaxes by dropping the support for end-of-life versions of Node.js. Especially, [RFC40](https://github.com/eslint/rfcs/pull/40) wants to use async iteration, but Node.js 8.x doesn't support that syntax.
14 |
15 | ## Detailed Design
16 |
17 | This proposal updates the `engines` field of our `package.json` (and updates our CI scripts).
18 |
19 | ```diff
20 | "engines": {
21 | - "node": "^8.10.0 || ^10.13.0 || >=11.10.1"
22 | + "node": "^10.12.0 || >=12.0.0"
23 | }
24 | ```
25 |
26 | ### Why is it `^10.12.0`?
27 |
28 | > **Resolution:** We will choose Node version support based on the lowest point release supporting our desired features.
29 | >
30 | > [2019-March-14 ESLint TSC Meeting Notes: Decide how to manage support for minor versions of Node](https://github.com/eslint/tsc-meetings/blob/137fbe2be55499cd75f28a008900803078d22dfd/notes/2019/2019-03-14.md#decide-how-to-manage-support-for-minor-versions-of-node)
31 |
32 | Therefore, we should check the added features in Node.js `10.x`.
33 |
34 | I found two features we want to use.
35 |
36 | - [10.10.0](https://nodejs.org/en/blog/release/v10.10.0/) ... the `withFileTypes` option of `fs.readdir`/`fs.readdirSync`. We can reduce the calls of `fs.statSync` in our `FileEnumerator` class with this option. It may improve performance (especially on Windows).
37 | - [10.12.0](https://nodejs.org/en/blog/release/v10.12.0/) ... `module.createRequireFromPath(filename)` function. We can remove [our polyfill](https://github.com/eslint/eslint/blob/24ca088fdc901feef8f10b050414fbde64b55c7d/lib/shared/relative-module-resolver.js#L20-L29) with this version.
38 |
39 | ## Documentation
40 |
41 | We need write an entry in the migration guide because this is a breaking change.
42 |
43 | ## Drawbacks
44 |
45 | Users lose the capability to run ESLint on old Node.js. They may have to update their CI scripts.
46 |
47 | ## Backwards Compatibility Analysis
48 |
49 | This is a breaking change clearly.
50 |
51 | Users lose the capability to run ESLint on old Node.js. They may have to update their CI scripts.
52 |
53 | ## Alternatives
54 |
55 | N/A.
56 |
57 | ## Related Discussions
58 |
59 | - https://github.com/eslint/eslint/issues/10981 - Allow use node version >8
60 | - https://github.com/eslint/eslint/issues/11022 - Decide how to manage support for minor versions of Node
61 |
--------------------------------------------------------------------------------
/designs/2019-esm-compatibilty/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-10-05
2 | - RFC PR: [#43](https://github.com/eslint/rfcs/pull/43)
3 | - Authors: Evan Plaice ([@evanplaice](https://github.com/evanplaice))
4 |
5 | # ES Module Compatibility
6 |
7 | ## Summary
8 |
9 | **This Issue**
10 |
11 | Node now supports ES modules. ESLint configurations that use the CommonJS format (i.e., `.eslintrc`, `.eslintrc.js`) are not compatible with ES module based packages.
12 |
13 | **The Impact**
14 |
15 | This applies to ES module based packages (i.e., packages using `"type": "module"`) using a CommonJS configuration (i.e., `.eslintrc`, `.eslintrc.js`).
16 |
17 | **The Fix**
18 |
19 | Node provides an 'escape hatch' via the `.cjs` extension. The extension explicitly signals to Node that the file should *always* evaluate as CommonJS. Support for the `.cjs` needs to be added to ESLint, and ESLint users building ES module based packages need to be notified.
20 |
21 | ## Motivation
22 |
23 | ES modules are here. ESLint is ubiquitous in the JS ecosystem. With some minor adjustments, ESLint can be made to be compatible with the new format.
24 |
25 | ## Detailed Design
26 |
27 | To understand the design outlined here requires some background info into the mechanics by which Node modules are loaded.
28 |
29 | ### Jargon
30 |
31 | - Package - A NPM package (i.e., contains `package.json`)
32 | - Module - A JS file
33 | - CJS - A CommonJS module
34 | - ESM - A standard ECMAScript module
35 | - Consumer - A package that uses/depends on this package
36 | - Dependent - Package(s) that this package needs to work
37 | - Boundary - The demarcation between package, consumer, dependent
38 |
39 | ### A Crash Course on Package Boundaries
40 |
41 | Historically, NPM packages have come in a variety of different package formats (e.g., IIFE, AMD, UMD, CJS). Prior to an actual spec, NPM settled on CJS as it's de-facto module format. CJS isn't going anywhere, the addition of ESM is additive.
42 |
43 | By default, all NPM packages are CJS-based. That means all `.js` files will be read as CJS modules. To include an ES module in a CJS package, it must have the `.mjs` extension.
44 |
45 | If, `package.json` contains `"type": "module"` then the package is ESM-based. Meaning, all `.js` files contained within will be treated as ESM. To include a CJS module in a ESM-based package, it must have the `.cjs` extension.
46 |
47 | This configuration option does *not* affect consumers or dependents. Whatever the configuration, it applies to all modules within the package boundary. Assuming packages only ever directly import within their package boundary, there should be no issues with interoperability between CJS/ESM.
48 |
49 | ### The Scenario
50 |
51 | A user is creating a new package. They prefer ESM for Browser <-> Node compatibility so they configure their package to be ESM-based.
52 |
53 | The user adds `eslint` as a devDependency and a typical NPM script to lint the package's contents.
54 |
55 | The user defines `.eslintrc.js` outlining the rule set they prefer to use within their package.
56 |
57 | ### The Issue
58 |
59 | When the user package is ESM-based, all `.js` files within are read as ESM.
60 |
61 | However, ESLint is CJS-based so, it loads all files within its package boundary as CJS.
62 |
63 | The configuration file is defined as a CJS module (i.e., `module.exports`), but has a `.js` extension syntax so requiring it throws an error. ESLint, by design reaches across the package boundary to load the user-defined configuration but the user has inadvertently signaled to Node to load it with the wrong module loader.
64 |
65 | ### The Fix
66 |
67 | Add support for the `.cjs` file extension
68 |
69 | *Note: In Node, `.cjs` will always load as a CommonJS module, irrespective of package configuration*
70 |
71 | ### Usage
72 |
73 | If a user:
74 |
75 | - is building a ESM-based package
76 | - is using a JS-based configuration
77 |
78 | They should give the configuration a `.cjs` extension.
79 |
80 | ### Priority
81 |
82 | With the addition of `.cjs`, the new priority for configuration files will be
83 |
84 | 1. .eslintrc.js
85 | 1. .eslintrc.cjs
86 | 1. .eslintrc.yaml
87 | 1. .eslintrc.yml
88 | 1. .eslintrc.json
89 | 1. .eslintrc
90 | 1. package.json
91 |
92 | ## Documentation
93 |
94 | The [Configuration File Formats](https://github.com/eslint/website/blob/master/docs/6.0.0/user-guide/configuring.md#configuration-file-formats) section will need to be updated.
95 |
96 | ```
97 | ## Configuration File Formats
98 |
99 | ESLint supports configuration files in several formats:
100 |
101 | * **JavaScript** - use `.eslintrc.js` and export an object containing your configuration.
102 | * **JavaScript (ESM) - same as `.eslintrc.js but used with ES module packages <---
103 | * **YAML** - use `.eslintrc.yaml` or `.eslintrc.yml` to define the configuration structure.
104 | * **JSON** - use `.eslintrc.json` to define the configuration structure. ESLint's JSON files also allow JavaScript-style comments.
105 | * **Deprecated** - use `.eslintrc`, which can be either JSON or YAML.
106 | * **package.json** - create an `eslintConfig` property in your `package.json` file and define your configuration there.
107 |
108 | If there are multiple configuration files in the same directory, ESLint will only use one. The priority order is:
109 |
110 | 1. `.eslintrc.js`
111 | 1. `.eslintrc.cjs` <---
112 | 1. `.eslintrc.yaml`
113 | 1. `.eslintrc.yml`
114 | 1. `.eslintrc.json`
115 | 1. `.eslintrc`
116 | 1. `package.json`
117 |
118 | **Note:** JavaScript (ESM) is for use with JavaScript packages that specify `"type":"module"` in `package.json`. <---
119 |
120 | ```
121 |
122 | ## Drawbacks
123 |
124 | ### Technical
125 |
126 | None. The change has no negative functionality or performance impacts.
127 |
128 | ### People
129 |
130 | Some developers within the Node ecosystem are strongly opposed to supporting `"type": "module"` at all.
131 |
132 | ## Backwards Compatibility Analysis
133 |
134 | *tl;dr: Backward compatibility will be unaffected*
135 |
136 | ### Story 1 - ESLint Compatibility
137 |
138 | This change is additive. It associates `.cjs` files with JS configurations.
139 |
140 | The existing semantics of loading CommonJS configurations for CommonJS-based packages do not change.
141 |
142 | ### Story 2 - Node Compatibility
143 |
144 | In Node, ES module and `.cjs` support roll out together.
145 |
146 | This change *only* impacts users who define their package as an ES module.
147 |
148 | Existing packages that use the default (i.e., CommonJS module resolution) will be unaffected.
149 |
150 | ## Alternatives
151 |
152 | ### Alternative 1 - Suppress the error
153 |
154 | **[PR#12333](https://github.com/eslint/eslint/pull/12333)**
155 |
156 | The fencing exists to prevent users from creating dual-mode (i.e., both ESM/CJS) packages as interoperability between the two format can cause issues. In the case of ESLint, loading a simple config file from a user-defined package should not cause any issues or side-effects.
157 |
158 | Eating the exception is viable solution.
159 |
160 | ### Alternative 2 - Support dynamic `import()`
161 |
162 | Instead of using 'import-fresh' to require the config, import it in a cross-format-compatible way using dynamic `import()`.
163 |
164 | There has been and will continue to be a lot of talk about using this approach as the CJS/ESM interoperability story is fleshed out.
165 |
166 | For now it presents too many unknowns.
167 |
168 | ## Open Questions
169 |
170 |
180 |
181 | ## Help Needed
182 |
183 |
189 |
190 | ## Frequently Asked Questions
191 |
192 | > Why doesn't compatibility w/ ESM-based packages 'just work'?
193 |
194 | ESLint reaches across the package boundary to retrieve the user-defined configuration. If the consumer package is ESM-based, the `.js` config file will be seen as an ES module. When the ES module loader encounters `module.exports` (i.e., the CommonJS module specifier) it will throw an error.
195 |
196 | > What does this interoperability issue affect?
197 |
198 | Only ESM-based packages (i.e., packages with `"type": "module"` defined in package.json).
199 |
200 | > What about 3rd-party linting tools that provide their own built-in rule set for ESLint (ex [StandardJS](https://standardjs.com/))?
201 |
202 | They aren't impacted by this change. 3rd-party modules define both their code and the ESLint rule set used within the same package boundary.
203 |
204 | > What about support for ES module configurations
205 |
206 | The scope of this RFC is limited to only ES module compatibility concerns. If there is a desire to add support for ESM-based configurations, it will need to be addressed in another RFC.
207 |
208 | ## Related Discussions
209 |
210 | - [Issue #12319](https://github.com/eslint/eslint/issues/12319)
211 | - [PR #12321](https://github.com/eslint/eslint/pull/12321)
212 | - [Transition Path Problems for Tooling - node/modules](https://github.com/nodejs/modules/issues/388)
213 |
--------------------------------------------------------------------------------
/designs/2019-expose-rules-to-formatters/readme.MD:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-01-16
2 | - RFC PR: https://github.com/eslint/rfcs/pull/10
3 | - Authors: Chris Meyer (@EasyRhinoMSFT)
4 |
5 | # Providing Rule Metadata to Formatters
6 |
7 | ## Summary
8 |
9 | This proposal describes a design enhancement that provides formatters with details about the rules that have been executed by ESLint.
10 |
11 | ## Motivation
12 |
13 | Currently, formatters only see the ID of each rule for which a violation was identified, plus an instance-specific description, as properties on each result object. Formatters are not able to access useful rule metadata, such as category, description, and help URL. Formatters are also not aware of the full set of rules that were run, information that may be useful in some cases.
14 |
15 | ## Detailed Design
16 |
17 | Design Summary
18 | 1. In `cli.js::printResults`, obtain the rules map from the `Engine` object.
19 | 2. Add a second argument to the formatter's exported interface function. The value should be an object with a `rulesMeta` property that is a map with the rule name as the key and the `rule.meta` object as the value. See the "Command Line Interface (cli.js) Changes" section below for implementation.
20 |
21 | We should use a container object as the argument, with a ruleId/rule.meta map as a property, in order to accommodate potential future expansions of the data we pass to formatters. This suggestion was previously made in the discussion of issue [#9841](https://github.com/eslint/eslint/issues/9841).
22 |
23 | ### Command Line Interface (cli.js) Changes
24 | The implementation of this feature is very simple and straightfoward. The code location that invokes the formatter's exported interface function already has access to the API it should use to obtain the list of all executed rules. The call to `Engine.getRules` must be made in the try block because `engine` may be null during unit testing.
25 |
26 | ```js
27 | function printResults(engine, results, format, outputFile) {
28 | let formatter;
29 | let rules;
30 |
31 | try {
32 | formatter = engine.getFormatter(format);
33 | rules = engine.getRules();
34 | } catch (e) {
35 | log.error(e.message);
36 | return false;
37 | }
38 |
39 | const rulesMeta = {};
40 | rules.forEach(function(rule, ruleId) {
41 | rulesMeta[ruleId] = rule.meta;
42 | });
43 | const output = formatter(results, { rulesMeta: rulesMeta });
44 | ...
45 | }
46 | ```
47 |
48 | ### Formatter Changes
49 |
50 | Formatters that implement the exported interface function would no changes. Future versions can make use of the rules data by adding the new argument to the exported interface function definition. This argument cannot be added unless it is used, as this will trip the JavaScript validation rule 'no-unused-vars.'
51 |
52 | A formatter that assigns a function reference to the exported interface function could exhibit unexpected behavior depending on the signature of the referenced function. For example, since this change's second argument is a complex object, a referenced function that expects a Number as its second argument could cause an exception.
53 |
54 | Currently the `html` formatter creates incorrect links rooted at the eslint.org domain for rules from plugins. We should fix this issue by using the meta.docs.url property that will become available with this change.
55 |
56 | The `json` formatter also requires attention. It simply stringifies the `results` object, and would therefore provide incomplete data by ignoring the new `data` argument. To avoid a breaking change to the existing `json` formatter, we should a new built-in formatter, perhaps named `json-with-metadata`, which returns a stringified object containing both objects:
57 |
58 | ```js
59 | module.exports = function(results, data) {
60 | return JSON.stringify({
61 | results: results,
62 | rulesMeta: data.rulesMeta
63 | });
64 | };
65 | ```
66 |
67 | ## Documentation
68 |
69 | Since custom formatter authors may want to take advantage of the newly-available rule metadata, a formal announcement may be justified (I don't have sufficient context in this regard so I will defer this determination.)
70 |
71 | The [Working with Custom Formatters](https://eslint.org/docs/developer-guide/working-with-custom-formatters) article will have to be updated:
72 | * Code samples will need the new `data` argument added wherever the exported interface function is shown, *but only when it is used*.
73 | * The `data` argument should be called out and described, and should include a link to the [Working with Rules](https://eslint.org/docs/developer-guide/working-with-rules) article. The primary goal here is to familiarize formatter author with the structure of the `data` argument and rulesMeta property.
74 | * It should be noted that the rulesMeta dictionary will be empty in cases where no rules have been run.
75 | * It should be noted that rule metadata properties such as description, category, and help URL are not required and may not be defined, and that custom formatter code should take this into account.
76 | * We should show the use of rule metadata in one of the examples by either modifying an existing one (maybe the [Detailed formatter](https://eslint.org/docs/developer-guide/working-with-custom-formatters#detailed-formatter) example) or adding a new one. One idea would be to suffix the results output with a list of rules that were violated, using a helper function something like this:
77 |
78 | ```js
79 | var rulesViolated = {};
80 | ...
81 | function printRules() {
82 | var lines = "*** RULES:\n";
83 | rulesViolated.forEach(function (ruleMetadata, ruleId) {
84 | lines += ruleId;
85 |
86 | if (ruleMetadata.docs.description) {
87 | lines += ": " + ruleMetadata.docs.description;
88 | }
89 |
90 | lines += "\n";
91 |
92 | if (ruleMetadata.docs.url) {
93 | lines += ruleMetadata.docs.url + "\n";
94 | }
95 | });
96 | return lines;
97 | }
98 | ```
99 |
100 | ## Drawbacks
101 |
102 | This is a fairly innocuous change in that it is additive, non-breaking (mostly, see Backwards Compatibility), and does not change any of ESLint's core functionality. A downside is that we will be exposing the Rule data model to third-party developers, so future changes could break existing formatters. For example, removing or renaming an existing property, or changing the structure of the Rule.meta object.
103 |
104 | ## Backwards Compatibility Analysis
105 |
106 | Since this change is manifested as a new argument to the formatter's exported interface function, existing formatter code that implements the exported interface function will not be affected and will continue to function even without adding the new argument to their exported function.
107 |
108 | (The following paragraph also appears in the Formatters section.)
109 | A formatter that assigns a function reference to the exported interface function could exhibit unexpected behavior depending on the signature of the referenced function. For example, since this change's second argument is a complex object, a referenced function that expects a Number as its second argument could cause an exception.
110 |
111 | ## Alternatives
112 |
113 |
119 | * Including the rule metadata in the result object. This approach results in redundant data being returned, and includes external metadata properties that are not directly relevant.
120 | * Pass the rules map itself as a argument to the formatter's exported interface function. This approach makes it messier to add additional data in the future, since new arguments would be necessary.
121 |
122 | ## Help Needed
123 |
124 | No help needed, I have implemented the change.
125 |
126 | ## Frequently Asked Questions
127 |
128 |
135 |
136 | ## Related Discussions
137 |
138 | Issue for this change:
139 | https://github.com/eslint/eslint/issues/11273
140 |
141 | Earlier related issue:
142 | https://github.com/eslint/eslint/issues/9841
143 |
144 | Initial inquiry:
145 | https://groups.google.com/forum/#!topic/eslint/kpHrxkeilwE
146 |
--------------------------------------------------------------------------------
/designs/2019-pass-cwd-from-cli-engine/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-07-25
2 | - RFC PR: https://github.com/eslint/rfcs/pull/35
3 | - Authors: Eric Wang
4 |
5 | # Pass cwd from cli engine
6 |
7 | ## Summary
8 | Make the CLIEngine `cwd` option available to rules to avoid the misalignment between it and process.cwd()
9 |
10 | see: https://github.com/eslint/eslint/issues/11218
11 |
12 | ## Motivation
13 | To avoid the misalignment between it and `process.cwd()`.
14 | There is a situation where a rule need to get the relative path of the current file to the root folder of the project.
15 | But it's hard to guarantee that all developers will open the root folder in their IDE (VSCode with eslint plugin for example), especially when the project grows.
16 |
17 | Use case:
18 | Say, the project is supposed to be open in `web` folder.
19 | But as the project grows, people tends to open some subfolder under the `web` to minimize sidebar.
20 | `eslint` plugin in VSCode will then run the `eslint` from the subfolder which cause the `process.cwd` not to return the expected value (e.g. the `web` folder)
21 |
22 | Expected:
23 | It is expected that the rule has access to the `cwd` in the option of the CLI engine.
24 |
25 | ## Detailed Design
26 | ### Change in Linter
27 | Modify the `Linter` constructor to accept an object with a optional string parameter `cwd` with a default value.
28 | ```
29 | const linter = new Linter({
30 | cwd: '/path/to/cwd'
31 | });
32 | ```
33 | or `const linter = new Linter({})`.
34 | The default value will be `process.cwd()` if the `process` exists, or `undefined` if not.
35 |
36 |
37 | ### Change in Context
38 | This RFC adds `getCwd()` method to rule context.
39 | The `getCwd()` method returns the value of the `cwd` option that was given in `Linter` constructor.
40 |
41 | https://github.com/eslint/eslint/pull/12021
42 |
43 | ## Documentation
44 | Adding the `cwd` into the `context` Api.
45 | Adding the `cwd` into the `Linter` constructor Api.
46 |
47 | ## Drawbacks
48 | Can't think of any.
49 |
50 | ## Backwards Compatibility Analysis
51 | Those rules using `process.cwd` will have exact same behavior, so there is no compatibility issue.
52 |
53 | ## Alternatives
54 | N/A
55 |
56 | ## Open Questions
57 | N/A
58 |
59 | ## Help Needed
60 | N/A
61 |
62 | ## Frequently Asked Questions
63 | N/A
64 |
65 | ## Related Discussions
66 | N/A
67 |
--------------------------------------------------------------------------------
/designs/2019-plugin-root-path-flag/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-03-13
2 | - RFC PR: https://github.com/eslint/rfcs/pull/18
3 | - Authors: Teddy Katz
4 |
5 | # Add a flag to specify the folder where plugins are resolved from
6 |
7 | ## Summary
8 |
9 | With the [2018-simplified-package-loading RFC](https://github.com/eslint/rfcs/blob/8bc0b80e0b3e54d10991a4774c41f7375dfcbbfe/designs/2018-simplified-package-loading/README.md) implemented, ESLint always resolves plugins relative to the current working directory. The CWD works well for the most common use case, but is inconvenient for certain integrations. This RFC proposes adding a CLI flag to specify an alternative place where plugins should be resolved from.
10 |
11 | ## Motivation
12 |
13 | We require whoever invokes ESLint to also install any necessary plugins. Generally speaking, in order to ensure that plugins are resolved correctly, ESLint should resolve plugins relative to whichever project installed the plugins. As a result, ESLint would ideally resolve plugins relative to the package that invokes ESLint.
14 |
15 | However, we can't reliably tell who is invoking ESLint, so the [2018-simplified-package-loading RFC](https://github.com/eslint/rfcs/blob/8bc0b80e0b3e54d10991a4774c41f7375dfcbbfe/designs/2018-simplified-package-loading/README.md) always loads plugins relative to the CWD. This works well when the end user invokes ESLint, because the CWD is usually somewhere in the end user's project. Unfortunately, it causes problems for third-party integrations that invoke ESLint on behalf of the end user, such as `standard` and `create-react-app`. The plugins used in these integrations are transitive dependencies for the end user, so they might not be reachable from the CWD.
16 |
17 | It is possible for these integrations to work around the issue by changing the CWD (with the `cwd` option in `CLIEngine`), but changing the CWD has a number of inconvenient side-effects. For example, if the `cwd` is set to the directory of a third-party package, the end user's `node_modules` folder is not ignored by default, and relative paths might be resolved in an unexpected way.
18 |
19 | ## Detailed Design
20 |
21 | This RFC adds a `--resolve-plugins-relative-to` CLI flag, and a corresponding `resolvePluginsRelativeTo` option in `CLIEngine`. The option value, if provided, must be an absolute path to a directory.
22 |
23 | When provided, ESLint loads all plugins relative to the `resolvePluginsRelativeTo` directory, rather than the CWD. The expectation is that integrations like `standard` and `create-react-app`, which already use `CLIEngine`, would pass an option like `{ resolvePluginsRelativeTo: __dirname }` to indicate that the package's own plugins should be used regardless of the CWD.
24 |
25 | The implementation is expected to be fairly simple when building on top of [eslint/eslint#11388](https://github.com/eslint/eslint/pull/11388). That implementation already has an equivalent directory path that gets passed around to specify plugin loading, but that path currently is always the same as the CWD.
26 |
27 | (Note: In the [2018-simplified-package-loading RFC](https://github.com/eslint/rfcs/blob/8bc0b80e0b3e54d10991a4774c41f7375dfcbbfe/designs/2018-simplified-package-loading/README.md), the `resolvePluginsRelativeTo` was referred to as the "project root".)
28 |
29 | ## Documentation
30 |
31 | This feature might be useful to add as a footnote for the larger announcement of package-loading changes. It would be worth mentioning in the parts of the documentation that describe how plugins are loaded, and it would also be added to the Node.js API docs. However, most end users would not need to use this option or be aware of its existence.
32 |
33 | ## Drawbacks
34 |
35 | Like any new feature, this flag will slightly increase the complexity and maintenance costs of ESLint core.
36 |
37 | If we change how plugins are loaded in the future, this flag may become obsolete or turn into a no-op.
38 |
39 | ## Backwards Compatibility Analysis
40 |
41 | This change is backwards-compatible. It adds a new CLI flag while keeping the behavior the same if the flag is not specified.
42 |
43 | ## Alternatives
44 |
45 | As described in [#14](https://github.com/eslint/rfcs/pull/14), We could change how plugin-loading works to always resolve plugins from the config that imports them, eliminating the need to load things from the CWD. That change would eliminate the need for this command-line flag. However, that change presents complex compatibility and design challenges which are still under discussion. If we change how the `plugins` directive works, it seems like it won't happen before v7.0.0, and this flag would be important to have in the meantime. Given that a user can already somehwat customize this behavior by changing the CWD, the existence of this flag is unlikely to impose a substantial compatibility burden even if we do change how plugins are loaded later.
46 |
47 | ## Open Questions
48 |
49 | None
50 |
51 |
52 | ## Frequently Asked Questions
53 |
54 | None yet
55 |
56 | ## Related Discussions
57 |
58 | * [#7](https://github.com/eslint/rfcs/pull/7)
59 | * [#14](https://github.com/eslint/rfcs/pull/14)
60 |
--------------------------------------------------------------------------------
/designs/2019-processor-shared-settings/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-07-01
2 | - RFC PR: https://github.com/eslint/rfcs/pull/29
3 | - Authors: @Conduitry
4 |
5 | # Access to options in processors
6 |
7 | ## Summary
8 |
9 | This would add the ability to send options to processors, via a new `processorOptions` object in the `.eslintrc` configuration.
10 |
11 | ## Motivation
12 |
13 | There is currently no good way to provide any sort of configuration to processor plugins. There are instances where this is useful (`eslint-plugin-html` and `eslint-plugin-svelte3` are two examples I am familiar with). The concept of "shared settings" exists, but these are only provided to rule plugins.
14 |
15 | ## Detailed Design
16 |
17 | A new top-level key called `processorOptions` would be added. (Typically, this would be configured alongside `processor` inside an override). This options object would then be sent as a third argument to `preprocess` and `postprocess`.
18 |
19 | For example, this is how configuring linting to use [`eslint-plugin-svelte3`](https://github.com/sveltejs/eslint-plugin-svelte3) with options might look:
20 |
21 | ```js
22 | module.exports = {
23 | // ...
24 | plugins: ['svelte3'],
25 | overrides: [
26 | {
27 | files: '*.svelte',
28 | processor: 'svelte3/svelte3',
29 | processorOptions: {
30 | ignoreStyles: () => true,
31 | // ...
32 | },
33 | },
34 | ],
35 | };
36 | ```
37 |
38 | ## Documentation
39 |
40 | We'd document how to send options to processors that support them in .
41 |
42 | We'd document how to receive them as a third argument in .
43 |
44 | ## Drawbacks
45 |
46 | Slight increased maintenance burden is all I can think of. The entire configuration object is readily available in `Linter.prototype._verifyWithProcessor` where we would be calling `preprocess` and `postprocess`.
47 |
48 | ## Backwards Compatibility Analysis
49 |
50 | Existing users and plugins will be unaffacted, as this will simply be a third argument passed to `preprocess` and `postprocess`.
51 |
52 | ## Alternatives
53 |
54 | There are currently no processor-specific options. The only real way to access the linting configuration from within a processor is to monkey-patch `Linter.prototype.verify` so that we can intercept the config values. This is [what's happening in `eslint-plugin-svelte3` (near the bottom of the file)](https://github.com/sveltejs/eslint-plugin-svelte3/blob/master/index.js). `eslint-plugin-html` is also doing something similar. It involves searching through `require.cache` for the appropriate file, and is brittle and ugly.
55 |
56 | ## Open Questions
57 |
58 | ~~Do we want to provide the entire configuration object to the pre- and postprocessors, or just the shared settings?~~ Now that we're going to have configuration that's explicitly intended for this processor, it probably makes sense to only send that object.
59 |
60 | ## Help Needed
61 |
62 | I can certainly give implementing this a shot, but I've only dug into the ESLint codebase to the extent necessary to work around that lack of this feature. I'd appreciate some help, particularly with writing tests.
63 |
64 | If there are other places in the code besides `Linter.prototype._verifyWithProcessor` where processors are run, I'd also like to hear about them.
65 |
66 | ## Frequently Asked Questions
67 |
68 | ## Related Discussions
69 |
70 | https://gitter.im/eslint/eslint?at=5d0e804ad4535e477a829360
71 |
--------------------------------------------------------------------------------
/designs/2019-recoverable-error-handling/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-03-29
2 | - RFC PR: https://github.com/eslint/rfcs/pull/19
3 | - Authors: Toru Nagashima <[@mysticatea](https://github.com/mysticatea)>
4 |
5 | # Recoverable Error Handling
6 |
7 | ## Summary
8 |
9 | ESLint cannot verify source code if the code has a syntax error. However, we can make valid AST even if several kinds of syntax errors existed. For example, conflict of variable names doesn't break AST. This RFC calls such a syntax error as "Recoverable Errors".
10 |
11 | This RFC adds handling of [Recoverable Errors] into ESLint.
12 |
13 | ## Motivation
14 |
15 | The goal of this RFC is that improve ESLint experience by reducing "fixing an error makes more errors."
16 |
17 | This feature intends to be used for the syntax errors which don't affect AST shape. For example, name conflicts, type errors, etc. This feature doesn't intend to support invalid AST.
18 |
19 | ## Detailed Design
20 |
21 | ### § Handling [Recoverable Errors] in ESLint
22 |
23 | - `Linter` class passes `parserOptions.recoverableErrors` option with `true` to `espree` or custom parsers.
24 | - If the object the parser returned has `recoverableErrors` property with an array, or if the error the parser thrown has `recoverableErrors` property with an array, `Linter` class converts the errors to messages.
25 |
26 | Each element of `recoverableErrors` array has the following form.
27 |
28 | ```jsonc
29 | {
30 | "message": "Identifier 'foo' has already been declared",
31 | "line": 1, // 1-based line number.
32 | "column": 10, // 0-based column number.
33 | "endLine": 1, // Optional. 1-based line number.
34 | "endColumn": 13 // Optional. 0-based column number.
35 | }
36 | ```
37 |
38 | Then `Linter` class converts that to a message:
39 |
40 | ```jsonc
41 | {
42 | "fatal": true,
43 | "ruleId": null,
44 | "severity": 2,
45 | "message": "Identifier 'foo' has already been declared",
46 | "line": 1, // 1-based line number.
47 | "column": 11, // 1-based column number.
48 | "endLine": 1, // Optional. 1-based line number.
49 | "endColumn": 14 // Optional. 1-based column number.
50 | }
51 | ```
52 |
53 | - Directive comments such as `/*eslint-disable*/` cannot hide the messages of recoverable errors.
54 | - ESLint doesn't run any rules if a recoverable error existed.
55 |
56 | Practically, this is the support for multiple syntax errors.
57 |
58 | #### `verifyOnRecoverableParsingErrors` option
59 |
60 | `verifyOnRecoverableParsingErrors` option is the following three:
61 |
62 | - `--verify-on-recoverable-parsing-errors` CLI option
63 | - `verifyOnRecoverableParsingErrors` in `CLIEngine` constructor option (`boolean`, default is `false`)
64 | - `verifyOnRecoverableParsingErrors` in `Linter#verify()` option (`boolean`, default is `false`)
65 |
66 |
67 | An aside:
68 | And #22 coreOptions.verifyOnRecoverableParsingErrors in config files if both RFCs accepted.
69 | |
70 |
71 | If the `verifyOnRecoverableParsingErrors` option was given, ESLint runs configured rules even if the parser returned recoverable errors. In that case, ESLint additionally controls lint messages to avoid confusion.
72 |
73 | If the parser returned any recoverable errors:
74 |
75 | - the `Linter` disables autofix by making [`disableFixes` option](https://eslint.org/docs/6.0.0/developer-guide/nodejs-api#linterverify) `true` internally as autofix is considered not safe.
76 | - the `Linter` catches exceptions which were thrown from rules and reports the exceptions as regular messages rather than crash in order to provide linting messages as best effort basis. For example,
77 |
78 | ```jsonc
79 | {
80 | "fatal": true,
81 | "ruleId": "a-rule",
82 | "severity": 2,
83 | "message": "'a-rule' failed to lint the code because of parsing error(s).",
84 | "line": 1,
85 | "column": 1,
86 | "endLine": 1,
87 | "endColumn": 1
88 | }
89 | ```
90 |
91 | If a rule has an exception and regular messages, the `Linter` drops the regular messages to avoid confusion of wrong messages.
92 |
93 | ### § Handling [Recoverable Errors] in Espree
94 |
95 | Acorn, the underlying of `espree`, has `raiseRecoverable(pos, message)` method to customize handling of recoverable errors.
96 |
97 | If `options.recoverableErrors` was `true` then `espree` collects recoverable errors and returns the errors along with AST. Otherwise, `espree` throws syntax errors on recoverable errors as is currently.
98 |
99 | In `acorn@6.1.1`, there are the following recoverable errors:
100 |
101 | - "Comma is not permitted after the rest element"
102 | - "Parenthesized pattern"
103 | - "Redefinition of `__proto__` property"
104 | - "Redefinition of property"
105 | - "Binding XXX in strict mode"
106 | - "Assigning to XXX in strict mode"
107 | - "Argument name clash"
108 | - "Export 'XXX' is not defined"
109 | - "Multiple default clauses"
110 | - "Identifier 'XXX' has already been declared"
111 | - "Escape sequence in keyword XXX"
112 | - "Invalid regular expression: /a regexp/: An error description"
113 |
114 | > https://github.com/acornjs/acorn/search?q=raiseRecoverable
115 |
116 |
117 | An aside:
118 | A crazy idea is that we can make the parsing errors which are caused by older ecmaVersion recoverable. The parser parses code with the latest ecmaVersion always, then reports newer syntaxes as recoverable errors with understandable messages such as "async functions are not supported in ES5. Please set '2017' to 'parserOptions.ecmaVersion'."
119 | |
120 |
121 | ### § Handling [Recoverable Errors] in Other Parsers
122 |
123 | This RFC doesn't contain the update of custom parsers. But this section considers if some popular custom parsers can applicate this feature.
124 |
125 | - `babel-eslint`
126 | I don't have enough knowledge about `babel-eslint` and recoverable errors.
127 | - `@typescript-eslint/parser`
128 | TypeScript parser parses source code loosely, then provides syntax/semantic errors by API along with AST. So currently `@typescript-eslint/parser` manually throws syntax errors if the parse result has syntax/semantic errors. Therefore, it can provide recoverable errors.
129 | - `vue-eslint-parser`
130 | It reports [HTML parse errors](https://html.spec.whatwg.org/multipage/parsing.html#parse-errors) and JavaScript syntax errors in `` as recoverable errors, then [vue/no-parsing-error](https://eslint.vuejs.org/rules/no-parsing-error.html) rule converts the errors to messages. Therefore, it can provide recoverable errors.
131 |
132 | ### How should custom parsers use AST with [Recoverable Errors]?
133 |
134 | This feature intends to be used for the syntax errors which don't affect AST shape. For example, name conflicts, type errors, etc. This feature doesn't intend to support invalid AST.
135 |
136 | If recovering of a syntax error makes invalid AST as ESTree or unexpected mapping between AST and tokens, it should not be recoverable errors.
137 |
138 | ### How should rules support AST with [Recoverable Errors]?
139 |
140 | If given AST is valid along with recoverable errors, rules should support that case.
141 |
142 | If mapping between AST nodes and tokens was broken, probably rules cannot support that case.
143 |
144 | ## Documentation
145 |
146 | - [Disabling Rules with Inline Comments](https://eslint.org/docs/user-guide/configuring#disabling-rules-with-inline-comments) section should note about recoverable errors. The directive comments cannot hide the recoverable errors.
147 | - [Working with Custom Parsers](https://eslint.org/docs/developer-guide/working-with-custom-parsers) page should describe about `options.recoverableErrors` and `recoverableErrors` property of the returned value. Custom parsers can use `recoverableErrors` property instead of throwing fatal errors to report syntax errors.
148 | - [Command Line Interface](https://eslint.org/docs/user-guide/command-line-interface) page should describe the newly `--verify-on-recoverable-parsing-errors` option.
149 | - [Node.js API](https://eslint.org/docs/6.0.0/developer-guide/nodejs-api) page should describe the newly `verifyOnRecoverableParsingErrors` option.
150 |
151 | ## Drawbacks
152 |
153 | - The value of this feature is relatively small because people can verify source code after they fixed syntax errors. This feature provides just efficient.
154 | - This feature makes core rules more complex if we wanted to support widely situations.
155 |
156 | ## Backwards Compatibility Analysis
157 |
158 | This is not a breaking change.
159 |
160 | - `Linter` class will handle a new property.
161 | - `espree` package will recognize `recoverableErrors` option and change that behavior if the `recoverableErrors` option was `true`.
162 |
163 | If users are depending `parserOptions.recoverableErrors`, possibly it will be broken. But I believe that we don't need to be worried about the case.
164 |
165 | ## Alternatives
166 |
167 | - `vue-eslint-parser`'s way is an alternative; A custom parser returns `errors` property along with AST and a plugin rule reports the errors in the property. But people can disable plugin rules, so it's not proper as the way that shows syntax errors.
168 |
169 | ## Open Questions
170 |
171 | Nothing in particular.
172 |
173 | ## Frequently Asked Questions
174 |
175 | Nothing in particular.
176 |
177 | ## Related Discussions
178 |
179 | - https://github.com/eslint/eslint/issues/3815
180 | - https://github.com/eslint/espree/issues/368
181 | - https://github.com/eslint/eslint/pull/11509#pullrequestreview-220174854
182 |
183 | [Recoverable Errors]: #recoverable-errors
184 |
--------------------------------------------------------------------------------
/designs/2019-rule-tester-improvements/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-06-07
2 | - RFC PR: https://github.com/eslint/rfcs/pull/25
3 | - Authors: Toru Nagashima ([@mysticatea](https://github.com/mysticatea))
4 |
5 | # `RuleTester` Improvements
6 |
7 | ## Summary
8 |
9 | This RFC improves `RuleTester` to check more mistakes.
10 |
11 | ## Motivation
12 |
13 | `RuleTester` overlooks some mistakes.
14 |
15 | - Using non-standard properties of AST ([typescript-eslint/typescript-eslint#405](https://github.com/typescript-eslint/typescript-eslint/issues/405)).
16 | Especially, `node.start` and `node.end` exist in AST `espree` made, but it's not standardized and some custom parsers don't make those properties. But `node.loc` has `start`/`end` properties, so it's hard to detect `node.start`/`node.end` with static analysis. Therefore, `RuleTester` should detect those.
17 | - Untested autofix.
18 | If people forgot to write `output` property in test cases, `RuleTester` doesn't test autofix silently.
19 | - `errors` property with a number (found in [eslint/eslint#11798](https://github.com/eslint/eslint/pull/11798)).
20 | `errors` property with a number ignores syntax errors in test code. We overlooked the mistake of [tests/lib/rules/complexity.js#L84](https://github.com/eslint/eslint/blob/cb1922bdc07e58de0e55c13fd992dd8faf3292a4/tests/lib/rules/complexity.js#L84) due to this. The number `errors` property cannot check the reported error was the expected error.
21 | - Typo property names in `errors` property with objects.
22 | [eslint/eslint#12096](https://github.com/eslint/eslint/pull/12096).
23 |
24 | ## Detailed Design
25 |
26 | 1. Disallowing `node.start` and `node.end`
27 | 1. Ensuring to test autofix
28 | 1. Changing the `errors` property of a number to fail on syntax errors
29 | 1. Changing the `errors` property of objects to fail on unknown properties
30 |
31 | ### 1. Disallowing `node.start` and `node.end`
32 |
33 | `RuleTester` fails test cases if a rule implementation used `node.start` or `node.end` in the test case.
34 |
35 | #### Implementation
36 |
37 | - In `RuleTester`, it registers an internal custom parser that wraps `espree` or the parser of `item.parser` to `Linter` object.
38 | - The internal custom parser fixes the AST that the original parser returned, as like [test-parser.js](https://github.com/eslint/eslint/blob/21f3131aa1636afa8e5c01053e0e870f968425b1/tools/internal-testers/test-parser.js).
39 |
40 | ### 2. Ensuring to test autofix
41 |
42 | `RuleTester` fails test cases if a rule implementation fixed code but `output` property was not defined in the test case.
43 |
44 | #### Implementation
45 |
46 | - If `output` property didn't exist but the rule fixed the code, `RuleTester` fails the test case as "The rule fixed the code. Please add 'output' property." It's implemented around [lib/rule-tester/rule-tester.js#L594](https://github.com/eslint/eslint/blob/21f3131aa1636afa8e5c01053e0e870f968425b1/lib/rule-tester/rule-tester.js#L594).
47 |
48 | ### 3. Changing the `errors` property of a number to fail on syntax errors
49 |
50 | `RuleTester` fails test cases always if the `code` has a syntax error.
51 |
52 | #### Implementation
53 |
54 | - Unwrap [lib/rule-tester/rule-tester.js#L414-L419](https://github.com/eslint/eslint/blob/02d7542cfd0c2e95c2222b1e9e38228f4c19df19/lib/rule-tester/rule-tester.js#L414-L419).
55 |
56 | ### 4. Changing the `errors` property of objects to fail on unknown properties
57 |
58 | `RuleTester` fails test cases if any item of `errors` has unknown properties.
59 |
60 | #### Implementation
61 |
62 | - [eslint/eslint#12096](https://github.com/eslint/eslint/pull/12096)
63 |
64 | ## Documentation
65 |
66 | [RuleTester](https://eslint.org/docs/developer-guide/nodejs-api#ruletester) should be updated.
67 |
68 | - `output` ("optional" → "required if the rule fixes code")
69 |
70 | ## Drawbacks
71 |
72 | This change may enforce plugin owners to fix their tests.
73 |
74 | ## Backwards Compatibility Analysis
75 |
76 | This is a breaking change that can break existing tests.
77 |
78 | But the breaking cases may indicate that the rule was not tested enough.
79 |
80 | ## Alternatives
81 |
82 | - About "Disallowing `node.start` and `node.end`", we can standardize those properties. But it's a breaking change for custom parser owners. On the other hand, using `node.start` and `node.end` breaks the rule if users used custom parsers, so the impact of this disallowing is limited.
83 |
84 | ## Related Discussions
85 |
86 | - https://github.com/eslint/eslint/issues/8956
87 | - https://github.com/eslint/eslint/pull/8984
88 | - https://github.com/eslint/eslint/pull/11798
89 | - https://github.com/eslint/eslint/pull/12096
90 | - https://github.com/typescript-eslint/typescript-eslint/issues/405
91 |
--------------------------------------------------------------------------------
/designs/2019-suggestions/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-07-04
2 | - RFC PR: https://github.com/eslint/rfcs/pull/30
3 | - Authors: Ilya Volodin ([@ilyavolodin](https://github.com/ilyavolodin)), Dan Abramov ([@gaearon](https://github.com/gaearon))
4 |
5 | # Code Suggestions
6 |
7 | ## Summary
8 |
9 | This RTC proposes introducing a new mechanism for providing users with suggestions. It's very similar to fixes mechanism that already exists in ESLint, but will provide ability to create multiple suggestions per rule, suggestions will not be automatically applied by using `--fix` flag, and will require user interaction. This feature will only be exposed through the ESLint API, and will not be available on the CLI.
10 |
11 | ## Motivation
12 |
13 | This has been suggested multiple times before, but the main driving force behind this proposal is ability to provide user with options on how to automatically refactor/fix their code, without a possibility of "stealthily" breaking it. Examples of such suggestions include ability to suggest adding `import` statement if certain code is added to a file by the user, ability to provide refactoring suggestions like extracting repeated value into a variable, reformatting code to extract repeated statements into reusable function, etc. I do not envision ESLint team to initially create any rules that would provide suggestions, so this would be a feature that would enable plugin authors to do so, but it might be something that ESLint team will eventually do. This feature is geared towards code editor integrations, as such, it will only be available via NodeJS API.
14 |
15 | ## Detailed Design
16 |
17 | Suggestion will be applied as a stand-alone change, without triggering multipass fixes. Each suggestion should focus on a singular change in the code and should not try to confirm to user defined styles. For example, if suggestion is adding a new statement into the codebase, it should not try to match correct indentation, or confirm to user preferences on presence/absence of semicolumns. All of those things will be corrected by multipass autofix when the user triggers it. It should be suggested that if a user wants to use suggestions, he/she should also enable auto-fix on save in their editor of choice.
18 |
19 | * Modify `context.report` function parameter to include new property called `suggest` of type array of objects. Each object will contain a description of the suggestion and a function that will be able to modify the code of the file to implement suggestion. Syntax is going to be exactly the same as for `fix` functions. Example of new signature below:
20 | ```js
21 | context.report({
22 | node,
23 | loc,
24 | messageId: 'messageId',
25 | fix(fixer) { },
26 | suggest: [
27 | { desc: 'Description of the suggestion', fix: (fixer) => { }},
28 | { desc: 'Description of the suggestion', fix: (fixer) => { }}
29 | ]
30 | });
31 | ```
32 | * Change `_verifyWithoutProcessors` and `_verifyWithProcessor` functions in linter.js to modify output of those functions to include `suggestions` array returned by the rule. After modification the signature of the return object should look like this:
33 | ```js
34 | interface LintMessage {
35 | //...(existing props)...
36 | suggestions: Array<{ desc: string, fix: { range: [number, number] } }>
37 | }
38 | ```
39 | * Modify output of `CLIEngine` `executeOnFiles` and `executeOnText` to include `suggestions` array returned by the rule. If `options.disableFixes` are passed into those functions, return result should not include any suggestions return by the rules.
40 | * Update `docs` section of rule metadata to include `suggestion: true|false` to make it easier to identify rules that can return suggestions.
41 | * Unlike fixes, suggestion will not expose a new API function on `CLIEngine` to output selected suggestion to a file. Instead, integration would be expected to apply suggestion in memory and let the user save the file on their own.
42 |
43 | ## Documentation
44 |
45 | This would need to be documented as part of the public API.
46 | * [Node.js API](https://eslint.org/docs/developer-guide/nodejs-api) page needs to be updated with corresponding changes to `linter` and `CLIEngine`.
47 | * [Working with Rules](https://eslint.org/docs/developer-guide/working-with-rules) page needs to be updated to show examples of returning suggestions. It would also need to include changes to metadata to identify rules that return suggestions.
48 | * Update rule documentation template to clearly show rules that return suggestions.
49 |
50 | ## Drawbacks
51 |
52 | * This might potentially be a bit confusing, since we do not have many functions that we provide through API only and not through CLI.
53 | * Suggestions might lead to user created rules that try to do too much modifications to the user's code. For example suggesting large refactoring that might introduce a lot of breaking changes. To mitigate this, we should add documentation that would warn rule creators against doing that.
54 |
55 | ## Backwards Compatibility Analysis
56 |
57 | Since `suggestions` property in `context.report` object is optional, this should be fully backwards compatible.
58 |
59 | ## Alternatives
60 |
61 | This functionality is basically the same as existing `autofix` functionality already included in ESLint. The only difference is there might be multiple suggestions per error and they are not applied automatically by ESLint.
62 |
63 | ## Open Questions
64 |
65 | Should we consider instead providing new type of `rules` that would not report any issues, but only provide suggestions?
66 |
67 | ## Related Discussions
68 |
69 | https://github.com/eslint/eslint/issues/7873
70 | https://github.com/eslint/eslint/issues/6733#issuecomment-234656842
71 |
--------------------------------------------------------------------------------
/designs/2019-update-default-ignore-patterns/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-11-28
2 | - RFC PR: https://github.com/eslint/rfcs/pull/51
3 | - Authors: Toru Nagashima ([@mysticatea](https://github.com/mysticatea))
4 |
5 | # Update Default Ignore Patterns
6 |
7 | ## Summary
8 |
9 | This proposal changes the default ignore patterns to include `node_modules` in subdirectories and to exclude `.eslintrc.js`.
10 |
11 | ## Motivation
12 |
13 | - In monorepo style development, there are `node_modules`s in subdirectories. But the current default ignore patterns don't include it, so we have to configure ESLint with `node_modules` to ignore it.
14 | - Because ESLint ignores dotfiles, it doesn't lint `.eslintrc.js` by default. We have to configure ESLint with `!.eslintrc.js` to lint it.
15 |
16 | ## Detailed Design
17 |
18 | This proposal changes the default ignore patterns:
19 |
20 | - (as-is) `.*`
21 | - (new) `!.eslintrc.js`
22 | - (change) `/node_modules/*` → `/**/node_modules/*`
23 | - (remove) ~~`/bower_components/*`~~
24 |
25 | Therefore, ESLint ignores `node_modules` in subdirectories, and no longer ignores `.eslintrc.js` and `bower_components`.
26 |
27 | ### How about the config files of other tools?
28 |
29 | For example, Babel has `.babelrc.js` and VuePress has `.vuepress/config.js`. ESLint ignores such files as dotfiles by default.
30 |
31 | This proposal doesn't change this behavior because we don't want to have allowlist/denylist for all tools in the world. Instead, we can have unignoring patterns in the `ignorePatterns` property of shareable configs for your tools.
32 |
33 | ## Documentation
34 |
35 | - It needs an entry in the migration guide for 7.0.0 (or another major release) because of breaking changes.
36 | - It needs to update the "[.eslintignore](https://eslint.org/docs/user-guide/configuring#eslintignore)" section to explain the new default ignore patterns.
37 |
38 | ## Drawbacks
39 |
40 | This proposal doesn't increase maintenance cost because it just updates a pattern list.
41 |
42 | If somebody wants to lint `node_modules` in subdirectories, they have to configure ESLint to unignore those. But I guess that it's rarer than wanting to ignore `node_modules` in subdirectories.
43 |
44 | ## Backwards Compatibility Analysis
45 |
46 | This is a breaking change.
47 |
48 | - ESLint may report more warnings on `.eslintrc.js`.
49 | - ESLint may report more warnings in `bower_components`.
50 | - ESLint may stop with fatal errors (all files are ignored) in the `node_modules` of subdirectories.
51 |
52 | ## Alternatives
53 |
54 | We can use the `ignorePatterns` of shareable configs to configure and reuse this kind of matter.
55 |
56 | ## Related Discussions
57 |
58 | - https://github.com/eslint/eslint/issues/1163 - ignore node_modules folder by default
59 | - https://github.com/eslint/eslint/issues/10089 - .babelrc.js should not be ignored by default
60 | - https://github.com/eslint/eslint/issues/10341 - do not ignore files started with `.` by default
61 | - https://github.com/eslint/tsc-meetings/blob/master/notes/2019/2019-12-05.md#remove-bower_components-from-default-ignores
62 |
--------------------------------------------------------------------------------
/designs/2019-variable-definition-information-of-config-files/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2019-03-04
2 | - RFC PR: https://github.com/eslint/rfcs/pull/17
3 | - Authors: Toru Nagashima ([@mysticatea])
4 |
5 | # Variable Definition Information of Config Files
6 |
7 | ## Summary
8 |
9 | This proposal adds variable definition information of config files to `Variable` objects.
10 |
11 | ## Motivation
12 |
13 | To make rules being able to report matters about variable definitions related to config files.
14 |
15 | Especially, this proposal minds [no-redeclare] rule to report redeclaration by a mix of regular variable declarations, `/*globals*/` comments, and config files. ([eslint/eslint#11370])
16 |
17 | ## Detailed Design
18 |
19 | Currently, `Variable` objects have the following properties to express `/*globals*/` definitions.
20 |
21 | - `variable.eslintExplicitGlobal` (`boolean`) ... `true` if this variable was defined by `/*globals*/` comment.
22 | - `variable.eslintExplicitGlobalComment` (`Comment` object) ... The `/*globals*/` comment that defines this variable. If two or more comments defined this variable, it adopts the last one.
23 | - `variable.writeable` (`boolean`) ... `true` if the final setting is `"writable"`.
24 |
25 | This proposal adds the following properties.
26 |
27 | - `variable.eslintExplicitGlobalComments` (an array of `Comment` objects) ... All `/*globals*/` comments that define this variable.
28 | - `variable.eslintImplicitGlobalSetting` (`"readonly"` | `"writable"` | `null`) ... The setting in config files. This is `null` if config files didn't define it.
29 |
30 | And this proposal removes existing `variable.eslintExplicitGlobalComment` property in favor of new `variable.eslintExplicitGlobalComments` property.
31 |
32 | Both `variable.eslintExplicitGlobalComments` and `variable.eslintImplicitGlobalSetting` are instantiated even if regular variable declarations (E.g. `let foo` syntax) exist. Therefore, [no-redeclare] rule can verify redeclaration by a mix of regular variable declarations, `/*globals*/` comments, and config files.
33 |
34 | This will be implemented into [`addDeclaredGlobals(globalScope, configGlobals, commentDirectives)`][lib/linter.js#L73-L131] function.
35 |
36 | ## Documentation
37 |
38 | This proposal is related to making rules. So [Working with Rules](https://eslint.org/docs/developer-guide/working-with-rules) page should describe those properties, including undocumented existing properties.
39 |
40 | - `variable.writeable`
41 | - `variable.eslintExplicitGlobal`
42 | - `variable.eslintExplicitGlobalComments`
43 | - `variable.eslintImplicitGlobalSetting`
44 |
45 | And, we should make an item in the migration guide. One undocumented property is removed.
46 |
47 | - `variable.eslintExplicitGlobalComment`
48 |
49 | ## Drawbacks
50 |
51 | Rules get accessible to `globals` setting values. It might restrict us to change the system which declares global variables.
52 |
53 | ## Backwards Compatibility Analysis
54 |
55 | If a plugin rule used `variable.eslintExplicitGlobalComment` property, because this proposal removes it, the rule will be broken. But that property was undocumented and [GitHub search](https://github.com/search?p=1&q=eslintExplicitGlobalComment+language%3Ajavascript&type=Code) shows only copies of `no-unused-vars` rule or ESLint core, so this removal is no danger. (One point, `eslint-plugin-closure` package has [the type definition of escope](https://github.com/google/eslint-closure/blob/bf5c0d4d2a67ea3e8394c228717ae23d1a1ae4ba/packages/eslint-plugin-closure/lib/externs/escope.js#L163). That will need to be tweaked regardless of the removal.)
56 |
57 | If a plugin rule used `variable.eslintExplicitGlobalComments` property or `variable.eslintImplicitGlobalSetting` property for some reason, the rule will be broken. But it's a pretty little possibility.
58 |
59 | ## Alternatives
60 |
61 | - Adding `context.getImplicitGlobalSettings()` method to `RuleContext` object to retrieve normalized `globals` setting. In this case, rules can know settings in config files even if `/*globals foo:off*/` comment existed. On the other hand, rules have to search and parse `/*globals*/` comments manually although ESLint core has handled those once.
62 |
63 | ## Related Discussions
64 |
65 | - [eslint/eslint#11370]
66 |
67 |
68 | [@mysticatea]: https://github.com/mysticatea
69 | [eslint/eslint#11370]: https://github.com/eslint/eslint/issues/11370
70 | [lib/linter.js#L73-L131]: https://github.com/eslint/eslint/blob/b00a5e9d8dc6c5f77eb0e4e0c58dfaf12a771d7b/lib/linter.js#L73-L131
71 | [no-redeclare]: https://eslint.org/docs/rules/no-redeclare
72 | [eslint-plugin-eslint-comments]: https://github.com/mysticatea/eslint-plugin-eslint-comments
73 |
--------------------------------------------------------------------------------
/designs/2020-cache-contents-option/README.md:
--------------------------------------------------------------------------------
1 | - Start Date: 2020-07-15
2 | - RFC PR: https://github.com/eslint/rfcs/pull/63
3 | - Authors: @c-home
4 |
5 | # Add option to allow use of file contents for cache
6 |
7 | ## Summary
8 |
9 | This RFC proposes improving cache for CI by providing an option to use a hash (instead of `mtime` and `fsize`) comparison to determine whether a file has changed.
10 |
11 | ## Motivation
12 |
13 | Motivation for this change is the same as [#11490](https://github.com/eslint/eslint/issues/11490), where details of files like `mtime` are not preserved in the CI environment. With a hash comparison, the cache can be used in CI and would significantly reduce the time ESLint takes to run.
14 |
15 | ESLint uses [`file-entry-create`](https://github.com/royriojas/file-entry-cache) as its cache provider. [fileEntryCache#create](https://github.com/royriojas/file-entry-cache#createcachename-directory-usechecksum) gives the option to use a md5 hash through the `useChecksum` argument, but ESLint currently does not utilize it.
16 |
17 | ## Detailed Design
18 |
19 | This RFC adds a `--cache-strategy` CLI [option](https://github.com/eslint/eslint/blob/e71e2980cd2e319afc70d8c859c7ffd59cf4157b/lib/options.js#L198). Users can specify the option to be:
20 | - `contents`, for the use of an md5 hash
21 | - `metadata`, for the use of `mtime` and `fsize`
22 |
23 | Modified time and size (`metadata`), does not need to be specified as it will remain the default.
24 |
25 | The implementation will be similar to [#11487](https://github.com/eslint/eslint/pull/11487), with differences in the naming of the options. The options were kept general so if the underlying implementation of the comparison method were to change, the usage and documentation can remain the same.
26 |
27 | The majority of changes will be made to [`LintResultCache`](https://github.com/eslint/eslint/blob/e71e2980cd2e319afc70d8c859c7ffd59cf4157b/lib/cli-engine/lint-result-cache.js#L47). The `cache-strategy` will be added to the constructor of `LintResultCache`. If `cache-strategy` is set as `contents`, an md5 hash will be used in [fileEntryCache#create](https://github.com/royriojas/file-entry-cache#createcachename-directory-usechecksum).
28 |
29 | ## Documentation
30 |
31 | Documentation for ESLint CLI will be updated with a description of the option.
32 |
33 | ## Drawbacks
34 |
35 | - Specifying the details of the cache behaviour through a public option can make it more difficult to change the design and implementation [without breaking its usage](https://github.com/eslint/eslint/issues/11490#issuecomment-471400143).
36 | - Comparing files based on their contents is slower than comparing files by their file metadata. However, a file contents comparison is not the default.
37 | - Like any new feature, this flag will slightly increase the complexity and maintenance costs of ESLint.
38 |
39 | ## Backwards Compatibility Analysis
40 |
41 | This change is backwards-compatible. It adds a new CLI option while keeping the behavior the same if the option is not specified.
42 |
43 | ## Alternatives
44 |
45 | Mentioned as an alternative in [#11487](https://github.com/eslint/eslint/pull/11487), and if `file-entry-cache` [#14](https://github.com/royriojas/file-entry-cache/pull/14) were to be merged, an environment variable could be passed through `eslint` to `file-entry-cache`.
46 |
47 | ## Open Questions
48 |
49 | None
50 |
51 | ## Frequently Asked Questions
52 |
53 | None yet
54 |
55 | ## Related Discussions
56 |
57 | - https://github.com/eslint/eslint/issues/11319
58 | - https://github.com/eslint/eslint/issues/11490
59 | - https://github.com/eslint/eslint/pull/11487
60 | - https://github.com/royriojas/file-entry-cache/pull/14
61 |
--------------------------------------------------------------------------------
/designs/2020-cwd-in-formatters/README.md:
--------------------------------------------------------------------------------
1 | - Repo: eslint/eslint
2 | - Start Date: 2020-06-05
3 | - RFC PR: https://github.com/eslint/rfcs/pull/57
4 | - Authors: Toru Nagashima (https://github.com/mysticatea)
5 |
6 | # Accessing `cwd` from formatters
7 |
8 | ## Summary
9 |
10 | This RFC lets formatters can access `cwd` in order to make relative paths or do something like.
11 |
12 | ## Motivation
13 |
14 | Our Node.js API has the `cwd` option, and ESLint uses it while linting. But formatters cannot access the `cwd` option. It prevents formatters from making relative paths for readability.
15 |
16 | ## Detailed Design
17 |
18 | Pass `cwd` to the `cwd` property of the second argument, then formatters can use it.
19 |
20 | ```js
21 | module.exports = (results, context) => {
22 | console.log(context.cwd) // → the absolute path to the current working directory.
23 | console.log(context.rulesMeta) // → the metadata of the used rules (it exists currently).
24 | }
25 | ```
26 |
27 | ### Implementation
28 |
29 | The adapter object that the `ESLint.prototype.loadFormatter` method makes gives the `cwd`.
30 |
31 | > ```diff
32 | > @@ -575,7 +575,7 @@ class ESLint {
33 | > throw new Error("'name' must be a string");
34 | > }
35 | >
36 | > - const { cliEngine } = privateMembersMap.get(this);
37 | > + const { cliEngine, options } = privateMembersMap.get(this);
38 | > const formatter = cliEngine.getFormatter(name);
39 | >
40 | > if (typeof formatter !== "function") {
41 | > @@ -595,6 +595,9 @@ class ESLint {
42 | > results.sort(compareResultsByFilePath);
43 | >
44 | > return formatter(results, {
45 | > + get cwd() {
46 | > + return options.cwd;
47 | > + },
48 | > get rulesMeta() {
49 | > if (!rulesMeta) {
50 | > rulesMeta = createRulesMeta(cliEngine.getRules());
51 | > ```
52 | >
53 | > https://github.com/eslint/eslint/commit/43a7e207ca8c0b816d4fba2b4b290f761c33adb4
54 |
55 | ## Documentation
56 |
57 | We should update the "[Working with Custom Formatters](https://eslint.org/docs/developer-guide/working-with-custom-formatters)" page to mention the `cwd`.
58 |
59 | ## Drawbacks
60 |
61 | Nothing in particular.
62 |
63 | ## Backwards Compatibility Analysis
64 |
65 | This addition is backward compatible.
66 |
67 | If custom formatters want to use the `cwd` property and continue to support old versions of ESLint, they must check if the `cwd` property exists or not.
68 |
69 | ## Alternatives
70 |
71 | ## Related Discussions
72 |
73 | - https://github.com/eslint/eslint/issues/13376 - Output relative paths rather than absolute paths
74 |
--------------------------------------------------------------------------------
/designs/2020-es-module-support/README.md:
--------------------------------------------------------------------------------
1 | - Repo: eslint/espree
2 | - Start Date: 2020-12-13
3 | - RFC PR: https://github.com/eslint/rfcs/pull/72
4 | - Authors: Mike Reinstein (https://github.com/mreinstein)
5 |
6 | # Adding es module support
7 |
8 | ## Summary
9 |
10 | Provide an ecmascript module for espree.
11 |
12 |
13 | ## Motivation
14 |
15 | Historically there have been a number of competing module standards: commonjs, requireJs, iife wrapped script tags, etc.
16 |
17 | This has contributed to a very complicated tooling scenario for node/npm/javascript based projects in general:
18 | * every module has it's own unique build system, with no consistency across modules.
19 | * projects are packed with complex configurations, much of which is plumbing related to packaging/bundling.
20 |
21 | Over time, browsers have adopted the ecma module standard. Today, support is pretty good:
22 | https://caniuse.com/es6-module
23 |
24 | Node also added support in v12.17.1 and up, and has officially marked the API stable:
25 | https://nodejs.org/dist/latest-v15.x/docs/api/esm.html#esm_modules_ecmascript_modules
26 |
27 |
28 | By adopting this new standard, we can achieve a few things:
29 |
30 | * eventual simplification of espree's build process by eliminating the need for multiple module formats
31 | * make it easier to directly importing espree in browser environments, deno, etc. Skypack is a CDN that already enables this, but they have internal logic that looks at every package in npm and tries to build an es module. https://www.skypack.dev/
32 |
33 | As more projects adopt this standardized module format, the hope is a lot of this "connective glue" will fall away, leaving us with leaner modules and less build tooling across our stack.
34 |
35 |
36 | ## Detailed Design
37 |
38 | ### Nomenclature
39 |
40 | * `ESM` ecmascript module. The standardized module format for javascript
41 | * `CJS` commonjs. Node's historical module format (i.e., `module.exports = ...`, `const a = require('foo')`)
42 | * `UMD` universal module definition. A legacy format for specifying javascript code as a module in a way that tries to guarantee compatibility with many loader formats including requireJS, commonjs, and global script tags.
43 | * `IIFE` immediately invoked function expressions. A legacy strategy for encapsulating javascript into a module that won't leak global variables into the execution environment. It is still the primary way of packaging global script tags for inclusion in a web page. `UMD` is usually wrapped in an `IIFE`
44 | * `CDN` content delivery network. can be thought of as a set of servers that host static content all around the globe to deliver assets quickly and at scale.
45 |
46 |
47 | ### build process
48 |
49 | Today, espree is written in commonjs (CJS) format, and a universal module definition (UMD) build is created too.
50 |
51 |
52 | today's build process:
53 | ```
54 | ┌-------------------┐ ┌-----------------┐
55 | │ │ │ │ package.json:
56 | │ espree.js | │ build/espree.js │
57 | │ (CJS entry point) ├──BUILD_STEP──▶│ (UMD bundle) │ CJS ---▶ "main": "espree.js",
58 | │ │ │ │
59 | └-------------------┘ └-----------------┘
60 | ```
61 |
62 |
63 | The strategy that probably makes the most sense in this initial work is to do what is known as a "dual package" solution, where
64 | espree provides an ESM, and a CJS module.
65 |
66 | Providing a dual package shouldn't affect any existing espree users, but provies an ESM option for users that want it.
67 |
68 |
69 | proposed build process in this RFC:
70 | ```
71 | ┌-------------------┐
72 | │ │
73 | │ dist/espree.cjs │
74 | │ (CJS entry point) │ package.json:
75 | ┌-------------------┐ │ │
76 | │ │ └--▲----------------┘ CJS ---▶ "main": "dist/espree.cjs",
77 | │ espree.js │ │ "exports": {
78 | │ (ESM entry point) ├───BUILD_STEP───┘ ESM ---▶ "import": "espree.js",
79 | │ │ CJS ---▶ "require": "./dist/espree.cjs"
80 | └-------------------┘ },
81 | "type": "module"
82 | ```
83 |
84 | browserify is specifically oriented around commonjs as the input format, so I also propose replacing it with rollup, which understands and expects ESM as it's input format.
85 |
86 | `UMD` is not part of the formal package interface and will be dropped altogether.
87 |
88 |
89 | ## Documentation
90 |
91 | The changes should be described in the migration guide for whichever major version of espree this goes into (currently considering the upcoming 8.x line.)
92 |
93 | Adding an `exports` field to `package.json` will break the existing API, it makes sense to formally announce this via blog post.
94 |
95 |
96 | ## Drawbacks
97 |
98 | The javascript build/tooling ecosystem is already monstrously complex, and this change adds another output format, which further complicates things (compare the 2 graphs above to see this additional complexity visualized.)
99 |
100 | I do believe this is a temporary scenario; as time goes on, the UMD and CJS entry points could be dropped altogether, along with the build step. But that will take months, maybe even years depending on how comfortable the community is with this change and how widely adopted the ESM format becomes.
101 |
102 |
103 | ## Backwards Compatibility Analysis
104 |
105 | The dual package approach _should_ not break espree for anyone currently using espree.
106 |
107 |
108 | ## Alternatives
109 |
110 | Another option is to just drop CJS, UMD and the build system altogether, provide an ESM only implementation,
111 | and make this a part of a semver-major version bump. (e.g., espree@8 is ESM only.)
112 |
113 | This would essentially eliminate the build step and make things very simple, but it has a big downside:
114 | * users that don't support ESM only projects would be stuck on `espree <= 7.x` forever (or at least until they added the ability to use ESM in their own stuff.)
115 |
116 |
117 | ## Open Questions
118 |
119 | None yet.
120 |
121 |
122 | ## Help Needed
123 |
124 | None yet.
125 |
126 |
127 | ## Frequently Asked Questions
128 |
129 | > what is this "dual package" stuff?
130 |
131 | It's a proposal by the node community on how a module may go about adopting ESM without totally breaking CJS for their users. https://nodejs.org/api/packages.html#packages_writing_dual_packages_while_avoiding_or_minimizing_hazards
132 |
133 |
134 | > are any other well known modules using this approach?
135 |
136 | acorn is producing a dual module, and has been the inspiration for my own work around the forthcoming espree PR https://github.com/acornjs/acorn/blob/master/acorn/package.json#L5-L11
137 |
138 |
139 | > does this mean local development now requires a build step?
140 |
141 | no. Tests should be written to run against the ESM source in `lib/`. The pre-release tests should be run against the CJS bundle as part of the pre-publish step.
142 |
143 |
144 | > how would one import espree now that it's providing an esm interface?
145 |
146 | There will be named exports. i.e.,
147 |
148 | ```javascript
149 | import { parse, tokenize /*, ... */ } from 'espree';
150 | ```
151 |
152 |
153 | > npm modules reference each other via commonjs, and the browser's loader expects URLs, so why are we concerned with making npm modules work in a browser?
154 |
155 | that's true, you can't simply do `import espree from 'https://github/eslint/espree.js'` naively because it's referencing other npm modules in it, such as `acorn`.
156 |
157 | However there are some CDNs which will transform npm modules to refer to URLs. For example, this works today:
158 |
159 | ```javascript
160 | import espree from 'https://cdn.skypack.dev/espree'
161 | ```
162 |
163 | By making espree a standard ESM it reduces the amount of transforms that need to be run on it to make it usable.
164 |
165 | It's also possible that in the semi-near future, node may consider offering URL loaders. Deno is already doing this.
166 |
167 |
168 | ## Related Discussions
169 |
170 | - [1] The discussion genesis https://github.com/eslint/espree/issues/457
171 |
--------------------------------------------------------------------------------
/designs/2020-generic-ast-support/README.md:
--------------------------------------------------------------------------------
1 | - Repo: eslint/eslint
2 | - Start Date: 2020-06-04
3 | - RFC PR: https://github.com/eslint/rfcs/pull/56
4 | - Authors: Ilya Volodin
5 |
6 | # Generic AST support
7 |
8 | ## Summary
9 |
10 | Allow ESLint to traverse and lint generic (non estree compliant AST) and allow parsers to specify information about AST that would drive ESLint's traversal and features.
11 |
12 | ## Motivation
13 |
14 | ESLint has cemented itself as a go to tool for JavaScript/TypeScript ecosystem for style checking and linting. However, there are a lot of connected languages/technologies that are used every day by ESLint users that are either lacking linters, or require yet another tool to be setup and configured for the repository. Examples include HTML, CSS, SCSS, LESS, GraphQL, JSON, etc.
15 | With addition of config overrides users can now setup separate configuration per glob, so that would allow them to have a single instance of ESLint and a single configuration that would allow linting of the entire repository, including all non javascript/typescript files.
16 |
17 | ## Detailed Design
18 |
19 | ### Initial assumptions
20 | * This RFC does not expect or implies that existing core or plugin rules will work for any files that are parsed through non-estree compliant parsers. Every parser that chooses to output custom AST will have to provide new rules that will work for this AST in the form of plugins.
21 | * This RFC doesn't propose full feature parity for new parser with existing ESLint capabilities. For example, it doesn't propose a new way to provide custom Code Path Analyzer, or support inline directives for controlling ESLint's flow for custom ASTs. However, those functionalities can be added later with a separate RFC(s).
22 |
23 | Currently ESLint has a lot of hardcoded assumptions about AST. For example, it assumes that nodes are identified by the property called `type` and that property exists on every node. It assumes that top-most node is always `Program` and that AST supports code-path-analysis (which is very specific to JavaScript). This RFC proposes to modify expected output from the `parseForESLint` method of the custom parsers to include additional property called `parserSupportOptions` of type object with four new properties (for now):
24 | - nodeIdentifier: function | string | undefined - Specifies a function that can retrieve the name of the ASTNode property that identifies node. Alternatively, can be a string that just returned the name of the property that identifies all nodes. If the property is not provided, ESLint will default to `type` for backwards compatibility.
25 | - codePathAnalysis: "off" | "default" - Indicates AST's support for built-in CodePathAnalysis support. Defaults to `”default”` for backwards compatibility.
26 | - rootNode: string - Provides the name of the top level node in the AST. Defaults to `Program` for backwards compatibility.
27 | - metadata: { language: string } - Used for informational purposes only. Identifies name of the language that this parser supports.
28 |
29 | This RFC also proposes modifying existing returned property "scopeManager" that's already returned by `parseForESLint`. Currently, while documentation states, that this property is not required, ESLint will crash if returning `null`. Instead, it can be modified to return tri-state value of type `"off" | "default" | ScopeManager`. If "off" is returned, scope analysis will be completely disabled for files parsed by custom parser. "default" will specify to use ESLint's default scope manager (eslint-scope). Providing an object of type `ScopeManager` will require ESLint to use it instead of analyzing code through eslint-scope.
30 |
31 | This change would allow basic support of linting non-estree compliant ASTs. This will still not enable full feature parity with ESLint's abilities when linting JavaScript. In the future new properties might be added to support parsing comments to enable inline directives for controlling rules. Other enhancements might include ability to provide replacement for CodePathAnalysis and scopes creation.
32 |
33 | ## Documentation
34 |
35 | This will be documented as part of the page that describes [working with custom parsers](https://eslint.org/docs/developer-guide/working-with-custom-parsers). In addition, blog post would let other users know that they can start creating parsers and rules for new languages. Although, it might be a good idea to wait until there's an example in the wild that could be pointed to as a reference.
36 |
37 | ## Drawbacks
38 |
39 | The only drawback that I can think of, is that JavaScript specific rules will not work for other ASTs. Each language will most likely have to implement their own set of rules, and they will be incompatible across different parsers. While this might cause some confusion for users, I do believe this makes sense and there is no need to try to somehow generalize rules to support multiple ASTs.
40 |
41 | ## Backwards Compatibility Analysis
42 |
43 | This change will be fully backwards compatible. In the future, default nodeIdentifier described above could be removed and moved into espree instead.
44 |
45 | ## Alternatives
46 |
47 | None that I can think of, other then not to support any non-compliant estree ASTs.
48 |
49 | ## Open Questions
50 |
51 | * Is it OK to release incomplete support for other AST types? Inline directives being the biggest obstacle, from my perspective.
52 | * Should `nodeIdentifier` be passed into rules through `context` to allow creations of very generic rules that can support multiple languages/ASTs?
53 | * Is `parserSupportOptions` descriptive enough name? I wasn't able to come up with anything better.
54 |
55 | ## Help Needed
56 |
57 | I have a working branch with a large refactoring effort to remove hardcoded `type` nodeIdentifier. Everything is working and all tests are passing, except for one in code-path-analysis. Unfortunately, I wasn't able to figure out what is causing this problem. Code is available here: https://github.com/ilyavolodin/eslint/tree/ast
58 | Another problem, is that I wasn't able to find a way to pass `nodeIdentifier` into formatters, since they live higher in the stack then execution of the custom parser provided. I need suggestions on how to fix this. Not all parsers require this information, but some might.
59 |
60 | ## Related Discussions
61 |
62 | * https://github.com/eslint/eslint/pull/13305
63 |
--------------------------------------------------------------------------------
/designs/2020-rule-tester-only/README.md:
--------------------------------------------------------------------------------
1 | - Repo: eslint/eslint
2 | - Start Date: 2020-12-27
3 | - RFC PR: (leave this empty, to be filled in later)
4 | - Authors: Brandon Mills ([@btmills](https://github.com/btmills))
5 |
6 | # `RuleTester` test isolation with `only`
7 |
8 | ## Summary
9 |
10 |
11 |
12 | `RuleTester` currently lacks a built-in way for developers to isolate individual tests during development.
13 | Temporarily deleting or commenting out the other tests is tedious.
14 | This adds an optional `only` property to run individual tests in isolation.
15 |
16 | ## Motivation
17 |
18 |
20 |
21 | When developers are working on a rule, we can run its tests with `npm run test:cli tests/lib/rules/my-rule.js`.
22 | Debugging sometimes requires focusing on a particular test and running it in isolation to avoid unrelated noise from other tests.
23 |
24 | Tools like Mocha already offer `it.only()` for low-friction test isolation.
25 | Developers may already be familiar with that API.
26 | Exposing a similar API via `RuleTester` would improve the ergonomics of debugging tests.
27 | This API is beneficial to any approach from `console.log("HERE");` to interactive debugging with breakpoints.
28 |
29 | ## Detailed Design
30 |
31 |
39 |
40 | Add an optional boolean `only` property to `RuleTester`'s `ValidTestCase` and `InvalidTestCase` types.
41 | Add `only` to the other `RuleTester`-only properties in the `RuleTesterParameters` array to exclude it from configuration schema validation.
42 |
43 | To allow using `RuleTester` with a custom test framework other than Mocha, parallel `RuleTester`'s existing `describe` and `it` implementations for `itOnly`:
44 |
45 | 1. Declare an `IT_ONLY` `Symbol`, set it as a static property on `RuleTester`, and initialize it to `null`.
46 | 1. Add an `itOnly` `set` accessor that sets `RuleTester[IT_ONLY]`.
47 | 1. Add an `itOnly` `get` accessor.
48 | 1. If `RuleTester[IT_ONLY]` is set, return it.
49 | 2. If global `it` and `it.only` are functions, return `Function.bind.call(it.only, it)`.
50 | 3. Throw an error:
51 | 1. If either `RuleTester[DESCRIBE]` or `RuleTester[IT]` is customized, recommend setting a custom `RuleTester.itOnly`.
52 | 2. If global `it` is a function, the current test framework does not support `only`.
53 | 3. Otherwise recommend installing a test framework like Mocha so that `only` can be used.
54 |
55 | At the end of `RuleTester`'s `run()` method, for each valid and invalid item, if `only` is `true`, call `RuleTester.itOnly` instead of `RuleTester.it`.
56 |
57 | Add [`--forbid-only`](https://mochajs.org/#-forbid-only) to the [`mocha` target in `Makefile.js`](https://github.com/eslint/eslint/blob/cc4871369645c3409dc56ded7a555af8a9f63d51/Makefile.js#L548) to prevent accidentally merging tests before removing `only`.
58 |
59 | Add a static `only()` convenience method on `RuleTester`.
60 | This adds `only: true` to a test and automatically converts string tests to objects if necessary.
61 |
62 | ```js
63 | /**
64 | * Adds the `only` property to a test to run it in isolation.
65 | * @param {string | ValidTestCase | InvalidTestCase} item A single test to run by itself.
66 | * @returns {ValidTestCase | InvalidTestCase}
67 | */
68 | static only(item) {
69 | if (typeof item === "string") {
70 | return { code: item, only: true };
71 | }
72 |
73 | return { ...item, only: true };
74 | }
75 | ```
76 |
77 | ## Documentation
78 |
79 |
83 |
84 | The [RuleTester section](https://eslint.org/docs/developer-guide/nodejs-api#ruletester) in our Node.js API docs will need to include a description of the `only` property on test case objects.
85 |
86 | The [Unit Tests](https://eslint.org/docs/developer-guide/unit-tests) page in the developer guide will also include a note about this next to the [Running Individual Tests](https://eslint.org/docs/developer-guide/unit-tests#running-individual-tests) section.
87 | This can also recommend enabling [`--forbid-only`](https://mochajs.org/#-forbid-only) to prevent accidentally merging tests before removing `only`.
88 |
89 | ## Drawbacks
90 |
91 |
101 |
102 | This exposes new API surface.
103 | That is mitigated by the implementation still being a wrapper around Mocha's existing API like the rest of `RuleTester`.
104 |
105 | Deleted or commented-out tests are hard to miss in diffs.
106 | An extra `only: true` could more easily sneak through, accidentally disabling all other tests.
107 | Calling Mocha with [`--forbid-only`](https://mochajs.org/#-forbid-only) will prevent that on CI runs, but it still requires third-party developers to opt in.
108 |
109 | ## Backwards Compatibility Analysis
110 |
111 |
116 |
117 | This change is backwards compatible.
118 | `RuleTester` currently fails any tests that include unrecognized properties like `only`, so we have no risk of breaking existing tests.
119 |
120 | If someone has customized `RuleTester` with a custom `it()` method, we cannot assume that `it.only()` has the same semantics as Mocha.
121 | Their existing tests will still work, but if they wish to use this new `only` property, they would need to provide a custom `itOnly()` alongside `it()`.
122 |
123 | ## Alternatives
124 |
125 |
131 |
132 | This builds upon Mocha's established `it.only()` API as prior art.
133 |
134 | The status quo has two alternatives:
135 |
136 | 1. We can temporarily delete or comment out the other tests. This is tedious.
137 | 1. We can filter through noise by scrolling through `console.log()` output or using conditional breakpoints, though the latter is not always possible.
138 |
139 | ## Open Questions
140 |
141 |
151 |
152 | 1. We could define a static `only()` convenience method on `RuleTester`.
153 | Given, for example, a valid string test `"var foo = 42;"`, it is easier to convert to `RuleTester.only("var foo = 42;")`, which is equivalent to `{ code: "var foo = 42;", only: true }`.
154 | The helper sets `only: true` on a test case argument after converting string cases to objects if necessary.
155 | For invalid cases that are already objects, adding `only: true` is likely easier than using the helper.
156 |
157 | A: The convenience is worth the simple implementation, so I've added this to the detailed design.
158 |
159 | 1. ~~Should using `only` cause the process to exit `1` even if tests pass as described in "Drawbacks"?~~
160 | Our Makefile can call Mocha with [`--forbid-only`](https://mochajs.org/#-forbid-only) instead.
161 | 3. In the `RuleTester.itOnly` `get` accessor, if `RuleTester[IT_ONLY]` is not customized and the global `it.only` is not a function, is throwing an error the right choice?
162 | We could instead pass through to `RuleTester.it`, ignoring `only` if it's not supported.
163 |
164 | A: There's [agreement](https://github.com/eslint/rfcs/pull/73#discussion_r557789067) that not throwing would be more surprising, so throwing an error is the right choice.
165 |
166 | 1. Do we need to support `skip`, the inverse of `only`?
167 |
168 | A: Not right now.
169 | We can ship `only` and evaluate adding `skip` later if we get feedback requesting it.
170 |
171 | ## Help Needed
172 |
173 |
179 |
180 | I can implement this myself.
181 |
182 | ## Frequently Asked Questions
183 |
184 |
191 |
192 | ## Related Discussions
193 |
194 |
200 |
201 | - This was suggested as an alternative solution to [RFC67](https://github.com/eslint/rfcs/pull/67) and [eslint/eslint#13625](https://github.com/eslint/eslint/issues/13625) because that change may not be possible as currently described.
202 |
--------------------------------------------------------------------------------
/designs/2020-timing-list-size/README.md:
--------------------------------------------------------------------------------
1 | - Repo: eslint/eslint
2 | - Start Date: 2020-09-11
3 | - RFC PR:
4 | - Authors: [bmish](https://github.com/bmish)
5 |
6 | # Configurable List Size For Per-Rule Performance Metrics
7 |
8 | ## Summary
9 |
10 | Running `TIMING=1 eslint` from the command-line outputs a list of [per-rule performance metrics](https://eslint.org/docs/developer-guide/working-with-rules#per-rule-performance-1). The list is currently limited to the top 10 longest-running rules. We would like to enable users to see a longer list of rules if desired.
11 |
12 | Sample existing output:
13 |
14 | ```sh
15 | $ TIMING=1 eslint lib
16 | Rule | Time (ms) | Relative
17 | :-----------------------|----------:|--------:
18 | no-multi-spaces | 52.472 | 6.1%
19 | camelcase | 48.684 | 5.7%
20 | no-irregular-whitespace | 43.847 | 5.1%
21 | valid-jsdoc | 40.346 | 4.7%
22 | handle-callback-err | 39.153 | 4.6%
23 | space-infix-ops | 35.444 | 4.1%
24 | no-undefined | 25.693 | 3.0%
25 | no-shadow | 22.759 | 2.7%
26 | no-empty-class | 21.976 | 2.6%
27 | semi | 19.359 | 2.3%
28 | ```
29 |
30 |
31 |
32 | ## Motivation
33 |
34 | Optimizing linting performance can become important in a large codebase or with particularly expensive rules. In order to determine what rules to focus on for performance optimizations, it's necessary to measure the running times of various rules while running linting on a codebase.
35 |
36 | While the current output of performance metrics for the top 10 longest-running rules helps, it doesn't give the complete picture, especially when there may be hundreds of rules enabled (from ESLint core and third-party plugins).
37 |
38 |
40 |
41 | ## Detailed Design
42 |
43 | We propose reusing the existing the `TIMING` environment variable to also specify the desired list size.
44 |
45 | - The performance list will continue to show only when the `TIMING` variable is present (no change).
46 | - When the list shows, we will read the integer value of the `TIMING` variable to determine the number of rules to show in the list.
47 | - A minimum list size of 10 will be used to maintain backwards compatability (and because there's not much reason to shorten the list beyond that anyway).
48 | - A special string value of `all` will remove the limit entirely (and make it clear that the user does not want a limit).
49 |
50 | | Command | Behavior |
51 | | --- | --- |
52 | | `eslint` | do not show |
53 | | `TIMING=true eslint` | show the first 10 rules (due to minimum of 10) |
54 | | `TIMING=0 eslint` | show the first 10 rules (due to minimum of 10) |
55 | | `TIMING=1 eslint` | show the first 10 rules (due to minimum of 10) |
56 | | `TIMING=5 eslint` | show the first 10 rules (due to minimum of 10) |
57 | | `TIMING=10 eslint` | show the first 10 rules |
58 | | `TIMING=11 eslint` | show the first 11 rules |
59 | | `TIMING=15 eslint` | show the first 15 rules |
60 | | `TIMING=100 eslint` | show the first 100 rules |
61 | | `TIMING=all eslint` | show all the rules |
62 |
63 | This is a concise, easy-to-type means of gaining additional configurability without adding additional environment variables.
64 |
65 | To implement this, we will add a function to calculate the list size in [TIMING.js](https://github.com/eslint/eslint/blob/master/lib/linter/timing.js):
66 |
67 | ```js
68 | /**
69 | * Decide how many rules to show in the output list.
70 | * @returns {number} the number of rules to show
71 | */
72 | function getListSize() {
73 | if (process.env.TIMING === "all") {
74 | return Number.POSITIVE_INFINITY;
75 | }
76 |
77 | const MINIMUM_SIZE = 10;
78 | const TIMING_ENV_VAR_AS_INTEGER = Number.parseInt(process.env.TIMING, 10);
79 |
80 | return TIMING_ENV_VAR_AS_INTEGER > 10 ? TIMING_ENV_VAR_AS_INTEGER : MINIMUM_SIZE;
81 | }
82 |
83 | function display() {
84 | // existing code
85 | .slice(0, getListSize());
86 | }
87 | ```
88 |
89 | Test cases will also be added to verify the expected behavior.
90 |
91 |
99 |
100 | ## Documentation
101 |
102 | A sentence should be added to the [Per-rule Performance](https://eslint.org/docs/developer-guide/working-with-rules#per-rule-performance-1) section of the documentation website.
103 |
104 | No formal announcement is needed.
105 |
106 |
110 |
111 | ## Drawbacks
112 |
113 | - Potential confusion around `TIMING=15` showing 15 results as expected but `TIMING=5` unexpectedly showing 10 results
114 | - Overloading of the `TIMING` environment variable to serve multiple purposes by being used as both a boolean and number
115 | - Inability to show list sizes from 1 to 9 (due to the minimum of 10)
116 |
117 |
127 |
128 | ## Backwards Compatibility Analysis
129 |
130 | This change should not reasonably be considered a breaking change, given that the vast majority of users of the existing `TIMING` environment variable specify `TIMING=1` (as mentioned in the [documentation](https://eslint.org/docs/developer-guide/working-with-rules#per-rule-performance-1)) or `TIMING=true`.
131 |
132 | However, if there was an existing usage that specified `TIMING=11` or above for some unknown reason, such a usage would be affected, albeit rather harmlessly.
133 |
134 |
139 |
140 | ## Alternatives
141 |
142 | ### Alternative A1: Add new `TIMING_LIMIT` environment variable (controls both visibility and limit)
143 |
144 | - The performance list will show if either `TIMING` is present or `TIMING_LIMIT` is greater than or equal to `1` (so that `TIMING_LIMIT` could be used by itself).
145 | - When the list shows, we will read the integer value of the `TIMING_LIMIT` variable to determine the number of rules to show in the list, only using the value if it is greater than or equal to `1` (otherwise falling back to the original value of `10`).
146 | - The existing `TIMING` environment variable will be left in place, since most people using this feature do not need to configure a specific limit.
147 |
148 | | Command | Behavior |
149 | | --- | --- |
150 | | `eslint` | do not show |
151 | | `TIMING_LIMIT=0 eslint` | do not show |
152 | | `TIMING=1 eslint` | show the first 10 rules |
153 | | `TIMING_LIMIT=1 eslint` | show the first 1 rule |
154 | | `TIMING_LIMIT=5 eslint` | show the first 5 rules |
155 | | `TIMING_LIMIT=50 eslint` | show the first 50 rules |
156 | | `TIMING_LIMIT=all eslint` | show all the rules |
157 |
158 | - Pro: provides full configurability
159 | - Pro: does not overload the existing `TIMING` boolean environment variable
160 | - Pro: if we want to add more settings for the performance output, we could add additional environment variables following the same convention (`TIMING_SETTING1`, `TIMING_SETTING2`, ...)
161 | - Pro: not a breaking change
162 | - Con: adds additional complexity in the form of more environment variables
163 | - Con: potential confusion around what the behavior should be with different combinations of the two environment variables
164 |
165 | ### Alternative A2: Add new `TIMING_LIMIT` environment variable (controls only limit and not visibility)
166 |
167 | A slight modification to Alternative A1.
168 |
169 | - The performance list will show only when the `TIMING` variable is present. Using `TIMING_LIMIT` alone is not sufficient to see the list.
170 |
171 | | Command | Behavior |
172 | | --- | --- |
173 | | `eslint` | do not show |
174 | | `TIMING_LIMIT=50 eslint` | do not show |
175 | | `TIMING=1 eslint` | show the first 10 rules |
176 | | `TIMING=1 TIMING_LIMIT=0 eslint` | show the first 10 rules |
177 | | `TIMING=1 TIMING_LIMIT=1 eslint` | show the first 1 rule |
178 | | `TIMING=1 TIMING_LIMIT=5 eslint` | show the first 5 rules |
179 | | `TIMING=1 TIMING_LIMIT=50 eslint` | show the first 50 rules |
180 | | `TIMING=1 TIMING_LIMIT=all eslint` | show all the rules |
181 |
182 | ### Alternative B: Eliminate the limit entirely
183 |
184 | Remove `.slice(0, 10)`.
185 |
186 | - Pro: always provides all possible results
187 | - Pro: no added complexity
188 | - Con: can easily flood the command-line with excess/unwanted output
189 | - Con: technically a breaking change
190 |
191 | ### Alternative C: Increase the limit substantially to 100
192 |
193 | Change to `.slice(0, 100)`).
194 |
195 | - Pro: more likely to include all the desired output
196 | - Pro: no added complexity
197 | - Con: can easily flood the command-line with excess/unwanted output
198 | - Con: technically a breaking change
199 |
200 |
206 |
207 | ## Open Questions
208 |
209 |
219 |
220 | ## Help Needed
221 |
222 |
228 |
229 | ## Frequently Asked Questions
230 |
231 |
238 |
239 | ## Related Discussions
240 |
241 | See issue [#13671](https://github.com/eslint/eslint/issues/13671).
242 |
243 |
249 |
--------------------------------------------------------------------------------
/designs/2021-break-on-parsing-errors/README.md:
--------------------------------------------------------------------------------
1 | - Repo: eslint/eslint
2 | - Start Date: 2021/1/12
3 | - RFC PR: https://github.com/eslint/rfcs/pull/75
4 | - Authors: [A-Katopodis](https://github.com/A-Katopodis)
5 |
6 | # (Break on parsing errors)
7 |
8 | ## Summary
9 |
10 | The suggested change is for ESLint tp support an argument that will make the CLI exit with code 2 if any fatal errors are reported. The option will be an opt-in boolean argument called `--exit-on-fatal-error`.
11 |
12 | ## Motivation
13 |
14 | We met with a couple of cases where we assumed a succesfull run of ESLint in CI enviroments. When ESLint wouldn't be able to read the tsconfig.json, or had a wrongly configured source type the error would be a `fatal` one but the exit code would be `1`. There was no distiction between a run with no fatal errors and with fatal errors.
15 |
16 | More specifically the tsconfig.json invalid read is a error from `@typescript-eslint parser`, the parser correctly indicates a fatal parsing failure like it should so it falls under the same category as the incorrectly configured `sourceType` as in both cases we can't actually run any rules in the file.
17 |
18 | According to the eslint docs about exit codes:
19 |
20 | - 0: Linting was successful and there are no linting errors. If the --max-warnings flag is set to n, the number of linting warnings is at most n.
21 | - 1: Linting was successful and there is at least one linting error, or there are more linting warnings than allowed by the --max-warnings option.
22 | - 2: Linting was unsuccessful due to a configuration problem or an internal error.
23 |
24 | Being able to exit with code `2` instead of `1` it will allow for some CI pipelines to better understand the results and react differently if ESLint reports any `fatal` errors. For example, an Azure DevOps build task that is responsible for running ESLint and output a SARIF would fail on exit code `2` but not 1 indicating there is a configuration and not all of the files where scanned properly for rules.
25 |
26 | ## Detailed Design
27 |
28 | Design Summary:
29 |
30 | 1. Add a `exit-on-fatal-error` option.
31 | 2. Gather and report the fatal messages from `cli-engine`.
32 | 3. Return exit code `2` in the case of any fatal errors on `cli`
33 |
34 | A command example:
35 |
36 | `eslint **.js ----exit-on-fatal-error`
37 |
38 | With this argument if there is at least one fatal error in the results ESLint produces it will exit with code 2.
39 |
40 | ## Add a `exit-on-fatal-error` option.
41 | We would require the option in regard to other ones as well.
42 | ```
43 | // inside of eslint.js
44 | ..
45 | ..
46 | * @property {number} fatalErrorCount Number of fatal errors for the result.
47 | ..
48 | ..
49 | ```
50 | ```
51 | // inside of lib/option.js
52 | ..
53 | ..
54 | {
55 | option: "exit-on-fatal-error",
56 | type: "Boolean",
57 | default: "false",
58 | description: "Trigger exit code 2 on any fatal errors."
59 | }
60 | ..
61 | ..
62 | ```
63 | ## Gather and report the fatal messages.
64 |
65 | In order for ESLint to be able to make a choice based on the fact that a `fatal` error has been found or not we must first retrieve this information. Altough we will not be needing the count of the fatal errors using count instead of a boolean and offer more flexibility than a Boolean parameter, keeps the codebase consistent.
66 |
67 | The changes for that are on `cli-engine.js`:
68 | ```
69 | function calculateStatsPerFile(messages) {
70 | return messages.reduce((stat, message) => {
71 | if (message.fatal || message.severity === 2) {
72 | stat.errorCount++;
73 | if (message.fatal) {
74 | stat.fatalErrorCount++;
75 | }
76 | if (message.fix) {
77 | stat.fixableErrorCount++;
78 | }
79 | } else {
80 | stat.warningCount++;
81 | if (message.fix) {
82 | stat.fixableWarningCount++;
83 | }
84 | }
85 | return stat;
86 | }, {
87 | errorCount: 0,
88 | fatalErrorCount: 0,
89 | warningCount: 0,
90 | fixableErrorCount: 0,
91 | fixableWarningCount: 0
92 | });
93 | }
94 | ```
95 | ```
96 | function calculateStatsPerRun(results) {
97 | return results.reduce((stat, result) => {
98 | stat.errorCount += result.errorCount;
99 | stat.fatalErrorCount += result.fatalErrorCount;
100 | stat.warningCount += result.warningCount;
101 | stat.fixableErrorCount += result.fixableErrorCount;
102 | stat.fixableWarningCount += result.fixableWarningCount;
103 | return stat;
104 | }, {
105 | errorCount: 0,
106 | fatalErrorCount: 0,
107 | warningCount: 0,
108 | fixableErrorCount: 0,
109 | fixableWarningCount: 0
110 | });
111 | }
112 | ```
113 |
114 | ## Return exit code `2` in the case of any fatal errors on `cli`
115 |
116 | Now we can retreve those fatal errors from `cli.js` and act accordingly:
117 |
118 | First change the function that retrieves those errors:
119 | ```
120 | function countErrors(results) {
121 | let errorCount = 0;
122 | let fatalErrorCount = 0;
123 | let warningCount = 0;
124 |
125 | for (const result of results) {
126 | errorCount += result.errorCount;
127 | fatalErrorCount += result.fatalErrorCount;
128 | warningCount += result.warningCount;
129 | }
130 |
131 | return { errorCount, warningCount };
132 | return { errorCount, fatalErrorCount, warningCount };
133 | }
134 | ```
135 |
136 | Now with the new information this method gives us we can use it:
137 | ```
138 | // on the execute method
139 | if (await printResults(engine, results, options.format, options.outputFile)) {
140 | const { errorCount, warningCount } = countErrors(results);
141 | const { errorCount, fatalErrorCount, warningCount } = countErrors(results);
142 | const tooManyWarnings =
143 | options.maxWarnings >= 0 && warningCount > options.maxWarnings;
144 | const fatalErrorExists =
145 | options.fatalParseError && fatalErrorCount > 0;
146 |
147 | if (!errorCount && tooManyWarnings) {
148 | log.error(
149 | "ESLint found too many warnings (maximum: %s).",
150 | options.maxWarnings
151 | );
152 | }
153 |
154 | if(fatalErrorExists){
155 | return 2;
156 | }
157 |
158 | return (errorCount || tooManyWarnings) ? 1 : 0;
159 | }
160 | return 2;
161 | ```
162 |
163 | ## Documentation
164 | The section of the documentation requiring the change is the Command Line Interface:
165 |
166 | [Command Line Interface](https://eslint.org/docs/user-guide/command-line-interface)
167 |
168 |
169 | ## Drawbacks
170 | Some users who may want to enable this new feature may find themselves needing to reconfigure their process. It can change some CI pipelines if used. But since its optional the impact is minimal and it may allow them to discover issues.
171 |
172 |
173 | ## Backwards Compatibility Analysis
174 | There are no expected backward compatibility issues. The parameter will be disabled by default and only users who will use this new parameter will experience any kind of change.
175 |
176 |
177 | ## Open Questions
178 |
179 | ### Question:
180 | Do we want to still report all errors or only failed linting errors when `break-on-lint-error` is passed?
181 |
182 | ### Answer:
183 | After discussions we will be reporting all errors.
184 |
185 | ----
186 | ### Question:
187 | Assume that for some files ESLint successfully lints them and reports a rule but for some others it doesnt.
188 | If ESLint finds a single file that has a parsing error should it report just that file or every rule as
189 | well?
190 | ### Answer:
191 | The choice is that we will be returing all of the found linting errors as discussed in these [comments](https://github.com/eslint/rfcs/pull/76#discussion_r572924056)
192 |
193 | ## Alternatives
194 | There 2 alternatives which we may want to consider, they were already discussed on the related issue:
195 |
196 | ## Use a new exit code 3
197 | The proposal remains the same but with exit code `3` instead of `2`
198 |
199 | ## Max Fatal Errors
200 | Another tweak to the proposal would be for the argument to be an integer similar to `--max-warnings`. Instead of reporting a different exit code than `1` we would
201 |
202 | ## Related Discussions
203 | https://github.com/eslint/eslint/issues/13711
204 |
--------------------------------------------------------------------------------
/designs/2021-fixable-disable-directives/design.md:
--------------------------------------------------------------------------------
1 | - Repo: eslint/eslint
2 | - Start Date: 2021-04-11
3 | - RFC PR: https://github.com/eslint/rfcs/pull/78
4 | - Authors: [Josh Goldberg](https://github.com/JoshuaKGoldberg)
5 |
6 | # Fixable Disable Directives
7 |
8 | ## Summary
9 |
10 | Inline disable directives such as `/* eslint-disable */` that do not suppress any violations may be detected with the `--report-unused-disable-directives` CLI option, but there is no way to automatically remove those comments.
11 | This RFC proposes adding removal of those directives to the `--fix` CLI option as a new [`--fix-type`](https://eslint.org/docs/user-guide/command-line-interface#-fix-type): _**`directive`**_.
12 |
13 | ## Motivation
14 |
15 | Manually deleting comments from `--report-unused-disable-directives` is cumbersome, especially in large repositories with many directives.
16 | Users would benefit from a quick way to fix these without having to manually map between CLI output lines and files.
17 |
18 | ## Detailed Design
19 |
20 | Recapping the existing flow of code:
21 |
22 | - When the `--fix-type` CLI option is specified, `options.fix` is patched to filter on the `rule.meta` of each fix's corresponding `ruleId`
23 | - Unused disable directives are calculated by an `applyDirectives` function within the `applyDisableDirectives` function called in the linter's `_verifyWithoutProcessors`
24 | - These problems have a `ruleId` of `null`
25 | - `_verifyWithoutProcessors` is called within the call stack of each of the 1-10 passes in the linter's `verifyAndFix`
26 |
27 | This RFC proposes making three changes:
28 |
29 | - In the patched `options.fix`, consider a problem's meta type to be `"directive"` if its `ruleId` is `null`
30 | - Pass the `SourceCode` being linted as a parameter to `applyDisableDirectives`
31 | - Add a `fix` method to the unused directive problems returned by `applyDirectives` that uses the `SourceCode`
32 |
33 | ### Fix Behavior
34 |
35 | Directives where at least one rule is still used will have only the unused rule names removed from their source text.
36 |
37 | Directives where all >=1 rules are unused will use the `SourceCode` to compute:
38 |
39 | - If they are the only non-whitespace on their line, delete that line
40 | - Otherwise, delete just the comment and any now-unnecessary surrounding whitespace
41 |
42 | #### Fix Behavior Examples
43 |
44 | ```diff
45 | - /* eslint-disable */
46 | ```
47 |
48 | ```diff
49 | - // eslint-disable-next-line -- related explanation
50 | ```
51 |
52 | ```diff
53 | - // eslint-disable-next-line unused, used
54 | + // eslint-disable-next-line used
55 | ```
56 |
57 | ```diff
58 | - before /* eslint-disable-next-line -- related explanation */ after
59 | + before after
60 | ```
61 |
62 | ```diff
63 | - before // eslint-disable-next-line -- related explanation
64 | + before
65 | ```
66 |
67 | ```diff
68 | // before
69 | - // eslint-disable-next-line
70 | // after
71 | ```
72 |
73 | Multiline block comments are already not allowed to be disable directives. [eslint/eslint#10334](https://github.com/eslint/eslint/issues/10334)
74 |
75 | ## Documentation
76 |
77 | - [Command Line Interface > Fixing Problems](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems)'s `--fix` documentation should mention the added directives fixing and the new `--fix-type`
78 | - [Rules > Report Unused ESLint Disable Comments](https://eslint.org/docs/user-guide/configuring/rules#report-unused-eslint-disable-comments) should link to that documentation
79 |
80 | ## Drawbacks
81 |
82 | Like any new feature, this flag will slightly increase the complexity and maintenance costs of ESLint core.
83 |
84 | This RFC's implementation would lock in the name for a new `--fix-type` even though we only have one concrete use case for it.
85 |
86 | ## Backwards Compatibility Analysis
87 |
88 | It is unlikely but not impossible that some `--fix` dependant users would rely on unused disable directives remaining in code.
89 |
90 | Otherwise, this change is additive in behavior.
91 |
92 | ## Alternatives
93 |
94 | - Applying directive fixes after the `verifyAndFix` passes
95 | - Not ideal: deleting a comment could introduce new rule violations that would warrant running >=1 other pass
96 | - Writing an external tool to apply these options
97 | - Not ideal: the internal implementation is simple; external would have to deal with variant formatters, etc and suffer from the same introduced rule violations
98 |
99 | ## Open Questions
100 |
101 | - Is `"directive"` a good name for the fix type?
102 | - It feels like it could be too specific to be extended for other meta/configuration in the future.
103 | - This RFC originally proposed `"meta"` but that goes too far in being overly vague.
104 |
105 | ## Help Needed
106 |
107 | I'm looking forward to implementing this if approved! 🙌
108 |
109 | ## Frequently Asked Questions
110 |
111 | > Will these be autofixed by default?
112 |
113 | If `--fix` and `--report-unused-disable-directives` are both true, then yes.
114 | There is no additional configuration that would need to be provided.
115 |
116 | If `--fix` is true but `--report-unused-disable-directives` is not, there will be no behavior change from this RFC.
117 | Unused directives would not add to reported problems and thus no new fixers would be added to them.
118 |
119 | If `--fix` is not true but `--report-unused-disable-directives` is, there will be no observable change from this RFC for CLI clients.
120 | Problems for unused directives will have a `fix` property attached but it will not be used without `--fix`.
121 |
122 | > Can I opt out?
123 |
124 | Yes.
125 | If you are in the peculiar situation of needing to enable `--fix` and `--report-unused-disable-directives` _without_ fixing those directives _(why?)_, you can use `--fix-type` with all types except `directive`.
126 |
127 | > How do the generated fixes compare to fixes from rules?
128 |
129 | Problems reported by directive usage checking are joined with remaining rule violation problems in a single array.
130 | This should allow directive fixing to seamlessly act similarly to rule fixing.
131 |
132 | ## Related Discussions
133 |
134 | - [eslint/eslint#10334](https://github.com/eslint/eslint/issues/10334) - Report an error for eslint-disable-line comments that span multiple lines #1033)
135 | - [eslint/eslint#9249](https://github.com/eslint/eslint/issues/9249) - Add CLI option to report unused eslint-disable directives
136 | - [eslint/eslint#11815](https://github.com/eslint/eslint/issues/11815) - --report-unused-disable-directives should be autofixable
137 |
--------------------------------------------------------------------------------
/designs/2021-init-command-eslint-cli/README.md:
--------------------------------------------------------------------------------
1 | - Repo: [eslint/eslint](https://github.com/eslint/eslint)
2 | - Start Date: 2020-06-29
3 | - RFC PR: https://github.com/eslint/rfcs/pull/79
4 | - Authors: Aniketh Saha ([anikethsaha](https://github.com/anikethsaha)) aladdin-add(weiran.zsd@outlook.com)
5 |
6 | # Move --init flag into a separate utility
7 |
8 | ## Summary
9 |
10 | - Remove the auto-config.
11 | - Move the `init` command from main repo ([eslint](https://github.com/eslint/eslint)) to a new repo named `@eslint/create-config`.
12 |
13 | ## Motivation
14 |
15 | Currently the whole `init` command is being shipped with the cli in the main repo. Though its not that much of size (roughly ~0.6MB for the [eslint/lib/init](https://github.com/eslint/eslint/tree/master/lib/init)), this command is not a type of command that we need everyday or everytime running eslint. It is mainly used when creating a new project or adding eslint to a project for the first time. So if we move this to a separate package that is meant to use in the command line,
16 | We will make use of tools like `npm` to simply run `npm init @eslint/config` or using `npx` to run `npx @eslint/create-config` or using `yarn` to run `yarn create @eslint/config` single time for a project instead of having it in the core project.
17 |
18 | ## Detailed Design
19 |
20 |
28 |
29 | The implementation is mainly migrating the code from main repo to `@eslint/create-config`.
30 |
31 | The approach would be following these steps
32 |
33 | ### 1. Remove eslint auto-config
34 |
35 | - Remove auto-config(`eslint/lib/init/autoconfig.js`), and its tests(`eslint/tests/lib/init/autoconfig.js`).
36 | - Remove dependency `progress`, as it's no longer used in production.
37 |
38 | ### 2. Move `eslint --init` related files to a separate repo
39 |
40 | - Move the `eslint/lib/init/*` to the new repo's `lib/*` directory
41 | - Add the following `dependencies` in `package.json`
42 |
43 | - `enquirer`
44 | - `semver`
45 | - `json-stable-stringify-without-jsonify`
46 | - `cross-spawn`
47 | - `debug`
48 | - `@eslint/eslintrc`
49 | - `eslint` (to be removed)
50 | - `espree` (to be removed)
51 |
52 | - for all those modules coming inside `../**/*`, it would be replaced using `eslint/**/*`
53 | - For `tests`, move the `tests/lib/init` to new repo's `tests/lib/`
54 | - Add the following `devDependencies` in `package.json`
55 |
56 | - `chai`
57 | - `sinon`
58 | - `js-yaml`
59 | - `proxyquire`
60 | - `shelljs`
61 |
62 | It was almost done: [aladdin-add/eslint-create](https://github.com/aladdin-add/eslint-create).
63 | I will make a PR once the official repo is created later.
64 |
65 | ### 3. Remove the usage of eslint & espree
66 |
67 | eslint was used:
68 | https://github.com/aladdin-add/eslint-create/blob/c27509cbbaea7c846fd9d7a373feece5254ac8d8/lib/config-file.js#L86
69 |
70 | It can be removed by using the local installed eslint, and switch to the new `ESLint` api.
71 |
72 | espree was used:
73 | https://github.com/aladdin-add/eslint-create/blob/c27509cbbaea7c846fd9d7a373feece5254ac8d8/lib/config-initializer.js#L161
74 |
75 | It can be removed after https://github.com/eslint/espree/issues/495 get landed.
76 |
77 | ### 4. Release the new package
78 |
79 | ### 5. Update eslint repo
80 |
81 | - Remove `eslint --init` related files.
82 | - the following dependencies can be removed in eslint repo:
83 | - enquirer
84 | - semver
85 | - Whenever `--init` command is being used, show a warning(e.g. "You can also run this command directly using 'npm init @eslint/config'") and run `npm init @eslint/config` using `child_process` of the native node modules.
86 |
87 | ## Documentation
88 |
89 |
93 | - [get-started](https://eslint.org/docs/user-guide/getting-started).
94 | - Documentation for `@eslint/create-config` will be created with proper usage and each prompt's details. In the main documentation, a link to `@eslint/create-config` will be given [here](https://github.com/eslint/eslint/blob/master/docs/user-guide/command-line-interface.md#--init) and basic usage and the package details will be documented.
95 | Also in the cli command options, [here](https://github.com/eslint/eslint/blob/master/docs/user-guide/command-line-interface.md#options), for `--init` we need to change the description.
96 | - A formal announcement in not needed, as it is not a breaking change.
97 |
98 | ## Drawbacks
99 |
100 |
110 |
111 | - This might increase the maintenance burden as this will add one more repo in the organization.
112 | - We may need to face challenges like re-directing of issues from `@eslint/create-config` repo to `eslint` and vice-versa.
113 |
114 | ## Backwards Compatibility Analysis
115 |
116 |
121 |
122 | - Users can use `eslint --init`, which is as the same as current.
123 | - Users can no longer use auto-config.
124 |
125 | ## Alternatives
126 |
127 |
133 |
134 | The current implementation in `eslint` repo is the alternative. No changes needed.
135 |
136 | ## Open Questions
137 |
138 |
148 |
149 | - Do we want to implement this as monorepo under `eslint` repo or a separate repo `@eslint/create-config` but github will make it as `create-config` or similar ?
150 |
151 | We decided not to go in the monorepo direction for this.
152 |
--------------------------------------------------------------------------------
/designs/2021-package-exports/README.md:
--------------------------------------------------------------------------------
1 | - Repo: eslint/eslint
2 | - Start Date: 2021-05-10
3 | - RFC PR: (leave this empty, to be filled in later)
4 | - Authors: Nicholas C. Zakas
5 |
6 | # Strict package exports
7 |
8 | ## Summary
9 |
10 | Node.js 12 introduced the ability to strictly define [package exports](https://nodejs.org/api/packages.html#packages_exports) that ensure internal package modules cannot be accessed externally. This RFC proposes implementing package exports for the `eslint` package to prevent unexpected use of internal modules.
11 |
12 | ## Motivation
13 |
14 | Since ESLint was first introduced, developers have attempted to build plugins and other tools on top of the `eslint` package, and in doing so, accessed internal modules. Despite documenting that only APIs published on our [Node.js API page](https://eslint.org/docs/developer-guide/nodejs-api) are officially supported and other APIs may change or disappear without notice, we continue to receive complaints when we change or remove an internal-only module.
15 |
16 | By eliminating access to internal modules, we can prevent this situation from happening in the future and be able to move forward with changes to our internal structure without fear of breaking existing tools that are built on top of undocumented ESLint APIs.
17 |
18 | ## Detailed Design
19 |
20 | This design is made up of four steps:
21 |
22 | 1. Add an `exports` field to `package.json`
23 | 1. Remove the `CLIEngine` class
24 | 1. Update the `ESLint` class
25 | 1. Remove the `linter` object
26 |
27 | These steps will lock down the external API for the `eslint` package.
28 |
29 | ### An an `exports` field to `package.json`
30 |
31 | Currently, ESLint defines [`api.js`](https://github.com/eslint/eslint/blob/master/lib/api.js), which is intended to define the public-facing API of the `eslint` module. This design assumes we keep `api.js` as the source of truth for the public-facing API and just add an `exports` field in `package.json` that points to `api.js`. Additionally, we will create a new `unsupported-api.js` file that lists APIs that we provide, but with no guarantees about the stability or availability of those APIs. Here's what the `package.json` file will look like:
32 |
33 | ```json
34 | {
35 | "name": "eslint",
36 | "main": "./lib/api.js",
37 | "exports": {
38 | ".": "./lib/api.js",
39 | "use-at-your-own-risk": "./lib/unsupported-api.js",
40 | "./package.json": "./package.json"
41 | }
42 | }
43 | ```
44 |
45 | For most users, this change will have no effect. For some users who are relying on undocumented APIs, this may be a breaking change. (Discussion on the nature of the breaking changes is documented in the **Backwards Compatibility Analysis** section below.)
46 |
47 | ### Remove the `CLIEngine` class
48 |
49 | The `CLIEngine` class is the old public interface for ESLint functionality that was superseded by the `ESLint` class. When `ESLint` was introduced last year, `CLIEngine` was deprecated and it was announced that it would be removed at a later date. Because we are already going to be breaking the public interface of the `eslint` module, it makes sense to formalize our API going forward by eliminating a redundant class.
50 |
51 | ### Update the `ESLint` class
52 |
53 | In order to facilitate removing `CLIEngine`, we need to implement a replacement for the `CLIEngine#getRules()` method, which was [missing](https://github.com/eslint/eslint/issues/13454#issuecomment-653362104) when the `ESLint` class was introduced. The `CLIEngine#getRules()` method itself produced unexpected results frequently because it's return value was based on the previous `CLIEngine#executeOnFiles()` or `CLIEngine#executeOnText()` calls, meaning it could return different results depending on when it was called.
54 |
55 | Instead of duplicating how `CLIEngine#getRules()` works, we will add a new `ESLint#getRulesMetaForReport(report)` method that accepts a single argument, the report object returned from `ESLint#lintFiles()` or `ESLint#lintText()`, and return a map of ruleIds to rule meta data for every rule represented in the report.
56 |
57 | ### Remove the `linter` object
58 |
59 | The `linter` object was deprecated in favor of the `Linter` class but was never removed from the public API. As with `CLIEngine`, this is a good time to remove deprecated APIs, so we should remove this as well.
60 |
61 | ### Remaining public API
62 |
63 | The remaining public API after all of these changes are:
64 |
65 | * `Linter`
66 | * `ESLint`
67 | * `RuleTester`
68 | * `SourceCode`
69 |
70 | Nothing outside of these classes will be accessible from outside of the `eslint` package.
71 |
72 | The `use-at-your-own-risk` entrypoint will support the following APIs:
73 |
74 | * `builtinRules` - an instance of [`LazyLoadingRuleMap`](https://github.com/eslint/eslint/blob/master/lib/rules/utils/lazy-loading-rule-map.js) that contains all of the core rules. This object maps rule IDs to rule implementations. (ESLint does not formally allow or encourage people to extend the core rules, but given that there are utilities that already do so with the understanding that the APIs are not guaranteed, we will continue to allow access to them.)
75 | * `FileEnumerator` - the internal [`FileEnumerator` class](https://github.com/eslint/eslint/blob/master/lib/cli-engine/file-enumerator.js). Some plugins rely on this class for searching for files (such as [`eslint-plugin-import`](https://github.com/benmosher/eslint-plugin-import/blob/ab0181d79c6959e6c5cfe25343d8648c1b275bf6/src/rules/no-unused-modules.js#L20)).
76 |
77 | ## Documentation
78 |
79 | Most of the documentation changes will take place on the [Node.js API page](https://eslint.org/docs/developer-guide/nodejs-api). We will also need to announce this change in a blog post ahead of time to give developers a heads-up.
80 |
81 | ## Drawbacks
82 |
83 | The biggest drawbacks for this proposal are the potential to break an unknown number of existing packages that are based on undocumented APIs and those still relying on `CLIEngine`.
84 |
85 | ## Backwards Compatibility Analysis
86 |
87 | The primary concern with regards to backwards compatibility is to ensure that there is either another way to accomplish the same thing as with the undocumented/deprecated APIs or the use case is something that should never have been supported in the first place. After some analysis, here are the top most concerning uses and how we can address them.
88 |
89 | ### `CLIEngine#getRules()`
90 |
91 | The most import consumer of the `CLIEngine#getRules()` method is the [VS Code ESLint extension](https://github.com/microsoft/vscode-eslint), which is one of the most popular extensions for the editor. Currently, the extension [uses `CLIEngine#getRules()`](https://github.com/microsoft/vscode-eslint/blob/e4b2738e713b7523824e0c72166f5cdd44f47052/server/src/eslintServer.ts#L1395) after running ESLint on a file in order to display additional information about the rule. In this case, it should be easy to switch to the new `ESLint#getRulesForResult()` method to maintain current functionality.
92 |
93 | [eslint-rule-finder](https://github.com/jnmorse/eslint-rule-finder) also uses [`CLIEngine#getRules()`](https://github.com/jnmorse/eslint-rule-finder/blob/master/src/load-config.ts#L34). Because the call is made without performing any linting, this instance can be replaced by using the `builtinRules` API in the `use-at-your-own-risk` entrypoint.
94 |
95 | ### `linter` Object
96 |
97 | Any utility using the deprecated `linter` object can update their code to use the `Linter` class:
98 |
99 | ```js
100 | // before
101 | const linter = require("eslint").linter;
102 |
103 | // after
104 | const Linter = require("eslint").Linter;
105 | const linter = new Linter();
106 | ```
107 |
108 | ### Access to rules
109 |
110 | One of the more common cases of accessing undocumented API is when plugins access core ESLint rules using `require()`, such as `require("eslint/lib/rules/eqeqeq")`. This appears to be fairly common for any plugins intended to work with custom ESLint parsers, such as:
111 |
112 | * [eslint-plugin-vue](https://github.com/vuejs/eslint-plugin-vue/blob/62f577dcfcb859c24c6e0d4615ad880f5e1d4688/lib/utils/index.js#L120)
113 | * [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/blob/25ea953cc60b118bd385c71e0a9b61c286c26fcf/packages/eslint-plugin/src/rules/no-loss-of-precision.ts#L7)
114 |
115 | The most common case is to use the existing rule as a base upon which to create a modified rule for the specific parser. This is a use case we never intended to support, and maintainers have acknowledged that seemingly small changes to core rules can introduce breaking changes to their derived rules.
116 |
117 | Going forward, these `require()` calls will no longer work. The recommended way for plugin to adapt to this change is to use the `builtinRules` API:
118 |
119 | ```js
120 | const { builtinRules } = require("eslint/use-at-your-own-risk");
121 |
122 | // check if a rule exists
123 | if (builtinRules.has("eqeqeq")) {
124 | // do something
125 | }
126 |
127 | // get rule definition
128 | const eqeqeq = builtinRules.get("eqeqeq");
129 |
130 | // iterate over all rules
131 | for (const [ruleId, rule] of builtinRules) {
132 | // do something
133 | }
134 | ```
135 |
136 | This is a safer alternative than reaching into the ESLint package file structure to access rules, as the location of rules in the source code is not guaranteed.
137 |
138 | ## Alternatives
139 |
140 | The primary alternative is to "bless" some of the currently undocumented APIs as public, document them, and support them going forward. While this would reduce the risk of disrupting the ecosystem, it also adds more maintenance burden to the ESLint project as a whole, making it more difficult to make significant changes going forward.
141 |
142 | ## Open Questions
143 |
144 | 1. Is there another use case of `CLIEngine#getRules()` that hasn't been discussed?
145 |
146 | ## Help Needed
147 |
148 | n/a
149 |
150 | ## Frequently Asked Questions
151 |
152 | TBD
153 |
154 | ## Related Discussions
155 |
156 | https://github.com/eslint/eslint/issues/13654
157 |
--------------------------------------------------------------------------------
/designs/2021-suppression-support/ESLint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eslint/rfcs/fc051289045fbbff909f4080f16b931d3e2ecdbb/designs/2021-suppression-support/ESLint.png
--------------------------------------------------------------------------------
/designs/2021-suppression-support/design_diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eslint/rfcs/fc051289045fbbff909f4080f16b931d3e2ecdbb/designs/2021-suppression-support/design_diagram.png
--------------------------------------------------------------------------------
/designs/2021-suppression-support/violation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eslint/rfcs/fc051289045fbbff909f4080f16b931d3e2ecdbb/designs/2021-suppression-support/violation.png
--------------------------------------------------------------------------------
/designs/2022-suggestion-parse-errors/README.md:
--------------------------------------------------------------------------------
1 | - Repo: eslint/eslint
2 | - Start Date: 2022-12-10
3 | - RFC PR:
4 | - Authors: [bmish](https://github.com/bmish)
5 |
6 | # Check for parsing errors in suggestion fixes
7 |
8 | ## Summary
9 |
10 |
11 |
12 | Check for parsing errors in [suggestion fixes](https://eslint.org/docs/latest/developer-guide/working-with-rules#providing-suggestions) when running rule tests, the same way we do with rule [autofix](https://eslint.org/docs/latest/developer-guide/working-with-rules#applying-fixes) output.
13 |
14 | ## Motivation
15 |
16 |
18 |
19 | Over time, `RuleTester` has become stricter about verifying the validity of test cases and rule behavior, including in the recent, related [RFC-84: Stricter validation of rule tests](../2021-stricter-rule-test-validation/README.md).
20 |
21 | One likely oversight and gap in our validation is that we don't currently check for parsing/syntax errors in suggestion fixes. This is a problem because a user applying an invalid suggestion will end up with broken code. This is confusing and a poor user experience.
22 |
23 | ## Detailed Design
24 |
25 |
33 |
34 | We should apply the same validation to suggestions as we do to autofixes, and throw an error if a suggestion produces invalid code.
35 |
36 | In the rule testers:
37 |
38 | - lib/rule-tester/flat-rule-tester.js
39 | - lib/rule-tester/rule-tester.js
40 |
41 | We will follow the same pattern that is used for validating autofix output. When encountering a test case that is testing suggestion output, after the existing call to `SourceCodeFixer.applyFixes()`, we need to call `linter.verify()` on the code with the applied suggestion, and then assert that there are no fatal errors.
42 |
43 | Note that as a result of [RFC-84: Stricter validation of rule tests](../2021-stricter-rule-test-validation/README.md), test cases are required to test all suggestions, so we know all suggestions will be covered by our new check.
44 |
45 | In the tests for the rule testers:
46 |
47 | - tests/lib/rule-tester/flat-rule-tester.js
48 | - tests/lib/rule-tester/rule-tester.js
49 |
50 | We need to add a test case with an invalid suggestion, and assert that it throws.
51 |
52 | Draft implementation:
53 |
54 | ## Documentation
55 |
56 |
60 |
61 | This doesn't need to be documented as its one of many checks in the rule tester for invalid rule conditions or test cases. Users should intuitively expect that parsing errors will cause failures. It can simply be called out in the migration guide for the major version it's implemented in.
62 |
63 | ## Drawbacks
64 |
65 |
75 |
76 | 1. There may be a slight performance overhead to checking for parsing errors when running tests. This is likely to be negligible and worthwhile to catch real bugs that will impact users.
77 | 2. It's theoretically possible that a rule author intended to provide a broken suggestion, and this RFC would prevent that. It is true that suggestions are not intended to be held to the same standards are autofixes. Suggestions can be used to provide incomplete or unsafe fixes that change code behavior, which is why a user must choose to apply a suggestion manually. However, I don't believe suggestions should be allowed to provide syntactically-invalid/unparsable fixes. Producing broken code puts an unnecessary burden on the user to figure out how to un-break their code, and prevents ESLint and other tools from running on the code in the meantime. It should be possible to turn any desired suggestion into valid code/syntax, even if it's still incomplete or not production ready.
78 |
79 | ## Backwards Compatibility Analysis
80 |
81 |
86 |
87 | Plugin authors whose rules produce invalid suggestions will experience a breaking change when running tests due to the new test failure. This is desirable so that the plugin author is forced to fix the issue and release a patch.
88 |
89 | ## Alternatives
90 |
91 |
97 |
98 | 1. Do nothing -- continue to allow invalid suggestions. But there are minimal [drawbacks](#drawbacks) to checking for invalid suggestions and real-world benefits to users.
99 |
100 | ## Open Questions
101 |
102 |
112 |
113 | 1. Should we check for parsing errors when applying suggestions in core too (not just in rule tester)?
114 |
115 | Pros:
116 |
117 | - Showing an exception to end-users about the parsing error could be more clear than simply allowing the suggestion to produce broken code for them, and could encourage the end-user to file a ticket to have the issue fixed with the plugin author.
118 |
119 | Cons:
120 |
121 | - However, this additional assertion may be of limited value, as the vast majority of invalid suggestions would have already been caught by rule tests (as long as the rule actually has tests).
122 | - Autofixes and suggestions should both be held to the same validation standards, i.e. we can validate them both in rule tester but not in core.
123 | - Validating suggestions could cause a performance hit for end-users.
124 |
125 | In discussion, we decided not to validate suggestions in core.
126 |
127 | ## Help Needed
128 |
129 |
135 |
136 | I am open to implementing this.
137 |
138 | ## Frequently Asked Questions
139 |
140 |
147 |
148 | ## Related Discussions
149 |
150 |
156 |
157 | - - the issue triggering this RFC
158 | - - original suggestions RFC
159 |
--------------------------------------------------------------------------------
/designs/2022-supress-ignored-file-warnings/README.md:
--------------------------------------------------------------------------------
1 | - Repo: eslint/eslint
2 | - Start Date: 2022-05-13
3 | - RFC PR:
4 | - Authors: Domantas Petrauskas
5 |
6 | # CLI option to suppress ignored file warnings
7 |
8 | ## Summary
9 |
10 |
11 |
12 | When ignored files are explicitly linted, ESLint shows a warning. When `--max-warnings 0` option is used, and an ignored file is passed to ESLint, the process exits with code 1. In some cases this is unexpected and currently, there is no way to suppress ignored file warnings via CLI.
13 |
14 | ## Motivation
15 |
16 |
18 |
19 | While this warning is reasonable in cases where filenames are passed to ESLint manually, it causes issues when the process is automated with tools like [lint-staged](https://github.com/okonet/lint-staged) and [pre-commit](https://github.com/pre-commit/pre-commit). These tools pass staged filenames to any command, not just ESLint. Therefore, they are not aware of the .eslintignore. Linting before a commit is commonly set up with `--max-warnings 0`. In such cases, ESLint will exit with an error and prevent commits, or even cause CI to fail.
20 |
21 | For example, with
22 |
23 | ```
24 | // .eslintignore
25 | foo.js
26 | ```
27 |
28 | Running
29 |
30 | ```
31 | eslint foo.js
32 | ```
33 |
34 | Will result in
35 |
36 | ```
37 | warning File ignored because of a matching ignore pattern. Use "--no-ignore" to override
38 | ```
39 |
40 | It is possible to filter out ignored files using ESLint `CLIEngine`. This was the main reason to reject the CLI flag idea during the [TSC meeting in 2018](https://gitter.im/eslint/tsc-meetings/archives/2018/08/02). The committee was only considering the use case of integrations. However, this problem affects end users as well. The lint-staged FAQ includes a section on [how can I ignore files from .eslintignore](https://github.com/okonet/lint-staged#how-can-i-ignore-files-from-eslintignore), which suggests adding an additional config file and using `CLIEngine.isPathIgnored` to filter out ignored files. Having a CLI flag for this would improve the end-user experience.
41 |
42 | This RFC proposes a `--warn-ignored/--no-warn-ignored` CLI option, which would allow suppression of the warning. For example, with
43 |
44 | ```
45 | // .eslintignore
46 | foo.js
47 | ```
48 |
49 | Running
50 |
51 | ```
52 | eslint --no-warn-ignored foo.js
53 | ```
54 |
55 | Would not result in a warning.
56 | To avoid breaking changes, this will only be available via FlatESLint, [as decided in the RFC discussion](https://github.com/eslint/rfcs/pull/90#issuecomment-1386024387).
57 |
58 | ## Detailed Design
59 |
60 |
68 |
69 | ESLint CLI should have an optional flag to disable the ignored file warning. The flag should be a boolean. It should be called `--warn-ignored/--no-warn-ignored`. If `--no-warn-ignored` is present, ESLint should suppress the `File ignored because of a matching ignore pattern.` warning. The flag should replicate `eslint.lintText`[warnIgnored option](https://eslint.org/docs/developer-guide/nodejs-api#-eslintlinttextcode-options) when it is set to `false`.
70 |
71 | Add the flag to `optionator()` in `options.js`:
72 |
73 | First, add the property to the `ParsedCLIOptions` typedef:
74 |
75 | ```
76 | @property {boolean} warnIgnored Show a warning when the file list includes ignored files
77 | ```
78 |
79 | Define the flag only when FlatESLint is used:
80 |
81 | ```js
82 | let warnIgnoredFlag;
83 |
84 | if (usingFlatConfig) {
85 | warnIgnoredFlag = {
86 | option: "warn-ignored",
87 | type: "Boolean",
88 | default: "true",
89 | description: "Show warning when the file list includes ignored files",
90 | };
91 | }
92 | ```
93 |
94 | It should then be placed under the `heading: "Handle Warnings"`.
95 |
96 | Add `warnIgnored` to `translateOptions()` in `cli.js`:
97 |
98 | ```js
99 | async function translateOptions({
100 | ...,
101 | warnIgnored
102 | }) {
103 | ...
104 | if (configType === "flat") {
105 | ...
106 | options.warnIgnored = warnIgnored;
107 | }
108 | }
109 | ```
110 |
111 | Add `warnIgnored` to `processOptions()` in `eslint-helpers.js` and set it to `true` by default, and add validation:
112 |
113 | ```js
114 | function processOptions({
115 | ...,
116 | warnIgnored = true
117 | }) {
118 | ...
119 | if (typeof warnIgnored !== "boolean") {
120 | errors.push("'warnIgnored' must be a boolean.");
121 | }
122 | ...
123 | return {
124 | ...,
125 | warnIgnored
126 | }
127 | }
128 | ```
129 |
130 | ### lintFiles
131 |
132 | Destructure `warnIgnored` from `eslintOptions` in `lintFiles()` function of `flat-eslint.js`:
133 |
134 | ```js
135 | const {
136 | ...,
137 | warnIgnored
138 | } = eslintOptions;
139 | ```
140 |
141 | When iterating over `filePaths`, add an additional condition for `createIgnoreResult`:
142 |
143 | ```js
144 | filePaths.map(({ filePath, ignored }) => {
145 | /*
146 | * If a filename was entered that matches an ignore
147 | * pattern, and warnIgnored is true, then notify the user.
148 | */
149 | if (ignored) {
150 | if (warnIgnored) {
151 | return createIgnoreResult(filePath, cwd);
152 | }
153 | return void 0;
154 | }
155 | ...
156 | })
157 | ```
158 |
159 | ### lintText
160 |
161 | In order to maintain backward compatibility, `lintText` should only be modified in `flat-eslint.js`. Currently, the `lintText` function already has a `warnIgnored` parameter, and it is set as `false` [by default](https://github.com/eslint/eslint/blob/87b247058ed520061fe1a146b7f0e7072a94990d/lib/eslint/flat-eslint.js#L970). To maintain consistency with the rest of ESLint, it should default to the constructor's `warnIgnored` option (`true` by default). It should still be possible to override it by passing `{ warnIgnored: false }` to `lintText`.
162 |
163 | Remove the default `false` in `lintText` in `flat-eslint.js`:
164 |
165 | ```diff
166 | const {
167 | filePath,
168 | - warnIgnored = false
169 | + warnIgnored,
170 | ...unknownOptions
171 | } = options || {};
172 | ```
173 |
174 | Destructure `warnIgnored` from `eslintOptions` and rename it to `constructorWarnIgnored` to make it distinct from the options from function parameters:
175 |
176 | ```js
177 | const {
178 | ...
179 | warnIgnored: constructorWarnIgnored
180 | } = eslintOptions;
181 | ```
182 |
183 | Extend the condition on which ignore result is created. `warnIgnored` from function arguments overrides `constructorWarnIgnored`:
184 |
185 | ```js
186 | const shouldWarnIgnored = typeof warnIgnored === "boolean" ? warnIgnored : constructorWarnIgnored;
187 | ...
188 | if (shouldWarnIgnored) {
189 | results.push(createIgnoreResult(resolvedFilename, cwd));
190 | }
191 | ```
192 |
193 | We could use nullish coalescing here (`warnIgnored ?? constructorWarnIgnored`), but it was only added in Node 14, and ESLint needs to support Node 12.
194 |
195 | ## Documentation
196 |
197 |
201 |
202 | The [CLI Documentation](https://eslint.org/docs/user-guide/command-line-interface) should be updated to include the new flag. It is necessary to mention that it is only available when using FlatESLint.
203 |
204 | ## Drawbacks
205 |
206 |
216 |
217 | Adding additional CLI flags adds additional mental overhead for the users. It might be confusing for users who don't experience this problem. Since we maintain backward compatibility, ESLint and FlatESLint are going to behave slightly differently.
218 |
219 | ## Backwards Compatibility Analysis
220 |
221 |
226 |
227 | Users of FlatESLint linting text via stdin might encounter unexpected `File ignored because of a matching ignore pattern` errors.
228 |
229 | ## Alternatives
230 |
231 |
237 |
238 | It was considered to output the warning to `stderr` instead, but that would cause breaking changes.
239 |
240 | ## Open Questions
241 |
242 |
252 |
253 | ESLint [collects suppressed warnings and errors](https://github.com/eslint/eslint/pull/15459). In case `--no-warning-on-ignored-files` or `warnIgnored: false` is used, should the warning be included in the suppressed messages array?
254 |
255 | > No, https://github.com/eslint/rfcs/pull/90#discussion_r907721233
256 |
257 | Should the `warnIgnored` option be available via `eslint.config.js` too?
258 |
259 | > No, https://github.com/eslint/rfcs/pull/90#discussion_r1137743213
260 |
261 |
262 |
263 |
269 |
270 |
271 |
272 |
279 |
280 | ## Related Discussions
281 |
282 |
288 |
289 | Main issue:
290 |
291 | https://github.com/eslint/eslint/issues/15010
292 |
293 | Related issues:
294 |
295 | https://github.com/eslint/eslint/issues/9977
296 |
297 | https://github.com/eslint/eslint/issues/12206
298 |
299 | https://github.com/eslint/eslint/issues/12249
300 |
--------------------------------------------------------------------------------
/designs/2023-only-run-reporting-rules/README.md:
--------------------------------------------------------------------------------
1 | - Repo: eslint/eslint
2 | - Start Date: 2023-01-16
3 | - RFC PR:
4 | - Authors: Maddy Miller
5 |
6 | # Only run reporting lint rules
7 |
8 | ## Summary
9 |
10 | Currently, ESLint will run all rules that are not marked as `off` in the configuration.
11 | This RFC proposes adding a way to configure which rules are actually run, to reduce linting
12 | time and better match the reporting outcome. Currently, when a rule is marked as `warn`,
13 | ESLint will still run the rule but not report the results when run under `--quiet`. The
14 | intended outcome of this RFC is to allow users to not run these rules when unnecessary.
15 |
16 | ## Motivation
17 |
18 | A common use-case of the `warn` level is to report errors to the developer in their IDE,
19 | but to completely ignore it when running ESLint on the command line via the `--quiet` flag.
20 | When running with many rules set to the `warn` level, this can cause a significant amount
21 | of overhead on ESLint runtime. On an example large codebase with 154988 lines of TypeScript
22 | code, I found that by disabling all rules set to warn, I was able to reduce ESLint runtime
23 | from 4 minutes and 39 seconds to just 22 seconds. As the `warn` rules are entirely ignored
24 | in this situation, this is a substantial amount of wasted time and resources that could be
25 | avoided.
26 |
27 | ## Detailed Design
28 |
29 | The proposed implementation is to modify the `--quiet` flag so that ESLint will skip
30 | running any rules that are set to the `warn` level.
31 |
32 | From an API perspective, this would be implemented by a filter function that filters down to
33 | which rules should be run. The function would take `(ruleId, severity)` and return true to
34 | include a rule or false to exclude it. For simplicity, this function should not be
35 | given rules marked as `off`, as if this function handled existing behavior then users of the
36 | API would have to mimic that when attempting to extend it.
37 |
38 | In `cli.js`'s `translateOptions` function, the `ruleFilter` option should be assigned to
39 | a function that filters to rules with a `severity` of 2 (error) when the `--quiet` flag is applied,
40 | otherwise always returns true. In `eslint/flat-eslint.js`, the `ruleFilter` should be taken from
41 | the `eslintOptions` object, and passed down to the `linter.verifyAndFix` call.
42 |
43 | Within `linter.js`, the API should be added to `VerifyOptions`, and will be passed down into and
44 | utilized within the `runRules` function during the `configuredRules` loop, after the check
45 | for disabled rules and the rule existing. The `ruleId`, and `severity` should be passed into the
46 | predicate function as an object. `normalizeVerifyOptions` should verify that the `ruleFilter`
47 | option is a function, and replace it with an always-true function if not. `processOptions` in
48 | `eslint-helpers.js` should also perform a validation check that the `ruleFilter` option is a function.
49 |
50 | The new `ruleFilter` function when implemented would look like this, using the `--quiet` flag
51 | rules outlined in this RFC as an example:
52 |
53 | ```typescript
54 | linter.verifyAndFix(text, configs, {
55 | ruleFilter: ({ ruleId: string, severity: number }) => {
56 | return severity === 2;
57 | },
58 | });
59 | ```
60 |
61 | The function acts as a filter on the rules to enable, where removal from the list disables a rule.
62 | This function would return true for all rules which have a severity of 2 (error), effectively filtering out
63 | all other rules.
64 |
65 | The check for unused `eslint-disable` directives (`--report-unused-disable-directives`)
66 | should continue to mark `warn` rules as used, even when running with `--quiet`. As the
67 | rules are not actually run, an assumption would have to be made that all directives
68 | on rules present only in the un-filtered list are used when in this mode. This is a reasonable
69 | assumption, as the user likely does not expect `warn` flags to be touched at all in this mode.
70 | This would also apply to blanket `eslint-disable` directives that disable all rules, which
71 | should always be assumed to be used when the filter function has not changed the length of the list.
72 |
73 | In cases of conflicting flags such as the `--max-warnings` flag, this altered `--quiet` flag
74 | behavior should be disabled while it is in use. This is to ensure that the `--max-warnings`
75 | flag continues to work as expected. The filter API should not be set, and existing
76 | behavior of filtering the output should be used. This altered behavior should be documented
77 | to clarify that it causes warnings to be run despite the `--quiet` flag.
78 |
79 | ## Documentation
80 |
81 | Both the behaviour modification of impacted flags and the API would make sense to document.
82 | As they are relatively simple additions, they would be documented inline with the rest
83 | of the CLI and API documentation. Due to the potentially major performance improvements for
84 | many setups, I feel mentioning it in the release blog post, alongside the potential benefits,
85 | would be a good idea to spread awareness.
86 |
87 | ## Drawbacks
88 |
89 | This change modifies a command line flag, as well as a new API method. This adds further
90 | maintenance burden. However, the implementation is relatively simple, and the benefits
91 | are significant.
92 |
93 | As this flag has many interactions with other systems such as `--max-warnings`, further
94 | maintenance overhead is introduced by having to ensure it behaves correctly between
95 | the different flags. This would require updating documentation across all flags and relevant
96 | settings.
97 |
98 | ## Backwards Compatibility Analysis
99 |
100 | This is a breaking change as it alters the behaviour of the `--quiet` flag.
101 | While the alterations to the quiet flag should not affect the actual outcome of the command,
102 | as cases where it would are covered by this RFC, it is still worth noting as the behaviour
103 | is changing.
104 |
105 | For the case of rules with side effects, such as the `react/jsx-uses-vars` rule, these side
106 | effects will no longer run in quiet mode when set to `warn`. Due to this, users will need to
107 | update their eslint configuration to ensure that any rules with side effects are set to `error`.
108 |
109 | ## Alternatives
110 |
111 | ### Implement this via a separate `--skip-warnings` flag
112 |
113 | This alternative would be to implement this via a separate `--skip-warnings` flag, rather
114 | than into the `--quiet` flag. The same API changes would still be required. The benefit of
115 | this alternative is that it doesn't break behavior of the `--quiet` flag, but it also can
116 | cause confusion with a new flag.
117 |
118 | ### Expose more power via the CLI flag
119 |
120 | Given the API takes a filter, another alternative would be to also expose this power
121 | to the CLI. This would allow users to specify their own filter function, which would
122 | allow for more complex filtering. The downside to this change is that it's vastly more
123 | complex for both the end user and to implement. As the API side of this would be done
124 | anyway, this could be implemented alongside the current proposal in the future if necessary.
125 | However, I feel the simple option as outlined in this proposal that covers the majority
126 | use-case is ideal to keep as a flag either way.
127 |
128 | ## Open Questions
129 |
130 | ### Are there any other considerations I've missed
131 |
132 | In the proposal I've outlined two checks that require warnings to run, however I'm not
133 | familiar with all internal and external features of ESLint, so might have missed some other
134 | checks that we need to ensure still function correctly with this flag.
135 |
136 | ## Help Needed
137 |
138 | I'm not entirely familiar with the ESLint internals, but would be willing to at least
139 | partially implement this if given guidance on the specific areas of code that would need
140 | to be changed.
141 |
142 | ## Frequently Asked Questions
143 |
144 |
151 |
152 | ## Related Discussions
153 |
154 | https://github.com/eslint/eslint/issues/16450
155 |
--------------------------------------------------------------------------------
/designs/2025-base-path-in-config-objects/README.md:
--------------------------------------------------------------------------------
1 | - Repo: eslint/rewrite (`@eslint/config-array` and `@eslint/config-helpers`), eslint/eslint
2 | - Start Date: 2025-03-31
3 | - RFC PR: https://github.com/eslint/rfcs/pull/131
4 | - Authors: Milos Djermanovic
5 |
6 | # Support `basePath` property in config objects
7 |
8 | ## Summary
9 |
10 | Currently, config array has a single property `basePath` (string) at the top level. When used from `eslint`, this property is set to the location of the config file, or the current working directory (when `--no-config-lookup` or `--config` options are used). `files` and `ignores` patterns in all config objects are treated as relative to the config array's `basePath`.
11 |
12 | This RFC proposes allowing config objects to specify their own `basePath` property. When present, config object's `basePath` overrides config array's `basePath`, meaning that `files` and `ignores` patterns in that config object should be treated as relative to the config object's `basePath` instead of the config array's `basePath`. Also, when `basePath` is present in a config object that has neither `files` nor `ignores`, then the config object applies to files in `basePath` and its subdirectories only.
13 |
14 | The new `basePath` property of config objects can be an absolute path, which is primarily intended for internal use by `eslint`, or a relative path, which is primarily intended for end users. A relative path is interpreted as relative to the config array's `basePath`.
15 |
16 | `defineConfig()` will be updated to apply base config's `basePath` to extended configs, meaning that all `files` and `ignores` patterns in extended configs become relative to the base config's `basePath`. This includes global ignores in extended configs. Extended config objects that have neither `files` nor `ignores` will apply to files in `basePath` and its subdirectories only. Specifying `basePath` in extended configs is not allowed. If `defineConfig()` encounters `basePath` property in an extended config, it will throw an error.
17 |
18 | ## Motivation
19 |
20 | Patterns passed through the `--ignore-pattern` CLI option / `ignorePatterns` API option should be treated as relative to the current working directory. When the current working directory must be the same or descendant of the base path directory, it is easy to transform the patterns by prepending the relative path from the base path directory to the current working directory, which is how it is [currently implemented](https://github.com/eslint/eslint/blob/03fb0bca2be41597fcea7c0e84456bbaf2e5acca/lib/config/config-loader.js#L568-L604).
21 |
22 | However, with the [new configuration file resolution](https://github.com/eslint/rfcs/tree/main/designs/2024-config-lookup-from-file) (currently available with the `unstable_config_lookup_from_file` flag; it will become the default behavior in ESLint v10), the current working directory no longer has to be a descendant of the base path directory, which makes pattern transformations very difficult, if not theoretically impossible. Instead of transforming patterns, we will introduce a new property that represents a directory to which the patterns are relative.
23 |
24 | Additional use cases may include user-specific scenarios, like manually merging configuration files from different directories, in which case specifying a `basePath` for imported patterns would be helpful to avoid transforming them manually.
25 |
26 | ## Detailed Design
27 |
28 | Proof of concept:
29 |
30 | - `@eslint/config-array` and `@eslint/config-helpers` changes: https://github.com/mdjermanovic/rewrite/pull/1
31 | - `eslint` changes: https://github.com/mdjermanovic/eslint/pull/6
32 |
33 | Most of the changes will be in the `@eslint/config-array` package.
34 |
35 | ### Changes in `@eslint/config-array`
36 |
37 | - `baseSchema` will be updated to allow the new `basePath` property in config objects.
38 | - `filesAndIgnoresSchema` will be updated to validate that the new `basePath` property, if present, has a string value.
39 | - `normalizeConfigPatterns()` will be updated to normalize the `basePath` property value to an equivalent namespace-prefixed path, for consistency with the config array's `basePath` property value.
40 | - `META_FIELDS` will be updated to include `"basePath"`, in order to treat config objects with `basePath` + `ignores` as global ignores.
41 | - `get ignores()` will be updated to return an array of config objects instead of an array of ignore patterns. This is because some of the patterns may be defined in config objects that have the `basePath` property and are therefore relative to different paths, so combining all patterns into one array would no longer be useful for users of this package.
42 | - `getConfigWithStatus()` will be updated to use config objects' `basePath` when present.
43 | - `shouldIgnorePath()` will be updated to take an array of config objects instead of ignore patterns, and use config objects' `basePath` when present.
44 |
45 | ### Changes in `@eslint/config-helpers`
46 |
47 | - `extendConfig()` will be updated to copy `basePath` from the base config to the resulting extended config.
48 | - `processExtends()` will be updated to throw an error if any of the extended configs contains `basePath` property.
49 |
50 | ### Changes in `eslint`
51 |
52 | - In `lib/config/flat-config-array.js`, `META_FIELDS` will be updated to include `"basePath"`, in order to treat config objects with `basePath` + `ignores` as global ignores while preprocessing config array to remove global ignores when the `--no-ignore` option is used.
53 | - In `lib/config/config-loader.js`, the code that handles `ignorePatterns` (`--ignore-pattern`) will be updated to add them to the config array with `basePath` set to `cwd`, without any transformations.
54 | - In `lib/types/index.d.ts`, `Linter.Config` type will be updated with the `basePath` property.
55 |
56 | ## Documentation
57 |
58 | The documentation for this feature will be added to the [Configuration Files](https://eslint.org/docs/latest/use/configure/configuration-files) page.
59 |
60 | ## Drawbacks
61 |
62 | This will add more complexity to the `@eslint/config-array` package.
63 |
64 | ## Backwards Compatibility Analysis
65 |
66 | ### `eslint`
67 |
68 | There will be no breaking changes in the `eslint` package.
69 |
70 | ### `@eslint/config-array`
71 |
72 | - Introducing a new predefined property of config objects is a breaking change by itself for this package, as it can't be used as a custom property anymore.
73 | - `get ignores()` will have a new signature - a different return value.
74 |
75 | Neither of these changes are breaking for the `eslint` package as it doesn't use these properties.
76 |
77 | ## Alternatives
78 |
79 | The `--ignore-pattern` / `ignorePatterns` problem could _maybe_ be solved by transforming patterns. However, even if it is theoretically possible, it would be immensely complex to implement correctly.
80 |
81 | ## Open Questions
82 |
83 | ## Help Needed
84 |
85 | I can implement this RFC.
86 |
87 | ## Frequently Asked Questions
88 |
89 | ### Does this allow linting files outside config array's `basePath`?
90 |
91 | No. This use case would be covered by the [`--base-path` feature](https://github.com/eslint/eslint/issues/19118).
92 |
93 | ### How would the new `basePath` property interact with `--base-path`?
94 |
95 | `--base-path` value replaces config array's `basePath`, so any relative `basePath`s in config objects would be interpreted as relative to the `--base-path` value.
96 |
97 | ## Related Discussions
98 |
99 | * https://github.com/eslint/eslint/issues/18948
100 |
--------------------------------------------------------------------------------
/templates/design.md:
--------------------------------------------------------------------------------
1 | - Repo: (the repo it will be implemented in, e.g. eslint/espree)
2 | - Start Date: (fill me in with today's date, YYYY-MM-DD)
3 | - RFC PR: (leave this empty, to be filled in later)
4 | - Authors: (the names of everyone contributing to this RFC)
5 |
6 | # (RFC title goes here)
7 |
8 | ## Summary
9 |
10 |
11 |
12 | ## Motivation
13 |
14 |
16 |
17 | ## Detailed Design
18 |
19 |
27 |
28 | ## Documentation
29 |
30 |
34 |
35 | ## Drawbacks
36 |
37 |
47 |
48 | ## Backwards Compatibility Analysis
49 |
50 |
55 |
56 | ## Alternatives
57 |
58 |
64 |
65 | ## Open Questions
66 |
67 |
77 |
78 | ## Help Needed
79 |
80 |
86 |
87 | ## Frequently Asked Questions
88 |
89 |
96 |
97 | ## Related Discussions
98 |
99 |
105 |
--------------------------------------------------------------------------------