Skip to content

stackvault/eslint-config-node

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@stackvault/eslint-config-node

Production-ready Node.js ESLint configuration with TypeScript, security rules, and performance optimizations. Built on top of @stackvault/eslint-config-typescript with Node.js-specific enhancements for backend development.

Features

  • 🛡️ Security First - Built-in security scanning with eslint-plugin-security
  • 🚀 Node.js 20+ Optimized - Leverages modern Node.js APIs and patterns
  • 📦 TypeScript Foundation - Inherits all strict type checking from base config
  • Performance Rules - Enforces async patterns and non-blocking operations
  • 🔒 Promise Safety - Comprehensive promise and async/await validation
  • 🎯 Modern Patterns - Prefers native Node.js APIs and ES2021+ features

Installation

npm install --save-dev @stackvault/eslint-config-node eslint typescript

or with Yarn:

yarn add --dev @stackvault/eslint-config-node eslint typescript

Requirements

  • Node.js >=20.0.0 (LTS)
  • ESLint >=9.22.0
  • TypeScript >=5.5.0

Usage

Create an eslint.config.js file in your project root:

import nodeConfig from '@stackvault/eslint-config-node'; export default [ ...nodeConfig, // Your custom rules here ];

With Custom Rules

import nodeConfig from '@stackvault/eslint-config-node'; export default [ ...nodeConfig, { rules: { // Override for your specific needs 'n/no-process-env': 'off', // If you need direct process.env access 'security/detect-object-injection': 'off', // If too many false positives } } ];

For Microservices

import nodeConfig from '@stackvault/eslint-config-node'; export default [ ...nodeConfig, { rules: { 'n/no-process-exit': 'off', // Allow process.exit for containers 'n/no-sync': 'off', // Allow sync operations at startup } } ];

What's Included

Inherited from TypeScript Config

This configuration extends @stackvault/eslint-config-typescript, providing:

  • Strict type checking with type-aware rules
  • Built-in formatting via @stylistic/eslint-plugin (no Prettier needed)
  • Import sorting and organization
  • Consistent code patterns and naming conventions

Security Rules

Comprehensive security scanning to prevent common vulnerabilities:

  • Path traversal protection - Detects non-literal fs operations
  • Command injection prevention - Flags dangerous child_process usage
  • RegExp DoS protection - Identifies unsafe regular expressions
  • Timing attack warnings - Alerts on potential timing vulnerabilities
  • Buffer safety - Ensures secure buffer operations

Node.js Best Practices

Modern Node.js patterns enforced via eslint-plugin-n:

  • Node: protocol imports - Requires node:fs instead of fs
  • Promise-based APIs - Prefers fs.promises over callbacks
  • Version compatibility - Ensures Node.js 20+ API usage
  • Global preferences - Enforces global Buffer, process, console
  • No blocking operations - Prevents synchronous I/O in async contexts

Promise & Async Handling

Enhanced async operation rules via eslint-plugin-promise:

  • Proper error handling - Enforces catch or return for promises
  • Async/await patterns - Prefers modern async over callbacks
  • Promise chain safety - Prevents common promise anti-patterns
  • No floating promises - Ensures all promises are handled

Modern JavaScript Patterns

Selected Unicorn rules for cleaner code:

  • Modern APIs - .at(), .replaceAll(), structured clone
  • Top-level await - For ES modules
  • Prefer for...of - Over forEach for better performance
  • Optional catch binding - Cleaner error handling
  • Native fetch - Uses Node.js built-in fetch

Key Rules Explained

Security Patterns

// ❌ Error: detect-non-literal-fs-filename const file = userInput; fs.readFile(file, callback); // ✅ Correct: Validate and sanitize paths import path from 'node:path'; const safePath = path.join(SAFE_DIR, path.basename(userInput)); fs.readFile(safePath, callback);

Node.js Protocol Imports

// ❌ Error: prefer-node-protocol import fs from 'fs'; import { readFile } from 'fs/promises'; // ✅ Correct: Use node: protocol import fs from 'node:fs'; import { readFile } from 'node:fs/promises';

Async File Operations

// ❌ Error: no-sync const data = fs.readFileSync('config.json'); // ✅ Correct: Use async operations const data = await fs.promises.readFile('config.json');

Promise Handling

// ❌ Error: no-floating-promises async function processData() { saveToDatabase(data); // Floating promise } // ✅ Correct: Handle the promise async function processData() { await saveToDatabase(data); // or void saveToDatabase(data); // Explicitly ignore }

Special File Handling

Configuration Files

Relaxed rules for *.config.js and *.config.ts:

  • Sync operations allowed
  • Environment variables permitted
  • Dynamic requires allowed

Test Files

Relaxed security and type checking for *.spec.ts and *.test.ts:

  • Unpublished imports allowed (for test libraries)
  • Object injection warnings disabled
  • Any types permitted for mocking

Migration Scripts

Special rules for scripts/ and migrations/ directories:

  • Console logging allowed
  • Process.exit permitted
  • File system warnings reduced

VSCode Integration

Add to .vscode/settings.json:

{ "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, "eslint.experimental.useFlatConfig": true, "eslint.validate": [ "javascript", "typescript" ] }

Package.json Scripts

{ "scripts": { "lint": "eslint .", "lint:fix": "eslint . --fix", "lint:ci": "eslint . --max-warnings 0", "lint:security": "eslint . --rule 'security/*: error'", "type-check": "tsc --noEmit" } }

Environment Variables

For projects using environment variables:

// Create a typed env.ts file const env = { PORT: process.env.PORT || '3000', NODE_ENV: process.env.NODE_ENV || 'development', DATABASE_URL: process.env.DATABASE_URL!, } as const; export default env;

This pattern provides type safety while satisfying linter rules.

Performance Tips

  • Enable ESLint cache: eslint . --cache
  • Use --max-warnings 0 in CI/CD pipelines
  • Consider disabling expensive rules in development:
    const isDev = process.env.NODE_ENV === 'development'; export default [ ...nodeConfig, { rules: { 'security/detect-object-injection': isDev ? 'off' : 'warn', } } ];

Common Patterns

Error Handling

// Recommended error class class AppError extends Error { constructor( message: string, public statusCode: number = 500, public isOperational = true ) { super(message); Error.captureStackTrace(this, this.constructor); } }

Async Middleware

// Express async handler wrapper const asyncHandler = (fn: RequestHandler): RequestHandler => { return (req, res, next) => { Promise.resolve(fn(req, res, next)).catch(next); }; };

Migration from ESLint 8

  1. Remove .eslintrc.* files
  2. Create eslint.config.js with flat config
  3. Update to Node.js 20+ for full feature support
  4. Replace eslint-plugin-node with eslint-plugin-n
  5. Update scripts to use flat config

Philosophy

This configuration prioritizes:

  1. Security - Prevent vulnerabilities before they reach production
  2. Performance - Non-blocking operations and modern APIs
  3. Type Safety - Full TypeScript benefits for backend code
  4. Modern Patterns - Leverage Node.js 20+ capabilities
  5. Developer Experience - Clear errors with actionable fixes

License

MIT

Contributing

Issues and PRs welcome at GitHub