UNPKG

nodebook

Version:

Node.js • Apprendre par la pratique. Familiarisez-vous avec JavaScript, Node.js et l'écosystème de modules npm. Apprenez à concevoir et à déployer des *applications web* et des *outils en ligne de commande*.

1,572 lines (1,500 loc) 163 kB
<!DOCTYPE html> <html lang="fr"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="generator" content="Asciidoctor 2.0.9"> <title>Jouer avec&#160;npm</title> <style> #header, #content, #footer { margin: 2rem auto; max-width: 46rem; } #header { margin-top: 0; } .title, #toctitle { color: var(--dark-accent); } a { /* white-space: nowrap; */ } img, iframe, video, audio { max-width: 100%; } p { font-weight: normal; } /* Taken out from book.css */ dl dt { margin-bottom: 0.3125em; font-weight: bold; } dl dd { margin-bottom: 1.25em; } dt, th.tableblock, td.content, div.footnote { text-rendering: optimizeLegibility; } .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { overflow: auto; word-wrap: break-word; } .literalblock pre.nowrap, .literalblock pre[class].nowrap, .listingblock pre.nowrap, .listingblock pre[class].nowrap { overflow-x: auto; white-space: pre; word-wrap: normal; } .listingblock { margin: 0 0 2em; } .listingblock > .content { position: relative; } .listingblock > .title { font-weight: bold; } .listingblock code[data-lang]::before { display: none; content: attr(data-lang); position: absolute; font-size: 0.75em; top: 1em; right: 1em; line-height: 1; text-transform: uppercase; color: #999; } .listingblock:hover code[data-lang]::before { display: block; } .listingblock.terminal pre .command::before { content: attr(data-prompt); padding-right: 0.5em; color: #999; } td.hdlist1, td.hdlist2 { vertical-align: top; padding-right: 0.625em; } td.hdlist1 { font-weight: bold; } .literalblock + .colist, .listingblock + .colist { margin-top: -1.5em; } .colist td:not([class]):first-child { padding: 0.4em 0.75em 0 0.75em; line-height: 1; vertical-align: top; } .colist td:not([class]):first-child img { max-width: none; } .colist td:not([class]):last-child { padding: 0.25em 0; } /* Custom classes */ .line-through { text-decoration: line-through; } .RemarquePreTitre, #toctitle { font-style: normal; font-weight: bold; } .RemarquePreTitre::after { content: "•"; padding-left: 5px; } .admonitionblock { } .admonitionblock > table, .exampleblock { --commented: rgba(17, 17, 68, .65); --border-radius-base: 8px; background-color: #fafafa; border: 1px solid var(--dark-shade); border-left: none; border-right: none; margin: 1.5em 0; padding: 1em; } .exampleblock .title { font-weight: bold; } .icon .title { font-size: 2em; } .admonitionblock > table td.icon { display: none; vertical-align: middle; } @media screen and (min-width: 769px) { .admonitionblock > table td.icon { display: table-cell; } } .admonitionblock > table td.icon { padding-right: 1em; } .admonitionblock > table td.icon img { max-width: none; } .colist ol { margin-left: 1.5em; /* aligns with the listing edge */ padding-left: 0; font-weight: bold; /* makes it stand out more */ } .colist ol p { margin: 0 0 .5em; } .listingblock:not(.prismjs) pre, .language-bash.hljs { background: #323232; color: wheat; margin: 0; padding: 1rem; } .language-bash.hljs .hljs-built_in, .language-bash.hljs .hljs-builtin-name { color: white; } .language-bash.hljs .hljs-string { color: lightgreen; } .language-bash.hljs .hljs-variable { color: lightskyblue; } .keyseq { font-weight: normal; white-space: nowrap; } .language-bash.hljs .keyseq { color: white; } .language-bash.hljs kbd { background: transparent; box-shadow: none; color: white; font-size: 0.8em; font-weight: bold; padding: 0.1em 0.4em; } .listingblock pre.highlightjs, .listingblock pre.prismjs { background-color: transparent; margin: 0; padding: 0; } .listingblock pre.highlightjs > code, .listingblock pre.prismjs { border-left: 4px solid var(--dark-accent); padding-left: 1em; font-size: .8em; } .listingblock pre.highlightjs > code.language-bash { border-left-color: limegreen; } .token.comment .conum { font-weight: normal; } .hdlist .hdlist1 { text-align: right; white-space: nowrap; } td > p:first-child { margin-top: 0; } .hljs-comment { font-style: normal !important; } #toc.toc2 a { text-decoration: none; white-space: normal; } #toc.toc2 a:hover, #toc.toc2 a:focus { text-decoration: underline; } #toc.toc2 ul { list-style: none; } #toc.toc2 > ul { padding-left: 0; } #toc.toc2 ul ul { padding-left: 1em; } @media screen and (min-width: 769px) { body { padding-left: 25vw !important; } #header, #content, #footer { margin-left: 0; } #toc.toc2 { height: 100%; left: 0; max-width: 20vw; overflow: auto; padding: 1rem; position: fixed; top: 0; z-index: 1000; } #toc.toc2 > ul { font-size: 0.85em; } #toc li.active > a[href^="#"], [id]:target { background: #ffc; } } .admonitionblock.context-callout > table { border-width: 5px; border-color: var(--brand-color); } </style> <style type="text/css" class="prism-theme">/** * prism.js default theme for JavaScript, CSS and HTML * Based on dabblet (http://dabblet.com) * @author Lea Verou */ code[class*="language-"], pre[class*="language-"] { color: black; background: none; text-shadow: 0 1px white; font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; font-size: 1em; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; word-wrap: normal; line-height: 1.5; -moz-tab-size: 4; -o-tab-size: 4; tab-size: 4; -webkit-hyphens: none; -moz-hyphens: none; -ms-hyphens: none; hyphens: none; } pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { text-shadow: none; background: #b3d4fc; } pre[class*="language-"]::selection, pre[class*="language-"] ::selection, code[class*="language-"]::selection, code[class*="language-"] ::selection { text-shadow: none; background: #b3d4fc; } @media print { code[class*="language-"], pre[class*="language-"] { text-shadow: none; } } /* Code blocks */ pre[class*="language-"] { padding: 1em; margin: .5em 0; overflow: auto; } :not(pre) > code[class*="language-"], pre[class*="language-"] { background: #f5f2f0; } /* Inline code */ :not(pre) > code[class*="language-"] { padding: .1em; border-radius: .3em; white-space: normal; } .token.comment, .token.prolog, .token.doctype, .token.cdata { color: slategray; } .token.punctuation { color: #999; } .namespace { opacity: .7; } .token.property, .token.tag, .token.boolean, .token.number, .token.constant, .token.symbol, .token.deleted { color: #905; } .token.selector, .token.attr-name, .token.string, .token.char, .token.builtin, .token.inserted { color: #690; } .token.operator, .token.entity, .token.url, .language-css .token.string, .style .token.string { color: #9a6e3a; background: hsla(0, 0%, 100%, .5); } .token.atrule, .token.attr-value, .token.keyword { color: #07a; } .token.function, .token.class-name { color: #DD4A68; } .token.regex, .token.important, .token.variable { color: #e90; } .token.important, .token.bold { font-weight: bold; } .token.italic { font-style: italic; } .token.entity { cursor: help; } </style> <link rel="stylesheet" href="https://oncletom.io/styles/blog.css?v=v3.3.2"> <!-- WebMentions --> <link rel="pingback" href="https://webmention.io/oncletom.io/xmlrpc"> <link rel="webmention" href="https://webmention.io/oncletom.io/webmention"> <!-- Open Graph --> <meta property="og:type" content="book"> <meta property="og:image" content="https://oncletom.io/images/publications/nodejs-cover.png"> <meta property="og:book:author" content="Thomas Parisot"> <meta property="og:book:isbn" content="978-2212139938"> <meta property="og:book:release_date" content="2018-12-06"> <meta property="og:book:tag" content="Node.js"> <meta property="og:book:tag" content="JavaScript"> <meta property="og:book:tag" content="npm"> <meta property="og:book:tag" content="Développement front-end"> <meta property="og:book:tag" content="Développement back-end"> <meta property="og:locale" content="fr_FR"> <meta property="og:site_name" content="Node.js • Apprendre par la pratique"> <!-- Twitter OpenGraph --> <meta name="twitter:card" content="summary_large_image"> <meta name="twitter:site" content="@oncletom"> <meta name="twitter:creator" content="@oncletom"> <style type="text/css" class="extension-interactive-runner">[data-interactive-runtime="loaded"] .interactive--javascript code[data-lang]:before { background-color: #fcfcfc; border: 1px solid #0c2; border-radius: 1px; color: #0c2; cursor: pointer; display: inline-block; font-weight: bold; padding: .5em 1em; top: -2em; } [lang="en"] [data-interactive-runtime="loaded"] .interactive--javascript code[data-lang]:before { content: "▶ run code"; } [lang="fr"] [data-interactive-runtime="loaded"] .interactive--javascript code[data-lang]:before { content: "▶ voir le résultat"; } .interactive--javascript code[data-label]:before { content: attr(data-label); } .interactive iframe { position: absolute; visibility: hidden; } .interactive.status--loaded iframe { position: inherit; visibility: visible; } .interactive.status--loading { opacity: .5; } </style> <script class="extension-interactive-runner"> (function(d){ document.addEventListener('DOMContentLoaded', function(){ const script = d.createElement('script'); script.src = 'https://embed.runkit.com/'; script.async = true; script.onload = function(){ function makeListingInteractive (element){ if (element.classList.contains('interactive--installed') || element.classList.contains('status--loading')) { return; } const code = element.querySelector('code'); const nodeVersion = /interactive--runtime--node-([^\s]+)/.exec(element.className)[1]; const isEndpoint = element.classList.contains('interactive--endpoint'); const mode = isEndpoint ? 'endpoint' : null; let preamble = ''; const source = code .textContent .replace(/\/\/\s*\(\d+\)$/gm, '') .replace(/^["']?use strict["'][; ]*\n/, ''); if (isEndpoint) { preamble = `process.nextTick(() => { if (typeof module.exports === 'function') { exports.endpoint = module.exports; } else if (typeof server !== 'undefined') { exports.endpoint = server.listeners("request").pop(); } });` } element.classList.add('status--loading'); // eslint-disable-next-line no-undef RunKit.createNotebook({ nodeVersion, element, source, mode, preamble, onLoad: function(ntbk) { element.classList.add('interactive--installed'); element.classList.remove('status--loading'); element.classList.add('status--loaded'); code.parentNode.setAttribute('hidden', true); ntbk.evaluate(); } }); } function installEvents () { function getParent(el, condition) { let parent = el; while(parent = parent.parentNode) { if (condition(parent)) { return parent; } } } function hasClass(className) { return function check(el) { return el.classList.contains(className); } } document.querySelector('body').addEventListener('click', function(el) { if (el.target.classList.contains('language-javascript') || el.target.classList.contains('language-js')) { const parentNode = getParent( el.target, hasClass('interactive--javascript') ); makeListingInteractive(parentNode); } }); } installEvents(); document.body.dataset.interactiveRuntime = 'loaded'; }; document.body.appendChild(script); }); })(document);</script> <style type="text/css"> .listingblock [data-bash-subs]::before { content: attr(data-bash-subs) " "; opacity: .5; } .listingblock [data-bash-conum]::before { content: "(" attr(data-bash-conum) ")"; font-weight: bold; opacity: .7; }</style> <script> (function(d){ d.addEventListener('DOMContentLoaded', function(){ const {origin} = window.location; Array.from(document.querySelectorAll('a[href]')) .filter(link => link.href.indexOf(origin) !== 0) .forEach(link => { link.setAttribute('target', '_blank'); link.setAttribute('rel', 'noopener'); }); }); })(document);</script> <script> (function(d){ d.addEventListener('DOMContentLoaded', function(){ const script = d.createElement('script'); script.src = 'https://unpkg.com/menuspy@1.3.0/dist/menuspy.js'; script.async = true; script.onload = () => new MenuSpy(document.querySelector('#toc'), {enableLocationHash: false}); d.body.appendChild(script); }); })(document);</script> <style type="text/css"> #toc li.active > a[href^="#"] { font-weight: bold; } #toc li.active > a[href^="#"]::before { content: "▶ "; display: inline-block; position: absolute; margin-left: -1.2em; font-size: .8em; margin-top: 3px; } </style> </head> <body class="book toc2 toc-left"> <div id="header"> <h1>Jouer avec&#160;npm</h1> <div id="toc" class="toc2"> <div id="toctitle">Table des matières</div> <ul class="sectlevel1"> <li><a href="#cli">1. Créer un fichier <code>package.json</code></a></li> <li><a href="#modules">2. Installer des modules&#160;npm</a> <ul class="sectlevel2"> <li><a href="#depuis_le_registre_npm">2.1. Depuis le registre npm</a></li> <li><a href="#registry">2.2. Trouver son bonheur dans le registre&#160;npm</a></li> <li><a href="#uninstall">2.3. Désinstaller un module</a></li> <li><a href="#install">2.4. Depuis un fichier <code>package.json</code></a></li> <li><a href="#install.version">2.5. Spécifier une version</a></li> <li><a href="#semver">2.6. Comprendre les numéros de versions (Semantic Versioning)</a></li> <li><a href="#update">2.7. Mises à jour</a></li> </ul> </li> <li><a href="#autres_manières_dinstaller_et_dutiliser_des_modulesnpm">3. Autres manières d&#8217;installer et d&#8217;utiliser des modules&#160;npm</a> <ul class="sectlevel2"> <li><a href="#install.git">3.1. Depuis GitHub, GitLab ou un dépôt&#160;Git</a></li> <li><a href="#install.dev">3.2. Dépendances de développement</a></li> <li><a href="#install.global">3.3. Exécutable système (installation globale)</a></li> </ul> </li> <li><a href="#scripts">4. Outiller un projet avec les scripts&#160;npm</a> <ul class="sectlevel2"> <li><a href="#start">4.1. Démarrer l&#8217;application</a></li> <li><a href="#test">4.2. Exécuter des tests</a></li> <li><a href="#run">4.3. Créer un script&#160;npm personnalisé</a></li> <li><a href="#run-pre-post">4.4. Exécuter des commandes avant et après des scripts&#160;npm</a></li> <li><a href="#run-all">4.5. Automatiser tout l&#8217;outillage projet</a></li> </ul> </li> <li><a href="#package.json">5. Anatomie du fichier <code>package.json</code></a></li> <li><a href="#commands">6. Quelques commandes pour aller plus&#160;loin</a> <ul class="sectlevel2"> <li><a href="#view">6.1. npm view : voir les informations d&#8217;un module</a></li> <li><a href="#npx">6.2. npx : exécuter un module sans l&#8217;installer</a></li> <li><a href="#home">6.3. npm home : visiter le site web d&#8217;un module</a></li> <li><a href="#audit">6.4. npm audit : vérifier la sécurité des dépendances</a></li> <li><a href="#ci">6.5. npm clean-install : installer à toute vitesse</a></li> <li><a href="#doctor">6.6. npm doctor : vérifier l&#8217;état du système</a></li> <li><a href="#config">6.7. npm config : changer les réglages de l&#8217;exécutable <code>npm</code></a></li> <li><a href="#publish">6.8. npm publish : publier un module&#160;npm</a></li> <li><a href="#version">6.9. npm version : déterminer une nouvelle version sans se tromper</a></li> </ul> </li> <li><a href="#questions">7. Questions et mystères autour de&#160;npm</a> <ul class="sectlevel2"> <li><a href="#npm.update">7.1. Quand mettre à jour l&#8217;exécutable&#160;npm ?</a></li> <li><a href="#package-lock">7.2. Je ne vois pas l&#8217;intérêt du fichier package-lock.json</a></li> <li><a href="#bower">7.3. npm c&#8217;est pour le back-end et bower pour le front-end</a></li> <li><a href="#est_ce_que_je_dois_versionner_le_répertoire_node_modules">7.4. Est-ce que je dois versionner le répertoire node_modules ?</a></li> <li><a href="#yarn">7.5. Il paraît que Yarn, c&#8217;est mieux</a></li> <li><a href="#all-your-base-are-belong-to-us">7.6. npm&#160;est lent, il installe la moitié d&#8217;Internet à chaque&#160;fois</a></li> <li><a href="#errors">7.7. Que signifient les erreurs affichées pendant npm install ?</a> <ul class="sectlevel3"> <li><a href="#error-deprecated">7.7.1. Module déprécié</a></li> <li><a href="#error-skipping">7.7.2. Problème avec une dépendance optionnelle</a></li> <li><a href="#error-404">7.7.3. Module introuvable</a></li> <li><a href="#error-crlf">7.7.4. Caractère de fin de ligne sous Windows</a></li> <li><a href="#error-pkg">7.7.5. Fichier package.json incomplet</a></li> <li><a href="#error-peer-dependency">7.7.6. Dépendance complémentaire à installer</a></li> </ul> </li> </ul> </li> <li><a href="#conclusion">8. Conclusion</a></li> </ul> </div> </div> <div id="content"> <div id="preamble"> <div class="sectionbody"> <div class="admonitionblock tip context-callout"> <table> <tr> <td class="icon"> <div class="title">💡</div> </td> <td class="content"> <div class="paragraph"> <p>Vous êtes en train de lire le Chapitre 5 du <a href="https://oncletom.io/node.js/">livre &#8220;Node.js&#8221;</a>, écrit par <a href="https://oncletom.io">Thomas Parisot</a> et publié aux <a href="https://editions-eyrolles.com/Livre/9782212139938">Éditions Eyrolles</a>.</p> </div> <div class="paragraph"> <p>L&#8217;ouvrage vous plaît&#160;? Achetez-le sur <a href="https://amzn.to/2E58PEw">Amazon.fr</a> ou en <a href="https://www.placedeslibraires.fr/livre/9782212139938">librairie</a>. <a href="https://opencollective.com/nodebook">Donnez quelques euros</a> pour contribuer à sa gratuité en ligne.</p> </div> </td> </tr> </table> </div> <div class="paragraph"> <p>Savoir naviguer dans la richesse de l&#8217;écosystème&#160;<code>npm</code> est une force pour la durabilité de nos projets. Ces modules nous aident à façonner un outillage résilient et adapté à chacun de nos projets.</p> </div> <div class="exampleblock"> <div class="content"> <div class="ulist"> <div class="title">Sommaire</div> <ul> <li> <p>Créer un fichier <code>package.json</code></p> </li> <li> <p>Installer un module&#160;<code>npm</code></p> </li> <li> <p>Outiller un projet avec les scripts&#160;<code>npm</code></p> </li> <li> <p>Anatomie du fichier <code>package.json</code></p> </li> <li> <p>Quelques commandes pour aller plus loin</p> </li> <li> <p>Questions et mystères autour de&#160;<code>npm</code></p> </li> </ul> </div> </div> </div> <div class="quoteblock abstract"> <blockquote> <div class="paragraph"> <p>Ce chapitre va nous permettre d&#8217;y voir plus clair du côté des <a href="#modules">modules <code>npm</code></a>. Nous apprendrons comment identifier des modules de confiance, les installer et les mettre à jour sans casser nos projets.</p> </div> <div class="paragraph"> <p>Nous nous tournerons ensuite du côté des <a href="#scripts">scripts&#160;<code>npm</code></a> pour créer un outillage sur mesure et de qualité. Grâce à eux, nous serons en mesure d&#8217;automatiser les tâches répétitives à notre rythme.</p> </div> <div class="paragraph"> <p>Enfin, nous découvrirons des commandes moins connues de&#160;<code>npm</code>. Elles pourrons nous faciliter la vie ou nous débloquer quand ça ne va pas.</p> </div> </blockquote> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">💬</div> </td> <td class="content"> <div class="title"><span class="RemarquePreTitre">Remarque</span> Versions de Node et npm</div> <div class="paragraph"> <p>Le contenu de ce chapitre utilise les versions <strong>Node&#160;v10</strong> et <strong>npm&#160;v6</strong>. Ce sont les versions stables recommandées en&#160;2019.</p> </div> </td> </tr> </table> </div> <div class="paragraph"> <p>Le mot <em>npm</em> correspond à trois concepts différents que nous aborderons tout au long de ce chapitre&#160;:</p> </div> <div class="ulist"> <ul> <li> <p>l'<strong>exécutable</strong> <code>npm</code>&#160;– un programme écrit en ECMAScript&#160;;</p> </li> <li> <p>le <strong>registre</strong> <code>npm</code>&#160;– une plate-forme de distribution de modules&#160;;</p> </li> <li> <p>un <strong>module</strong> <code>npm</code>&#160;– en général installé depuis le registre et utilisable avec les fonctions <code>require()</code> et <code>import</code>.</p> </li> </ul> </div> <div class="paragraph"> <p>Je préciserai toujours si l&#8217;utilisation de <code>npm</code> fait référence à l'<em>exécutable</em>, au <em>registre</em> ou à un <em>module</em>.</p> </div> <div class="paragraph"> <p>L&#8217;exécutable <code>npm</code> est installé par défaut avec Node. Vérifions la version installée en ouvrant un terminal et en écrivant la commande suivante&#160;:</p> </div> <div class="listingblock"> <div class="content"> <pre><span data-bash-subs="$"></span>npm --version 6.4.0</pre> </div> </div> <div class="paragraph"> <p>Si un message s&#8217;affiche en indiquant que <code>npm</code> n&#8217;est pas un programme reconnu, veuillez vous référer au <a href="../chapter-02/index.html">chapitre&#160;2</a> et vérifier que Node v10 est bien installé.</p> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">💡</div> </td> <td class="content"> <div class="title"><span class="RemarquePreTitre">Pratique</span> Jouer avec les exemples dans un terminal</div> <div class="paragraph"> <p>Les exemples titrés d&#8217;un nom de fichier peuvent être installés sur votre ordinateur. Exécutez-les dans un terminal et amusez-vous à les modifier en parallèle de votre lecture pour voir ce qui change.</p> </div> <div class="listingblock"> <div class="title">Installation des exemples via le module npm <code>nodebook</code></div> <div class="content"> <pre><span data-bash-subs="$"></span>npm install --global nodebook <span data-bash-subs="$"></span>nodebook install chapter-05 <span data-bash-subs="$"></span>cd $(nodebook dir chapter-05)</pre> </div> </div> <div class="paragraph"> <p>La commande suivante devrait afficher un résultat qui confirme que vous êtes au bon endroit&#160;:</p> </div> <div class="listingblock"> <div class="content"> <pre><span data-bash-subs="$"></span>node hello.js</pre> </div> </div> <div class="paragraph"> <p>Suivez à nouveau les instructions d&#8217;installation pour rétablir les exemples dans leur état initial.</p> </div> </td> </tr> </table> </div> </div> </div> <div class="sect1"> <h2 id="cli">1. Créer un fichier <code>package.json</code></h2> <div class="sectionbody"> <div class="paragraph"> <p> </p> </div> <div class="paragraph"> <p>La présence d&#8217;un fichier <code>package.json</code> devient nécessaire dès qu&#8217;un projet inclut un <a href="#modules">module&#160;<code>npm</code></a> ou a vocation à être publié pour être repris dans un autre projet&#160;– que ce soit dans un cadre professionnel ou personnel.</p> </div> <div class="paragraph"> <p>Le fichier <code>package.json</code> est la clé de voûte servant à reproduire l&#8217;installation du projet et créer un <a href="#scripts">outillage autonome</a>. La commande <code>npm init</code> génère un tel fichier. L&#8217;utilisation de l&#8217;option <code>--yes</code> va plus vite car elle nous évite de répondre aux questions&#160;:</p> </div> <div class="listingblock"> <div class="content"> <pre><span data-bash-subs="$"></span>npm init --yes</pre> </div> </div> <div class="paragraph"> <p>Si aucun fichier <code>package.json</code> n&#8217;existe dans le répertoire courant, il sera créé avec des valeurs par défaut&#160;– le nom du module correspondra au nom du répertoire courant. Si ce fichier existait déjà, il sera alors préservé et son contenu sera affiché&#160;:</p> </div> <div class="listingblock"> <div class="title">package.json</div> <div class="content"> <pre>{ "name": "nodebook.chapter-05", "private": true, "version": "1.0.0", "main": "./examples/index.js", "description": "", "scripts": { "lint": "eslint ./examples", "print-args": "node examples/print-args.js", "start": "micro examples/app.js", "test": "mocha examples/tests.js", "pretest": "npm run lint" }, "engines": { "node": "^10.0.0" }, "author": "Thomas Parisot (https://oncletom.io)", "license": "MIT", "bugs": { "url": "https://github.com/oncletom/nodebook/issues" }, "homepage": "https://github.com/oncletom/nodebook", "dependencies": { "cowsay": "^1.3.1", "lodash": "^4.17.11", "micro": "^9.3.3" }, "devDependencies": { "eslint": "^5.9.0", "mocha": "^5.2.0" } }</pre> </div> </div> <div class="paragraph"> <p>Nous reviendrons sur la structure du fichier. En attendant, focalisons-nous sur les opérations courantes comme l&#8217;installation de modules.</p> </div> </div> </div> <div class="sect1"> <h2 id="modules">2. Installer des modules&#160;npm</h2> <div class="sectionbody"> <div class="paragraph"> <p> </p> </div> <div class="paragraph"> <p>Le mécanisme des modules est documenté dans le <a href="../chapter-04/index.html#modules">chapitre&#160;4</a>. Les fonctions <code>require()</code> et <code>import</code> chargent nos propres modules mais aussi les modules de base, installés avec Node. Les modules <code>npm</code> sont <strong>complémentaires et téléchargeables</strong> à l&#8217;aide de l&#8217;exécutable <code>npm</code>.</p> </div> <div class="paragraph"> <p>Cette section va nous aider à comprendre ce qui se passe pendant les phases d&#8217;installation, de mise à jour et de désinstallation des modules <code>npm</code>.</p> </div> <div class="sect2"> <h3 id="depuis_le_registre_npm">2.1. Depuis le registre npm</h3> <div class="paragraph"> <p></p> </div> <div class="paragraph"> <p>Le registre&#160;<code>npm</code> (<span class="URL"><a href="https://npmjs.com" class="bare">npmjs.com</a></span>) est l&#8217;hébergement principal des modules ECMAScript, pour Node et le front-end.</p> </div> <div class="paragraph"> <p>La commande <code>npm install</code> s&#8217;utilise directement quand nous connaissons déjà le nom d&#8217;un module à installer, par exemple <em>cowsay</em> (<span class="URL"><a href="https://npmjs.com/cowsay" class="bare">npmjs.com/cowsay</a></span>)&#160;: </p> </div> <div class="listingblock"> <div class="content"> <pre><span data-bash-subs="$"></span>npm install cowsay + cowsay@1.3.1 added 10 packages from 3 contributors in 1.667s found 0 vulnerabilities</pre> </div> </div> <div class="paragraph"> <p>Le module est installé et prêt à être inclus dans un script. Nous constatons aussi que le champ <code>dependencies</code> est apparu dans le fichier <code>package.json</code>&#160;: </p> </div> <div class="listingblock"> <div class="title">package.json</div> <div class="content"> <pre class="highlight highlight-prismjs prismjs language-json"><code class="language-json" data-lang="json"><span class="token punctuation">{</span> ... <span class="token property">"dependencies"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"cowsay"</span><span class="token operator">:</span> <span class="token string">"^1.3.1"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span></code></pre> </div> </div> <div class="paragraph"> <p>L&#8217;exécutable <code>npm</code> tient les comptes des modules installés à notre demande. Cela nous sera utile pour <a href="#install">installer les modules sur un autre ordinateur</a>. Nous reviendrons plus tard sur la notation des versions – on en reparlera sous le nom de <em>versions sémantiques</em> (<em>Semantic Versionning</em>). </p> </div> <div class="listingblock interactive interactive--javascript interactive--runtime--node-v10"> <div class="title">cow.js</div> <div class="content"> <pre class="highlight highlight-prismjs prismjs language-javascript"><code class="language-javascript" data-lang="javascript"><span class="token string">'use strict'</span><span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token punctuation">{</span>say<span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'cowsay'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">const</span> message <span class="token operator">=</span> <span class="token function">say</span><span class="token punctuation">(</span><span class="token punctuation">{</span> text<span class="token punctuation">:</span> <span class="token string">'Bonjour !'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> </div> </div> <div class="paragraph"> <p>L&#8217;inclusion d&#8217;un module&#160;<code>npm</code> est identique à celle d&#8217;un <a href="../chapter-04/index.html#modules-builtin">module de&#160;base</a>. </p> </div> <div class="paragraph"> <p>Regardons le résultat sans plus tarder&#160;:</p> </div> <div class="listingblock"> <div class="content"> <pre><span data-bash-subs="$"></span>node cow.js ___________ < Bonjour ! > ----------- \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w |</pre> </div> </div> <div class="paragraph"> <p>Le module&#160;<code>npm</code> nous a permis d&#8217;utiliser du code sans avoir à le créer, alors qu&#8217;il n&#8217;était pas fourni par la plate-forme Node.</p> </div> <div class="paragraph"> <p>Maintenant que nous savons installer un module&#160;<code>npm</code>, nous pouvons en chercher d&#8217;autres et comprendre comment les utiliser.</p> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">💬</div> </td> <td class="content"> <div class="title"><span class="RemarquePreTitre">Question</span> Où sont stockés les modules&#160;npm ?</div> <div class="paragraph"> <p> Les modules <code>npm</code> et leurs dépendances sont stockés dans un répertoire <code>node_modules</code>, situé au même niveau que le fichier <code>package.json</code>.</p> </div> </td> </tr> </table> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">💬</div> </td> <td class="content"> <div class="title"><span class="RemarquePreTitre">Sous le capot</span> Ce que fait l&#8217;exécutable&#160;npm pendant l&#8217;installation</div> <div class="paragraph"> <p> L&#8217;exécutable <code>npm</code> effectue un bon nombre d&#8217;actions lorsqu&#8217;on valide la commande <code>npm install cowsay</code>&#160;:</p> </div> <div class="olist arabic"> <ol class="arabic"> <li> <p>Il interroge le registre <span class="URL">npmjs.com</span> pour obtenir des informations sur le module demandé.</p> </li> <li> <p>Il détermine que <code>1.3.1</code> est la version la plus récente.</p> </li> <li> <p>Il télécharge une archive compressée (<code>.tar.gz</code>) qui contient tous les fichiers de la version <code>1.3.1</code>.</p> </li> <li> <p>L&#8217;archive est décompressée dans le répertoire <code>node_modules</code>.</p> </li> <li> <p>Les dépendances sont elles aussi téléchargées puis décompressées dans le répertoire <code>node_modules</code>.</p> </li> <li> <p>Le module <code>cowsay</code> est inscrit dans le fichier <code>package.json</code>.</p> </li> </ol> </div> </td> </tr> </table> </div> <div class="admonitionblock note"> <table> <tr> <td class="icon"> <div class="title">💬</div> </td> <td class="content"> <div class="title"><span class="RemarquePreTitre">Remarque</span> Dépendances de développement</div> <div class="paragraph"> <p> Il existe une variante de la commande pour distinguer les dépendances spécifiques à l&#8217;outillage du projet. Rendez-vous dans la section &#8220;<a href="#install.dev">Dépendances de développement</a>&#8221; pour en savoir plus.</p> </div> </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="registry">2.2. Trouver son bonheur dans le registre&#160;npm</h3> <div class="paragraph"> <p> </p> </div> <div class="paragraph"> <p>Le registre <code>npm</code> (<span class="URL"><a href="https://npmjs.com" class="bare">npmjs.com</a></span>) fourmille de modules – de simples fonctions, des bibliothèques ou des frameworks complets. Ils couvrent un spectre d&#8217;usages très larges&#160;: accès aux bases de données, frameworks web, outils <em>front-end</em>, utilitaires de test, compression de données, paiement bancaire, des frameworks mobiles, etc.</p> </div> <div class="paragraph"> <p>Cherchons une bibliothèque pour nous connecter à une base de données MySQL ou MariaDB. Tapez &#8220;mysql&#8221; dans le champ de recherche du registre&#160;<code>npm</code> ou saisissez directement l&#8217;URL menant aux résultats de cette recherche (<span class="URL"><a href="https://npmjs.com/search?q=mysql" class="bare">npmjs.com/search?q=mysql</a></span>)&#160;:</p> </div> <div class="imageblock"> <div class="content"> <img src="./images/npm-registry-search.png" alt="npm registry search"> </div> <div class="title">Figure 1. Extrait des résultats d&#8217;une recherche de modules&#160;npm avec le mot-clé &#8220;mysql&#8221;</div> </div> <div class="paragraph"> <p>Les résultats sont triés par pertinence&#160;– un mélange entre popularité, qualité et maintenance des projets.</p> </div> <div class="paragraph"> <p>Je trouve qu&#8217;il est difficile de décider uniquement en regardant la liste. J&#8217;ai tendance à ouvrir un onglet par module pour en lire la documentation. Prenons le cas du module <em>mysql2</em> (<span class="URL"><a href="https://npmjs.com/mysql2" class="bare">npmjs.com/mysql2</a></span>) justement&#160;:</p> </div> <div class="imageblock"> <div class="content"> <img src="./images/npm-package-mysql2.png" alt="npm package mysql2"> </div> <div class="title">Figure 2. Extrait de la page consacrée au module&#160;npm&#160;mysql2</div> </div> <div class="paragraph"> <p>Plusieurs éléments de cette page tendent à me rassurer et m&#8217;aident à juger de la robustesse de ce module&#160;:</p> </div> <div class="ulist"> <ul> <li> <p>les badges colorés qui affichent le statut d&#8217;exécution des tests&#160;;</p> </li> <li> <p>une introduction de <strong>documentation claire et concise</strong>&#160;;</p> </li> <li> <p>un <strong>nombre de téléchargements</strong> en progrès réguliers&#160;;</p> </li> <li> <p>l&#8217;utilisation avec des <a href="../chapter-03/index.html#promise">promesses</a>&#160;;</p> </li> <li> <p>le nombre important de modules dépendants&#160;;</p> </li> <li> <p><strong>je reconnais une personne</strong> qui contribue du code de qualité&#160;– Rebecca&#160;Turner (<span class="URL"><a href="https://npmjs.com/~iarna" class="bare">npmjs.com/~iarna</a></span>).</p> </li> </ul> </div> <div class="paragraph"> <p>J&#8217;ai un doute quand je lis <span class="Menu">108&#160;issues</span> et <span class="Menu">13&#160;pull&#160;requests</span>. Dans ce cas-là, je me dis que les personnes qui maintiennent le projet ne sont pas forcément très réactives.</p> </div> <div class="paragraph"> <p>Cependant, il y a suffisamment d&#8217;indicateurs au vert pour l&#8217;installer avec <code>npm install mysql2</code> puis l&#8217;essayer dans un script. </p> </div> <div class="paragraph"> <p>Le module <em>mysql-libmysqlclient</em> (<span class="URL"><a href="https://npmjs.com/mysql-libmysqlclient" class="bare">npmjs.com/mysql-libmysqlclient</a></span>) ne me fait pas du tout le même effet.</p> </div> <div class="imageblock"> <div class="content"> <img src="./images/npm-package-mysql-libmysqlclient.png" alt="npm package mysql libmysqlclient"> </div> <div class="title">Figure 3. Extrait de la page consacrée au module&#160;npm&#160;mysql-libmysqlclient</div> </div> <div class="paragraph"> <p>La page du module ne met pas d&#8217;exemple simple à comprendre et fait référence à des versions de Node antédiluviennes. Rien n&#8217;indique qu&#8217;il ne peut pas fonctionner avec Node&#160;v10, mais la présence du mot <span class="Menu">binding</span> m&#8217;évoque que l&#8217;installation du module compile un programme écrit dans un autre langage – en l&#8217;occurrence, <em>libmysqlclient</em>. </p> </div> <div class="paragraph"> <p>Point positif&#160;: il n&#8217;y a que <span class="Menu">14&#160;issues</span> GitHub. C&#8217;est peu, mais l&#8217;une d&#8217;entre elles est intitulée <span class="Menu">Does not work with any modern version of Node.js</span>. Cela confirme mes doutes&#160;; c&#8217;est suffisant pour que je passe mon chemin.</p> </div> <div class="paragraph"> <p>En continuant plus loin dans la liste des résultats de recherche, je suis tombé sur le module nommé <em>falchion</em>.</p> </div> <div class="imageblock"> <div class="content"> <img src="./images/npm-package-falchion.png" alt="npm package falchion"> </div> <div class="title">Figure 4. Extrait de la page consacrée au module&#160;npm&#160;falchion</div> </div> <div class="paragraph"> <p>Il n&#8217;y a qu&#8217;une seule version du module, qui date de quatre années avec une documentation qui tient sur une ligne. Il y a très peu de chances que nous puissions en faire quelque chose.</p> </div> <div class="paragraph"> <p>Voici au final ce que j&#8217;estime être le plus important pour me faire une idée d&#8217;un module et décider de l&#8217;installer ou&#160;non&#160;: </p> </div> <div class="ulist"> <ul> <li> <p>Présence d&#8217;une <strong>documentation</strong>&#160;– je peux me faire une idée des fonctionnalités et de la complexité d&#8217;utilisation du module.</p> </li> <li> <p>Des badges d'<strong>intégration continue</strong>&#160;– je sais ainsi qu&#8217;il y a des tests unitaires qui sont exécutés automatiquement avant que le module soit publié.</p> </li> <li> <p>Le nombre de <strong>téléchargements</strong>&#160;– je sais si d&#8217;autres personnes s&#8217;en servent en espérant qu&#8217;ils remontent les problèmes rencontrés.</p> </li> <li> <p>Le nombre de <strong>versions</strong>&#160;– cela me donne une idée de la maturité du projet et de la réactivité aux demandes de la communauté.</p> </li> </ul> </div> <div class="paragraph"> <p>Ce sont des <strong>critères subjectifs</strong>. Un module est parfois populaire par ancienneté alors qu&#8217;il existe des alternatives, plus légères ou plus simples d&#8217;utilisation. C&#8217;est le cas du module <em>moment.js</em>, plus populaire que <em>date-fns</em>&#160;– que je préfère.</p> </div> <div class="paragraph"> <p>Il y a aussi des modules dans lesquels j&#8217;ai une confiance quasi-aveugle. Ils sont publiés par les personnes présentes dans cette liste non&#160;exhaustive&#160;:</p> </div> <table class="tableblock frame-all grid-all stretch"> <caption class="title">Tableau 1. Personnes ayant écrit des modules npm à suivre</caption> <colgroup> <col style="width: 33.3333%;"> <col style="width: 33.3333%;"> <col style="width: 33.3334%;"> </colgroup> <tbody> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://npmjs.com/~dougwilson" class="URL">dougwilson</a></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/jdalton" class="URL">jdalton</a></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://npmjs.com/~sindresorhus" class="URL">sindresorhus</a></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://npmjs.com/~feross" class="URL">feross</a></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/jshttp" class="URL">jshttp</a></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://npmjs.com/~substack" class="URL">substack</a></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://npmjs.com/~fgribreau" class="URL">fgribreau</a></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://npmjs.com/~mbostock" class="URL">mbostock</a></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://npmjs.com/~zkat" class="URL">zkat</a></p></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://npmjs.com/~iarna" class="URL">iarna</a></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://github.com/nodejitsu" class="URL">nodejitsu</a></p></td> <td class="tableblock halign-left valign-top"></td> </tr> <tr> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://npmjs.com/~isaacs" class="URL">isaacs</a></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="https://npmjs.com/~rwaldron" class="URL">rwaldron</a></p></td> <td class="tableblock halign-left valign-top"></td> </tr> </tbody> </table> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">💡</div> </td> <td class="content"> <div class="title"><span class="RemarquePreTitre">Pratique</span> Sélection de modules npm</div> <div class="paragraph"> <p>J&#8217;ai compilé une liste de modules utiles pour mieux démarrer dans vos projets. Vous la trouverez en <a href="../appendix-a/index.html">annexe&#160;A</a>.</p> </div> </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="uninstall">2.3. Désinstaller un module</h3> <div class="paragraph"> <p> </p> </div> <div class="paragraph"> <p>L&#8217;utilisation de la commande <code>npm uninstall</code> supprime en toute sécurité un module&#160;<code>npm</code> et les fichiers qu&#8217;il a installés. La commande le retire ensuite de la liste des dépendances du fichier <code>package.json</code>.</p> </div> <div class="listingblock"> <div class="content"> <pre><span data-bash-subs="$"></span>npm uninstall cowsay removed 10 packages in 1.963s found 0 vulnerabilities</pre> </div> </div> <div class="paragraph"> <p>Le module <em>cowsay</em> n&#8217;est plus installé. Que se passe-t-il si nous exécutons à nouveau un l&#8217;exemple <code>cow.js</code>&#160;?</p> </div> <div class="listingblock"> <div class="content"> <pre><span data-bash-subs="$"></span>node cow.js internal/modules/cjs/loader.js:596 throw err; ^ Error: Cannot find module 'cowsay'</pre> </div> </div> <div class="paragraph"> <p></p> </div> <div class="paragraph"> <p>Le chargement du module a échoué car Node n&#8217;arrive pas à le trouver – et c&#8217;est normal.</p> </div> <div class="paragraph"> <p>Nous devons relancer la commande <code>npm install cowsay</code> pour que le script fonctionne à nouveau.</p> </div> </div> <div class="sect2"> <h3 id="install">2.4. Depuis un fichier <code>package.json</code></h3> <div class="paragraph"> <p> </p> </div> <div class="paragraph"> <p>Jusqu&#8217;à présent, nous avons installé des modules en les ajoutant un par&#160;un. La procédure est légèrement différente quand nous installons le projet de zéro ou quand le fichier <code>package.json</code> a été mis à jour par un·e collègue, par exemple.</p> </div> <div class="paragraph"> <p>L&#8217;exemple suivant illustre la remise à zéro des modules utilisés en exemple de ce chapitre&#160;:</p> </div> <div class="listingblock"> <div class="content"> <pre><span data-bash-subs="$"></span>cd $(nodebook dir {chapter-id} --root) <span data-bash-subs="$"></span>rm -rf node_modules <span data-bash-subs="$"></span>npm install added 164 packages from 583 contributors in 4.781s found 0 vulnerabilities</pre> </div> </div> <div class="paragraph"> <p>Nous nous sommes positionnés dans un répertoire qui contient un fichier <code>package.json</code> puis nous avons supprimé tout ce qui aurait pu être installé.</p> </div> <div class="paragraph"> <p>La commande <code>npm install</code> s&#8217;utilise de manière systématique quand nous récupérons du code avec Git pour la première fois (<code>git clone</code>) ou après une mise à jour, par exemple avec <code>git pull</code>.</p> </div> <div class="paragraph"> <p>L&#8217;exécutable <code>npm</code> vérifie que la correspondance est bien respectée entre ce qui est installé dans le répertoire <code>node_modules</code> et les modules listés dans le fichier <code>package.json</code>. La commande <code>npm install</code> installe, met à jour et retire les modules nécessaires.</p> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">💡</div> </td> <td class="content"> <div class="title"><span class="RemarquePreTitre">Pratique</span> npm&#160;clean-install (npm ci)</div> <div class="paragraph"> <p> La commande <code>npm clean-install</code> réinstalle un projet de zéro de manière prédictible. <a href="#ci">Nous y reviendrons plus loin</a>.</p> </div> </td> </tr> </table> </div> </div> <div class="sect2"> <h3 id="install.version">2.5. Spécifier une version</h3> <div class="paragraph"> <p></p> </div> <div class="paragraph"> <p>Par défaut, l&#8217;exécutable <code>npm</code> installe la dernière version d&#8217;un module. Nous avons la liberté d&#8217;en installer d&#8217;autres qui sont antérieures. C&#8217;est pratique quand des modules arrêtent de prendre en charge des navigateurs web ou des versions de Node alors que nous les utilisons encore.</p> </div> <div class="paragraph"> <p>Nous allons nous servir du module <em>lodash</em> (<span class="URL"><a href="https://npmjs.com/lodash" class="bare">npmjs.com/lodash</a></span>) pour illustrer nos allées et venues entre différentes versions. À l&#8217;heure où j&#8217;écris ces lignes, sa version la plus récente est la <code>4.17.11</code>. C&#8217;est ce que rapporte le résultat de la commande <code>npm install lodash</code>&#160;:</p> </div> <div class="listingblock"> <div class="content"> <pre><span data-bash-subs="$"></span>npm install lodash + lodash@4.17.11</pre> </div> </div> <div class="paragraph"> <p>L&#8217;utilisation du caractère <code>@</code> conjointement à un numéro de version précise la version à installer&#160;:</p> </div> <div class="listingblock"> <div class="content"> <pre><span data-bash-subs="$"></span>npm install lodash@3.0.0 + lodash@3.0.0</pre> </div> </div> <div class="paragraph"> <p>Nous avons installé une version précise, mais il y a sûrement des mises à jour qui ont suivi pour corriger des bogues. Le problème est que, à ce stade, nous ne connaissons pas le numéro de version à spécifier. Idéalement, je préférerais installer la version la plus récente de la série&#160;3. Il se trouve que l&#8217;exécutable <code>npm</code> sait le faire pour nous et sans effort&#160;:</p> </div> <div class="listingblock"> <div class="content"> <pre><span data-bash-subs="$"></span>npm install lodash@3 + lodash@<mark>3.10.1</mark></pre> </div> </div> <div class="paragraph"> <p>Nous pouvons faire la même chose avec les versions les plus récentes de la série&#160;3 et de la série&#160;2.2&#160;:</p> </div> <div class="listingblock"> <div class="content"> <pre><span data-bash-subs="$"></span>npm install lodash@3 + lodash@3.10.1 <span data-bash-subs="$"></span>npm install lodash@2.2 + lodash@2.2.1</pre> </div> </div> <div class="admonitionblock tip"> <table> <tr> <td class="icon"> <div class="title">💡</div> </td> <td class="content"> <div class="title"><span class="RemarquePreTitre">Pratique</span> Connaître toutes les versions d&#8217;un module</div> <div class="paragraph"> <p> La <a href="#view">commande <code>npm view</code></a> affiche les informations d&#8217;un module&#160;<code>npm</code> directement depuis notre terminal. Elle affiche toutes les versions publiées avec l&#8217;argument <code>versions</code>&#160;:</p> </div> <div class="listingblock"> <div class="content"> <pre><span data-bash-subs="$"></span>npm view lodash <mark>versions</mark> [ '0.1.0', '0.2.0', ... '1.0.0', '1.0.1', '1.0.2', ... ]</pre> </div> </div> </td> </tr> </table> </div> <div class="paragraph"> <p>Revenons à la version la plus récente en réutilisant la <a href="#install">commande d&#8217;installation</a> abordée auparavant&#160;: </p> </div> <div class="listingblock"> <div class="content"> <pre><span data-bash-subs="$"></span>npm install lodash + lodash@2.4.2</pre> </div> </div> <div class="paragraph"> <p>Quelque chose d&#8217;inattendu s&#8217;est produit&#160;: la version la plus récente de la série&#160;2 a été installée au lieu de la version&#160;4.17.11. Nous trouverons un élément de réponse dans le fichier <code>package.json</code>&#160;: </p> </div> <div class="listingblock"> <div class="title">package.json</div> <div class="content"> <pre class="highlight highlight-prismjs prismjs language-json"><code class="language-json" data-lang="json">{ ... "dependencies": { "cowsay": "^1.3.1", "lodash": "<mark>^2.4.2</mark>" } }</code></pre> </div> </div> <div class="paragraph"> <p>L&#8217;exécutable <code>npm</code> respecte la version indiquée dans le fichier <code>package.json</code> si aucune autre n&#8217;est précisée dans la commande. Si la dépendance n&#8217;est pas listée, alors l&#8217;exécutable <code>npm</code> installe la version la plus récente.</p> </div> <div class="paragra