tiee
EN
Runnable onboarding guide

Make the Node.js runtime requirement fail before install

A package can declare "node": ">=20" and still leave a developer with a vague failure several commands later. Treat the runtime version as an executable contract: state it, check it, and stop with a repair instruction before installing dependencies.

Prepared by Tiee · Four boundary assertions passed · AI-assisted drafting, manually reviewed

The small check

Add a dependency-free script at scripts/check-node-version.mjs:

const requiredMajor = 20;
const actualMajor = Number.parseInt(process.versions.node.split(".")[0], 10);

if (!Number.isInteger(actualMajor) || actualMajor < requiredMajor) {
  console.error(
    `Node.js ${requiredMajor}+ is required; found ${process.versions.node}. ` +
    "Install an active LTS release, then rerun npm install."
  );
  process.exit(1);
}

console.log(`Node.js ${process.versions.node} satisfies the Node.js ${requiredMajor}+ requirement.`);

Run it before the normal install path:

{
  "engines": { "node": ">=20" },
  "scripts": { "preinstall": "node scripts/check-node-version.mjs" }
}

The engines field lets package managers and hosting systems inspect the requirement. The script makes the same requirement visible to someone using a package manager that only warns about engine mismatches.

Make the docs use the same source of truth

The quickstart should say exactly what the check enforces:

### Prerequisite

- Node.js 20 or newer (`node --version`)

If the install stops at the runtime check, install an active Node.js LTS
release and run `npm install` again.

A phrase such as “a recent Node.js version” cannot be tested, and it drifts as soon as the product adopts a new baseline.

Test both branches without changing runtimes

Extract the comparison into a pure function so the regression test can cover supported and unsupported versions:

export function supportsNode(version, requiredMajor = 20) {
  const major = Number.parseInt(version.split(".")[0], 10);
  return Number.isInteger(major) && major >= requiredMajor;
}
import assert from "node:assert/strict";
import { supportsNode } from "./supports-node.mjs";

assert.equal(supportsNode("18.20.8"), false);
assert.equal(supportsNode("20.0.0"), true);
assert.equal(supportsNode("22.19.0"), true);
assert.equal(supportsNode("not-a-version"), false);

This check is deliberately narrow. The normal test suite still owns compatibility across newer major releases. This check keeps the declared minimum, the first command, and the quickstart from contradicting one another.

Acceptance checklist

This client-neutral sample does not claim testing against a third-party repository. Its code and claims were manually reviewed after AI-assisted drafting.

Have a first-run blocker?

Send the repository and expected outcome. We can define a small first delivery with clear acceptance criteria.

Email Tiee