Le fichier manifeste d'un projet Node
Tout projet Node.js possède un package.json à sa racine. C'est un simple fichier JSON qui décrit le projet — son nom, sa version, ses dépendances, les commandes qu'il expose — et c'est ce que lit npm à chaque fois qu'il fait quoi que ce soit. Si tu le supprimes, npm n'a plus aucune idée de ce qu'est ton projet.
Le moyen le plus rapide d'en créer un, c'est npm init :
npm init -y
L'option -y passe outre les questions interactives et applique les valeurs par défaut. On obtient alors quelque chose de ce genre :
{
"name": "my-app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Voilà le squelette minimal. La plupart de ces champs ne servent pas à grand-chose tels quels — ils prennent tout leur sens au fur et à mesure que vous ajoutez des dépendances et des scripts.
dependencies vs devDependencies
Deux champs font l'essentiel du boulot : dependencies et devDependencies. Les deux associent un nom de paquet à une plage de versions.
La distinction a son importance pour une raison précise : les dependencies sont les paquets dont ton code a besoin pour tourner. Les devDependencies, elles, ne servent que pendant le développement — frameworks de tests, linters, outils de build, vérificateurs de types. Quand quelqu'un installe ton paquet comme dépendance du sien, npm récupère tes dependencies et ignore tes devDependencies.
npm met ces champs à jour tout seul. npm install express ajoute une ligne dans dependencies. npm install --save-dev vitest en ajoute une dans devDependencies. Tu auras rarement à les modifier à la main.
Plages de versions : ^, ~ et version exacte
Les chaînes de version comme ^4.19.0 ne désignent pas une version précise — ce sont des plages. npm s'appuie sur le semver, qui découpe chaque version en MAJOR.MINOR.PATCH :
- MAJOR : incrémenté lors d'un changement cassant la compatibilité.
- MINOR : incrémenté quand on ajoute des fonctionnalités, sans rien casser.
- PATCH : incrémenté pour les corrections de bugs.
Les deux opérateurs que tu croiseras partout :
"express": "^4.19.0" // >= 4.19.0 et < 5.0.0 (tout 4.x.x supérieur ou égal à 4.19.0)
"express": "~4.19.0" // >= 4.19.0 et < 4.20.0 (tout 4.19.x supérieur ou égal à 4.19.0)
"express": "4.19.0" // exactement 4.19.0
^ est la valeur par défaut que npm utilise quand tu installes un paquet. Il fait confiance aux montées de version mineures et correctives pour rester compatibles. ~ est plus prudent : uniquement les correctifs. Une version écrite telle quelle, sans préfixe, épingle la version exacte.
Le piège : « ce que je viens d'installer » et « ce que la plage autorise », ce n'est pas la même chose. Si tu installes express@4.19.0 aujourd'hui et qu'un collègue installe ton projet dans un mois, ^4.19.0 pourrait se résoudre en 4.19.5. C'est précisément là qu'intervient package-lock.json : il enregistre les versions exactes qui ont été résolues, pour que tout le monde obtienne le même arbre de dépendances. Commite-le.
Les scripts npm : la surface de commandes de ton projet
Le champ scripts te permet de définir des raccourcis pour les commandes courantes. Tout ce que tu y mets peut être lancé avec npm run <nom> :
Quelques points à retenir sur les scripts :
npm start,npm testet quelques autres noms fonctionnent sans le mot-clérun. Pour tout le reste, il faut passer parnpm run <nom>.- Les scripts s'exécutent dans un shell qui a
node_modules/.bindans lePATH, ce qui permet d'appeler directement les binaires des paquets installés."test": "vitest"fonctionne même sivitestn'est pas installé globalement. - On peut enchaîner les scripts :
"build": "npm run lint && npm run compile". Le&&signifie « exécute en séquence, arrête-toi à la première erreur ». - Les scripts
pre<nom>etpost<nom>se déclenchent automatiquement. Si tu as unprebuild, il sera lancé avantbuildsans rien avoir à configurer.
Les scripts, c'est la porte d'entrée du projet en ligne de commande. Un bon package.json permet à un nouveau contributeur de cloner le dépôt, lancer npm install, puis npm run dev ou npm test sans avoir à consulter le moindre wiki.
Points d'entrée : main, exports et type
Ces champs indiquent à Node (et aux bundlers) comment charger ton paquet.
typedétermine comment les fichiers.jssont interprétés."module"active l'ESM (import/export). Si tu l'omets ou que tu mets"commonjs", c'est CommonJS (require) qui prend le relais. Pour tous les détails, va voir la doc CommonJS vs ESM.mainest le point d'entrée historique — c'est ce que résoutrequire("my-lib"). Les anciens outils s'appuient encore dessus.exportsest son remplaçant moderne, beaucoup plus strict. Il définit précisément quels fichiers les consommateurs peuvent importer, et sous quels sous-chemins. Si un fichier n'est pas listé, l'import échoue — et c'est voulu, pas un bug. Tu gardes la main sur ton API publique.
Si tu développes simplement une application (sans publier de package), le seul champ qui compte vraiment pour toi ici, c'est type.
Un package.json réaliste
En mettant tout bout à bout, voici à quoi ressemble concrètement le package.json d'une petite appli Node :
Remarquez le champ engines.node. Il est purement indicatif : npm affiche un avertissement (ou carrément une erreur avec engine-strict) si la version de Node de l'utilisateur ne correspond pas. Une bonne habitude à prendre dès que vous publiez quelque chose.
Les champs à connaître
Quelques autres champs que vous croiserez tôt ou tard :
private: true— empêche la publication accidentelle du paquet sur npm. À activer sur tout projet qui n'a pas vocation à être publié.license— un identifiant SPDX du genre"MIT"ou"ISC". Important pour tout projet public.repository,bugs,homepage— ces champs s'affichent sur la page du registre npm.bin— si votre paquet fournit une CLI, c'est ici que vous associez les noms de commande aux fichiers de script. Après installation, ces commandes deviennent directement exécutables.workspaces— pour les monorepos ; indique à npm de considérer certains sous-dossiers comme des paquets liés entre eux.
Vous n'avez pas besoin de tout ça. Vous avez besoin de ce qui correspond à votre projet, point.
Les pièges classiques
Voici quelques trucs sur lesquels on se casse régulièrement les dents :
- Versionner
node_modules. Non, vraiment pas. Ajoutez-le à votre.gitignore. Unpackage.jsonaccompagné dupackage-lock.jsonsuffit largement pour que n'importe qui reconstruise l'arborescence avecnpm install. - Oublier de versionner
package-lock.json. Celui-là, par contre, il doit être dans Git. Sans le lockfile, le fameux « ça marche chez moi » devient une vraie menace, parce que les plages semver peuvent résoudre vers des versions différentes au fil du temps. - Mettre des dépendances runtime dans
devDependencies. Votre appli peut très bien tourner en local — normal, les dev deps y sont installées — puis casser en production, où elles sont ignorées. Si le code que vous livrez en a besoin, sa place est dansdependencies. - Modifier les versions à la main sans réinstaller. Si vous changez une version dans
package.json, lanceznpm installdans la foulée — sinonnode_moduleset le lockfile finissent désynchronisés.
La suite : le runtime Node
Le package.json dit à Node ce qu'est votre projet. Le runtime Node, lui, décide comment il s'exécute : résolution des modules, modules natifs, variables globales, boucle d'événements en coulisses. C'est le sujet de la page suivante.
Questions fréquentes
À quoi sert le fichier package.json ?
C'est le manifeste d'un projet Node.js. Il contient le nom et la version du projet, la liste des paquets dont il dépend, les scripts que tu peux lancer avec npm run, ainsi que des métadonnées comme le point d'entrée et le type de module. Quand tu fais npm install, npm lit ce fichier pour savoir quoi télécharger.
Quelle est la différence entre dependencies et devDependencies ?
Les dependencies sont les paquets nécessaires à l'exécution de ton code — par exemple express ou react. Les devDependencies ne servent qu'en développement ou au build : runners de tests, bundlers, linters… Quand quelqu'un installe ton paquet comme dépendance de son propre projet, npm ignore tes devDependencies.
Que signifient ^ et ~ dans les versions de package.json ?
Ce sont des opérateurs de plage semver. ^1.2.3 autorise toute version 1.x.x supérieure ou égale à 1.2.3 (même version majeure). ~1.2.3 est plus strict : il accepte 1.2.x à partir de 1.2.3 (même mineure). Un 1.2.3 sans préfixe fige la version exacte. Le fichier package-lock.json, lui, enregistre les versions précisément résolues pour garantir des installations reproductibles.
Comment créer un fichier package.json ?
Place-toi dans un dossier vide et lance npm init pour répondre aux questions, ou npm init -y pour accepter les valeurs par défaut et obtenir le fichier immédiatement. Tu peux aussi l'écrire à la main — c'est du JSON classique. Les seuls champs réellement obligatoires sont name et version.