diff --git a/.vitepress/data/rules.json b/.vitepress/data/rules.json index a9eda307591..53a365d0ea6 100644 --- a/.vitepress/data/rules.json +++ b/.vitepress/data/rules.json @@ -1228,7 +1228,7 @@ "value": "no-useless-computed-key", "category": "style", "type_aware": false, - "fix": "pending", + "fix": "conditional_fix", "default": false, "docs_url": "https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-useless-computed-key.html" }, @@ -4601,7 +4601,7 @@ { "scope": "typescript", "value": "prefer-find", - "category": "nursery", + "category": "style", "type_aware": true, "fix": "none", "default": false, @@ -4682,7 +4682,7 @@ { "scope": "typescript", "value": "prefer-readonly", - "category": "nursery", + "category": "style", "type_aware": true, "fix": "none", "default": false, @@ -4709,7 +4709,7 @@ { "scope": "typescript", "value": "prefer-regexp-exec", - "category": "nursery", + "category": "style", "type_aware": true, "fix": "none", "default": false, @@ -4727,7 +4727,7 @@ { "scope": "typescript", "value": "prefer-string-starts-ends-with", - "category": "nursery", + "category": "style", "type_aware": true, "fix": "none", "default": true, @@ -6056,6 +6056,15 @@ "default": false, "docs_url": "https://oxc.rs/docs/guide/usage/linter/rules/vitest/no-importing-vitest-globals.html" }, + { + "scope": "vitest", + "value": "prefer-called-exactly-once-with", + "category": "style", + "type_aware": false, + "fix": "fixable_dangerous_fix", + "default": false, + "docs_url": "https://oxc.rs/docs/guide/usage/linter/rules/vitest/prefer-called-exactly-once-with.html" + }, { "scope": "vitest", "value": "prefer-called-once", @@ -6137,6 +6146,15 @@ "default": false, "docs_url": "https://oxc.rs/docs/guide/usage/linter/rules/vitest/prefer-to-be-truthy.html" }, + { + "scope": "vitest", + "value": "require-awaited-expect-poll", + "category": "correctness", + "type_aware": false, + "fix": "none", + "default": false, + "docs_url": "https://oxc.rs/docs/guide/usage/linter/rules/vitest/require-awaited-expect-poll.html" + }, { "scope": "vitest", "value": "require-local-test-context-for-concurrent-snapshots", @@ -6146,6 +6164,24 @@ "default": false, "docs_url": "https://oxc.rs/docs/guide/usage/linter/rules/vitest/require-local-test-context-for-concurrent-snapshots.html" }, + { + "scope": "vitest", + "value": "require-mock-type-parameters", + "category": "correctness", + "type_aware": false, + "fix": "none", + "default": false, + "docs_url": "https://oxc.rs/docs/guide/usage/linter/rules/vitest/require-mock-type-parameters.html" + }, + { + "scope": "vitest", + "value": "require-test-timeout", + "category": "restriction", + "type_aware": false, + "fix": "none", + "default": false, + "docs_url": "https://oxc.rs/docs/guide/usage/linter/rules/vitest/require-test-timeout.html" + }, { "scope": "vitest", "value": "warn-todo", diff --git a/src/docs/guide/usage/formatter/generated-config.md b/src/docs/guide/usage/formatter/generated-config.md index 6d328e3d4ef..8addf08073b 100644 --- a/src/docs/guide/usage/formatter/generated-config.md +++ b/src/docs/guide/usage/formatter/generated-config.md @@ -81,7 +81,7 @@ Whether to insert a final newline at the end of the file. ## jsdoc -type: `object` +type: `object | boolean` Enable JSDoc comment formatting. @@ -89,7 +89,7 @@ When enabled, JSDoc comments are normalized and reformatted: tag aliases are canonicalized, descriptions are capitalized, long lines are wrapped, and short comments are collapsed to single-line. -Pass an object (`jsdoc: {}`) to enable with defaults, or omit to disable. +Pass `true` or an object to enable with defaults, or omit/set `false` to disable. - Default: Disabled @@ -302,7 +302,7 @@ Whether to insert a final newline at the end of the file. ##### overrides[n].options.jsdoc -type: `object` +type: `object | boolean` Enable JSDoc comment formatting. @@ -310,7 +310,7 @@ When enabled, JSDoc comments are normalized and reformatted: tag aliases are canonicalized, descriptions are capitalized, long lines are wrapped, and short comments are collapsed to single-line. -Pass an object (`jsdoc: {}`) to enable with defaults, or omit to disable. +Pass `true` or an object to enable with defaults, or omit/set `false` to disable. - Default: Disabled @@ -487,13 +487,15 @@ For JSX, you can set the `jsxSingleQuote` option. ##### overrides[n].options.sortImports -type: `object` +type: `object | boolean` Sort import statements. Using the similar algorithm as [eslint-plugin-perfectionist/sort-imports](https://perfectionist.dev/rules/sort-imports). For details, see each field's documentation. +Pass `true` or an object to enable with defaults, or omit/set `false` to disable. + - Default: Disabled ###### overrides[n].options.sortImports.customGroups @@ -713,7 +715,7 @@ Sort the `scripts` field alphabetically. ##### overrides[n].options.sortTailwindcss -type: `object` +type: `object | boolean` Sort Tailwind CSS classes. @@ -721,6 +723,8 @@ Using the same algorithm as [prettier-plugin-tailwindcss](https://github.com/tai Option names omit the `tailwind` prefix used in the original plugin (e.g., `config` instead of `tailwindConfig`). For details, see each field's documentation. +Pass `true` or an object to enable with defaults, or omit/set `false` to disable. + - Default: Disabled ###### overrides[n].options.sortTailwindcss.attributes @@ -876,13 +880,15 @@ For JSX, you can set the `jsxSingleQuote` option. ## sortImports -type: `object` +type: `object | boolean` Sort import statements. Using the similar algorithm as [eslint-plugin-perfectionist/sort-imports](https://perfectionist.dev/rules/sort-imports). For details, see each field's documentation. +Pass `true` or an object to enable with defaults, or omit/set `false` to disable. + - Default: Disabled ### sortImports.customGroups @@ -1102,7 +1108,7 @@ Sort the `scripts` field alphabetically. ## sortTailwindcss -type: `object` +type: `object | boolean` Sort Tailwind CSS classes. @@ -1110,6 +1116,8 @@ Using the same algorithm as [prettier-plugin-tailwindcss](https://github.com/tai Option names omit the `tailwind` prefix used in the original plugin (e.g., `config` instead of `tailwindConfig`). For details, see each field's documentation. +Pass `true` or an object to enable with defaults, or omit/set `false` to disable. + - Default: Disabled ### sortTailwindcss.attributes diff --git a/src/docs/guide/usage/linter/generated-config.md b/src/docs/guide/usage/linter/generated-config.md index 00a5f842861..7e6422fdaf7 100644 --- a/src/docs/guide/usage/linter/generated-config.md +++ b/src/docs/guide/usage/linter/generated-config.md @@ -207,6 +207,25 @@ Basic usage with a local plugin path. } ``` +Basic usage with a TypeScript plugin and a local plugin path. + +TypeScript plugin files are supported in the following environments: + +- Deno and Bun: TypeScript files are supported natively. +- Node.js >=22.18.0 and Node.js ^20.19.0: TypeScript files are supported natively with built-in + type-stripping enabled by default. + +For older Node.js versions, TypeScript plugins are not supported. Please use JavaScript plugins or upgrade your Node version. + +```json +{ + "jsPlugins": ["./custom-plugin.ts"], + "rules": { + "custom/rule-name": "warn" + } +} +``` + Using a built-in Rust plugin alongside a JS plugin with the same name by giving the JS plugin an alias. diff --git a/src/docs/guide/usage/linter/rules/eslint/no-unused-vars.md b/src/docs/guide/usage/linter/rules/eslint/no-unused-vars.md index 466b061cb83..58060deeb00 100644 --- a/src/docs/guide/usage/linter/rules/eslint/no-unused-vars.md +++ b/src/docs/guide/usage/linter/rules/eslint/no-unused-vars.md @@ -299,7 +299,7 @@ Fine-grained auto-fix controls for `no-unused-vars`. #### fix.imports -type: `"off" | "suggestion" | "fix"` +type: `"off" | "suggestion" | "fix" | "safe-fix"` ##### `"off"` @@ -313,9 +313,14 @@ Emit suggestion-style fixes (current behavior). Emit fix-style fixes. +##### `"safe-fix"` + +Like `Fix`, but does not mark them as dangerous. +Only applicable for imports, unavailable for variables. + #### fix.variables -type: `"off" | "suggestion" | "fix"` +type: `"off" | "suggestion" | "fix" | "safe-fix"` ##### `"off"` @@ -329,6 +334,11 @@ Emit suggestion-style fixes (current behavior). Emit fix-style fixes. +##### `"safe-fix"` + +Like `Fix`, but does not mark them as dangerous. +Only applicable for imports, unavailable for variables. + ### ignoreClassWithStaticInitBlock type: `boolean` diff --git a/src/docs/guide/usage/linter/rules/eslint/no-useless-computed-key.md b/src/docs/guide/usage/linter/rules/eslint/no-useless-computed-key.md index 418794c309f..1367b7d1284 100644 --- a/src/docs/guide/usage/linter/rules/eslint/no-useless-computed-key.md +++ b/src/docs/guide/usage/linter/rules/eslint/no-useless-computed-key.md @@ -3,7 +3,7 @@ title: "eslint/no-useless-computed-key" category: "Style" default: false type_aware: false -fix: "pending" +fix: "conditional_fix" --- diff --git a/src/docs/guide/usage/linter/rules/jest/prefer-to-have-been-called-times.md b/src/docs/guide/usage/linter/rules/jest/prefer-to-have-been-called-times.md index f535b86f12e..6f2f5ad41ea 100644 --- a/src/docs/guide/usage/linter/rules/jest/prefer-to-have-been-called-times.md +++ b/src/docs/guide/usage/linter/rules/jest/prefer-to-have-been-called-times.md @@ -44,6 +44,17 @@ expect(uncalledFunction).not.toBeCalled(); expect(method.mock.calls[0][0]).toStrictEqual(value); ``` +This rule is compatible with [eslint-plugin-vitest](https://github.com/vitest-dev/eslint-plugin-vitest/blob/main/docs/rules/prefer-to-have-been-called-times.md), +to use it, add the following configuration to your `.oxlintrc.json`: + +```json +{ + "rules": { + "vitest/prefer-to-have-been-called-times": "error" + } +} +``` + ## How to use diff --git a/src/docs/guide/usage/linter/rules/jsdoc/require-property.md b/src/docs/guide/usage/linter/rules/jsdoc/require-property.md index 874400940d4..fa31faf5e4a 100644 --- a/src/docs/guide/usage/linter/rules/jsdoc/require-property.md +++ b/src/docs/guide/usage/linter/rules/jsdoc/require-property.md @@ -36,7 +36,7 @@ Examples of **incorrect** code for this rule: */ /** - * @namespace {Object} SomeNamesoace + * @namespace {Object} SomeNamespace */ ``` diff --git a/src/docs/guide/usage/linter/rules/typescript/prefer-find.md b/src/docs/guide/usage/linter/rules/typescript/prefer-find.md index d75730f8bf2..861f4409c0f 100644 --- a/src/docs/guide/usage/linter/rules/typescript/prefer-find.md +++ b/src/docs/guide/usage/linter/rules/typescript/prefer-find.md @@ -1,6 +1,6 @@ --- title: "typescript/prefer-find" -category: "Nursery" +category: "Style" default: false type_aware: true fix: "none" diff --git a/src/docs/guide/usage/linter/rules/typescript/prefer-readonly.md b/src/docs/guide/usage/linter/rules/typescript/prefer-readonly.md index e3fbe5d45de..96cd410402f 100644 --- a/src/docs/guide/usage/linter/rules/typescript/prefer-readonly.md +++ b/src/docs/guide/usage/linter/rules/typescript/prefer-readonly.md @@ -1,6 +1,6 @@ --- title: "typescript/prefer-readonly" -category: "Nursery" +category: "Style" default: false type_aware: true fix: "none" diff --git a/src/docs/guide/usage/linter/rules/typescript/prefer-regexp-exec.md b/src/docs/guide/usage/linter/rules/typescript/prefer-regexp-exec.md index 87cf3cf4ac1..db664ccc02a 100644 --- a/src/docs/guide/usage/linter/rules/typescript/prefer-regexp-exec.md +++ b/src/docs/guide/usage/linter/rules/typescript/prefer-regexp-exec.md @@ -1,6 +1,6 @@ --- title: "typescript/prefer-regexp-exec" -category: "Nursery" +category: "Style" default: false type_aware: true fix: "none" diff --git a/src/docs/guide/usage/linter/rules/typescript/prefer-string-starts-ends-with.md b/src/docs/guide/usage/linter/rules/typescript/prefer-string-starts-ends-with.md index 7120baa9e1a..e1587d93df2 100644 --- a/src/docs/guide/usage/linter/rules/typescript/prefer-string-starts-ends-with.md +++ b/src/docs/guide/usage/linter/rules/typescript/prefer-string-starts-ends-with.md @@ -1,6 +1,6 @@ --- title: "typescript/prefer-string-starts-ends-with" -category: "Nursery" +category: "Style" default: true type_aware: true fix: "none" diff --git a/src/docs/guide/usage/linter/rules/version.data.js b/src/docs/guide/usage/linter/rules/version.data.js index 699d52d5ba2..7c6097f96d4 100644 --- a/src/docs/guide/usage/linter/rules/version.data.js +++ b/src/docs/guide/usage/linter/rules/version.data.js @@ -1,5 +1,5 @@ export default { load() { - return "180dd0f8f60c383dff3325334b595e67845824a1"; + return "6620ad82e20eab334f1ec8710cb938e2a6e5cd4d"; }, }; diff --git a/src/docs/guide/usage/linter/rules/vitest/prefer-called-exactly-once-with.md b/src/docs/guide/usage/linter/rules/vitest/prefer-called-exactly-once-with.md new file mode 100644 index 00000000000..f96d6ebbbcd --- /dev/null +++ b/src/docs/guide/usage/linter/rules/vitest/prefer-called-exactly-once-with.md @@ -0,0 +1,56 @@ +--- +title: "vitest/prefer-called-exactly-once-with" +category: "Style" +default: false +type_aware: false +fix: "fixable_dangerous_fix" +--- + + + + + + + +### What it does + +It checks when a target is expected with `toHaveBeenCalledOnce` and `toHaveBeenCalledWith` instead of +`toHaveBeenCalledExactlyOnceWith`. + +### Why is this bad? + +The user must deduct from both expects that the spy function is called once and with a specific arguments. + +### Examples + +Examples of **incorrect** code for this rule: + +```js +test("foo", () => { + const mock = vi.fn(); + mock("foo"); + expect(mock).toHaveBeenCalledOnce(); + expect(mock).toHaveBeenCalledWith("foo"); +}); +``` + +Examples of **correct** code for this rule: + +```js +test("foo", () => { + const mock = vi.fn(); + mock("foo"); + expect(mock).toHaveBeenCalledExactlyOnceWith("foo"); +}); +``` + +## How to use + + + +## References + + diff --git a/src/docs/guide/usage/linter/rules/vitest/prefer-import-in-mock.md b/src/docs/guide/usage/linter/rules/vitest/prefer-import-in-mock.md index 862c29876bf..bb15fd40c13 100644 --- a/src/docs/guide/usage/linter/rules/vitest/prefer-import-in-mock.md +++ b/src/docs/guide/usage/linter/rules/vitest/prefer-import-in-mock.md @@ -17,7 +17,7 @@ const source = `https://github.com/oxc-project/oxc/blob/${ data }/crates/oxc_lin ### What it does -This rule enforces using a dynamic `import()` in `vi.mock()`, which improves type information and IntelliSense for the mocked module. +This rule enforces using a dynamic `import()` in `vi.mock()` or `vi.doMock()`, which improves type information and IntelliSense for the mocked module. ### Why is this bad? @@ -29,12 +29,14 @@ Examples of **incorrect** code for this rule: ```js vi.mock("./path/to/module"); +vi.doMock("./path/to/module"); ``` Examples of **correct** code for this rule: ```js vi.mock(import("./path/to/module")); +vi.doMock(import("./path/to/module")); ``` ## Configuration diff --git a/src/docs/guide/usage/linter/rules/vitest/require-awaited-expect-poll.md b/src/docs/guide/usage/linter/rules/vitest/require-awaited-expect-poll.md new file mode 100644 index 00000000000..c377ae9508a --- /dev/null +++ b/src/docs/guide/usage/linter/rules/vitest/require-awaited-expect-poll.md @@ -0,0 +1,61 @@ +--- +title: "vitest/require-awaited-expect-poll" +category: "Correctness" +default: false +type_aware: false +fix: "none" +--- + + + + + + + +### What it does + +This rule ensures that promises returned by `expect.poll` and `expect.element` calls are handled properly. + +### Why is this bad? + +`expect.poll` and `expect.element` return promises. If not awaited or returned, +the test completes before the assertion resolves, meaning the test will pass +regardless of whether the assertion succeeds or fails. + +### Examples + +Examples of **incorrect** code for this rule: + +```js +test("element exists", () => { + asyncInjectElement(); + + expect.poll(() => document.querySelector(".element")).toBeInTheDocument(); +}); +``` + +Examples of **correct** code for this rule: + +```js +test("element exists", () => { + asyncInjectElement(); + + return expect.poll(() => document.querySelector(".element")).toBeInTheDocument(); +}); +test("element exists", async () => { + asyncInjectElement(); + + await expect.poll(() => document.querySelector(".element")).toBeInTheDocument(); +}); +``` + +## How to use + + + +## References + + diff --git a/src/docs/guide/usage/linter/rules/vitest/require-mock-type-parameters.md b/src/docs/guide/usage/linter/rules/vitest/require-mock-type-parameters.md new file mode 100644 index 00000000000..8827d9ce963 --- /dev/null +++ b/src/docs/guide/usage/linter/rules/vitest/require-mock-type-parameters.md @@ -0,0 +1,99 @@ +--- +title: "vitest/require-mock-type-parameters" +category: "Correctness" +default: false +type_aware: false +fix: "none" +--- + + + + + + + +### What it does + +Enforces the use of type parameters on vi.fn(), and optionally on vi.importActual() and vi.importMock(). + +By default, only vi.fn() is checked. Set checkImportFunctions to true to also check vi.importActual() and vi.importMock(). + +### Why is this bad? + +Without explicit type parameters, vi.fn() creates a mock typed as (...args: any[]) => any. +This disables type checking between the mock and the real implementation, which can lead to two problems: + +- tests that fail due to incorrect mock usage when they should pass, or worse, tests that pass while the mock silently diverges from the actual runtime behavior. + +### Examples + +Examples of **incorrect** code for this rule configured as `{ "checkImportFunctions": false }`: + +```js +import { vi } from "vitest"; + +test("foo", () => { + const myMockedFn = vi.fn(); +}); +``` + +Examples of **incorrect** code for this rule configured as `{ "checkImportFunctions": true }`: + +```js +import { vi } from "vitest"; + +vi.mock("./example.js", async () => { + const originalModule = await vi.importActual("./example.js"); + + return { ...originalModule }; +}); +const fs = await vi.importMock("fs"); +``` + +Examples of **correct** code for this rule configured as `{ "checkImportFunctions": false }`: + +```js +import { vi } from 'vitest' + + test('foo', () => { + const myMockedFnOne = vi.fn<(arg1: string, arg2: boolean) => number>() + const myMockedFnTwo = vi.fn<() => void>() + const myMockedFnThree = vi.fn() + }) +``` + +Examples of **correct** code for this rule configured as `{ "checkImportFunctions": true }`: + +```js +import { vi } from "vitest"; + +vi.mock("./example.js", async () => { + const originalModule = (await vi.importActual) < any > "./example.js"; + + return { ...originalModule }; +}); +const fs = (await vi.importMock) < any > "fs"; +``` + +## Configuration + +This rule accepts a configuration object with the following properties: + +### checkImportFunctions + +type: `boolean` + +default: `false` + +Also require type parameters for `importActual` and `importMock`. + +## How to use + + + +## References + + diff --git a/src/docs/guide/usage/linter/rules/vitest/require-test-timeout.md b/src/docs/guide/usage/linter/rules/vitest/require-test-timeout.md new file mode 100644 index 00000000000..35e256a597c --- /dev/null +++ b/src/docs/guide/usage/linter/rules/vitest/require-test-timeout.md @@ -0,0 +1,66 @@ +--- +title: "vitest/require-test-timeout" +category: "Restriction" +default: false +type_aware: false +fix: "none" +--- + + + + + + + +### What it does + +Requires every test to have a timeout specified, either as a numeric third +argument, a `{ timeout }` option, or via `vi.setConfig({ testTimeout: ... })`. + +### Why is this bad? + +Tests without an explicit timeout rely on the default, which may be too +generous to catch performance regressions or too short for slow CI +environments, leading to flaky failures. + +### Examples + +Examples of **incorrect** code for this rule: + +```js +it("slow test", async () => { + await doSomethingSlow(); +}); +``` + +Examples of **correct** code for this rule: + +```js +// good (numeric timeout) +test("slow test", async () => { + await doSomethingSlow(); +}, 1000); + +// good (options object) +test("slow test", { timeout: 1000 }, async () => { + await doSomethingSlow(); +}); + +// good (file-level) +vi.setConfig({ testTimeout: 1000 }); + +test("slow test", async () => { + await doSomethingSlow(); +}); +``` + +## How to use + + + +## References + + diff --git a/src/docs/guide/usage/rule-count.data.js b/src/docs/guide/usage/rule-count.data.js index 01973fa55ac..d9d62a63082 100644 --- a/src/docs/guide/usage/rule-count.data.js +++ b/src/docs/guide/usage/rule-count.data.js @@ -1,5 +1,5 @@ export default { load() { - return 701; + return 705; }, };