eslint-worker-pool
v0.1.4
Published
High-performance ESLint worker pool for programmatic parallel linting in Node.js applications
Maintainers
Readme
High-performance ESLint worker pool for programmatic parallel linting
Features
- 👷 Put Workers to work: Run ESLint in worker threads to keep main thread responsive
- ⚡ Parallel Processing: Process multiple files simultaneously across worker threads
- ⚖️ Automatic Load Balancing: Tasks distributed across available workers
- 🧹 Explicit Resource Management: Modern
using/await usingsyntax for automatic cleanup
Why ESLint Worker Pool?
When running ESLint programmatically, it blocks the main thread and processes files sequentially, causing performance bottlenecks and freezing applications during linting. This package moves ESLint to dedicated worker threads, enabling parallel processing across multiple CPU cores while keeping your main thread responsive.
This package itself was built for Nuxt Audit which needed to run potentially thousands of HTML files in parallel without blocking the main thread.
Installation
# npm
npm install eslint-worker-pool
# yarn
yarn add eslint-worker-pool
# pnpm
pnpm add eslint-worker-poolQuick Start
import { createESLintWorkerPool } from 'eslint-worker-pool'
// Using modern Explicit Resource Management for automatic cleanup
await using pool = createESLintWorkerPool({
cache: true,
fix: false,
overrideConfig: {
rules: {
'no-console': 'warn',
'semi': ['error', 'never']
}
}
})
// Lint multiple files in parallel
const results = await pool.lintFilesParallel([
'src/file1.js',
'src/file2.js',
'src/file3.js'
])
console.log('Linting complete:', results)
// Pool automatically terminated when scope ends - no manual cleanup needed!API
createESLintWorkerPool(options?, config?)
Creates a worker pool for parallel ESLint processing.
import { createESLintWorkerPool } from 'eslint-worker-pool'
await using pool = createESLintWorkerPool(
// ESLint options
{
cache: true,
fix: false,
overrideConfig: {
rules: { 'no-console': 'error' }
}
},
// Pool configuration
{
poolSize: 4, // Number of workers (default: CPU count)
timeouts: { enableTimeouts: false }
}
)
// Pool automatically cleaned up when scope endsPool Methods
lintFiles(files, options?)
Lint files using the worker pool.
// Single file
const result = await pool.lintFiles('src/app.js')
// Multiple files (processed by one worker)
const results1 = await pool.lintFiles(['src/a.js', 'src/b.js'])
// With custom options
const results2 = await pool.lintFiles('src/app.js', { fix: true })lintText(text, filePath, options?)
Lint text content directly.
const results = await pool.lintText(
'console.log("hello")',
'virtual.js'
)lintFilesParallel(files, options?)
Process multiple files in parallel across all workers.
const results = await pool.lintFilesParallel([
'src/file1.js',
'src/file2.js',
'src/file3.js',
'src/file4.js'
])
// Each file processed by different worker when possiblegetStats()
Get pool statistics.
const stats = pool.getStats()
console.log({
poolSize: stats.poolSize,
activeWorkers: stats.activeWorkers,
queuedTasks: stats.queuedTasks,
totalWorkers: stats.totalWorkers,
terminated: stats.terminated
})drain()
Wait for all pending tasks to complete.
await pool.drain() // Wait for queue to emptyterminate()
Shut down all workers. Always call this when done.
await pool.terminate()Real-World Examples
Express.js API Endpoint
import { createESLintWorkerPool } from 'eslint-worker-pool'
import express from 'express'
const app = express()
// Create pool that will be automatically cleaned up
await using pool = createESLintWorkerPool({ cache: true })
app.post('/lint', async (req, res) => {
try {
const { code, filename } = req.body
const results = await pool.lintText(code, filename)
res.json({ success: true, results })
}
catch (error) {
res.status(500).json({ success: false, error: error.message })
}
})
// Pool automatically terminated when application shuts downBuild Tool Integration
import { createESLintWorkerPool } from 'eslint-worker-pool'
import { glob } from 'glob'
async function lintProject() {
const files = await glob('src/**/*.{js,ts}')
await using pool = createESLintWorkerPool({
cache: true,
fix: process.env.NODE_ENV === 'development'
})
console.log(`Linting ${files.length} files...`)
const results = await pool.lintFilesParallel(files)
const errors = results.flat().filter(r =>
r.messages.some(m => m.severity === 2)
)
if (errors.length > 0) {
console.error(`Found ${errors.length} files with errors`)
process.exit(1)
}
console.log('✅ All files passed linting')
// Pool automatically terminated when function ends
}
lintProject().catch(console.error)File Watcher
import { watch } from 'chokidar'
import { createESLintWorkerPool } from 'eslint-worker-pool'
// Global pool for long-running watcher
await using pool = createESLintWorkerPool({ cache: true, fix: true })
watch('src/**/*.js').on('change', async (filePath) => {
try {
console.log(`Linting ${filePath}...`)
const results = await pool.lintFiles(filePath)
const hasErrors = results[0].messages.some(m => m.severity === 2)
if (hasErrors) {
console.error(`❌ ${filePath} has errors`)
}
else {
console.log(`✅ ${filePath} is clean`)
}
}
catch (error) {
console.error(`Error linting ${filePath}:`, error.message)
}
})
// Pool automatically terminated when process endsError Handling
The worker pool includes comprehensive error types for better debugging:
import {
createESLintWorkerPool,
PoolTerminatedError,
ValidationError,
WorkerTimeoutError
} from 'eslint-worker-pool'
await using pool = createESLintWorkerPool()
try {
await pool.lintFiles('../invalid-path')
}
catch (error) {
if (error instanceof ValidationError) {
console.error('Invalid input:', error.message)
}
else if (error instanceof PoolTerminatedError) {
console.error('Pool was terminated')
}
else if (error instanceof WorkerTimeoutError) {
console.error('Worker timed out')
}
}
// Pool automatically cleaned up even if errors occurResource Management
This package supports modern Explicit Resource Management (using/await using) for automatic cleanup, eliminating the need for manual terminate() calls.
✅ Recommended: Using await using (Async)
For most use cases, use await using for automatic async cleanup:
await using pool = createESLintWorkerPool()
// Pool automatically terminated when scope endsAlternative: Using using (Sync)
For synchronous scopes, use using (though less ideal for worker cleanup):
using pool = createESLintWorkerPool()
// Pool cleanup triggered synchronously when scope endsLegacy: Manual Cleanup
Still supported but not recommended:
const pool = createESLintWorkerPool()
try {
// Use pool...
}
finally {
await pool.terminate() // Manual cleanup required
}License
MIT
