
Biome Validator
- 155 installs
- 31 repo stars
- Updated August 2, 2026
- shipshitdev/library
Run Biome lint and format checks on JS/TS before merge or release to catch style, correctness, and config drift early.
About
biome-validator audits JavaScript and TypeScript projects against Biome linter and formatter rules before code ships. It flags style violations, unsafe patterns, and config mismatches so teams fix issues during review instead of after release. Ideal for SaaS, CLI, and API repos standardizing on Biome over ESLint plus Prettier.
- Enforces Biome lint and format rules consistently
- Catches JS/TS style and correctness issues pre-merge
- Reduces manual code review noise on formatting
- Supports fast feedback loops in CI or local dev
- Aligns repos with a single toolchain standard
Biome Validator by the numbers
- 155 all-time installs (skills.sh)
- +1 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #372 of 1,352 Code Review & Quality skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/shipshitdev/library --skill biome-validatorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 155 |
|---|---|
| repo stars | ★ 31 |
| Last updated | August 2, 2026 |
| Repository | shipshitdev/library ↗ |
What it does
Run Biome lint and format checks on JS/TS before merge or release to catch style, correctness, and config drift early.
Files
Biome Validator
Validates Biome 2.3+ configuration and prevents outdated patterns. Ensures type-aware linting, domains, and modern Biome features are properly configured.
When This Activates
- Setting up linting for a new project
- Before any code quality work
- Auditing existing Biome configurations
- After AI generates biome.json
- CI/CD pipeline validation
Quick Start
python3 scripts/validate.py --root .
python3 scripts/validate.py --root . --strictWhat Gets Checked
1. Biome Version & Schema
GOOD - Biome 2.3+:
{
"$schema": "https://biomejs.dev/schemas/2.3.12/schema.json"
}BAD - Old schema:
{
"$schema": "https://biomejs.dev/schemas/1.9.0/schema.json"
}2. Package Version
// GOOD: v2.3+
"@biomejs/biome": "^2.3.0"
// BAD: v1.x or v2.0-2.2
"@biomejs/biome": "^1.9.0"3. Linter Configuration
GOOD - Biome 2.x:
{
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"suspicious": {
"noExplicitAny": "warn"
}
}
}
}4. Biome Assist (2.0+)
GOOD - Using assist:
{
"assist": {
"actions": {
"source": {
"organizeImports": "on"
}
}
}
}BAD - Old organizeImports location:
{
"organizeImports": {
"enabled": true
}
}5. Domains (2.0+)
GOOD - Using domains for framework-specific rules:
{
"linter": {
"domains": {
"react": "on",
"next": "on"
}
}
}6. Suppression Comments
GOOD - Biome 2.0+ comments:
// biome-ignore lint/suspicious/noExplicitAny: legacy code
// biome-ignore-all lint/style/useConst
// biome-ignore-start lint/complexity
// biome-ignore-endBAD - Wrong format:
// @ts-ignore // Not Biome
// eslint-disable // Wrong toolBiome 2.3+ Features
Type-Aware Linting
Biome 2.0+ includes type inference without requiring TypeScript compiler:
{
"linter": {
"rules": {
"correctness": {
"noUndeclaredVariables": "error",
"useAwaitThenable": "error"
}
}
}
}Assist Actions
{
"assist": {
"actions": {
"source": {
"organizeImports": "on",
"useSortedKeys": "on"
}
}
}
}Multi-file Analysis
Lint rules can query information from other files for more powerful analysis.
Framework Domains
{
"linter": {
"domains": {
"react": "on", // React-specific rules
"next": "on", // Next.js rules
"test": "on" // Testing framework rules
}
}
}Recommended Configuration
{
"$schema": "https://biomejs.dev/schemas/2.3.12/schema.json",
"assist": {
"actions": {
"source": {
"organizeImports": "on"
}
}
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"complexity": {
"noForEach": "off"
},
"style": {
"noNonNullAssertion": "off"
},
"suspicious": {
"noArrayIndexKey": "off",
"noExplicitAny": "warn"
},
"correctness": {
"useAwaitThenable": "error",
"noLeakedRender": "error"
}
},
"domains": {
"react": "on",
"next": "on"
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"trailingCommas": "es5",
"semicolons": "always"
}
},
"files": {
"ignore": [
"node_modules",
"dist",
"build",
".next",
"out",
".cache",
".turbo",
"coverage"
]
}
}Deprecated Patterns
| Deprecated | Replacement (2.3+) |
|---|---|
organizeImports.enabled | assist.actions.source.organizeImports |
| Schema < 2.0 | Schema 2.3.11+ |
@biomejs/biome < 2.3 | @biomejs/biome@latest |
| No domains config | Use linter.domains for frameworks |
Validation Output
=== Biome 2.3+ Validation Report ===
Package Version: @biomejs/biome@2.3.11 ✓
Configuration:
✓ Schema version: 2.3.11
✓ Linter enabled with recommended rules
✓ Using assist.actions for imports
✗ No domains configured (consider enabling react, next)
✓ Formatter configured
Rules:
✓ noExplicitAny: warn
✓ useAwaitThenable: error
✗ noLeakedRender not enabled (recommended)
Summary: 2 issues foundMigration from ESLint
Step 1: Install Biome
bun remove eslint prettier eslint-config-* eslint-plugin-*
bun add -D @biomejs/biome@latestStep 2: Create biome.json
bunx biome initStep 3: Migrate rules
bunx biome migrate eslint --writeStep 4: Update scripts
{
"scripts": {
"lint": "biome lint .",
"lint:fix": "biome lint --write .",
"format": "biome format --write .",
"check": "biome check .",
"check:fix": "biome check --write ."
}
}Step 5: Remove old configs
rm .eslintrc* .prettierrc* .eslintignore .prettierignoreVS Code Integration
// .vscode/settings.json
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "biomejs.biome",
"editor.codeActionsOnSave": {
"source.organizeImports.biome": "explicit",
"quickfix.biome": "explicit"
}
}CI/CD Integration
# .github/workflows/lint.yml
- name: Validate Biome Config
run: |
python3 scripts/validate.py \
--root . \
--strict \
--ci
- name: Lint
run: bunx biome check --error-on-warnings .Integration
linter-formatter-init- Sets up Biome from scratchnextjs-validator- Validates Next.js (enable next domain)bun-validator- Validates Bun workspace
{
"name": "biome-validator",
"version": "1.0.0",
"description": "Validate Biome 2.3+ configuration and detect outdated patterns",
"author": {
"name": "Ship Shit Dev",
"email": "hello@shipshit.dev",
"url": "https://shipshit.dev"
},
"license": "MIT",
"skills": "."
}
#!/usr/bin/env python3
"""
Biome 2.3+ Validator
Validates Biome configuration and detects outdated patterns.
Ensures proper schema version, domains, assists, and recommended rules.
"""
import argparse
import json
import re
import sys
from pathlib import Path
from typing import NamedTuple
class Issue(NamedTuple):
severity: str
file: str
line: int | None
message: str
fix: str | None
class ValidationResult:
def __init__(self):
self.issues: list[Issue] = []
self.passed: list[str] = []
self.biome_version: str | None = None
self.schema_version: str | None = None
def add_issue(self, severity: str, file: str, message: str, line: int | None = None, fix: str | None = None):
self.issues.append(Issue(severity, file, line, message, fix))
def add_passed(self, message: str):
self.passed.append(message)
@property
def has_errors(self) -> bool:
return any(i.severity == 'error' for i in self.issues)
def check_package_version(root: Path, result: ValidationResult) -> bool:
"""Check if Biome 2.3+ is installed."""
pkg_path = root / 'package.json'
if not pkg_path.exists():
result.add_issue('error', 'package.json', 'package.json not found')
return False
try:
with open(pkg_path) as f:
pkg = json.load(f)
except json.JSONDecodeError:
result.add_issue('error', 'package.json', 'Invalid JSON')
return False
deps = {**pkg.get('dependencies', {}), **pkg.get('devDependencies', {})}
if '@biomejs/biome' not in deps:
result.add_issue('error', 'package.json',
'@biomejs/biome not found in dependencies',
fix='bun add -D @biomejs/biome@latest')
return False
version = deps['@biomejs/biome']
result.biome_version = version
# Parse version
match = re.search(r'(\d+)\.(\d+)', version)
if match:
major, minor = int(match.group(1)), int(match.group(2))
if major < 2:
result.add_issue('error', 'package.json',
f'Biome 1.x detected ({version}). Must use v2.3+',
fix='bun add -D @biomejs/biome@latest')
return False
if major == 2 and minor < 3:
result.add_issue('warning', 'package.json',
f'Biome {version} detected. Consider upgrading to 2.3+',
fix='bun add -D @biomejs/biome@latest')
else:
result.add_passed(f'Biome version: {version}')
return True
result.add_issue('warning', 'package.json', f'Could not parse Biome version: {version}')
return True
def check_biome_config(root: Path, result: ValidationResult) -> dict | None:
"""Check biome.json configuration."""
config_path = root / 'biome.json'
if not config_path.exists():
config_path = root / 'biome.jsonc'
if not config_path.exists():
result.add_issue('error', 'biome.json', 'biome.json not found',
fix='bunx biome init')
return None
try:
with open(config_path) as f:
content = f.read()
# Remove comments for jsonc
content = re.sub(r'//.*$', '', content, flags=re.MULTILINE)
content = re.sub(r'/\*.*?\*/', '', content, flags=re.DOTALL)
config = json.loads(content)
except json.JSONDecodeError as e:
result.add_issue('error', config_path.name, f'Invalid JSON: {e}')
return None
return config
def check_schema_version(config: dict, result: ValidationResult):
"""Check $schema version."""
schema = config.get('$schema', '')
if not schema:
result.add_issue('warning', 'biome.json',
'No $schema defined',
fix='Add "$schema": "https://biomejs.dev/schemas/2.3.12/schema.json"')
return
# Extract version from schema URL
match = re.search(r'/schemas/(\d+\.\d+\.\d+)/', schema)
if match:
version = match.group(1)
result.schema_version = version
parts = version.split('.')
major, minor = int(parts[0]), int(parts[1])
if major < 2:
result.add_issue('error', 'biome.json',
f'Schema version {version} is outdated. Use 2.3+',
fix='Update $schema to "https://biomejs.dev/schemas/2.3.12/schema.json"')
elif major == 2 and minor < 3:
result.add_issue('warning', 'biome.json',
f'Schema version {version}. Consider upgrading to 2.3+',
fix='Update $schema to "https://biomejs.dev/schemas/2.3.12/schema.json"')
else:
result.add_passed(f'Schema version: {version}')
else:
result.add_issue('warning', 'biome.json',
f'Could not parse schema version from: {schema}')
def check_linter_config(config: dict, result: ValidationResult):
"""Check linter configuration."""
linter = config.get('linter', {})
if not linter.get('enabled', True):
result.add_issue('warning', 'biome.json',
'Linter is disabled',
fix='Set linter.enabled: true')
return
result.add_passed('Linter enabled')
# Check rules
rules = linter.get('rules', {})
if rules.get('recommended'):
result.add_passed('Using recommended rules')
else:
result.add_issue('warning', 'biome.json',
'Recommended rules not enabled',
fix='Add "recommended": true to linter.rules')
# Check for useful rules
correctness = rules.get('correctness', {})
if correctness.get('useAwaitThenable'):
result.add_passed('useAwaitThenable rule enabled')
else:
result.add_issue('warning', 'biome.json',
'useAwaitThenable not enabled (catches await on non-Promise)',
fix='Add correctness.useAwaitThenable: "error"')
if correctness.get('noLeakedRender'):
result.add_passed('noLeakedRender rule enabled')
# Check domains (Biome 2.0+)
domains = linter.get('domains', {})
if domains:
enabled_domains = [k for k, v in domains.items() if v == 'on']
if enabled_domains:
result.add_passed(f'Domains enabled: {", ".join(enabled_domains)}')
else:
result.add_issue('warning', 'biome.json',
'No domains configured (consider enabling react, next, test)',
fix='Add linter.domains: { "react": "on", "next": "on" }')
def check_assist_config(config: dict, result: ValidationResult):
"""Check assist configuration (Biome 2.0+)."""
assist = config.get('assist', {})
# Check for old organizeImports location
if 'organizeImports' in config:
result.add_issue('error', 'biome.json',
'organizeImports at root level is deprecated',
fix='Move to assist.actions.source.organizeImports')
return
actions = assist.get('actions', {})
source = actions.get('source', {})
if source.get('organizeImports') == 'on':
result.add_passed('Using assist.actions for import organization')
else:
result.add_issue('warning', 'biome.json',
'Import organization not configured',
fix='Add assist.actions.source.organizeImports: "on"')
def check_formatter_config(config: dict, result: ValidationResult):
"""Check formatter configuration."""
formatter = config.get('formatter', {})
if not formatter.get('enabled', True):
result.add_issue('warning', 'biome.json',
'Formatter is disabled',
fix='Set formatter.enabled: true')
return
result.add_passed('Formatter enabled')
# Check basic settings
if formatter.get('indentStyle'):
result.add_passed(f'Indent style: {formatter["indentStyle"]}')
def check_old_configs(root: Path, result: ValidationResult):
"""Check for old ESLint/Prettier configs that should be removed."""
old_files = [
'.eslintrc',
'.eslintrc.js',
'.eslintrc.json',
'.eslintrc.yml',
'.eslintrc.yaml',
'.prettierrc',
'.prettierrc.js',
'.prettierrc.json',
'.prettierrc.yml',
'.prettierrc.yaml',
'prettier.config.js',
'.eslintignore',
'.prettierignore',
]
for old_file in old_files:
if (root / old_file).exists():
result.add_issue('warning', old_file,
f'Old config file found - Biome replaces ESLint/Prettier',
fix=f'rm {old_file}')
def print_report(result: ValidationResult, verbose: bool = False):
"""Print validation report."""
print("\n" + "=" * 50)
print(" Biome 2.3+ Validation Report")
print("=" * 50 + "\n")
if result.biome_version:
print(f"Package Version: @biomejs/biome@{result.biome_version.lstrip('^~>=')}")
if result.schema_version:
print(f"Schema Version: {result.schema_version}")
print()
if verbose and result.passed:
print("Passed Checks:")
for msg in result.passed:
print(f" [PASS] {msg}")
print()
if result.issues:
errors = [i for i in result.issues if i.severity == 'error']
warnings = [i for i in result.issues if i.severity == 'warning']
if errors:
print("Errors:")
for issue in errors:
print(f" [ERROR] {issue.file}")
print(f" {issue.message}")
if issue.fix:
print(f" Fix: {issue.fix}")
print()
if warnings:
print("Warnings:")
for issue in warnings:
print(f" [WARN] {issue.file}")
print(f" {issue.message}")
if issue.fix:
print(f" Fix: {issue.fix}")
print()
print("-" * 50)
error_count = len([i for i in result.issues if i.severity == 'error'])
warning_count = len([i for i in result.issues if i.severity == 'warning'])
if error_count == 0 and warning_count == 0:
print("Result: All checks passed! Biome configured correctly.")
else:
print(f"Result: {error_count} error(s), {warning_count} warning(s)")
print()
def main():
parser = argparse.ArgumentParser(description='Validate Biome 2.3+ configuration')
parser.add_argument('--root', '-r', type=str, default='.', help='Project root directory')
parser.add_argument('--strict', action='store_true', help='Treat warnings as errors')
parser.add_argument('--ci', action='store_true', help='CI mode: exit with non-zero on errors')
parser.add_argument('--verbose', '-v', action='store_true', help='Show passed checks')
parser.add_argument('--json', action='store_true', help='Output as JSON')
args = parser.parse_args()
root = Path(args.root).resolve()
if not root.exists():
print(f"Error: Directory not found: {root}")
sys.exit(1)
result = ValidationResult()
# Run all checks
check_package_version(root, result)
config = check_biome_config(root, result)
if config:
check_schema_version(config, result)
check_linter_config(config, result)
check_assist_config(config, result)
check_formatter_config(config, result)
check_old_configs(root, result)
# Output
if args.json:
output = {
'biome_version': result.biome_version,
'schema_version': result.schema_version,
'passed': result.passed,
'issues': [
{
'severity': i.severity,
'file': i.file,
'message': i.message,
'fix': i.fix
}
for i in result.issues
]
}
print(json.dumps(output, indent=2))
else:
print_report(result, verbose=args.verbose)
# Exit code
if args.ci or args.strict:
if result.has_errors:
sys.exit(1)
if args.strict and any(i.severity == 'warning' for i in result.issues):
sys.exit(1)
sys.exit(0)
if __name__ == '__main__':
main()