From 8a9d5edd7aee0c20d518256c894c5b9d6f4fc76c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 23 Oct 2026 13:42:04 +0000 Subject: [PATCH] perf: hand Biome and oxlint every file at once Each lint of the Biome and oxlint checks is a process of its own that reads the configuration and, for Biome, the project again. Handed in batches of 64 while webpack built, a build of 1,500 files started 16-21 of them, two at a time. Both adapters now say `lintsOnce` (#377), so they are handed every file when the module graph is done: 4 processes, one at a time. 1,500 files, one-shot, CPU of the processes the build started: Biome 2.9-3.0 s -> 2.15-2.2 s, oxlint 1.2-1.7 s -> 0.26-0.31 s. Wall time unchanged (within noise). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01App18sYbe6Vf6HDCjCXbvf --- .changeset/cli-checks-one-run.md | 5 +++++ README.md | 4 ++++ src/checks/biome.js | 4 ++++ src/checks/oxlint.js | 4 ++++ test/lints-once.test.js | 7 +++++++ types/checks/biome.d.ts | 1 + types/checks/oxlint.d.ts | 1 + 7 files changed, 26 insertions(+) create mode 100644 .changeset/cli-checks-one-run.md diff --git a/.changeset/cli-checks-one-run.md b/.changeset/cli-checks-one-run.md new file mode 100644 index 0000000..fc723bb --- /dev/null +++ b/.changeset/cli-checks-one-run.md @@ -0,0 +1,5 @@ +--- +"diagnostics-webpack-plugin": patch +--- + +Hand Biome and oxlint every file at once when webpack is done building them, rather than in batches while it builds: each batch was a process of its own reading the configuration, and for Biome the project, again. diff --git a/README.md b/README.md index 65ea1dd..b2a7b4f 100644 --- a/README.md +++ b/README.md @@ -782,6 +782,10 @@ new DiagnosticsPlugin({ checks: ["biome"] }); Like the oxlint check this runs a binary and reads the JSON it answers with, which Biome calls an experimental reporter — a release of its own may move the shape, and it is the only reporter carrying the severities this check needs. +A build hands it every file at once when webpack is done building them, one +run for each 500, rather than in batches while webpack builds: each run reads +the configuration and the project again. Over 1,500 files that took the CPU of +the processes from 2.9 s to 2.2 s, and for oxlint from 1.2–1.7 s to 0.3 s. Biome's project and types rules — `noImportCycles`, `noFloatingPromises` — answer for a file from the files it imports, and which rules those are is not diff --git a/src/checks/biome.js b/src/checks/biome.js index 2d49f57..749e1ed 100644 --- a/src/checks/biome.js +++ b/src/checks/biome.js @@ -167,5 +167,9 @@ export default { // read for the answer the way oxlint's can. A rebuild lints again what a // change reaches, which a run of Biome barely notices; a project using none // of those rules can write `readsAcrossFiles: false`. + // Each run is a process of its own that reads the configuration and, for + // Biome, the project again: handed every file at once, a build starts one + // for each 500 files rather than one for each batch webpack builds. + lintsOnce: () => true, readsAcrossFiles: () => true, }; diff --git a/src/checks/oxlint.js b/src/checks/oxlint.js index ca0a73c..f6c3b5d 100644 --- a/src/checks/oxlint.js +++ b/src/checks/oxlint.js @@ -274,6 +274,10 @@ export default { create, resultPath: (/** @type {EXPECTED_ANY} */ result) => /** @type {FileResult} */ (result).filename, + // Each run is a process of its own that reads the configuration and, for + // Biome, the project again: handed every file at once, a build starts one + // for each 500 files rather than one for each batch webpack builds. + lintsOnce: () => true, readsAcrossFiles, }; diff --git a/test/lints-once.test.js b/test/lints-once.test.js index cd09c1e..a18b8bd 100644 --- a/test/lints-once.test.js +++ b/test/lints-once.test.js @@ -9,7 +9,9 @@ import { afterEach, describe, it } from "node:test"; import webpack from "webpack"; +import biome from "../src/checks/biome.js"; import eslint from "../src/checks/eslint.js"; +import oxlint from "../src/checks/oxlint.js"; import DiagnosticsPlugin from "../src/index.js"; import { countThreads } from "../src/threads.js"; @@ -50,6 +52,11 @@ describe("linting once", () => { }); }); + it("should hand Biome and oxlint every file at once, each run being a process", () => { + assert.strictEqual(biome.lintsOnce && biome.lintsOnce(), true); + assert.strictEqual(oxlint.lintsOnce && oxlint.lintsOnce(), true); + }); + describe("a check that lints once", () => { /** @type {string | undefined} */ let project; diff --git a/types/checks/biome.d.ts b/types/checks/biome.d.ts index 64dc9c3..84812d3 100644 --- a/types/checks/biome.d.ts +++ b/types/checks/biome.d.ts @@ -9,6 +9,7 @@ declare namespace _default { export function defaultExclude(): string; export { create }; export function resultPath(result: EXPECTED_ANY): string; + export function lintsOnce(): boolean; export function readsAcrossFiles(): boolean; } export default _default; diff --git a/types/checks/oxlint.d.ts b/types/checks/oxlint.d.ts index ea07726..e303aca 100644 --- a/types/checks/oxlint.d.ts +++ b/types/checks/oxlint.d.ts @@ -9,6 +9,7 @@ declare namespace _default { export function defaultExclude(): string; export { create }; export function resultPath(result: EXPECTED_ANY): string; + export function lintsOnce(): boolean; export { readsAcrossFiles }; } export default _default;