A Node package lints and formats with Biome, reports its coverage as LCOV and installs without the scripts of its packages¶
- Status: accepted
- Date: 2026-10-18
Context¶
The Node package was the weakest kind of the blueprint (#293):
- No lint and no format.
scripts/check.shchecked the types, the tests and the package, while a Python project had Ruff and a Nextcloud app php-cs-fixer: an unused variable, a promise that nobody awaits or a file in another format passed every check. - No coverage in the report of the PR coach. The CI kept no artifact
coverage, and the coach read only Cobertura, while the test runner of Node writes LCOV. - Install scripts on the computers of the contributors. The CI installs with
npm ci --ignore-scripts, but the dev container,CONTRIBUTING.mdandAGENTS.mdran a plainnpm ci, which runs thepreinstall,installandpostinstallscripts of every package: the way in of a package that was taken over, such as the worm that spread through npm in September 2025.
Options¶
- ESLint with typescript-eslint, and Prettier. The most common pair, with the most rules, but two tools with two settings, a few hundred packages in the lock and a run that takes seconds; type-aware rules need a program of TypeScript of their own.
- Biome. One tool for the lint, the format and the order of the imports, one package with a native binary for each platform and no install script, one settings file, and a run of milliseconds. Its rules cover most of ESLint's recommended ones and of typescript-eslint's that need no types.
- Only the format, with Prettier or Biome, and no lint.
Decision¶
Option 2:
@biomejs/biomeis a devDependency of the Node packages, locked with its hash instacks/node/package-lock.json, which Renovate keeps current. The new step Lint and format ofscripts/check.shrunsnpx --no -- biome ci --error-on-warnings .: a warning fails as well, so none piles up, and in the CI Biome writes each finding as an annotation at its line.npx --nonever fetches a Biome that the lock didn't install.biome.jsoncbelongs to the blueprint: the recommended rules, sorted imports and the indentation of.editorconfig, for the files that git doesn't ignore, other than.github/, whose tools and settings keep the format of the blueprint..editorconfigindents JSONC files with two spaces, as they are written. The fix bot runsnpx --no -- biome check --write ., which makes the format and the fixes that Biome calls safe.npm testwrites, beside its usual output, the LCOV reportlcov.info, which git and the package leave out; the CI keeps it as the artifactcoverage. The coach and the dashboard read a report by its name: LCOV in a file that ends in.info, Cobertura in any other. The test runner names each file from where it ran; a tool that names it by its absolute path, such as c8, gets it named from the root of the checkout, as one source of a Cobertura report does.- Every install of a Node project runs without the scripts of its packages: the CI, the dev container and the commands of the documentation are
npm ci --ignore-scripts, which a test checks in every rendered file.
Consequences¶
package.jsonbelongs to the project, so a package made before keeps its own until it adds Biome withnpm install --save-dev --ignore-scripts @biomejs/biome: until then the step says so and passes, so that the update of the blueprint fails no check. The same holds for the reporter of the scripttest; without it the CI keeps no report, and the coach reports without coverage, as before.- A package that needs an install script, such as one that builds a native module, runs it on purpose, with
npm rebuild NAME, in a script of its own. - Mutation tests for Node, with Stryker, are a step of their own still to take; the Mutation workflow covers Python and PHP.