feat: Consider bulk suppressions via the Node.js API#142
feat: Consider bulk suppressions via the Node.js API#142adf0nt3s wants to merge 16 commits intoeslint:mainfrom
Conversation
|
Hi @adf0nt3s!, thanks for the Pull Request The pull request title isn't properly formatted. We ask that you update the pull request title to match this format, as we use it to generate changelogs and automate releases.
To Fix: You can fix this problem by clicking 'Edit' next to the pull request title at the top of this page. Read more about contributing to ESLint here |
|
|
| - `suppressionsLocation: string | undefined`: Specifies the path to the suppressions file (`eslint-suppressions.json`). This path can be absolute or relative to the `cwd`. | ||
| - If `suppressionsLocation` is provided, ESLint will attempt to load suppressions from that specific file path. | ||
| - If `suppressionsLocation` is not provided (or is `undefined`), ESLint will default to looking for `eslint-suppressions.json` in the `cwd`. | ||
| - `applySuppressions` |
There was a problem hiding this comment.
@nzakas per your suggestion on the previous version of this RFC, i've introduced a new constructor option
| ## Backwards Compatibility Analysis | ||
|
|
||
| This change is designed to be backward-compatible. | ||
|
|
||
| - New Options are Optional: The new `suppressionsLocation` and `applySuppressions` options are optional. Existing code that does not provide these options will continue to work, with `applySuppressions` defaulting to `true` and `suppressionsLocation` defaulting to looking for `eslint-suppressions.json` in the `cwd`. |
There was a problem hiding this comment.
When someone is already using the default suppression file eslint-suppressions.json, will applySuppressions defaulting to true change the current behavior for API users by not returning suppressed messages in LintResult#messages anymore? For example, suppressed messages will no longer appear in IDEs? If that's the case, it could be argued that this is a breaking change.
Note: I'm not suggesting to change the default to false, but would like to discuss if this is breaking change and how to handle it if that's the case.
There was a problem hiding this comment.
Good question. I can't speak definitively about the current API usage patterns amongst ESLint's consumers, but i'm very curious to hear from those that can.
There was a problem hiding this comment.
If we're unsure, maybe it would be safer to have applySuppressions default to false for a start and switch it to true in a future major release?
There was a problem hiding this comment.
Seems reasonable, i'll make that change in the RFC
There was a problem hiding this comment.
👍 to defaulting to false. I think this is the expected behavior from API consumers.
| 2. Service Instantiation and Configuration | ||
| - Upon instantiation, the `ESLint` and `LegacyESLint` constructors will create an instance of `SuppressionsService`. | ||
| - 2 new constructor options will be added to both classes. | ||
| - `suppressionsLocation` (string, optional) |
There was a problem hiding this comment.
Maybe suppressionsLocation should have a special value (e.g. null) to not look up the suppression file. This allows restoring the old behavior and eliminates the file lookup (as mentioned in the drawbacks).
There was a problem hiding this comment.
@DMartens Would it be better instead to just not look up the file if applySuppressions is false? That also preserves the previous behavior and eliminates the performance hit, but may be a bit more intuitive than introducing a third option to suppressionsLocation.
Additionally, if we went the suppressionsLocation: null route, we'd also have to add some logic to throw if suppressionsLocations was null and applySuppressions was true.
Thoughts?
There was a problem hiding this comment.
On second thought, I agree with you as the cache option is also split into "enabled" and "file path".
On the other hand, how should we handle suppressionFile: /some/path/to/suppressions.json with applySuppressions set to false (throw an error, print a warning, ...)?
There was a problem hiding this comment.
IMHO, print a warning. Doesn't feel important enough to me to throw an error and prevent the linting operation.
There was a problem hiding this comment.
We don't typically print warnings for API-level mistakes, only user mistakes (through the CLI). I'd say it's fine to quietly ignore suppressionFile when applySuppressions is false. That's not a situation an end user will run into.
| - If not provided, defaults to `true`. | ||
|
|
||
| 3. Applying Suppressions | ||
| - When `applySuppressions` is `true` (the default), the `lintFiles()` and `lintText()` methods will automatically apply suppressions to results before returning. |
There was a problem hiding this comment.
It would be good to note, when cache (--cache CLI flag or cache API option) is used, what will be stored in the cache file: original results or results with applied suppressions? Storing original results probably makes more sense.
There was a problem hiding this comment.
Agreed, seems like storing the suppressed results is asking for all sorts of dirty-state problems (like if a user changes suppressions without changing the source file). Adding a line now.
Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>
|
Any other thoughts here? Thanks! :) |
| }), | ||
| ); | ||
|
|
||
| if (!fs.existsSync(suppressionsFilePath) || eslintOptions.applySuppressions === false) { |
There was a problem hiding this comment.
When options.filePath is not set, looking up the suppressions file seems unnecessary. In that case, the filePath property of the lint result contains the string "<text>".
There was a problem hiding this comment.
I think you are suggesting something like this?
| if (!fs.existsSync(suppressionsFilePath) || eslintOptions.applySuppressions === false) { | |
| if (!providedFilePath || eslintOptions.applySuppressions === false) { |
Is that correct? Im in favor.
There was a problem hiding this comment.
I think the variable to check in lintText is called just filePath. Apart from that, this should work. In fact there's no need to check explicitly if the suppressions file exists because the suppressions service will simply do nothing if it doesn't find it.
| if (!fs.existsSync(suppressionsFilePath) || eslintOptions.applySuppressions === false) { | |
| if (!filePath || eslintOptions.applySuppressions === false) { |
There was a problem hiding this comment.
@fasttime Wait, i'm confused. According to the TSDoc strings, options.filePath is for the source code, not the suppression file.
/**
* Executes the current configuration on text.
* @param {string} code A string of JavaScript code to lint.
* @param {Object} [options] The options.
* @param {string} [options.filePath] The path to the file of the source code.
* @param {boolean} [options.warnIgnored] When set to true, warn if given filePath is an ignored path.
* @returns {Promise<LintResult[]>} The results of linting the string of code given.
*/
async lintText(code, options = {}) {
What are we trying to handle here? An invalid source code path or a non-existent suppression file?
There was a problem hiding this comment.
Ok, i think i understand. We are saying two things here:
- If
filePath(the path for the source code) is falsey, skip applying suppressions - There is no need to check for the existence of the suppressions file at all, since we'll just no-op if it isn't present, so nothing needs to replace the
fs.existsSync(suppressionsFilePath)check
There was a problem hiding this comment.
Correct. filePath is the path of the file that contains the provided source code, or undefined if not specified.
| ## Backwards Compatibility Analysis | ||
|
|
||
| This change is designed to be backward-compatible. | ||
|
|
||
| - New Options are Optional: The new `suppressionsLocation` and `applySuppressions` options are optional. Existing code that does not provide these options will continue to work, with `applySuppressions` defaulting to `true` and `suppressionsLocation` defaulting to looking for `eslint-suppressions.json` in the `cwd`. |
There was a problem hiding this comment.
If we're unsure, maybe it would be safer to have applySuppressions default to false for a start and switch it to true in a future major release?
nzakas
left a comment
There was a problem hiding this comment.
I really like this approach. It's straightforward both to implement and use. 👍
| 2. Service Instantiation and Configuration | ||
| - Upon instantiation, the `ESLint` and `LegacyESLint` constructors will create an instance of `SuppressionsService`. | ||
| - 2 new constructor options will be added to both classes. | ||
| - `suppressionsLocation` (string, optional) |
There was a problem hiding this comment.
We don't typically print warnings for API-level mistakes, only user mistakes (through the CLI). I'd say it's fine to quietly ignore suppressionFile when applySuppressions is false. That's not a situation an end user will run into.
| - When `applySuppressions` is `true`, the `lintFiles()` and `lintText()` methods will automatically apply suppressions to results before returning. | ||
| - Within these methods, *after* the initial linting results are obtained, the `applySuppressions` method of the instantiated `SuppressionsService` will be called. | ||
| - This method takes the raw linting results and the loaded suppressions (from the resolved file path) and returns the results with suppressions applied. | ||
| - When caching is enabled, the **original lint results** are stored in the cache. Suppressions are applied after cache retrieval, so that changes to the suppression file take effect without needing to bust the cache. |
|
|
||
| ## Open Questions | ||
|
|
||
| - Should functionality equivalent to CLI flags like `--suppress-all` or `--suppress-rule` be supported via separate API methods in the future? |
There was a problem hiding this comment.
I'd say no. The intent of this feature is to honor existing suppression files, not to provide APIs for all aspects of suppressions.
|
Given that we have two approvals from the TSC, moving to Final Commenting. |
Summary
This RFC continues work on the proposal for integrating bulk suppressions support into the Node.js API via the ESLint and LegacyESLint classes. It specifically focusing on considering existing bulk suppressions when linting files or text through the API, ensuring that suppression files (eslint-suppressions.json) created via CLI commands are automatically respected when using the programmatic API.
The scope is limited to applying existing suppressions during linting and does not include suppression file manipulation features (such as --suppress-all, --suppress-rule, or --prune-suppressions), which remain CLI-exclusive functionalities.
Related Issues
eslint/eslint#19603
#133