Follow on after #19193. Refactor.
That PR introduced support for legacy call styles for `context.report`.
Move that implementation into `report.ts`, shorten the code a little, and produce consistent object shapes. Avoid calling `convertLegacyCallArgs` in the common case that `report` is called in the modern way.
`RuleTester` test that suggestions provided by the rule match what's expected in the test case - that code after applying each suggestion matches the test case's `output` property, and that suggestions' descriptions also match those specified in test cases.
Skip one conformance test for ESLint's `prefer-regex-literals` rule where the suggestion description in Oxlint is different from ESLint, but in a completely cosmetic way. There's no need to match it exactly. That apart, no change to conformance test results, which strongly suggests that Oxlint is treating suggestions correctly.
Refactor. Simplify the type used to represent a fix when sending from JS to Rust.
Previously we avoided creating new objects by using the same fix object returned by `fix` function, and so were locked to ESLint's design.
That worked, but the code was excessively complicated. Fixes should be fairly rare, so this optimization is on a cold path - it's not worth it.
Simplify implementation by converting `Fix`es to a simpler type (`FixReport`) before sending to Rust.
Implement support for suggestions.
Implementation exactly matches ESLint's.
The tests demonstrate that:
* `--fix-suggestions` applies suggestions.
* `--fix` does *not* apply suggestions.
* When a rule provides multiple suggestions, only the 1st is applied.
I've also confirmed that suggestions show up correctly in VSCode (once language server supports JS plugins).
Pure refactor. Move the code for interpolating data into error messages out of `report` and into `getMessages`. This logic can now also be used for getting the fixes for suggestions in a later PR.
Similar to #18341. Loosen checks for `loc` passed to `report()`. Allow `line` to 0, if `column` is 0. In this case, treat it offset 0. `eslint-plugin-sonarjs` reports errors with `loc` like this.
Hi, this is my first contribution here, so I'm not sure whether opening
a PR directly is appropriate.
If anything is wrong, please let me know!
---
In this PR, I've corrected the `DiagnosticData` type to `Record<string,
string | number | boolean | bigint | null | undefined>;`
In ESLint v9.39.2 (which uses `@eslint/core@0.17.0`), this type was
widened to `Record<string, unknown>`.
Refs:
- https://github.com/eslint/rewrite/releases/tag/core-v0.17.0
- https://github.com/eslint/rewrite/pull/298
However, in the ESLint v10.0.0 pre-release (which uses
`@eslint/core@1.0.1`), this type have been narrowed to `Record<string,
string | number | boolean | bigint | null | undefined>;`.
Refs:
- https://github.com/eslint/rewrite/releases/tag/core-v1.0.1
- https://github.com/eslint/rewrite/pull/327
If the type's intention is to ensure maximum compatibility with any
ESLint version, then `Record<string, string | number | boolean | bigint
| null | undefined>` seems the most appropriate choice here.
Loosen checks for `loc` passed to `report()`. Allow `line` to be 1 larger than number of lines in source, if `column` is 0. In this case, treat it as representing the end of the file.
ESLint's `func-call-spacing` rule sometimes reports errors with `loc` containing `column: -1`. As ESLint doesn't complain, probably other plugins do the same too.
Make the checks for validity of `loc` in `report` more relaxed, to support this behavior.
Previously we had a pattern of destructuring properties of global objects into top-level vars. e.g.:
```js
const { keys: ObjectKeys, hasOwn } = Object,
{ isArray } = Array;
```
Doing this is more performant, but it makes the code harder to read.
Instead, write code normally, and use a TSDown plugin to convert:
```js
const keys = Object.keys(obj);
```
to:
```js
import { ObjectKeys } from "../utils/globals.ts";
const keys = ObjectKeys(obj);
```
Defining all the destructured globals in a single file (`utils/globals.ts`) is advantageous for bundle size. If the TSDown plugin created top level `const ObjectKeys = Object.keys;` statements in each file, then you can end up with this in bundle as minifier doesn't de-duplicate:
```js
const ObjectKeys = Object.keys;
const ObjectKeys$1 = Object.keys;
const ObjectKeys$2 = Object.keys;
```
The globals we use have to be manually added to `utils/globals.ts`, but TSDown plugin warns when this needs to be done, and doesn't generate a broken build in the meantime.
This plugin is run in debug builds too, so that the tests will catch any bugs.
Part of #16018.
Implement `RuleTester` class for testing rules. Has same API as ESLint's `RuleTester`.
A few features not implemented yet:
* Fixes are not executed, or checked against provided `output`.
* Suggestions are not checked (we don't yet have support for suggestions in JS plugins).
* No use of config provided through `RuleTester` class constructor or via `RuleTester.setDefaultConfig`.
Store `messageId` in `DiagnosticReport` objects. It's not required for sending to Rust, but it is needed for rule tester (#16206).
I think it's fine to add a little code that's redundant in the main runtime, because it only has an overhead when a rule produces diagnostics, which should be an uncommon path.
Adapted from #15868. Enable `strictNullChecks` in TSConfig for `apps/oxlint`.
Rather than using `!` all over the place, I've added a function `assertIsNonNull`, which performs the same function. Idea is that we can later on produce a debug build where `assertIsNonNull` does an actual runtime assertion (rather than just a TS `asserts`) that the value is not `null` or `undefined`. We can use the debug build in tests, and catch any logic errors.
Unfortunately, TypeScript doesn't seem able to track side effects, so there seems to be no way to tell it "after calling `initAst()`, `ast` is always non-null". Don't want to switch to a having a `getAst` function which returns a non-null value, as that'd introduce unnecessary temp vars, and function calls, on hot paths. So this seems to be the best we can do without incurring runtime cost.
Improve `Diagnostic` and `Suggestion` types, by using `RequireAtLeastOne` utility type from `type-fest` package, to enforce that:
* Either `message` or `messageId` property must be provided.
* Either `node` or `loc` property must be provided.
Change to TSDown config `dts: { resolve: true }` is get it to bundle `type-fest` into the `.d.ts` file.
Add `Suggestion` type, and `suggest` field to `Diagnostic`.
Suggestions are not implemented yet, but this completes the interface for diagnostics defined by ESLint's docs.