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