🎯 Ejemplos recomendados
Balanced sample collections from various categories for you to explore
Ejemplos de Biome JavaScript Toolchain
Ejemplos de toolchain JavaScript Biome incluyendo configuración, linting, formatting, bundling y tooling moderno JavaScript/TypeScript
💻 Configuración Básica de Biome json
🟢 simple
⭐
Configuración completa de Biome para proyectos JavaScript/TypeScript con configuración moderna y mejores prácticas
⏱️ 20 min
🏷️ biome, setup, toolchain, configuration
Prerequisites:
Node.js, npm/yarn, Modern JavaScript/TypeScript
// Biome Basic Setup and Configuration
// Biome is the successor to Rome - an all-in-one JavaScript toolchain
// 1. Installation
// npm install --save-dev @biomejs/biome
// Or: yarn add --dev @biomejs/biome
// Or: pnpm add -D @biomejs/biome
// 2. Initialize Biome in your project
// npx @biomejs/biome init
// 3. biome.json - Main Biome Configuration
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"complexity": {
"noExtraBooleanCast": "error",
"noMultipleSpacesInRegularExpressionLiterals": "error",
"noUselessCatch": "error",
"noWith": "error"
},
"correctness": {
"noUnusedVariables": "error",
"noUndeclaredVariables": "error",
"noUnreachable": "error",
"noConstantCondition": "warn"
},
"style": {
"noNegationElse": "error",
"useShorthandArrayType": "error",
"useTemplate": "error",
"useImportType": "error"
},
"suspicious": {
"noArrayIndexKey": "warn",
"noAsyncPromiseExecutor": "error",
"noCatchAssign": "error",
"noDebugger": "error",
"noDuplicateClassMembers": "error",
"noEmptyBlockStatements": "error",
"noExplicitAny": "warn",
"noFallthroughSwitchClause": "error",
"noGlobalObjectCalls": "error",
"noImportAssign": "error",
"noMisleadingCharacterClass": "error",
"noRedeclare": "error",
"noShadowRestrictedNames": "error",
"noUnsafeNegation": "error"
},
"nursery": {
"useSortedClasses": "error",
"noUndeclaredDependencies": "warn",
"noUselessRename": "error",
"useIsNan": "error"
}
}
},
"formatter": {
"enabled": true,
"formatWithErrors": false,
"indentStyle": "space",
"indentSize": 2,
"lineWidth": 80,
"lineEnding": "lf",
"quoteStyle": "single",
"jsxQuoteStyle": "double",
"attributePosition": "auto",
"bracketSpacing": true,
"bracketSameLine": false,
"arrowParentheses": "asNeeded",
"semicolons": "asNeeded",
"trailingComma": "es5",
"quoteProperties": "preserve"
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"jsxQuoteStyle": "double",
"quoteProperties": "preserve",
"trailingComma": "es5",
"semicolons": "asNeeded",
"arrowParentheses": "asNeeded",
"bracketSpacing": true,
"bracketSameLine": false,
"attributePosition": "auto"
},
"globals": [
"window",
"document",
"console",
"process",
"Buffer",
"global"
]
},
"typescript": {
"formatter": {
"quoteStyle": "single",
"jsxQuoteStyle": "double",
"quoteProperties": "preserve",
"trailingComma": "es5",
"semicolons": "asNeeded",
"arrowParentheses": "asNeeded",
"bracketSpacing": true,
"bracketSameLine": false,
"attributePosition": "auto"
}
},
"json": {
"formatter": {
"indentSize": 2,
"lineWidth": 80,
"trailingComma": "none"
}
},
"css": {
"formatter": {
"enabled": true,
"indentSize": 2,
"lineWidth": 80
}
},
"files": {
"ignore": [
"node_modules/**",
"dist/**",
"build/**",
"coverage/**",
"*.min.js",
"*.min.css",
".next/**",
".nuxt/**",
".vite/**"
],
"ignoreUnknown": true,
"include": ["src/**/*", "tests/**/*"]
}
}
// 4. package.json Scripts Integration
{
"name": "my-biome-project",
"version": "1.0.0",
"type": "module",
"scripts": {
"lint": "biome check",
"lint:fix": "biome check --apply",
"lint:unsafe": "biome check --apply-unsafe",
"format": "biome format",
"format:write": "biome format --write",
"ci": "biome ci",
"check": "biome check --write",
"check:unsafe": "biome check --write --apply-unsafe",
"organize-imports": "biome check --write --only=source.organizeImports"
},
"devDependencies": {
"@biomejs/biome": "^1.9.4"
}
}
// 5. Example Modern JavaScript File (src/app.js)
import { Database } from './database.js';
import { AuthService } from './auth.js';
import { User } from './types.js';
import './styles.css';
class App {
#db;
#auth;
#currentUser = null;
constructor() {
this.#db = new Database();
this.#auth = new AuthService();
this.init();
}
async init() {
try {
await this.#db.connect();
this.setupEventListeners();
await this.checkAuthStatus();
console.log('✅ App initialized successfully');
} catch (error) {
console.error('❌ App initialization failed:', error);
}
}
setupEventListeners() {
const form = document.querySelector('#login-form');
form?.addEventListener('submit', async (event) => {
event.preventDefault();
await this.handleLogin(event);
});
document.querySelector('#logout-btn')?.addEventListener('click', () => {
this.handleLogout();
});
}
async handleLogin(event) {
const formData = new FormData(event.target);
const email = formData.get('email');
const password = formData.get('password');
if (!email || !password) {
this.showError('Please fill in all fields');
return;
}
try {
const user = await this.#auth.login(email, password);
this.#currentUser = user;
this.updateUI();
this.showSuccess('Login successful');
} catch (error) {
this.showError(error.message);
}
}
async handleLogout() {
try {
await this.#auth.logout();
this.#currentUser = null;
this.updateUI();
this.showSuccess('Logged out successfully');
} catch (error) {
this.showError('Logout failed:', error.message);
}
}
async checkAuthStatus() {
try {
const user = await this.#auth.getCurrentUser();
if (user) {
this.#currentUser = user;
this.updateUI();
}
} catch (error) {
console.warn('Auth status check failed:', error.message);
}
}
updateUI() {
const authSection = document.querySelector('#auth-section');
const userSection = document.querySelector('#user-section');
if (this.#currentUser) {
authSection?.classList.add('hidden');
userSection?.classList.remove('hidden');
const userName = document.querySelector('#user-name');
if (userName) {
userName.textContent = this.#currentUser.name;
}
} else {
authSection?.classList.remove('hidden');
userSection?.classList.add('hidden');
}
}
showSuccess(message) {
this.showNotification(message, 'success');
}
showError(message) {
this.showNotification(message, 'error');
}
showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 3000);
}
}
// Initialize app when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
new App();
});
export { App };
// 6. Example TypeScript File (src/types.ts)
export interface User {
id: string;
email: string;
name: string;
avatar?: string;
role: 'admin' | 'user' | 'moderator';
createdAt: Date;
updatedAt: Date;
}
export interface AuthResponse {
user: User;
token: string;
refreshToken: string;
}
export interface LoginCredentials {
email: string;
password: string;
}
export interface DatabaseConfig {
host: string;
port: number;
database: string;
username: string;
password: string;
}
export interface ApiResponse<T = unknown> {
data: T;
message: string;
success: boolean;
errors?: string[];
}
// Example generic API wrapper
export class ApiClient {
private baseUrl: string;
private apiKey?: string;
constructor(baseUrl: string, apiKey?: string) {
this.baseUrl = baseUrl;
this.apiKey = apiKey;
}
private async request<T>(
endpoint: string,
options: RequestInit = {}
): Promise<ApiResponse<T>> {
const url = `${this.baseUrl}${endpoint}`;
const headers = {
'Content-Type': 'application/json',
...(this.apiKey && { Authorization: `Bearer ${this.apiKey}` }),
...options.headers,
};
try {
const response = await fetch(url, {
...options,
headers,
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw new Error(`API request failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
async get<T>(endpoint: string): Promise<ApiResponse<T>> {
return this.request<T>(endpoint, { method: 'GET' });
}
async post<T>(endpoint: string, data?: unknown): Promise<ApiResponse<T>> {
return this.request<T>(endpoint, {
method: 'POST',
body: JSON.stringify(data),
});
}
async put<T>(endpoint: string, data?: unknown): Promise<ApiResponse<T>> {
return this.request<T>(endpoint, {
method: 'PUT',
body: JSON.stringify(data),
});
}
async delete<T>(endpoint: string): Promise<ApiResponse<T>> {
return this.request<T>(endpoint, { method: 'DELETE' });
}
}
// 7. Example Utility File (src/utils.js)
export const debounce = (callback, wait) => {
let timeoutId = null;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => callback(...args), wait);
};
};
export const throttle = (callback, limit) => {
let waiting = false;
return (...args) => {
if (!waiting) {
callback(...args);
waiting = true;
setTimeout(() => (waiting = false), limit);
}
};
};
export const formatCurrency = (amount, currency = 'USD', locale = 'en-US') => {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency,
}).format(amount);
};
export const formatDate = (date, locale = 'en-US') => {
return new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'long',
day: 'numeric',
}).format(date);
};
export const generateId = () => {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
};
export const validateEmail = (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
export const validatePassword = (password) => {
return {
minLength: password.length >= 8,
hasUpperCase: /[A-Z]/.test(password),
hasLowerCase: /[a-z]/.test(password),
hasNumbers: /\d/.test(password),
hasSpecialChar: /[!@#$%^&*(),.?":{}|<>]/.test(password),
};
};
export const copyToClipboard = async (text) => {
try {
await navigator.clipboard.writeText(text);
return true;
} catch (error) {
// Fallback for older browsers
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.opacity = '0';
document.body.appendChild(textArea);
textArea.select();
const success = document.execCommand('copy');
textArea.remove();
return success;
}
};
// 8. Example CSS File (src/styles.css)
:root {
--primary-color: #007bff;
--secondary-color: #6c757d;
--success-color: #28a745;
--danger-color: #dc3545;
--warning-color: #ffc107;
--info-color: #17a2b8;
--light-color: #f8f9fa;
--dark-color: #343a40;
--font-family-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--font-family-mono: 'SF Mono', Monaco, 'Inconsolata', 'Roboto Mono', monospace;
--border-radius: 4px;
--box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
--transition: all 0.3s ease;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font-family-sans);
line-height: 1.6;
color: var(--dark-color);
background-color: var(--light-color);
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
}
.form-group {
margin-bottom: 1rem;
}
.form-label {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
color: var(--dark-color);
}
.form-input {
width: 100%;
padding: 0.75rem;
border: 1px solid #ced4da;
border-radius: var(--border-radius);
font-size: 1rem;
transition: var(--transition);
}
.form-input:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
}
.btn {
display: inline-block;
padding: 0.75rem 1.5rem;
border: none;
border-radius: var(--border-radius);
font-size: 1rem;
font-weight: 600;
text-decoration: none;
text-align: center;
cursor: pointer;
transition: var(--transition);
}
.btn--primary {
background-color: var(--primary-color);
color: white;
}
.btn--primary:hover {
background-color: #0056b3;
}
.btn--secondary {
background-color: var(--secondary-color);
color: white;
}
.btn--danger {
background-color: var(--danger-color);
color: white;
}
.hidden {
display: none !important;
}
.notification {
position: fixed;
top: 1rem;
right: 1rem;
padding: 1rem;
border-radius: var(--border-radius);
color: white;
z-index: 1000;
animation: slideIn 0.3s ease;
}
.notification--success {
background-color: var(--success-color);
}
.notification--error {
background-color: var(--danger-color);
}
.notification--info {
background-color: var(--info-color);
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
// 9. Biome Ignore File (.biomeignore)
# Dependencies
node_modules/
# Build outputs
dist/
build/
out/
coverage/
# Environment files
.env
.env.local
.env.*.local
# IDE files
.vscode/
.idea/
*.swp
*.swo
# OS files
.DS_Store
Thumbs.db
# Logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Temporary folders
tmp/
temp/
# Package manager files
package-lock.json
yarn.lock
pnpm-lock.yaml
# Generated files
*.generated.*
*.d.ts
# Third-party
vendor/
third-party/
// 10. Example Test File (tests/app.test.js)
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { App } from '../src/app.js';
// Mock DOM APIs
global.document = {
querySelector: vi.fn(),
createElement: vi.fn(() => ({
addEventListener: vi.fn(),
classList: { add: vi.fn(), remove: vi.fn() },
textContent: '',
appendChild: vi.fn(),
remove: vi.fn()
})),
body: { appendChild: vi.fn() }
};
global.addEventListener = vi.fn();
describe('App', () => {
let app;
beforeEach(() => {
vi.clearAllMocks();
app = new App();
});
it('should initialize successfully', () => {
expect(app).toBeDefined();
});
it('should show error for empty login fields', async () => {
const mockEvent = {
preventDefault: vi.fn(),
target: {
get: vi.fn((name) => {
if (name === 'email') return '';
if (name === 'password') return '';
return null;
})
}
};
await app.handleLogin(mockEvent);
expect(mockEvent.preventDefault).toHaveBeenCalled();
// Add more assertions based on your showError implementation
});
it('should handle successful logout', async () => {
await app.handleLogout();
// Add assertions based on your logout implementation
});
});
// 11. Project Structure
my-biome-project/
├── src/
│ ├── app.js
│ ├── database.js
│ ├── auth.js
│ ├── types.ts
│ ├── utils.js
│ └── styles.css
├── tests/
│ ├── app.test.js
│ └── utils.test.js
├── public/
│ └── index.html
├── biome.json
├── .biomeignore
├── package.json
├── vitest.config.js
└── README.md
// 12. Modern HTML File (public/index.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Biome Example App</title>
<meta name="description" content="Modern JavaScript app with Biome toolchain">
<link rel="stylesheet" href="../src/styles.css">
</head>
<body>
<div class="container">
<header class="header">
<h1>Biome Example App</h1>
</header>
<main class="main">
<section id="auth-section">
<form id="login-form" class="form">
<h2>Login</h2>
<div class="form-group">
<label for="email" class="form-label">Email</label>
<input
type="email"
id="email"
name="email"
class="form-input"
placeholder="Enter your email"
required
>
</div>
<div class="form-group">
<label for="password" class="form-label">Password</label>
<input
type="password"
id="password"
name="password"
class="form-input"
placeholder="Enter your password"
required
>
</div>
<button type="submit" class="btn btn--primary">
Login
</button>
</form>
</section>
<section id="user-section" class="hidden">
<h2>Welcome back!</h2>
<p>Signed in as: <span id="user-name"></span></p>
<button id="logout-btn" class="btn btn--secondary">
Logout
</button>
</section>
</main>
</div>
<script type="module" src="../src/app.js"></script>
</body>
</html>
// 13. Biome CLI Commands Usage
/*
# Check files for linting and formatting issues
npx @biomejs/biome check
# Auto-fix linting issues (safe fixes only)
npx @biomejs/biome check --apply
# Auto-fix linting issues (including unsafe fixes)
npx @biomejs/biome check --apply-unsafe
# Format files
npx @biomejs/biome format
# Format and write changes to files
npx @biomejs/biome format --write
# Run CI checks (fails if any issues found)
npx @biomejs/biome ci
# Check specific file or directory
npx @biomejs/biome check src/
npx @biomejs/biome check src/app.js
# Format specific file
npx @biomejs/biome format src/utils.js
# Organize imports only
npx @biomejs/biome check --write --only=source.organizeImports
# Generate configuration file
npx @biomejs/biome init
# Show Biome version
npx @biomejs/biome --version
# Get help
npx @biomejs/biome --help
npx @biomejs/biome check --help
*/
export { App };
💻 Configuración Avanzada de Biome json
🟡 intermediate
⭐⭐⭐⭐
Configuración avanzada de Biome incluyendo reglas personalizadas, optimización de rendimiento, gestión de workspace y colaboración en equipo
⏱️ 45 min
🏷️ biome, advanced, configuration, performance
Prerequisites:
Biome basics, JavaScript/TypeScript, Monorepo concepts, CI/CD
// Biome Advanced Configuration Examples
// Advanced configuration patterns for complex projects and teams
// 1. Advanced biome.json Configuration
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
// Complexity rules
"complexity": {
"noExtraBooleanCast": "error",
"noMultipleSpacesInRegularExpressionLiterals": "error",
"noUselessCatch": "error",
"noWith": "error",
"noExcessiveCognitiveComplexity": {
"level": "warn",
"options": {
"max": 15
}
}
},
// Correctness rules
"correctness": {
"noUnusedVariables": "error",
"noUndeclaredVariables": "error",
"noUnreachable": "error",
"noUnreachableSuper": "error",
"noConstantCondition": "warn",
"noUnusedLabels": "error",
"noUndeclaredVariables": "error",
"noSelfAssign": "error",
"noSetterReturn": "error",
"noInvalidConstructorSuper": "error"
},
// Security rules
"security": {
"noDangerouslySetInnerHtml": "error",
"noGlobalEval": "error",
"noImpliedEval": "error"
},
// Style rules
"style": {
"noNegationElse": "error",
"useShorthandArrayType": "error",
"useTemplate": "error",
"useImportType": "error",
"useExportType": "error",
"noVar": "error",
"useConst": "error",
"useCollapsedElseIf": "error",
"noArguments": "error",
"useArrowFunction": "error",
"useNumericLiterals": "error",
"useWhile": "error",
"useYield": "error",
"useSimplifiedLogicExpression": "error",
"noCommaOperator": "error"
},
// Suspicious rules
"suspicious": {
"noArrayIndexKey": "warn",
"noAsyncPromiseExecutor": "error",
"noCatchAssign": "error",
"noClassAssign": "error",
"noCompareNegZero": "error",
"noControlCharactersInRegex": "error",
"noDebugger": "error",
"noDuplicateCase": "error",
"noDuplicateClassMembers": "error",
"noDuplicateObjectKeys": "error",
"noDuplicateParameters": "error",
"noEmptyBlockStatements": "error",
"noExplicitAny": "warn",
"noFallthroughSwitchClause": "error",
"noFunctionAssign": "error",
"noGlobalObjectCalls": "error",
"noImportAssign": "error",
"noMisleadingCharacterClass": "error",
"noPrototypeBuiltins": "error",
"noRedeclare": "error",
"noShadowRestrictedNames": "error",
"noUnsafeNegation": "error",
"useAwait": "error",
"useGetterReturn": "error",
"useValidTypeof": "error",
"noHookUseState": "error",
"noReactSpecificProps": "error"
},
// Nursery rules (experimental)
"nursery": {
"useSortedClasses": "error",
"noUndeclaredDependencies": "warn",
"noUselessRename": "error",
"useIsNan": "error",
"noImportSideEffects": "error",
"noStaticOnlyClass": "error",
"useAtIndexInsteadOfForEach": "error",
"noDuplicateElseIf": "error",
"noSelfImport": "error",
"useLiteralEnumMembers": "error",
"useLiteralKeys": "error",
"noUnnecessaryContinue": "error",
"noUselessRename": "error",
"noLabelVar": "error",
"noEmptyCharacterClassInRegex": "error",
"noInvalidConstructorSuper": "error",
"useConsistentArrayType": "error",
"useDefaultParameterLast": "error",
"useExponentiationOperator": "error",
"useFocusReturnInteractive": "error",
"useForOf": "error",
"useImportExtensions": "error",
"useNodeAssertStrict": "error",
"useNumberToFixedDigitsArgument": "error",
"useOptionalChain": "error",
"useRegexLiterals": "error",
"useShorthandAssign": "error",
"useThrowNewError": "error",
"useUndeclaredVariables": "error",
"noRestrictedGlobals": "error",
"noUnnecessaryContinue": "error",
"noMimeSniff": "error"
}
}
},
"formatter": {
"enabled": true,
"formatWithErrors": false,
"indentStyle": "space",
"indentSize": 2,
"lineWidth": 100,
"lineEnding": "lf",
"quoteStyle": "single",
"jsxQuoteStyle": "double",
"attributePosition": "auto",
"bracketSpacing": true,
"bracketSameLine": false,
"arrowParentheses": "asNeeded",
"semicolons": "asNeeded",
"trailingComma": "es5",
"quoteProperties": "preserve",
"ignore": ["src/generated/**", "**/*.min.js", "dist/**"]
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"jsxQuoteStyle": "double",
"quoteProperties": "preserve",
"trailingComma": "es5",
"semicolons": "asNeeded",
"arrowParentheses": "asNeeded",
"bracketSpacing": true,
"bracketSameLine": false,
"attributePosition": "auto"
},
"globals": [
"window",
"document",
"console",
"process",
"Buffer",
"global",
"setImmediate",
"clearImmediate"
]
},
"typescript": {
"formatter": {
"quoteStyle": "single",
"jsxQuoteStyle": "double",
"quoteProperties": "preserve",
"trailingComma": "es5",
"semicolons": "asNeeded",
"arrowParentheses": "asNeeded",
"bracketSpacing": true,
"bracketSameLine": false,
"attributePosition": "auto"
},
"globals": []
},
"json": {
"formatter": {
"enabled": true,
"indentSize": 2,
"lineWidth": 100,
"trailingComma": "none"
}
},
"css": {
"formatter": {
"enabled": true,
"indentSize": 2,
"lineWidth": 120,
"quoteStyle": "double"
},
"linter": {
"enabled": true,
"rules": {
"cssValueNoUnknown": "error",
"noDuplicateProperties": "error",
"noEmptyBlockStatements": "error"
}
}
},
"files": {
"ignore": [
"node_modules/**",
"dist/**",
"build/**",
"coverage/**",
"*.min.js",
"*.min.css",
".next/**",
".nuxt/**",
".vite/**",
".turbo/**",
"src/generated/**",
"vendor/**",
"third-party/**",
"**/*.bundle.js",
"**/*.d.ts"
],
"ignoreUnknown": true,
"include": ["src/**/*", "tests/**/*"],
"maxSize": 1000000
}
}
// 2. Monorepo Workspace Configuration
// biome.json (Root)
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"workspace": {
"enabled": true
},
"files": {
"ignore": [
"node_modules/**",
"dist/**",
"build/**",
"*.d.ts"
]
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
}
}
// packages/frontend/biome.json
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"extends": "../../biome.json",
"linter": {
"rules": {
"style": {
"useConst": "warn"
},
"suspicious": {
"noConsoleLog": "off"
}
}
},
"javascript": {
"globals": [
"window",
"document",
"console",
"localStorage",
"sessionStorage",
"fetch",
"URLSearchParams"
]
},
"css": {
"formatter": {
"enabled": true,
"indentSize": 2
}
}
}
// packages/backend/biome.json
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"extends": "../../biome.json",
"linter": {
"rules": {
"suspicious": {
"noConsoleLog": "error"
},
"security": {
"noDangerouslySetInnerHtml": "error"
}
}
},
"javascript": {
"globals": [
"process",
"Buffer",
"console",
"global",
"__dirname",
"__filename",
"module",
"exports",
"require"
]
},
"typescript": {
"globals": [
"process",
"Buffer",
"global",
"__dirname",
"__filename"
]
}
}
// 3. Environment-Specific Configurations
// biome.production.json
{
"linter": {
"rules": {
"suspicious": {
"noConsoleLog": "error",
"noDebugger": "error"
},
"correctness": {
"noUnusedVariables": "error"
}
}
},
"formatter": {
"lineWidth": 80
}
}
// biome.development.json
{
"linter": {
"rules": {
"suspicious": {
"noConsoleLog": "off",
"noDebugger": "off"
},
"correctness": {
"noUnusedVariables": "warn"
}
}
},
"formatter": {
"lineWidth": 120
}
}
// 4. Performance-Optimized Configuration
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"linter": {
"enabled": true,
"rules": {
"recommended": false,
"style": {
"useConst": "error",
"noVar": "error"
},
"correctness": {
"noUnusedVariables": "error",
"noUndeclaredVariables": "error"
}
}
},
"formatter": {
"enabled": true,
"lineWidth": 100
},
"files": {
"maxSize": 1000000,
"ignoreUnknown": true,
"ignore": [
"node_modules/**",
"dist/**",
"build/**",
"*.min.js",
"large-files/**",
"coverage/**"
]
}
}
// 5. Custom Rules Configuration
{
"linter": {
"enabled": true,
"rules": {
"suspicious": {
"noExplicitAny": {
"level": "warn",
"options": {
"fixToUnknown": false
}
}
},
"complexity": {
"noExcessiveCognitiveComplexity": {
"level": "warn",
"options": {
"max": 10,
"ignoreSimpleControlFlow": true
}
}
},
"style": {
"useNamingConvention": {
"level": "error",
"options": {
"strictCase": false
}
}
}
}
}
}
// 6. Language-Specific Overrides
{
"overrides": [
{
"include": ["**/*.test.js", "**/*.test.ts", "**/*.spec.js", "**/*.spec.ts"],
"linter": {
"rules": {
"suspicious": {
"noExplicitAny": "off"
},
"style": {
"useConst": "off"
}
}
}
},
{
"include": ["**/*.config.js", "**/*.config.ts"],
"linter": {
"rules": {
"security": {
"noGlobalEval": "off"
}
}
}
},
{
"include": ["**/stories/**/*", "**/.storybook/**/*"],
"linter": {
"rules": {
"suspicious": {
"noConsoleLog": "off"
}
}
}
},
{
"include": ["**/*.d.ts"],
"linter": {
"enabled": false
}
}
]
}
// 7. Custom Presets
// biome.presets.json
{
"strict": {
"linter": {
"rules": {
"recommended": true,
"suspicious": {
"noExplicitAny": "error"
},
"style": {
"useNamingConvention": "error"
}
}
}
},
"lenient": {
"linter": {
"rules": {
"recommended": true,
"suspicious": {
"noExplicitAny": "off"
},
"style": {
"useNamingConvention": "off"
}
}
}
},
"react": {
"linter": {
"rules": {
"recommended": true,
"suspicious": {
"noReactSpecificProps": "error",
"noHookUseState": "error"
}
}
},
"javascript": {
"globals": ["React", "JSX"]
}
}
}
// 8. CI/CD Integration Configuration
// .github/workflows/biome.yml
name: Biome CI
on: [push, pull_request]
jobs:
biome:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20]
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run Biome check
run: npx @biomejs/biome ci
- name: Check formatting
run: npx @biomejs/biome format --write
biome-performance:
name: Biome Performance
runs-on: ubuntu-latest
needs: biome
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Measure Biome performance
run: |
echo "📊 Measuring Biome performance..."
time npx @biomejs/biome check src/
time npx @biomejs/biome format src/
// 9. Pre-commit Hooks
// package.json
{
"scripts": {
"lint": "biome check",
"lint:fix": "biome check --apply",
"format": "biome format",
"format:write": "biome format --write",
"ci": "biome ci",
"prepare": "husky install"
},
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"husky": "^8.0.3",
"lint-staged": "^13.2.0"
},
"lint-staged": {
"*.{js,jsx,ts,tsx,json,css}": [
"biome check --apply",
"biome format --write"
]
}
}
// 10. Advanced Project Configuration Scripts
// scripts/setup-biome.js
import { execSync } from 'child_process';
import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
class BiomeSetup {
constructor(projectPath = process.cwd()) {
this.projectPath = projectPath;
}
async setup() {
console.log('🚀 Setting up Biome for the project...');
try {
await this.installBiome();
await this.initializeConfig();
await this.setupScripts();
await this.setupGitHooks();
await this.createBiomeignore();
console.log('✅ Biome setup completed successfully!');
} catch (error) {
console.error('❌ Biome setup failed:', error.message);
process.exit(1);
}
}
async installBiome() {
console.log('📦 Installing Biome...');
execSync('npm install --save-dev @biomejs/biome', { stdio: 'inherit' });
}
async initializeConfig() {
console.log('⚙️ Initializing Biome configuration...');
execSync('npx @biomejs/biome init', { stdio: 'inherit' });
}
async setupScripts() {
console.log('📝 Setting up npm scripts...');
const packageJsonPath = join(this.projectPath, 'package.json');
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
packageJson.scripts = {
...packageJson.scripts,
lint: 'biome check',
'lint:fix': 'biome check --apply',
'lint:unsafe': 'biome check --apply-unsafe',
format: 'biome format',
'format:write': 'biome format --write',
ci: 'biome ci',
'organize-imports': 'biome check --write --only=source.organizeImports'
};
writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
}
async setupGitHooks() {
console.log('🪝 Setting up Git hooks...');
try {
execSync('npm install --save-dev husky lint-staged', { stdio: 'inherit' });
execSync('npm pkg set scripts.prepare="husky install"', { stdio: 'inherit' });
execSync('echo "npx husky install" | npm run prepare', { stdio: 'inherit' });
const huskyConfig = {
'.husky/pre-commit': 'npx lint-staged'
};
for (const [hook, command] of Object.entries(huskyConfig)) {
const hookPath = join(this.projectPath, hook);
execSync(`echo '${command}' > ${hookPath}`, { stdio: 'inherit' });
execSync(`chmod +x ${hookPath}`, { stdio: 'inherit' });
}
} catch (error) {
console.warn('⚠️ Git hooks setup failed:', error.message);
}
}
async createBiomeignore() {
console.log('🚫 Creating .biomeignore file...');
const biomeignoreContent = `
# Dependencies
node_modules/
# Build outputs
dist/
build/
out/
coverage/
# Environment files
.env
.env.local
.env.*.local
# IDE files
.vscode/
.idea/
# OS files
.DS_Store
Thumbs.db
# Logs
*.log
# Generated files
*.d.ts
*.generated.*
`.trim();
const biomeignorePath = join(this.projectPath, '.biomeignore');
writeFileSync(biomeignorePath, biomeignoreContent);
}
}
if (import.meta.url === `file://${process.argv[1]}`) {
const setup = new BiomeSetup();
setup.setup();
}
export { BiomeSetup };
// 11. Performance Monitoring
// scripts/biome-performance.js
import { performance } from 'perf_hooks';
import { execSync } from 'child_process';
class BiomePerformanceMonitor {
constructor() {
this.files = [];
}
measurePerformance() {
console.log('📊 Measuring Biome performance...');
// Discover files
this.discoverFiles();
// Measure formatting performance
const formatStart = performance.now();
this.runFormat();
const formatEnd = performance.now();
// Measure linting performance
const lintStart = performance.now();
this.runLint();
const lintEnd = performance.now();
const formatTime = formatEnd - formatStart;
const lintTime = lintEnd - lintStart;
this.displayResults({
formatTime,
lintTime,
totalTime: formatTime + lintTime,
fileCount: this.files.length
});
return {
formatTime,
lintTime,
totalTime: formatTime + lintTime,
fileCount: this.files.length
};
}
discoverFiles() {
try {
const result = execSync('find src -name "*.js" -o -name "*.ts" -o -name "*.jsx" -o -name "*.tsx"', { encoding: 'utf8' });
this.files = result.trim().split('\n').filter(Boolean);
} catch (error) {
console.warn('Could not discover files:', error.message);
}
}
runFormat() {
if (this.files.length > 0) {
execSync(`npx @biomejs/biome format ${this.files.join(' ')}`, { stdio: 'pipe' });
}
}
runLint() {
if (this.files.length > 0) {
execSync(`npx @biomejs/biome check ${this.files.join(' ')}`, { stdio: 'pipe' });
}
}
displayResults(results) {
console.log('\n📈 Performance Results:');
console.log(` Files processed: ${results.fileCount}`);
console.log(` Formatting: ${results.formatTime.toFixed(2)}ms`);
console.log(` Linting: ${results.lintTime.toFixed(2)}ms`);
console.log(` Total: ${results.totalTime.toFixed(2)}ms`);
console.log(` Avg per file: ${(results.totalTime / results.fileCount).toFixed(2)}ms`);
}
}
if (import.meta.url === `file://${process.argv[1]}`) {
const monitor = new BiomePerformanceMonitor();
monitor.measurePerformance();
}
export { BiomePerformanceMonitor };
💻 Guía de Migración de Biome javascript
🔴 complex
⭐⭐⭐⭐⭐
Guía completa de migración de ESLint/Prettier/Rome a Biome con scripts de automatización y mejores prácticas
⏱️ 90 min
🏷️ biome, migration, automation, advanced
Prerequisites:
Advanced JavaScript/TypeScript, CLI usage, Build tools, Package managers
// Biome Migration Guide
// Complete migration from ESLint/Prettier/Rome to Biome
// 1. Migration Assessment Script
// scripts/assess-migration.js
import { readFileSync, existsSync, writeFileSync } from 'fs';
import { execSync } from 'child_process';
import { join } from 'path';
class MigrationAssessment {
constructor(projectPath = process.cwd()) {
this.projectPath = projectPath;
}
assess() {
console.log('🔍 Assessing project for Biome migration...');
const assessment = {
hasEslint: this.checkESLint(),
hasPrettier: this.checkPrettier(),
hasRome: this.checkRome(),
hasTypeScript: this.checkTypeScript(),
framework: this.detectFramework(),
dependencies: this.analyzeDependencies(),
complexity: this.assessComplexity(),
migrationComplexity: 'moderate',
recommendations: []
};
this.generateRecommendations(assessment);
this.displayReport(assessment);
return assessment;
}
checkESLint() {
const files = [
'.eslintrc.js',
'.eslintrc.json',
'.eslintrc.yml',
'.eslintrc.yaml',
'eslint.config.js'
];
return files.some(file => existsSync(join(this.projectPath, file)));
}
checkPrettier() {
const files = [
'.prettierrc',
'.prettierrc.json',
'.prettierrc.yml',
'.prettierrc.yaml',
'prettier.config.js'
];
return files.some(file => existsSync(join(this.projectPath, file)));
}
checkRome() {
const files = [
'rome.json',
'.romerc'
];
return files.some(file => existsSync(join(this.projectPath, file)));
}
checkTypeScript() {
return existsSync(join(this.projectPath, 'tsconfig.json'));
}
detectFramework() {
const packageJson = this.readPackageJson();
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
if (deps.next) return 'nextjs';
if (deps.react) return 'react';
if (deps.vue) return 'vue';
if (deps['@angular/core']) return 'angular';
if (deps.svelte) return 'svelte';
return 'vanilla';
}
analyzeDependencies() {
const packageJson = this.readPackageJson();
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
const lintingDeps = Object.keys(deps).filter(dep =>
dep.includes('eslint') ||
dep.includes('prettier') ||
dep.includes('rome')
);
return {
lintingDeps,
totalDeps: Object.keys(deps).length,
hasLintingDeps: lintingDeps.length > 0
};
}
assessComplexity() {
let score = 0;
// Check for configuration files
if (this.checkESLint()) score += 3;
if (this.checkPrettier()) score += 2;
if (this.checkRome()) score += 1;
// Check TypeScript
if (this.checkTypeScript()) score += 2;
// Check dependencies
const { lintingDeps } = this.analyzeDependencies();
if (lintingDeps.length > 5) score += 2;
if (lintingDeps.length > 10) score += 1;
if (score <= 3) return 'simple';
if (score <= 7) return 'medium';
return 'complex';
}
generateRecommendations(assessment) {
const recommendations = [];
if (assessment.hasRome) {
recommendations.push('✅ You already use Rome! Migration to Biome will be seamless');
recommendations.push('🔄 Simply replace @romejs/biome with @biomejs/biome');
} else {
recommendations.push('🚀 Biome provides better performance and unified tooling');
}
if (assessment.hasESLint && assessment.hasPrettier) {
recommendations.push('🎯 Biome can replace both ESLint and Prettier with a single tool');
recommendations.push('⚡ Expect 10-100x faster formatting and linting');
}
if (assessment.framework === 'nextjs') {
recommendations.push('📦 Next.js has built-in Biome support in recent versions');
}
if (assessment.complexity === 'complex') {
recommendations.push('🔧 Consider migrating in stages for complex projects');
recommendations.push('📋 Document your current linting rules before migration');
}
recommendations.push('🧪 Test thoroughly after migration');
recommendations.push('👥 Involve your team in the migration process');
assessment.recommendations = recommendations;
}
displayReport(assessment) {
console.log('\n📊 Migration Assessment Report\n');
console.log(`🔧 ESLint: ${assessment.hasEslint ? '✅' : '❌'}`);
console.log(`🎨 Prettier: ${assessment.hasPrettier ? '✅' : '❌'}`);
console.log(`🦀 Rome: ${assessment.hasRome ? '✅' : '❌'}`);
console.log(`📘 TypeScript: ${assessment.hasTypeScript ? '✅' : '❌'}`);
console.log(`🎯 Framework: ${assessment.framework}`);
console.log(`📊 Complexity: ${assessment.complexity}`);
console.log(`📦 Linting dependencies: ${assessment.dependencies.lintingDeps.length}\n`);
console.log('💡 Recommendations:');
assessment.recommendations.forEach(rec => {
console.log(` ${rec}`);
});
}
readPackageJson() {
const packageJsonPath = join(this.projectPath, 'package.json');
return JSON.parse(readFileSync(packageJsonPath, 'utf8'));
}
}
// 2. ESLint to Biome Migration
// scripts/migrate-eslint.js
class ESLintToBiomeMigrator {
constructor(projectPath = process.cwd()) {
this.projectPath = projectPath;
}
migrate() {
console.log('🔄 Migrating from ESLint to Biome...');
const eslintConfig = this.loadESLintConfig();
const prettierConfig = this.loadPrettierConfig();
const biomeConfig = this.convertToBiomeConfig(eslintConfig, prettierConfig);
this.writeBiomeConfig(biomeConfig);
this.updatePackageJson();
this.removeOldConfig();
this.installBiome();
console.log('✅ ESLint to Biome migration completed!');
}
loadESLintConfig() {
const possibleConfigs = [
'eslint.config.js',
'.eslintrc.js',
'.eslintrc.json',
'.eslintrc.yml',
'.eslintrc.yaml'
];
for (const configFile of possibleConfigs) {
const configPath = join(this.projectPath, configFile);
if (existsSync(configPath)) {
try {
return require(configPath);
} catch (error) {
console.warn(`Could not load ${configFile}:`, error.message);
}
}
}
return null;
}
loadPrettierConfig() {
const possibleConfigs = [
'prettier.config.js',
'.prettierrc',
'.prettierrc.json',
'.prettierrc.yml',
'.prettierrc.yaml'
];
for (const configFile of possibleConfigs) {
const configPath = join(this.projectPath, configFile);
if (existsSync(configPath)) {
try {
if (configFile.endsWith('.js')) {
return require(configPath);
} else {
return JSON.parse(readFileSync(configPath, 'utf8'));
}
} catch (error) {
console.warn(`Could not load ${configFile}:`, error.message);
}
}
}
return null;
}
convertToBiomeConfig(eslintConfig, prettierConfig) {
const biomeConfig = {
$schema: "https://biomejs.dev/schemas/1.9.4/schema.json",
linter: {
enabled: true,
rules: {
recommended: true
}
},
formatter: {
enabled: true,
formatWithErrors: false
},
javascript: {
globals: []
},
files: {
ignore: [
"node_modules/**",
"dist/**",
"build/**"
]
}
};
// Convert ESLint rules to Biome rules
if (eslintConfig && eslintConfig.rules) {
biomeConfig.linter.rules = this.convertRules(eslintConfig.rules);
}
// Convert Prettier config to Biome formatter config
if (prettierConfig) {
biomeConfig.formatter = {
...biomeConfig.formatter,
...this.convertPrettierConfig(prettierConfig)
};
}
return biomeConfig;
}
convertRules(eslintRules) {
const biomeRules = { recommended: true };
// Rule mapping from ESLint to Biome
const ruleMap = {
// Style rules
'no-var': { style: { noVar: 'error' } },
'prefer-const': { style: { useConst: 'error' } },
'prefer-arrow-callback': { style: { useArrowFunction: 'error' } },
'prefer-template': { style: { useTemplate: 'error' } },
'no-console': { suspicious: { noConsoleLog: 'error' } },
'no-debugger': { suspicious: { noDebugger: 'error' } },
'no-unused-vars': { correctness: { noUnusedVariables: 'error' } },
'no-undef': { correctness: { noUndeclaredVariables: 'error' } },
'no-duplicate-imports': { style: { noDuplicateImports: 'error' } }
};
for (const [eslintRule, config] of Object.entries(eslintRules)) {
if (config === 'error' || config === 2) {
if (ruleMap[eslintRule]) {
this.mergeRule(biomeRules, ruleMap[eslintRule]);
}
} else if (config === 'warn' || config === 1) {
if (ruleMap[eslintRule]) {
this.mergeRule(biomeRules, ruleMap[eslintRule], 'warn');
}
}
}
return biomeRules;
}
mergeRule(biomeRules, rule, level = 'error') {
for (const [category, rules] of Object.entries(rule)) {
if (!biomeRules[category]) {
biomeRules[category] = {};
}
Object.assign(biomeRules[category], rules);
}
}
convertPrettierConfig(prettierConfig) {
const formatterConfig = {};
if (prettierConfig.tabWidth !== undefined) {
formatterConfig.indentSize = prettierConfig.tabWidth;
}
if (prettierConfig.useTabs !== undefined) {
formatterConfig.indentStyle = prettierConfig.useTabs ? 'tab' : 'space';
}
if (prettierConfig.printWidth !== undefined) {
formatterConfig.lineWidth = prettierConfig.printWidth;
}
if (prettierConfig.singleQuote !== undefined) {
formatterConfig.quoteStyle = prettierConfig.singleQuote ? 'single' : 'double';
}
if (prettierConfig.trailingComma !== undefined) {
formatterConfig.trailingComma = prettierConfig.trailingComma;
}
if (prettierConfig.semi !== undefined) {
formatterConfig.semicolons = prettierConfig.semi ? 'always' : 'asNeeded';
}
return formatterConfig;
}
writeBiomeConfig(biomeConfig) {
const configPath = join(this.projectPath, 'biome.json');
writeFileSync(configPath, JSON.stringify(biomeConfig, null, 2));
console.log('⚙️ Created biome.json');
}
updatePackageJson() {
const packageJsonPath = join(this.projectPath, 'package.json');
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
// Remove ESLint and Prettier dependencies
const depsToRemove = [
'eslint',
'@typescript-eslint/eslint-plugin',
'@typescript-eslint/parser',
'eslint-config-prettier',
'eslint-plugin-react',
'eslint-plugin-react-hooks',
'prettier'
];
depsToRemove.forEach(dep => {
delete packageJson.devDependencies?.[dep];
delete packageJson.dependencies?.[dep];
});
// Add Biome dependency
packageJson.devDependencies = {
...packageJson.devDependencies,
'@biomejs/biome': '^1.9.4'
};
// Update scripts
packageJson.scripts = {
...packageJson.scripts,
lint: 'biome check',
'lint:fix': 'biome check --apply',
format: 'biome format',
'format:write': 'biome format --write',
ci: 'biome ci'
};
// Remove old scripts
const oldScripts = ['eslint', 'prettier', 'eslint:fix', 'prettier:write'];
oldScripts.forEach(script => {
delete packageJson.scripts?.[script];
});
writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
console.log('📦 Updated package.json');
}
removeOldConfig() {
const oldConfigs = [
'.eslintrc.js',
'.eslintrc.json',
'.eslintrc.yml',
'.eslintrc.yaml',
'eslint.config.js',
'.prettierrc',
'.prettierrc.json',
'.prettierrc.yml',
'.prettierrc.yaml',
'prettier.config.js'
];
oldConfigs.forEach(config => {
const configPath = join(this.projectPath, config);
if (existsSync(configPath)) {
execSync(`rm "${configPath}"`, { stdio: 'pipe' });
console.log(`🗑️ Removed ${config}`);
}
});
}
installBiome() {
console.log('📦 Installing Biome...');
execSync('npm install', { stdio: 'inherit' });
}
}
// 3. Rome to Biome Migration
// scripts/migrate-rome.js
class RomeToBiomeMigrator {
constructor(projectPath = process.cwd()) {
this.projectPath = projectPath;
}
migrate() {
console.log('🔄 Migrating from Rome to Biome...');
const romeConfig = this.loadRomeConfig();
const biomeConfig = this.convertRomeToBiome(romeConfig);
this.writeBiomeConfig(biomeConfig);
this.updatePackageJson();
this.removeRomeConfig();
this.installBiome();
console.log('✅ Rome to Biome migration completed!');
}
loadRomeConfig() {
const configPath = join(this.projectPath, 'rome.json');
if (!existsSync(configPath)) {
throw new Error('rome.json not found');
}
return JSON.parse(readFileSync(configPath, 'utf8'));
}
convertRomeToBiome(romeConfig) {
// Most Rome configurations are compatible with Biome
// Just need to update the schema URL and any breaking changes
const biomeConfig = {
...romeConfig,
$schema: "https://biomejs.dev/schemas/1.9.4/schema.json"
};
// Update any deprecated options
if (biomeConfig.javascript) {
biomeConfig.javascript = this.updateJavaScriptConfig(biomeConfig.javascript);
}
return biomeConfig;
}
updateJavaScriptConfig(jsConfig) {
// Handle any breaking changes between Rome and Biome
return {
...jsConfig,
// Update any deprecated options here
};
}
writeBiomeConfig(biomeConfig) {
const configPath = join(this.projectPath, 'biome.json');
writeFileSync(configPath, JSON.stringify(biomeConfig, null, 2));
console.log('⚙️ Created biome.json');
}
updatePackageJson() {
const packageJsonPath = join(this.projectPath, 'package.json');
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
// Remove Rome dependency
delete packageJson.devDependencies?.['rome'];
delete packageJson.dependencies?.['rome'];
// Add Biome dependency
packageJson.devDependencies = {
...packageJson.devDependencies,
'@biomejs/biome': '^1.9.4'
};
// Update scripts to use Biome CLI
const scriptMap = {
'rome check': 'biome check',
'rome check --apply': 'biome check --apply',
'rome format': 'biome format',
'rome format --write': 'biome format --write',
'rome ci': 'biome ci'
};
for (const [oldScript, newScript] of Object.entries(scriptMap)) {
if (packageJson.scripts?.[oldScript]) {
packageJson.scripts[newScript] = packageJson.scripts[oldScript];
delete packageJson.scripts[oldScript];
}
}
writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
console.log('📦 Updated package.json');
}
removeRomeConfig() {
const configPath = join(this.projectPath, 'rome.json');
if (existsSync(configPath)) {
execSync(`rm "${configPath}"`, { stdio: 'pipe' });
console.log('🗑️ Removed rome.json');
}
}
installBiome() {
console.log('📦 Installing Biome...');
execSync('npm install', { stdio: 'inherit' });
}
}
// 4. Migration Verification
// scripts/verify-migration.js
class MigrationVerifier {
constructor(projectPath = process.cwd()) {
this.projectPath = projectPath;
}
async verify() {
console.log('🔍 Verifying migration...');
const checks = [
this.checkBiomeConfig(),
this.checkPackageJson(),
this.checkBiomeFunctionality(),
this.checkCodeQuality()
];
const results = await Promise.allSettled(checks);
const failures = results.filter(r => r.status === 'rejected');
if (failures.length === 0) {
console.log('✅ All verification checks passed!');
return true;
} else {
console.log(`❌ ${failures.length} verification checks failed:`);
failures.forEach((failure, index) => {
console.log(` ${index + 1}. ${failure.reason}`);
});
return false;
}
}
async checkBiomeConfig() {
const configPath = join(this.projectPath, 'biome.json');
if (!existsSync(configPath)) {
throw new Error('biome.json not found');
}
const config = JSON.parse(readFileSync(configPath, 'utf8'));
if (!config.linter && !config.formatter) {
throw new Error('Invalid biome.json configuration');
}
}
async checkPackageJson() {
const packageJsonPath = join(this.projectPath, 'package.json');
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
if (!packageJson.devDependencies?.['@biomejs/biome']) {
throw new Error('@biomejs/biome not found in dependencies');
}
// Check if old dependencies are removed
const oldDeps = ['eslint', 'prettier', 'rome'];
const hasOldDeps = oldDeps.some(dep =>
packageJson.devDependencies?.[dep] || packageJson.dependencies?.[dep]
);
if (hasOldDeps) {
console.warn('⚠️ Old linting dependencies still present');
}
}
async checkBiomeFunctionality() {
try {
execSync('npx @biomejs/biome --version', { stdio: 'pipe' });
} catch (error) {
throw new Error('Biome CLI not working');
}
try {
execSync('npx @biomejs/biome check --write', { stdio: 'pipe' });
execSync('npx @biomejs/biome format --write', { stdio: 'pipe' });
} catch (error) {
throw new Error('Biome check/format failed');
}
}
async checkCodeQuality() {
// Run CI to check for issues
try {
execSync('npx @biomejs/biome ci', { stdio: 'pipe' });
} catch (error) {
throw new Error('Biome CI checks failed');
}
}
}
// 5. Usage Examples
// CLI execution
if (import.meta.url === `file://${process.argv[1]}`) {
const args = process.argv.slice(2);
const command = args[0];
switch (command) {
case 'assess':
const assessor = new MigrationAssessment();
assessor.assess();
break;
case 'eslint':
const eslintMigrator = new ESLintToBiomeMigrator();
eslintMigrator.migrate();
break;
case 'rome':
const romeMigrator = new RomeToBiomeMigrator();
romeMigrator.migrate();
break;
case 'verify':
const verifier = new MigrationVerifier();
verifier.verify().then(success => {
process.exit(success ? 0 : 1);
});
break;
default:
console.log(`
Usage: npm run migrate [command]
Commands:
assess - Assess project migration readiness
eslint - Migrate from ESLint/Prettier to Biome
rome - Migrate from Rome to Biome
verify - Verify migration was successful
`);
}
}
export { MigrationAssessment, ESLintToBiomeMigrator, RomeToBiomeMigrator, MigrationVerifier };