Руководство по Node.js для начинающих. Часть 4
Доброго времени суток, друзья!
Продолжаю публиковать перевод этого руководства по 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
:
❯ 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
:
❯ 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
.
Вот список новых релизов пакетов из одного репозитория, который какое-то время не обновлялся:
Некоторые из релизов являются мажорными. Запуск 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
.
Например: 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
является то, что она позволяет выполнять команды без установки пакетов.
Это очень полезно, поскольку:
- ничего не надо устанавливать
- можно запускать разные версии команды с помощью
@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
и других инструментов управления версиями. Запуск проивзольного сниппета из URLnpx
не ограничивает вас пакетами, опубликованными в реестре npm
.
Вы можете запускать код, содержащийся в GitHub gist, например:
npx https://gist.github.com/zkat/4bc19503fe9e9309e2bfaa2c58074d32
Разумеется, вам следует быть осторожным при запуске чужого кода, поскольку с большими возможностями приходит большая ответственность.
Продолжение следует…