A node --run gyorsabb lehet az npm run parancsnál – de nem ugyanazt futtatja

A node --run közvetlenül indítja a package.json szkriptjeit, ezért gyorsabb lehet az npm run parancsnál. A váltás előtt azonban ellenőrizni kell az életciklus-hookokat és az npm környezeti változóit.

A package.json szkriptjeit nem csak 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. A node --run szándékosan kevesebbet csinál, és a kihagyott lépésekre nem figyelmeztet.

Diagram: felül az npm run láncolt pre-teszt-post lépéssora egy lassú piros órával a végén, alul a node --run egyetlen közvetlen lépése gyors zöld órával és sebességvonalakkal.

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 a -- után lehet továbbadni a szkriptnek.

A funkció a Node.js 22.0.0-ban jelent meg 2024. április 24-én. Nem indítja el az npm parancssori felületét, nem olvassa be annak teljes konfigurációját, és nem építi fel az npm által megszokott környezetet. A Node közvetlenül keresi meg és futtatja a szkriptet.

A különbség főleg rövid szkripteknél látszik

Az indítási többletet 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 nem általános benchmark, hanem 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 ö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. Ezt az npm szkriptekről szóló dokumentáció is rögzíti.

{
  "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. A node --run test csak a középsőt indítja el. Nem jelez hibát, és nem ír ki figyelmeztetést, 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. 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. Build- és kiadási szkriptek gyakran ezekből olvassák ki a verziót vagy az aktuális lifecycle eseményt.

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. Egy gondatlan, Docker image-et címkéző szkript akár az undefined szót is beépítheti a címkébe. Ezért a migráció előtt érdemes rákeresni a szkriptjeinkben a process.env.npm_ használatára, nem csak arra hagyatkozni, hogy a parancs sikeresen lefut.

A Node saját változókat ad

A node --run két dokumentált 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. Ezek a Node.js 22.3.0 óta érhetők el.

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 szkriptje a gyökérben telepített binárisokat is elérheti.

Mikor érdemes váltani?

Jó jelölt 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 három dolgot néznék végig:

  • tartozik-e a szkripthez pre vagy post hook;
  • olvas-e npm_* környezeti változót;
  • elérhető-e minden szükséges bináris a node --run által felépített PATH-on.

Ha mindhárom rendben van, a node --run egyszerű és gyors eszköz. Örökölt, sok lifecycle 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.

Források

Leave a Reply

Az e-mail címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük