A package.json szkriptjeit nemcsak az npm run tudja elindítani. A Node.js 22 óta maga a Node is képes rá, csomagkezelő nélkül:
node --run test
node --run build -- --watch
A rövidebb út érezhetően gyorsabb lehet, különösen akkor, ha sok apró szkript indul egymás után, a két parancs azonban nem csereszabatos, mert a node --run szándékosan kevesebbet csinál, és a kihagyott lépésekre nem figyelmeztet.

Mit csinál a node --run?
A Node.js CLI dokumentációja szerint a kapcsoló megkeresi a package.json scripts mezőjében a megadott parancsot, majd lefuttatja, az argumentumokat pedig a -- után adja tovább a szkriptnek.
A funkció a Node.js 22.0.0-ban jelent meg 2024. április 24-én, és mivel a Node közvetlenül keresi meg, majd futtatja a szkriptet, nem indítja el az npm parancssori felületét, nem olvassa be annak teljes konfigurációját, valamint az npm által megszokott környezetet sem építi fel.
A különbség főleg rövid szkripteknél látszik
Az indítási többletidőt egy olyan szkripttel mértem meg, amely azonnal kilép: node -e "process.exit(0)". Node.js 22.22.3 és npm 10.9.8 alatt húsz egymást követő futtatásból ezt kaptam:
npm run hello × 20: ≈ 3,02 s
node --run hello × 20: ≈ 0,76 s
Ezen a gépen tehát nagyjából négyszeres volt a különbség, ez azonban nem általános benchmark, csupán az indítási költség szemléltetése: egy többperces buildnél a megtakarított néhány tizedmásodperc eltűnik a zajban, monorepóban, sok workspace-en végigfutó CI-lépéseknél vagy gyakran újrainduló fejlesztői szkripteknél viszont már összeadódhat.
A pre- és post-szkriptek kimaradnak
Az npm életciklusához hozzátartozik, hogy egy test szkript körül automatikusan lefuttatja a pretest és posttest parancsokat is, ha léteznek, amit az npm szkriptekről szóló dokumentáció is rögzít.
{
"scripts": {
"pretest": "npm run lint",
"test": "node --test",
"posttest": "npm run report"
}
}
Az npm run test ebben a példában mindhárom lépést sorban végrehajtja, míg a node --run test csak a középsőt indítja el, és sem hibát, sem figyelmeztetést nem ír ki, hiszen ez dokumentált, szándékos korlátozás.
A különbség könnyen elbújik egy CI-módosításban, mert ha a lintelés vagy a riportkészítés életciklus-hookban él, a parancs egyszerű cseréjével az ellenőrzés csendben kiesik a pipeline-ból.
Az npm környezeti változói sem jelennek meg
Az npm futás közben több npm_* kezdetű környezeti változót állít be, ilyen például az npm_package_version és az npm_lifecycle_event, amelyekből a build- és kiadási szkriptek gyakran a verziót vagy az aktuális életciklus-eseményt olvassák ki.
npm run showvars:
npm_package_version=1.2.3
npm_lifecycle_event=showvars
node --run showvars:
npm_package_version=undefined
npm_lifecycle_event=undefined
A hiányzó változó önmagában nem feltétlenül állítja le a folyamatot, ezért egy gondatlan, Docker-image-et címkéző szkript akár az undefined szót is beépítheti a címkébe; a migráció előtt tehát érdemes rákeresni a szkriptjeinkben a process.env.npm_ használatára, és nem pusztán arra hagyatkozni, hogy a parancs sikeresen lefut.
A Node saját változókat ad
A node --run két dokumentált, a Node.js 22.3.0 óta elérhető környezeti változót állít be: a NODE_RUN_SCRIPT_NAME a futtatott szkript nevét, a NODE_RUN_PACKAGE_JSON_PATH pedig a feldolgozott package.json elérési útját tartalmazza.
Ugyanettől a verziótól a parancs felfelé halad a könyvtárfában, amíg talál egy package.json-t. Az érintett szülőkönyvtárak node_modules/.bin mappáit hozzáadja a PATH-hoz, ezért egy mélyebben fekvő workspace-szkript a gyökérben telepített binárisokat is elérheti.
Mikor érdemes váltani?
Jó jelölt lehet egy önálló build-, teszt- vagy segédszkript, amelyet gyakran indítunk, és nem támaszkodik az npm környezetére, a csere előtt azonban három dolgot mindenképpen végignéznék:
- tartozik-e a szkripthez
prevagyposthook; - olvas-e
npm_*környezeti változót; - elérhető-e minden szükséges bináris a
node --runáltal felépítettPATH-on.
Ha mindhárom rendben van, a node --run egyszerű és gyors eszköz, egy örökölt, sok életciklus-hookból felépülő szkriptrendszernél viszont nem gyorsabb npm-helyettesítő, hanem eltérő viselkedésű futtató; a valódi kérdés ezért nem az, mennyivel hamarabb indul el, hanem az, hogy ugyanazt a munkát indítja-e el.