Skip to main content

What It Does

The engine check validates Node.js version requirements by examining the engines.node field in package.json and comparing it against your currently running Node version. Key Features:
  • Verifies engines.node field exists
  • Detects End-of-Life (EOL) Node versions
  • Fails if your current Node version is too old
  • Provides upgrade recommendations
Source: /src/checks/engine.ts

What It Checks

The check performs three validations:
  1. Field Existence:
    • Warns if engines.node is missing from package.json
    • Suggests adding it with your current Node version
  2. EOL Version Detection:
    • Warns if specified version is below Node 18 (EOL threshold)
    • Recommends upgrading to current LTS
  3. Current Version Compatibility:
    • Fails if you’re running Node below the required version
    • Shows your version vs. required version
/src/checks/engine.ts:16-90
export async function checkEngine(): Promise<CheckResult> {
  const pkgPath = path.join(process.cwd(), "package.json");

  if (!fs.existsSync(pkgPath)) {
    return {
      checkName: "engine",
      status: "skip",
      messages: [{ level: "info", text: "No package.json found" }],
    };
  }

  const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
  const messages: CheckResult["messages"] = [];
  let status: CheckResult["status"] = "pass";
  const currentMajor = getCurrentNodeMajor();

  // Check if engines.node is specified
  if (!pkg.engines?.node) {
    return {
      checkName: "engine",
      status: "warn",
      messages: [
        {
          level: "warn",
          text: 'No "engines.node" field in package.json',
        },
        {
          level: "info",
          text: `Add "engines": { "node": ">=${currentMajor}.0.0" } so consumers know what Node version is required`,
        },
      ],
    };
  }

  const specifiedMin = parseMinNodeVersion(pkg.engines.node);

  if (specifiedMin === null) {
    messages.push({
      level: "warn",
      text: `Could not parse engines.node value: "${pkg.engines.node}"`,
    });
    status = "warn";
  } else {
    // Check for EOL Node versions (anything below 18 as of 2024)
    const EOL_THRESHOLD = 18;

    if (specifiedMin < EOL_THRESHOLD) {
      status = "warn";
      messages.push({
        level: "warn",
        text: `engines.node specifies >= ${specifiedMin}, but Node ${specifiedMin} is End-of-Life`,
      });
      messages.push({
        level: "info",
        text: `Consider bumping to >= ${EOL_THRESHOLD} (current LTS)`,
      });
    } else {
      messages.push({
        level: "info",
        text: `engines.node = "${pkg.engines.node}" — looks good`,
      });
    }

    // Check if currently running Node satisfies the requirement
    if (currentMajor < specifiedMin) {
      status = "fail";
      messages.push({
        level: "error",
        text: `You are running Node ${currentMajor} but this project requires Node >= ${specifiedMin}`,
      });
    }
  }

  return { checkName: "engine", status, messages };
}

Example Output

 engine engines.node = ">=18.0.0" looks good

Why It Matters

Compatibility & Security RisksNot specifying or enforcing Node version requirements causes:
  • Runtime failures due to missing language features
  • Security vulnerabilities in EOL Node versions
  • Build failures in CI/CD pipelines
  • Dependency conflicts with packages requiring newer Node
  • Unpredictable behavior across team members

How to Fix

1. Add Missing engines.node

Check your current Node version:
node --version
# v20.11.0
Add to package.json:
package.json
{
  "engines": {
    "node": ">=20.0.0"
  }
}
Best Practice: Use >= with the minimum major version your project supports.

2. Update EOL Version

If you’re specifying an End-of-Life Node version:
package.json
{
  "engines": {
-    "node": ">=14.0.0"
+    "node": ">=18.0.0"
  }
}
Current Node.js LTS versions (2024):
  • Node 20: Active LTS (recommended)
  • Node 18: Maintenance LTS
  • Node 16 and below: End-of-Life ❌

3. Upgrade Your Node Version

If the check fails because your Node is too old:
nvm install 20
nvm use 20
Verify the upgrade:
node --version
stackprobe audit --only engine

4. Enforce in CI

Add Node version checks to your CI workflow:
.github/workflows/ci.yml
name: CI
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version-file: '.nvmrc"
      - run: npm ci
      - run: npm test
Create .nvmrc for consistency:
.nvmrc
20

Version Parsing

The check handles various engines.node formats:
/src/checks/engine.ts:5-10
function parseMinNodeVersion(enginesField: string): number | null {
  // Handle patterns like: ">=16.0.0", "^18", "14.x", ">=14 <20"
  const match = enginesField.match(/(\d+)/);
  if (!match) return null;
  return parseInt(match[1], 10);
}
Supported formats:
  • >=18.0.0 → 18
  • ^20 → 20
  • 20.x → 20
  • >=18 <22 → 18 (takes first number)
  • lts/* → Cannot parse (shows warning)

EOL Threshold

The check currently flags Node versions below 18 as End-of-Life:
/src/checks/engine.ts:60
const EOL_THRESHOLD = 18;
This threshold is hardcoded and should be updated periodically as Node versions reach EOL. Node.js Release Schedule:
  • Node 16: EOL September 2023 ❌
  • Node 18: Maintenance until April 2025 ⚠️
  • Node 20: Active LTS until April 2026 ✅
  • Node 22: Current (April 2024) ✅
See nodejs.org/en/about/releases for the full schedule.

Common Patterns

Use a range to support multiple major versions:
package.json
{
  "engines": {
    "node": ">=18.0.0 <22.0.0"
  }
}
Or use || for non-continuous ranges:
{
  "engines": {
    "node": "^18.0.0 || ^20.0.0"
  }
}
Use = to require a specific version (rare):
package.json
{
  "engines": {
    "node": "20.11.0"
  }
}
This is not recommended — it prevents patch updates and security fixes.
Also specify package manager versions:
package.json
{
  "engines": {
    "node": ">=20.0.0",
    "npm": ">=10.0.0",
    "pnpm": ">=8.0.0"
  }
}
Enable strict engine checking:
.npmrc
engine-strict=true
Use CI matrix testing:
.github/workflows/ci.yml
strategy:
  matrix:
    node-version: [18, 20, 22]
steps:
  - uses: actions/setup-node@v3
    with:
      node-version: ${{ matrix.node-version }}

Configuration

To disable this check:
stackprobe.config.json
{
  "ignore": ["engine"]
}
Or run it exclusively:
stackprobe audit --only engine

Next Steps

Circular Check

Detect circular dependency chains

Dependencies Check

Check for outdated npm packages

Build docs developers (and LLMs) love