@skagerakenergi/biome-config
@skagerakenergi/biome-config
Shared Biome configuration for Skagerak Energi projects. Covers formatting, TypeScript/Vue linting, and HTML accessibility rules — including Vue <template> a11y via a dedicated second-pass config.
Installation
npm install --save-dev @skagerakenergi/biome-config @biomejs/biome
# or
pnpm add -D @skagerakenergi/biome-config @biomejs/biome
Quick Start
1. biome.json — formatting + standard linting
{
"$schema": "https://biomejs.dev/schemas/2.5.1/schema.json",
"extends": ["@skagerakenergi/biome-config/biome.json"]
}
2. biome.a11y.json — Vue template accessibility (second pass)
Create a second config file at your project root:
{
"$schema": "https://biomejs.dev/schemas/2.5.1/schema.json",
"extends": ["./biome.json"],
"html": {
"experimentalFullSupportEnabled": true
},
"formatter": {
"enabled": false
}
}
Why two files? Biome's
experimentalFullSupportEnabledunlocks a11y linting of Vue<template>blocks but breaksbiome formatfor Vue files (closed upstream as not planned). Keeping it in a formatter-off second config gives you both features safely until this is fixed upstream
Recommended package.json Scripts
{
"scripts": {
"format": "biome format --write .",
"lint": "biome check .",
"lint:fix": "biome check --write .",
"lint:a11y": "biome lint --config-path biome.a11y.json .",
"check": "biome check . && biome lint --config-path biome.a11y.json .",
"check:fix": "biome check --write . && biome lint --config-path biome.a11y.json ."
}
}
| Script | What it does |
|---|---|
format |
Formats all files (Vue indentScriptAndStyle, quote style, etc.) |
lint |
Checks for lint errors and formatter drift; does not write anything |
lint:fix |
biome check --write: applies formatter and safe lint fixes in one pass |
lint:a11y |
Lints Vue <template> a11y; does not reformat |
check |
Both passes — full coverage, no writes |
check:fix |
Both passes — formats + applies safe lint fixes |
--writevs--fix: They are synonyms in Biome. This config uses--writethroughout (the canonical modern form) to avoid ambiguity.biome check --writeapplies both the formatter and safe lint fixes in a single pass; it is equivalent to runningbiome format --writeandbiome lint --writetogether.
Why check:fix instead of format + lint:fix?
The two passes are designed to be safe in any order and will never undo each other:
- Pass 1 (
biome check --write) formats and fixes script-level issues.
It is blind to Vue template a11y (noexperimentalFullSupportEnabled). - Pass 2 (
biome lint --config-path biome.a11y.json) detects template a11y violations.
It cannot reformat becauseformatter.enabledisfalse.
CI Setup
Run both passes as separate steps:
# GitHub Actions example
- name: Format & lint
run: biome check .
- name: Vue template a11y
run: biome lint --config-path biome.a11y.json .
Or combine them in one step:
- name: Full check
run: pnpm check
What's Included
Formatter
- 2-space indent, LF line endings, 100-char line width
- Single quotes, no semicolons (ASI), trailing commas off
indentScriptAndStyle: truefor Vue/HTML filesselfCloseVoidElements: always- Respects
.editorconfig
Linter rules
| Category | Rules |
|---|---|
| a11y | All 15 Biome 2.4 accessibility rules (applied to .html; Vue templates via biome.a11y.json) |
| suspicious | noVar, noExplicitAny, noExtraNonNullAssertion, noNonNullAssertedOptionalChain, noUnsafeDeclarationMerging, noMisleadingInstantiator, useNamespaceKeyword |
| style | noCommonJs, noNamespace, useArrayLiterals, useAsConstAssertion, useConst |
| complexity | noArguments, noUselessTypeConstraint |
| correctness | noUnusedVariables, noVueDuplicateKeys, noVueReservedKeys, noVueReservedProps |
Vue / TypeScript overrides
noUnusedVariablesandnoUnusedImportsare off for.vuefiles (template references aren't visible to the script-only parser).useConstanduseImportTypeare off for.vue/.svelte/.astrofiles.- JS files (
.js,.cjs,.mjs) usesemicolons: always.
Ignored paths (hardcoded via files.includes)
**/node_modules, **/dist, **/dist-ssr, **/.quasar, **/.nuxt, **/.next, **/.output, **/build, **/coverage, **/.vitepress/cache, **/.vitepress/dist
These live in files.includes so they apply to every tool (formatter, linter, assist) without per-tool repetition. Consumers can extend the list by adding exclusions to their own files.includes.
VCS integration
vcs.useIgnoreFile: true — your .gitignore is automatically respected on top of the hardcoded ignores.
Known Upstream Biome Bugs
These are confirmed issues in Biome itself that affect this config. Workarounds are documented below.
1. --config-path resets VCS root resolution
When you run biome lint --config-path biome.a11y.json, Biome resolves the VCS root relative to the config file path rather than the working directory. This means vcs.useIgnoreFile: true in the shared config may not respect your .gitignore, and directories like dist/ can be scanned even though they are excluded by files.includes in the main config.
Workaround: Ensure your project root contains a .gitignore that also excludes build artefacts, or add them explicitly to your biome.a11y.json via files.includes. The hardcoded files.includes exclusions in this shared config are not affected (they resolve correctly via extends).
2. experimentalFullSupportEnabled overrides recommended: false
The root cause is that experimentalFullSupportEnabled is genuinely experimental — Biome's HTML/template support is still being stabilised and the flag re-initialises rule defaults without honouring the inherited recommended: false. Enabling it in biome.a11y.json causes Biome to activate its full recommended ruleset, overriding the linter.rules.recommended: false setting inherited from the shared config. Rules not explicitly configured here (e.g. noNonNullAssertion, useTemplate, useLiteralKeys) may fire unexpectedly.
Workaround: Explicitly disable any unwanted rules in your biome.a11y.json:
{
"$schema": "https://biomejs.dev/schemas/2.5.1/schema.json",
"extends": ["./biome.json"],
"html": { "experimentalFullSupportEnabled": true },
"formatter": { "enabled": false },
"linter": {
"rules": {
"style": {
"noNonNullAssertion": "off",
"useTemplate": "off",
"useLiteralKeys": "off"
}
}
}
}
Custom Overrides
Add any project-specific rules after extends in your biome.json:
{
"$schema": "https://biomejs.dev/schemas/2.5.1/schema.json",
"extends": ["@skagerakenergi/biome-config/biome.json"],
"linter": {
"rules": {
"style": {
"useConst": "off"
}
}
}
}
To add extra path exclusions, extend files.includes with only your exclusions — do not repeat **, as it is already provided by the shared config (repeating it triggers a noBiomeFirstException warning):
{
"$schema": "https://biomejs.dev/schemas/2.5.1/schema.json",
"extends": ["@skagerakenergi/biome-config/biome.json"],
"files": {
"includes": ["!**/generated/**", "!**/vendor/**"]
}
}
To disable VCS integration:
{
"$schema": "https://biomejs.dev/schemas/2.5.1/schema.json",
"extends": ["@skagerakenergi/biome-config/biome.json"],
"vcs": {
"enabled": false
}
}