Triky s NPM: aktualizujeme a zamykáme moduly

Mít nepořádek mezi NPM moduly se nevyplácí. O codebase aplikace je třeba pečovat a NPM k tomu nabízí dostatek nástrojů, které vám zajistí stabilitu aplikace a klidné spaní bez starostí.

Nejprve: Co je sémantické verzování (SEMVER)

Bez pochopení sémantického verzování se vám budou NPM moduly spravovat velmi špatně. Ale není to nic složitého:

[1.2.3]
 | | '- PATCH: jen záplaty  
 | '--- MINOR: nové features, kompatibilita zachována
 '----- MAJOR: nová verze není zpětně kompatibilní

Při práci s dependencemi se pak v package.json používají speciální značky, jež jsou podrobně zdokumentovány na NPM. Ve stručnosti jsou to tyto:

  1. Caret ranges: ^1.2.3

    • nejpoužívanější, protože zajišťují jen přísun aktualizací, které nerozbijí aplikaci
    • povoluje instalace všech záplat (PATCH) a nových features (MINOR)
    • zamezuje instalaci verzí, které nebudou zpětně kompatibilní (MAJOR)
    • výjimkou jsou verze začínající nulou (0.2.3), kde je zamezeno i aktualizaci nových features

      ^1.2.3    :=   >=1.2.3 <2.0.0
      ^0.2.3    :=   >=0.2.3 <0.3.0
      
  2. Tilde ranges: ~1.2.3

    • striktnější než Caret range
    • povoluje jen instalaci záplat (PATCH)
    • zamezuje instalace veškerých nových features (MAJOR i MINOR)

      ~1.2.3    :=   >=1.2.3 <1.3.0
      ~0.2.3    :=   >=0.2.3 <0.3.0
      
  3. X-Ranges: 1.x.x

    • ne úplně ideální, protože rozšiřují spodní hranici
    • místo x nebo * povolují kteroukoliv verzi

      1.2.x    :=   >=1.2.0 <1.3.0
      0.2.x    :=   >=0.2.0 <0.3.0
      
  4. Rozsahy: 1.2.3 - 2.0.0 nebo >=1.2.3 <=2.0.0

    • V rámci SEMVERu pro NPM je možné specifikovat verzi i rozsahem. Zpravidla je však jednodušší použít jednu z možností výše.
    • Různá pravidla je dokonce možné kombinovat. NPM pak hledá průnik všech pravidel (AND).

K vyzkoušení nejlépe poslouží SEMVER kalkulátor.

Příkaz npm install --save jako výchozí způsob zanamenání požadované verze používá Caret ranges. Pokud použijete přepínač -E (EXACT), tak se do package.json zaznamená přesná verze modulu. To však nezajistí, že se modul nainstaluje vždy se stejnými subdependencemi. K tomu slouží shrinkwrap.

Hledáme zastaralé moduly

NPM má skvělý příkaz, který nám pomůže najít všechny moduly, jejichž aktuální verze se liší od té instalované:

$ npm outdated

Package                    Current         Wanted  Latest  Location  
bootstrap            4.0.0-alpha.2  4.0.0-alpha.2   3.3.6  trell-gantt  
eslint                       2.5.1          2.5.3   2.7.0  trell-gantt  
  • Wanted nám sděluje maximální možnou verzi, kterou dovoluje package.json
  • Lastest informuje o poslední produkční verzi balíčku
  • Current značí momentálně instalovanou verzi

Výsledek v tomto případě říká, že verze bootstrapu stále není produkční a že pro eslint sice existuje aktualizace (Lastest: 2.7.0), avšak package.json dovoluje nainstalovat pouze verzi 2.5.3 (Wanted), protože je u balíčku specifikována požadovaná verze jako ~2.5.1. Tilde rage totiž zamezuje instalacím nových features.

Aktualizujeme balíčky

Pro aktualizaci existuje v NPM příkaz update:

$ npm update

Pro kontrolu, zda se všechny balíčky správně zaktualizovaly, použijeme znovu příkaz npm outdated:

$ npm outdated

Package                    Current         Wanted  Latest  Location  
bootstrap            4.0.0-alpha.2  4.0.0-alpha.2   3.3.6  trell-gantt  
eslint                       2.5.3          2.5.3   2.7.0  trell-gantt  

Proč se ale mezi zastaralými moduly stále zobrazuje eslint, když měla proběhnout aktualizace? Odpověď je prostá: Aby bylo možné nainstalovat nejnovější verzi eslintu, je třeba nastavení verze balíčku v package.json zaktualizovat opětovnou instalací s parametrem --save(-dev):

$ npm install --save eslint

Nejen že tím bude nainstalována nejnovější verze, ale NPM zaktualizuje projektový package.json. Samozřejmě je pak potřeba spustit testy aplikace, aby aktualizace balíčku nezpůsobila nefunkčnost kódu.

Zaktualizovat balíček je možné také ručně přímo v package.json. Pokud používáte Caret ranges, tak si pravděpodobně vystačíte s npm install --save, ale jinak je vhodnější mít pod kontrolou, na jakou verzi se bude aktualizovat.

Shrinkwrap: Zamykáme přesné verze modulu

Největší výhodou používání shrinkwrapu je snížení rizika, že nevhodná aktualizace některé ze subdependencí vašich dependencí vám na produkčním serveru nerozbije aplikaci. NPM tam umožňuje paranoidním jedincům zajistit, že při nasazení na produkčním serveru se budou instalovat jiné balíčky, než se používaly k vývoji. Stačí použít následující příkaz:

$ npm shrinkwrap

NPM pak zaznamená všechny verze balíčků, včetně jejich subdependencí a při spuštění npm install v čerstvě naklonovaném repozitáři celou původní strukturu zreplikuje. K zaznamenání verzí se používá soubor npm-shrinkwrap.json, který je pak součástí projektu, stejně jako package.json. Důležité je však říci, že:

  • k aktualizaci modulů je pak třeba buď shrinkwrap soubor smazat a vytvořit znovu, nebo používat příkaz npm install --save
  • shrinkwrap nezajistí úplnou binární kopii složky node_modules

Ze svých zkušeností můžu říct, že shrinkwrap nás zbavil i různých drobných problémů při vývoji v týmu, kdy se stávalo, že odlišná verze subdependence způsobovala jiné chování stejného kusu kódu u dvou vývojářů.

David Menger

Read more posts by this author.