Le manifeste
Le package.json est le fichier manifeste d'un projet JavaScript. Il raconte ce que ton projet est, ce dont il dépend et comment il se construit ou se lance.
L'origine
Au debut de Node.js, chaque module devait declarer son nom, sa version et ses dépendances pour etre installé correctement. Le package.json s'est imposé avec npm comme format standard pour decrire un package.
C'etait un moyen simple et lisible de partager un module et de permettre a npm de résoudre les dépendances.
Il n'y a pas que npm qui utilise le
package.json: yarn, pnpm et d'autres gestionnaires de paquets s'appuient aussi dessus avec une façon différente de gérer les dépendances et surtout, le moyen de les résoudre.
Les attributs
Dans les grandes lignes, on trouve plusieurs categories de champs dans le package.json :
| Catégorie | Description |
|---|---|
| Identité |
name, version, description, license, author
|
| Dépendances |
dependencies, devDependencies, peerDependencies, optionalDependencies
|
| Scripts | commandes standardisées (npm run dev, build, test, etc.) ou non |
| Compatibilité |
engines, os, cpu pour cadrer l'environnement |
| Publication | ce qui est inclus dans le package, la version, les fichiers d'entrée |
| Customisation | des champs spécifiques aux outils (babel, eslint, jest, etc.) ou tout simplement que vous souhaitez utiliser |
Dans la partie scripts, j'ai récemmenet découvert un mécanisme sympa :
pre<nomScript>etpost<nomScript>se lancent automatiquement avant/après n'importe quel script (ex:prebuild/postbuildautour debuild).
{
"name": "mon-projet", // identite du package
"version": "1.0.0", // version publiee
"description": "Mon app de demo",
"license": "MIT", // license du package
"author": "necraidan",
"scripts": {
"dev": "vite", // commande de dev
"build": "vite build", // build de production
"test": "vitest" // tests
},
"dependencies": {
"react": "^18.3.0" // deps runtime
},
"devDependencies": {
"vite": "^5.4.0", // outils de dev
"vitest": "^2.1.0"
},
"peerDependencies": {
"react": ">=18" // compatibilité attendue
},
"optionalDependencies": {
"fsevents": "^2.3.3" // optionnel (ex: macOS)
},
"engines": {
"node": ">=20" // version node ciblee
},
"eslintConfig": {
"extends": ["eslint:recommended"] // champs custom
}
}
Les types de dépendances
| Type | Description |
|---|---|
dependencies |
indispensables au fonctionnement de l'app en production |
devDependencies |
utiles en développement (tests, lint, build), pas nécessaires en prod |
peerDependencies |
indiquent la compatibilité attendue avec un autre package sans l'embarquer soi‑même (ex. une lib React qui attend React installé) |
optionalDependencies |
optionnelles ; si l'installation échoue, npm continue quand même. À toi de gérer l'absence au runtime |
bundleDependencies |
dépendances embarquées lors de la publication du package |
Pour aller plus loin, la documentation officielle du package.json.
Focus sur les peerDependencies
Une peerDependency, c'est une dépendance que ton package n'installe pas lui-même : il déclare seulement une compatibilité.
le but : éviter les doublons et les conflits de versions (cas typiques : eslint, vite, react côté librairies).
Depuis npm 7+, les peers sont prises en compte automatiquement pendant la résolution et des conflits de versions peuvent faire échouer l'installation :
{
"name": "eslint-plugin-acme",
"peerDependencies": {
"eslint": "^9.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
}
Ici, le plugin n'installe pas eslint : il annonce juste la version attendue. C'est le projet hôte qui doit fournir eslint (en dependencies ou devDependencies selon son usage).
Le bloc peerDependenciesMeta indique que typescript est une peer optionnelle : si typescript n'est pas présent dans le projet hôte, l'installation ne doit pas échouer pour cette raison.
Le lockfile de npm : le package-lock.json
Un lockfile permet de fixer des dépendances. Il est conçu pour être versionné dans votre repo afin de pouvoir avoir une rejouabilité des installations prédictibles. Cependant, cela peut varier selon l’OS ou l'architecture, les dépendances optionnelles ou les champs encore les ce qui est spécifié dans les champs os/cpu.
Le package-lock.json décrit l’état exact résolu : l’arbre complet des dépendances (y compris transitives), avec versions précises, sources de téléchargement (resolved) et empreintes (integrity).
Ce qu’on trouve dedans
Concrètement, on va y trouver un lockfileVersion pour la version du format. La partie packages est la description des packages par chemin, avec resolved et integrity (présent avec le package-lock.json v2+, npm 7+ ; aujourd'hui, npm 9+ génère un lockfileVersion: 3).
npm install vs npm ci
La commande npm install va installer les dépendances en s’appuyant sur package.json et le package-lock.json. Il peut le régénérer ou le modifier selon les cas.
La commande npm ci va suivre le package-lock.json, elle échoue si le package.json et le package-lock.json ne correspondent pas, supprime les node_modules (en théorie 😅) avant de lancer l'installation et ne touche pas au package-lock.json.
Cas avancés
npm-shrinkwrap.jsona priorité surpackage-lock.jsonet fige l'arbre des dépendances pour un package publié. En pratique, c'est surtout adapté aux applications/CLI publiées, et généralement déconseillé pour les bibliothèques.L'attribut
overridespeut être utiliser pour de forcer une version d’une dépendance transitive pour surcharger une version particulière.
Conclusion
Le package.json décrit l’intention de ton projet (scripts, compatibilité, plages de versions). Le package-lock.json, lui, fige l’état exact installé : c’est ce qui rend les builds reproductibles. En pratique : on versionne le lockfile, on installe avec npm ci en CI, et on relit ses diffs comme du code. C’est ce duo qui évite les “ça marche chez moi” et stabilise le projet dans le temps.
Encore une fois c'est un mémo sans doute trivial en partie mais qui permet de réunir en un seul endroit l'essentiel de ce qu'est le package.json.
Top comments (0)