This is a small output update to fix some noisy snapshot tests that output the number of rules. When using nested configs, outputting the number of rules is not helpful I think since it varies from file to file, but open to feedback on this.
Adds support for merging the `plugins` arrays in configuration files referenced in `extends` to the top-level `plugins` array of a configuration file. Essentially acts as if you had placed it in the plugins array at the top-level to begin with.
- part of https://github.com/oxc-project/oxc/issues/9307
Allows for using the `extends` keyword to chain multiple extended config files together: for example, a package config that extends a base config that extends a config from `node_modules` and so on.
I don't really love what we're having to do here with using both `override_rules` and `ConfigStoreBuilder.with_rule`. I think there is a refactoring opportunity to merge some functionality together here, but I don't want to refactor and optimize before we have a more comprehensive test suite.
- part of https://github.com/oxc-project/oxc/issues/9307
This gives the `extends` keyword some functionality: specifically to allow inheriting rules from other configuration files. Rules that are in the extended configuration will be used as the base, and then files in the config doing the extending will override the extended rules.
So, you can, for example, specify a base configuration that defines rules that you generally want to be enabled. Then, in each nested configuration file, you could disable rules depending on the directory:
```text
.oxlintrc.json // { "rules: { "no-useless-escape": "error" } }
package1/
.oxlintrc.json // { "extends": ["../.oxlintrc.json"], "rules": { "no-useless-escape": "off" } }
package2/
src // uses ../.oxlintrc.json with `no-useless-escape` enabled
```
As a side effect of this, building a config from a `.oxlintrc.json` file can result in config errors, even if all of the syntax and rules are configured correctly, because an extended configuration file might be incorrect or unparseable. For now, we simply raise an error if this occurs and stop execution, but in the future we could make this more graceful if we wanted and just ignore that file.
We will also need to revisit performance here, as I think a global cache config might be necessary so that we never load a file more than once and parse it more than once.
- part of https://github.com/oxc-project/oxc/issues/7408
Adds some regression tests to ensure that filters like `-D <rule>` or `-A <rule>` will override whatever is loaded from a configuration file that is either automatically loaded as a nested config, or explicitly loaded from the `--config` option.
- part of https://github.com/oxc-project/oxc/issues/7408
Ensures that when a config file is explicitly passed via `-c` or `--config`, that file will take precedence over all nested config files, and we will not search for config files within any directories.
- part of https://github.com/oxc-project/oxc/issues/7408
This is the initial implementation for nested config support, without the `extends` keyword factoring into it yet. So we only load a single config file for any given file still, but it will always be the one closest to the file being linted.
Performance is somewhat of a big TODO here, obviously there is some inherent cost to adding support for nested configurations since we need to check if any directory contains an `.oxlintrc.json` file, load it if it exists, and then for every file below that directory check if we should use it. I've left a few TODOs of different ways we might be able to cache more data at the cost of memory but at the potential gain of some CPU time. Optimizing iteration over files/directories is one way in which we might be able to make this faster, so we never need to visit the same directory twice.
For now, this will remain an experimental feature that should not affect the current functionality until we enable it for everyone. PRs stacked on top of this will focus on ensuring that other features work well with this, such as `--config`, `-D <rule>`, and then we'll add new features like `extends`.
The configuration loading all happens upfront currently, while the config resolution happens at lint-time. There is a new `nested_configs` field on the `Linter` that passes all of the configs that are available, so it can pick which one to use later. Eventually we'll refactor this to not have two duplicate config fields, but I'm leaving it as-is to ensure we're not messing up the existing functionality.
- part of https://github.com/oxc-project/oxc/issues/7408
Since the nested configuration work is too big to fit in one PR or even a stack of PRs, I suggest that we make it temporarily an opt-in CLI flag. Once we feel confident in the implementation replacing the existing code, we can remove the flag and make it true by default.
Replace `#[allow]` with `#[expect]` through the whole codebase.
This also surfaced many places where the `#[allow]` attributes were outdated/pointless and could be removed entirely.
Only places `#[allow]` remains are:
1. In generated code and macros, where lint errors may or may not be triggered depending on the generated code.
2. A few places where clippy malfunctions. These use e.g. `#[allow(unused, clippy::allow_attributes)]`.
closes#8485
Since we currently support two rules with the same `rule_name` but
different `plugin_names`, some of the original logic is no longer
applicable. As a result, I have made some adjustments.
Welcome to the new testing area for oxlint CLI 🥳
There is still one test left to be snapshotted (for invalid options), but everything else works great.
I put some comments on code which I think are the key points of the PR
windows will fail, looks like the offset missmatch is because of `\r\n`
vs `\n`.
```
Snapshot file: apps\oxlint\src\snapshots\--format=json test.js@oxlint.snap
Snapshot: --format=json test.js@oxlint
Source: C:\dev\oxc:74
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────-old snapshot
+new results
────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 0 0 │ ##########
1 1 │ --format=json test.js
2 2 │ ----------
3 3 │ [
4 │- {"message": "`debugger` statement is not allowed","code": "eslint(no-debugger)","severity": "error","causes": [],"url": "https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html","help": "Delete this code.","filename": "test.js","labels": [{"span": {"offset": 38,"length": 9}}],"related": []},
4 │+ {"message": "`debugger` statement is not allowed","code": "eslint(no-debugger)","severity": "error","causes": [],"url": "https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html","help": "Delete this code.","filename": "test.js","labels": [{"span": {"offset": 42,"length": 9}}],"related": []},
5 5 │ {"message": "Function 'foo' is declared but never used.","code": "eslint(no-unused-vars)","severity": "warning","causes": [],"url": "https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-unused-vars.html","help": "Consider removing this declaration.","filename": "test.js","labels": [{"label": "'foo' is declared here","span": {"offset": 9,"length": 3}}],"related": []},
6 6 │ {"message": "Parameter 'b' is declared but never used. Unused parameters should start with a '_'.","code": "eslint(no-unused-vars)","severity": "warning","causes": [],"url": "https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-unused-vars.html","help": "Consider removing this parameter.","filename": "test.js","labels": [{"label": "'b' is declared here","span": {"offset": 16,"length": 1}}],"related": []}
7 7 │ ]
────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────To update snapshots run `cargo insta review`
Stopped on the first failure. Run `cargo insta test` to run all snapshots.
thread 'output_formatter::test::test_output_formatter_diagnostic_json' panicked at C:\Users\sysix\.cargo\registry\src\index.crates.io-6f17d22bba15001f\insta-1.42.0\src\runtime.rs:679:13:
snapshot assertion for '--format=json test.js@oxlint' failed in line 74
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
---- output_formatter::test::test_output_formatter_diagnostic_stylish stdout ----
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Snapshot Summary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━Snapshot file: apps\oxlint\src\snapshots\--format=stylish test.js@oxlint.snap
Snapshot: --format=stylish test.js@oxlint
Source: C:\dev\oxc:74
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────-old snapshot
+new results
────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── 3 3 │
4 4 │ ␛[4mtest.js␛[0m␊
5 5 │ ␛[2m9:3 ␛[0m ␛[33mwarning␛[0m Function 'foo' is declared but never used. ␛[2meslint(no-unused-vars)␛[0m␊
6 6 │ ␛[2m16:1␛[0m ␛[33mwarning␛[0m Parameter 'b' is declared but never used. Unused parameters should start with a '_'. ␛[2meslint(no-unused-vars)␛[0m␊
7 │- ␛[2m38:9␛[0m ␛[31merror␛[0m `debugger` statement is not allowed ␛[2meslint(no-debugger)␛[0m␊
7 │+ ␛[2m42:9␛[0m ␛[31merror␛[0m `debugger` statement is not allowed ␛[2meslint(no-debugger)␛[0m␊
8 8 │
9 9 │ ␛[31m✖ 3 problems (1 error, 2 warnings)␛[0m
────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────To update snapshots run `cargo insta review`
Stopped on the first failure. Run `cargo insta test` to run all snapshots.
thread 'output_formatter::test::test_output_formatter_diagnostic_stylish' panicked at C:\Users\sysix\.cargo\registry\src\index.crates.io-6f17d22bba15001f\insta-1.42.0\src\runtime.rs:679:13:
snapshot assertion for '--format=stylish test.js@oxlint' failed in line 74
failures:
output_formatter::test::test_output_formatter_diagnostic_json
output_formatter::test::test_output_formatter_diagnostic_stylish
test result: FAILED. 85 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.12s
```
This is needed so we can use a custom `Write` implementation (or just `[u8]`) to make snapshots.
In this step I also updated `OutputFormatter::all_rules` so the Formatter does not need to handle the write-error.
Now every lint output is owned by is right OutputFormatter and his DiagnosticReporter 🥳
Next step is to setup a snapshot Tester, so I can remove the ToDos.
Reorded some lines so the outfor is now for: `cargo run -p oxlint -- test.js --max-warnings=2`
```
Found 4 warnings and 0 errors.
Exceeded maximum number of warnings. Found 4.
Finished in 5ms on 1 file with 97 rules using 24 threads.
```
and for `cargo run -p oxlint -- test.js`
```
Found 4 warnings and 0 errors.
Finished in 5ms on 1 file with 97 rules using 24 threads.
```
The output time and warnings/error count wil be always printed.