Cos'è davvero npm
npm sono tre cose riunite sotto un unico nome. È un registro: un enorme database pubblico di pacchetti JavaScript su npmjs.com. È uno strumento da riga di comando che arriva con Node.js per installare e gestire quei pacchetti. Ed è una specifica (il formato di package.json) che descrive ciò di cui un progetto ha bisogno.
Quando esegui npm install express, la CLI parla con il registro, scarica express e tutto ciò da cui dipende, mette i file in una cartella chiamata node_modules e registra il pacchetto e la sua versione nel tuo package.json. Questo è tutto il giro.
Se hai installato Node.js, ce l'hai già. Controlla:
node --version
npm --version
Se entrambi stampano una versione, sei a posto.
Iniziare un progetto: npm init
Ogni progetto npm ha bisogno di un package.json. È il manifesto: elenca il nome del progetto, la versione, gli script e le dipendenze. Il modo più veloce per crearne uno è npm init -y, che accetta tutti i valori predefiniti:
mkdir my-app
cd my-app
npm init -y
Questo scrive qualcosa del genere:
{
"name": "my-app",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Senza -y, npm ti guida campo per campo in modo interattivo. In entrambi i casi ottieni un package.json, il file a cui si aggancia tutto il resto. Vedremo i suoi campi nel dettaglio nella prossima pagina.
Installare un pacchetto
Una volta che hai un package.json, installa un pacchetto con npm install (o npm i in breve):
npm install lodash
Succedono tre cose:
- npm scarica
lodashe le sue dipendenze dentronode_modules/. - Aggiunge
"lodash": "^4.17.21"(o l'ultima versione disponibile) alla sezionedependenciesdipackage.json. - Scrive un
package-lock.jsonche registra le versioni esatte di ogni pacchetto dell'albero.
Ora puoi usarlo:
La chiamata require (o import in un progetto ESM) trova il pacchetto cercando dentro node_modules. Non scrivi un percorso: se ne occupa il resolver dei moduli di Node.
dependencies e devDependencies
Non tutti i pacchetti servono quando la tua app gira in produzione. Framework di test, linter e bundler contano solo durante lo sviluppo. Installali con --save-dev (o -D):
npm install --save-dev jest
npm install -D eslint prettier
Questi finiscono in devDependencies invece che in dependencies:
{
"dependencies": {
"lodash": "^4.17.21"
},
"devDependencies": {
"jest": "^29.7.0",
"eslint": "^8.57.0",
"prettier": "^3.2.5"
}
}
Su un server di produzione, npm install --omit=dev salta del tutto la sezione di sviluppo, rendendo l'installazione più piccola e veloce. Fare bene questa divisione conta più di quanto sembri: un webpack messo per sbaglio in dependencies appesantisce ogni deploy in produzione.
Installare tutto in una volta
Quando cloni un repository che ha già un package.json, non elenchi i pacchetti uno per uno. Esegui semplicemente:
npm install
Senza argomenti, npm legge package.json (e rispetta le versioni esatte di package-lock.json) e installa l'intero albero dentro node_modules. È la prima cosa da eseguire dopo ogni checkout nuovo.
È anche per questo che node_modules va in .gitignore. È riproducibile a partire dal lockfile, è enorme e cambia ogni volta che qualcuno esegue npm install. Fai il commit di package.json e package-lock.json; lascia che tutti gli altri rigenerino node_modules da soli.
Aggiornare i pacchetti
npm outdated mostra cosa è rimasto indietro:
npm outdated
Vedrai una tabella con le colonne Current, Wanted e Latest. Wanted è la versione più recente consentita dall'intervallo in package.json (per ^4.17.21, qualsiasi versione sotto 5.0.0). Latest è la versione più recente pubblicata, che potrebbe essere una major che non hai ancora scelto di adottare.
Per aggiornare dentro l'intervallo consentito:
npm update
Per passare davvero all'ultima versione, compresi i salti di versione major, installa di nuovo il pacchetto con @latest:
npm install lodash@latest
I salti di versione major possono rompere il tuo codice: è proprio quello che il numero di versione sta segnalando. Leggi il changelog prima di attraversarne uno.
Disinstallare
Rimuovere un pacchetto è l'operazione simmetrica:
npm uninstall lodash
Lo toglie da node_modules ed elimina la voce da package.json. Aggiungi -D se era una dipendenza di sviluppo (npm se ne accorge comunque, ma essere espliciti evita sorprese negli script).
Globale o locale
Quasi ogni installazione dovrebbe essere locale, fissata a un solo progetto dentro il suo node_modules. L'eccezione sono gli strumenti da riga di comando che vuoi avere ovunque:
npm install -g typescript
npm install -g http-server
Un'installazione globale mette lo strumento in una posizione di sistema e la sua voce bin nel tuo PATH, così puoi eseguire tsc o http-server da qualsiasi cartella. Ma le installazioni globali non vengono tracciate per progetto e possono disallinearsi tra una macchina e l'altra.
Una via di mezzo migliore per i comandi occasionali è npx, che arriva insieme a npm:
npx create-react-app my-app
npx prettier --write .
npx esegue un pacchetto senza installarlo globalmente: lo scarica al momento, lo esegue e hai finito. Per gli strumenti che usi una volta sola, è più pulito di un'installazione globale permanente.
Un promemoria minimo
I comandi che userai davvero ogni giorno:
npm init -y # create package.json
npm install # install everything in package.json
npm install <pkg> # add a runtime dependency
npm install -D <pkg> # add a dev dependency
npm install -g <pkg> # install a CLI tool globally
npm uninstall <pkg> # remove a dependency
npm outdated # see what's out of date
npm update # update within allowed ranges
npm install <pkg>@latest # jump to the newest version
npm run <script> # run a script from package.json
npx <pkg> # run a package without installing it
Questo è gran parte di npm. Il resto (pubblicazione, workspace, pacchetti con scope) puoi impararlo quando ti serve.
Cosa c'è davvero in node_modules
Un ultimo modello mentale. node_modules è una cartella più o meno piatta che contiene ogni pacchetto da cui dipende il tuo progetto, più tutto ciò da cui dipendono quei pacchetti, in modo transitivo. Installi un pacchetto e potresti tirartene dietro cento: è normale. npm elimina i duplicati dove può, quindi due pacchetti che dipendono dalla stessa versione di lodash ne condividono una sola copia.
Il lockfile (package-lock.json) registra la versione esatta risolta di ognuno di quei pacchetti. È questo che rende le build riproducibili: due sviluppatori che eseguono npm install dallo stesso lockfile ottengono alberi identici byte per byte, anche a mesi di distanza.
Tratta node_modules come un output generato. Non modificare mai i file al suo interno: le tue modifiche spariranno alla prossima installazione di chiunque.
Prossimo passo: package.json
package.json è il file che npm continua a leggere e riscrivere dietro le quinte. Conoscere i suoi campi (scripts, main, type, intervalli di versione, engines) è ciò che trasforma npm da scatola nera a qualcosa che controlli tu. È l'argomento della prossima pagina.
Domande frequenti
Cos'è npm?
npm è il gestore di pacchetti predefinito di Node.js. Arriva insieme a Node, ospita un enorme registro pubblico di pacchetti JavaScript e fornisce uno strumento da riga di comando per installarli, aggiornarli e pubblicarli. Quando esegui npm install lodash, npm scarica lodash dal registro dentro node_modules e lo registra in package.json.
Qual è la differenza tra dependencies e devDependencies?
Le dependencies sono i pacchetti di cui la tua app ha bisogno per girare in produzione, come express o react. Le devDependencies servono solo durante lo sviluppo o la build: test runner, bundler, linter. Installi queste ultime con npm install --save-dev <pkg> (o -D). In produzione, npm install --omit=dev salta le devDependencies.
Devo fare il commit di node_modules su git?
No. node_modules può facilmente pesare centinaia di megabyte ed è completamente riproducibile a partire da package.json + package-lock.json. Aggiungilo a .gitignore e fai il commit del lockfile. Chiunque cloni il tuo repository esegue npm install e ottiene esattamente lo stesso albero di dipendenze.
Cosa significa installazione globale o locale con npm?
Un'installazione locale (npm install <pkg>) mette il pacchetto dentro il node_modules del tuo progetto e lo registra in package.json. Un'installazione globale (npm install -g <pkg>) lo installa a livello di sistema, di solito per strumenti da riga di comando che vuoi avere a disposizione ovunque. Per le dipendenze di progetto preferisci le installazioni locali, così le versioni restano fissate progetto per progetto.