.nvmrc: instructs nvm which Node to use.
Who reads it: nvm.
Action: nvm use auto-switches your shell to that version.
File: .nvmrc:1 (value 20).
.node-version: instructs other version managers (nodenv, asdf, some IDEs) which Node to use.
Who reads it: nodenv, asdf-nodejs, some editors/CI images.
Action: auto-switches in those ecosystems.
File: .node-version:1 (value 20).
package.json engines: declares the supported Node range for the project.
Who reads it: npm/yarn/pnpm and tooling.
Action: by default npm only warns; CI/tools can enforce.
File: package.json:21 ("node": ">=18.18.0 <23").
.npmrc engine-strict: turns that engines declaration into a hard error on install.
Who reads it: npm.
Action: fails npm i if Node doesn’t satisfy engines.
File: .npmrc:1 (engine-strict=true).
.husky/pre-commit checker: fast-fails at commit time with a clear message.
Who runs it: Husky git hook.
Action: blocks commit if Node too old and suggests nvm use.
File: .husky/pre-commit calls scripts/check-node.mjs.
Why all three?
Different environments: teammates may use nvm, nodenv, asdf, or plain Node.