Руководство по Node.js для начинающих. Часть 4

phzybyqyo4afy-fkn51qrxgqupc.png

Доброго времени суток, друзья!

Продолжаю публиковать перевод этого руководства по Node.js.

Другие части:

Часть 1
Часть 2
Часть 3
Часть 4

Файл package-lock.json


В пятой версии Node.js был представлен файл package-lock.json.

Что это такое? Для чего он нужен, если есть файл package.json?

Задачей package-lock.json является фиксация конкретных версий установленных пакетов для обеспечения 100% работы продукта при обновлении пакетов их разработчиками.

Это решает проблему package.json. В этом файле вы можете указать, какие пакеты нуждаются в обновлении (патчевом или минорном), используя спецификаторы версий, например:

  • Если указано ~0.13.0, значит, допустимы только патчевые релизы: 0.13.1 подойдет, а 0.14.0 нет.
  • Если указано ^0.13.0, значит, допустимы патчевые и минорные релизы: 0.13.1, 0.14.0 и т.д.
  • Если указано 0.13.0, значит, будет использоваться только указанная версия.


Обычно, вы не отправляете в Git папку node_modules, и при установке проекта на другом компьютере с помощью npm install, если присутствует спецификатор ~, допускающий патчевые релизы, и пакет обновился, будет установлена новая версия. Тоже самое справедливо для спецификатора ^, допускающего патчевые и минорные релизы.

Это можете быть вы или другой человек на другом конце света, устанавливающий проект посредством npm install.

Таким образом, исходный проект и заново проиницализированный проект — это два разных проекта. Даже если патчевый или минорный релизы не содержат критичных изменений, это может привести (и часто приводит) к ошибкам.

package-lock.json хранит записи о конкретных версиях установленных пакетов, и npm установит именно эти версии при выполнении npm install.
Эта концепция не является новой и другие пакетные менеджеры (такие как Composer в PHP) используют ее на протяжении многих лет.

package-lock.json следует отправлять в Git, чтобы другие люди могли его получить, если проект является открытым или у вас есть соавторы, или когда вы используете Git как источник для разработки.

Версии зависимостей в package-lock.json обновляются вручную с помощью npm update.

Пример
Вот пример структуры package-lock.json, который мы получаем при выполнении npm install cowsay в пустой директории:

{
  "requires": true,
  "lockfileVersion": 1,
  "dependencies": {
    "ansi-regex": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.
0.0.tgz",
      "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
    },
    "cowsay": {
      "version": "1.3.1",
      "resolved": "https://registry.npmjs.org/cowsay/-/cowsay-1.3.1.tgz"
,
      "integrity": "sha512-3PVFe6FePVtPj1HTeLin9v8WyLl+VmM1l1H/5P+BTTDkM
Ajufp+0F9eLjzRnOHzVAYeIYFF5po5NjRrgefnRMQ==",
      "requires": {
        "get-stdin": "^5.0.1",
        "optimist": "~0.6.1",
        "string-width": "~2.1.1",
        "strip-eof": "^1.0.0"
      }
    },
    "get-stdin": {
      "version": "5.0.1",
      "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.
1.tgz",
      "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g="
    },
    "is-fullwidth-code-point": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/
is-fullwidth-code-point-2.0.0.tgz",
      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
    },
    "minimist": {
      "version": "0.0.10",
      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10
.tgz",
      "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
    },
    "optimist": {
      "version": "0.6.1",
      "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
      "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",

      "requires": {
        "minimist": "~0.0.1",
        "wordwrap": "~0.0.2"
      }
    },
    "string-width": {
      "version": "2.1.1",
      "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
      "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
      "requires": {
        "is-fullwidth-code-point": "^2.0.0",
        "strip-ansi": "^4.0.0"
      }
    },
    "strip-ansi": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
      "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
      "requires": {
        "ansi-regex": "^3.0.0"
      }
    },
    "strip-eof": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
    },
    "wordwrap": {
      "version": "0.0.3",
      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
      "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
    }
  }
}


Мы устанавливаем cowsay, который зависит от:

  • get-stdin
  • optimist
  • string-width
  • strip-eof


В свою очередь, эти пакеты нуждаются в других пакетах, указанных в свойстве requires:

  • ansi-regex
  • is-fullwidth-code-point
  • minimist
  • wordwrap
  • strip-eof


Они добавляются в файл в алфавитном порядке, каждый имеет поле version, поле resolved, указывающее на местонахождения пакета, и строку integrity, используемую для идентификации пакета.

Поиск версии установленного пакета


Для того, чтобы увидеть последние версии установленных пакетов, включая их зависимости, необходимо выполнить npm list.

Например:

❯ npm list
/Users/joe/dev/node/cowsay
└─┬ cowsay@1.3.1
  ├── get-stdin@5.0.1
  ├─┬ optimist@0.6.1
  │ ├── minimist@0.0.10
  │ └── wordwrap@0.0.3
  ├─┬ string-width@2.1.1
  │ ├── is-fullwidth-code-point@2.0.0
  │ └─┬ strip-ansi@4.0.0
  │   └── ansi-regex@3.0.0
  └── strip-eof@1.0.0


Вы, конечно, можете просто открыть файл package-lock.json, но такое представление является более наглядным.

npm list -g делает тоже самое, но для глобально установленных пакетов.

Для того, чтобы получить список пакетов верхнего уровня (обычно это те пакеты, которые вы устанавливали вручную), необходимо выполнить npm list --depth=0:

❯ npm list --depth=0
/Users/joe/dev/node/cowsay
└── cowsay@1.3.1


Версию определенного пакета можно получить, указав его имя:

❯ npm list cowsay
/Users/joe/dev/node/cowsay
└── cowsay@1.3.1


Это также работает с зависимостями установленного пакета:

❯ npm list minimist
/Users/joe/dev/node/cowsay
└─┬ cowsay@1.3.1
  └─┬ optimist@0.6.1
    └── minimist@0.0.10


Если вы хотите увидеть последнюю доступную версию пакета, имеющуюся в npm, выполните npm view version:

❯ npm view cowsay version

1.3.1

Установка старых версий пакета


Вы можете устанавливать старые версии пакетов с помощью спецификатора @:

npm install @


Например:

npm install cowsay


установит версию 1.3.1 (последнюю на момент написания этих строк).

Установим версию 1.2.0:

npm install cowsay@1.2.0


Тоже самое можно делать с глобальными пакетами:

npm install -g webpack@4.16.4


Для того, чтобы увидеть список предыдущих версий пакета, необходимо выполнить npm view versions:

❯ npm view cowsay versions

[ '1.0.0',
  '1.0.1',
  '1.0.2',
  '1.0.3',
  '1.1.0',
  '1.1.1',
  '1.1.2',
  '1.1.3',
  '1.1.4',
  '1.1.5',
  '1.1.6',
  '1.1.7',
  '1.1.8',
  '1.1.9',
  '1.2.0',
  '1.2.1',
  '1.3.0',
  '1.3.1' ]

Обновление зависимостей до последних версий


При установке пакета с помощью npm install , в папку node_modules загружается последняя доступная версия пакета, запись о пакете добавляется в package.json и package-lock.json, находящиеся в текущей директории.

npm также устанавливает последние доступные версии зависимостей пакета.

Допустим, вы установили cowsay.

При выполнении npm install cowsay запись о пакете была добавлена в package.json:

{
  "dependencies": {
    "cowsay": "^1.3.1"
  }
}


А вот что было записано в package-lock.json (мы удалили вложенные зависимости для ясности):

{
  "requires": true,
  "lockfileVersion": 1,
  "dependencies": {
    "cowsay": {
      "version": "1.3.1",
      "resolved": "https://registry.npmjs.org/cowsay/-/cowsay-1.3.1.tgz",
      "integrity": "sha512-3PVFe6FePVtPj1HTeLin9v8WyLl+VmM1l1H/5P+BTTDkMAjufp+0F9eLjzRnOHzVAYeIYFF5po5NjRrgefnRMQ==",
      "requires": {
        "get-stdin": "^5.0.1",
        "optimist": "~0.6.1",
        "string-width": "~2.1.1",
        "strip-eof": "^1.0.0"
      }
    }
  }
}


Оба файла говорят нам, что мы установили cowsay версии 1.3.1, правило обновления (^1.3.1) гласит, что нам подойдут патчевые и минорные релизы: 1.3.2, 1.4.0 и т.д.

Если появится новая минорная или патчевая версия, мы выполним npm update, и установленная версия обновится, в package-lock.json появится запись о новой версии.

package.json не изменится.

Для того, чтобы увидеть новые релизы пакетов, необходимо выполнить npm outdated.

Вот список новых релизов пакетов из одного репозитория, который какое-то время не обновлялся:

ldpeku9brlbmus1ujsiagjluua0.png

Некоторые из релизов являются мажорными. Запуск npm update не обновит такие пакеты. Мажорные релизы никогда не обновляются таким способом, поскольку они включают в себя несовместимые изменения и npm заботится о работоспособности вашего приложения.

Для обновления мажорных версий всех пакетов, установите пакет npm-check-updates глобально: npm i npm-check-updates -g и запустите его: ncu -u.

Это обновит все правила обновлений пакетов, указанные в разделах dependencies и devDependencies файла package.json так, что npm сможет обновить их до новых мажорных версий.

После этого выполняем обновление: npm update.

Семантическое версионирование в npm


Концепция семантического версионирования очень проста: все версии имеют три цифры: x.y.z:

  • первая цифра — основная (главная, мажорная) версия
  • вторая цифра — второстепенная (минорная) версия
  • третья цифра — патч (патчевая версия)


Когда вы делаете новый релиз, вы не присваиваете версию как вам хочется, но следуете определенным правилам:

  • вы увеличиваете мажорную версию при внесении в интерфейс несовместимых с предыдущей версией изменений
  • вы увеличиваете минорную версию при добавлении функционала, совместимого с предыдущей версией
  • вы увеличиваете патчевую версию при исправлении ошибок


Правила семантического версионирования едины для всех языков программирования, очень важно, чтобы каждый пакет в npm следовал этим правилам, поскольку на этом основана вся система.

Почему это так важно?

Потому что в package.json мы определяем правила или условия обновления, т.е. какие версии являются приемлемыми при выполнении npm update.

В качестве спецификаторов используются следующие символы:

  • ^
  • ~
  • >
  • >=
  • <
  • <=
  • =
  • -
  • ||


Рассмотрим их подробнее:

  • ^: если указано ^0.13.0, значит, допустимы патчевые и минорные релизы: 0.13.1, 0.14.0 и т.д.
  • ~: если указано ~0.13.0, значит, допустимы только патчевые релизы: 0.13.1 подойдет, а 0.14.0 нет.
  • >: допустимы любые новые версии
  • >=: допустимы аналогичные или новые версии
  • <=: допустимы аналогичные или старые версии
  • <: допустимы любые старые версии
  • =: допустима только указанная версия
  • -: допустим диапазон версий. Например: 2.1.0-2.6.2
  • ||: допустима комбинация версий. Например: < 2.1 || > 2.6


Спецификаторы можно комбинировать, например: 1.0.0 || >= 1.1.0 < 1.2.0 — допустима указанная версия или диапазон между версиями 1.1.0 и 1.2.0 (не включая последнюю).

Есть парочка дополнительных правил:

  • без символов: допустима только указанная версия
  • latest: последняя доступная версия

Удаление пакетов


Для удаления пакета, установленного локально (с помощью npm i в папку node_modules), запустите npm unistall из корневой директории проекта (директории, в которой находится node_modules).

Если добавить флаг -S или --save, то из package.json будет удалена запись об удаленном пакете.

Для удаление записи о пакете для разработки, запись о котором содержится в разделе devDependencies файла package.json, следует использовать флаг -D / --save-dev:

npm uninstall -S 
npm uninstall -D 


Если пакет установлен глобально, необходимо использовать фалг -g / --global: npm uninstall -g.

Например: npm uninstall -g webpack.

Глобальные и локальные пакеты


Основное различие между локальными и глобальными пакетами состоит в следующем:

  • локальные пакеты устанавливаются в директорию, в которой вы запускаете npm install , и помещаются в папку node_modules, находящуюся в этой директории
  • глобальные пакеты устанавливаются в определенное место вашей файловой системы (которое зависит от настроек), независимо от того, где вы запускаете npm install -g


Так какой способ лучше выбрать?

Как правило, все пакеты устанавливаются локально.

Это позволяет гарантировать, что разные приложения на вашем компьютере смогут использовать нужные им версии пакетов.

Обновление глобального пакета приведет к использованию всеми проектами нового релиза, это может привести к кошмару в терминах поддержки, поскольку некоторые пакеты или их зависимости могут оказаться несовместимыми с этим релизом.

Все проекты должны иметь собственные локальные версии пакетов. Это может показаться пустой тратой ресурсов, но это минимальная плата за отсутвие негативных последствий.

Пакеты устанавливаются глобально, когда они предоставляют исполняемые команды, которые запускаются из командной строки (CLI) и используются во многих проектах.

Вы можете устанавливать исполянемые команды локально и запускать их с помощью npx, но в некоторых случаях все же лучше предпочесть глобальную установку.

Отличными примерами глобальных пакетов, которые могут быть вам известны, являются следующие:

  • npm
  • create-react-app
  • vue-cli
  • grunt-cli
  • mocha
  • react-native-cli
  • gatsby-cli
  • forever
  • nodemon


Возможно, в вашей системе уже установлены какие-то глобальные пакеты. Вы можете увидеть список таких пакетов, запустив npm list -g --depth 0.

dependencies и devDependencies


При установке пакета посредством npm install , вы устанавливаете его как зависимость (dependency).

Запись об установленном пакете добавляется в раздел dependencies файла package.json.

При добавлении флага -D или --save-dev вы устанавливаете зависимость для разработки, запись о ней добавляется в раздел devDependencies.

Зависимости для разработки предназначены только для разработки, они не нужны в продакше. Например, для тестирования продукта, webpack (сборщик проекта) или Babel (транспилятор).

При выполнении npm install и при наличии package.json, npm предполагает, что вы разворачиваете тестовое приложение, поэтому все dependencies и devDependencies устанавливаются.

Во избежание установки зависимостей для разработке необходимо выполнить npm install с флагом --production: npm install --production.

Запуск пакета с помощью npx


npx — это очень мощная команда, доступная начиная с версии 5.2, представленной в июле 2017.

Если вы не хотите устанавливать npm, то можете установить npx как самостеятельный пакет.

npx позволяет запускать сборку проекту и публиковать пакеты в реестре npm.

Запуск локальных команд
Node.js-разработчики раньше устанавливали исполняемые команды глобально с целью немедленного выполнения.

Это было не очень удобным, поскольку нельзя было установить разные версии одной команды.

Запуск npx commandname автоматически находит правильную ссылку на команду в папке node_modules проекта без необходимости указывать точный путь к файлу и устанавливать пакет глобально.

Выполнение команды без установки
Еще одной замечательной особенностью npx является то, что она позволяет выполнять команды без установки пакетов.

Это очень полезно, поскольку:

  1. ничего не надо устанавливать
  2. можно запускать разные версии команды с помощью @version


Типичной демонстрацией использования npx является выполнение команды cowsay. В терминал выводится корова, «говорящая» переданную строку. Например:

cowsay "Hello" выведет следующее:

 _______
< Hello >
 -------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||


Для успешного выполнения этой команды, cowsay должен быть установлен глобально или локально, в противном случае, вы получите ошибку.

npx позволяет выполнить эту команду без установки пакета:

npx cowsay "Hello"


Это забавная, но бесполезная команда. Другие сценарии:

  • выполнение CLI vue для создания нового приложения и его запуска: npx vue create my-vue-app
  • создание нового React-проекта: npx create-react-app my-react-app


После загрузки и выполнения, ненужный код автоматически удаляется. Запуск кода с использованием разных версий Node.js
Используйте @ для определения версии и комбинируйте его с пакетным менеджером:

npx node@10 -v #v10.18.1
npx node@12 -v #v12.14.1


Это позволяет обойтись без nvm и других инструментов управления версиями. Запуск проивзольного сниппета из URL
npx не ограничивает вас пакетами, опубликованными в реестре npm.

Вы можете запускать код, содержащийся в GitHub gist, например:

npx https://gist.github.com/zkat/4bc19503fe9e9309e2bfaa2c58074d32


Разумеется, вам следует быть осторожным при запуске чужого кода, поскольку с большими возможностями приходит большая ответственность.

Продолжение следует…

© Habrahabr.ru