Введение

В свое время управление зависимостями было все более серьезной проблемой, особенно в таких языках, как C/C++. Не было стандартизированного инструмента для управления зависимостями и их версиями, а на управление ими для проекта уходило несколько часов усилий разработчиков.

Перенесемся в 2023 год: есть несколько инструментов для управления зависимостями, например maven и gradle для Java, pip для Python, npm, pnpm и yarn для Javascript и Cargo для Rust. , назвать несколько. Теперь каждому менеджеру пакетов нужен способ отслеживать, какие версии каких пакетов предполагается использовать в вашем текущем проекте. Как правило, создается файл, который сопоставляет эти зависимости с их соответствующими версиями — например, вы обычно найдете файл requirements.txt в большинстве проектов Python.

Точно так же основная задача файла package.json — отслеживать все зависимости и зависимости разработчиков. которые требуются в вашем проекте. При запуске команды install в вашем любимом диспетчере пакетов JS будут установлены соответствующие версии пакетов, упомянутых в файле package.json. Помимо отслеживания зависимостей, файл package.json также хранит имя и версию вашего пакета, что обычно считается метаданными для некоторых инструментов. Допустим, вы должны опубликовать свой проект в npmjs (или любом другом реестре NPM), вам потребуется, чтобы все метаданные о пакете были в вашем файле package.json, расположенном в корневом каталоге вашего проекта.

Создание файла package.json

Вы можете создать файл package.json в своем проекте Javascript/Typescript с помощью команды npm init. Когда вы запустите эту команду, она задаст вам ряд вопросов, и все ответы, которые вы введете, появятся в вашем файле package.json.

Вот соответствующий файл package.json, созданный при выполнении вышеуказанной команды npm init.

{ "name": "dyte", "version": "1.0.0", "description": "Dyte is the most developer-friendly video and audio SDK.", "main": "index.js", "scripts": { "test": "vitest" }, "repository": { "type": "git", "url": "https://github.com/dyte-in" }, "keywords": [ "dyte", "video-sdk", "video-conferencing", "communication" ], "author": "roerohan (https://github.com/roerohan)", "license": "MIT" }

Чаще всего при публикации пакета в игру вступают ключи, отличные от scripts и dependencies. С этого момента обсуждение будет больше относиться к пакетам, которые предполагается публиковать в любом реестре NPM. Однако, если у вас есть, например, автономный проект Node.js, свойства в файле package.json по-прежнему означают то же самое.

Общие ключи в package.json

Свойства в package.json являются либо описательными, либо функциональными. Например, name пакета является описательным свойством, тогда как scripts, определенные в package.json, являются функциональными свойствами.

Вот некоторые из наиболее полезных свойств package.json и их значение.

Поле имени используется для идентификации пакета. Поля name и version являются обязательными в файле package.json, и вместе они считаются уникальными. Например, если имя web-core, а версия 0.1.2, предполагается, что [email protected] уникален и не относится ни к какому другому пакету. Имя пакета имеет определенные ограничения - оно не может быть длиннее 214 символов и должно содержать все строчные буквы. name не может начинаться с . или _. Кроме того, имя часто является частью URL-адреса, поэтому оно должно быть безопасным для URL-адресов.

Имена пакетов также могут быть ограничены. Например, имя пакета может быть @dytesdk/web-core. Это форма @organization/package.

Поле версии является одним из ключей, используемых для идентификации пакета. Как правило, этот номер версии должен анализироваться node-semver. Семантическое управление версиями — это набор правил управления версиями, при котором изменение номера версии указывает на вид изменений в пакете. Версия пишется в виде MAJOR.MINOR.PATCH. Если в новой версии есть исправление ошибки, PATCH увеличивается. Если есть новая функция, часть MINOR версии увеличивается. Если в новой версии есть критические изменения или она несовместима со старыми версиями, часть MAJOR версии увеличивается.

Например, если текущая версия пакета 1.0.9:

  • Если в следующем выпуске будут только исправления ошибок, новая версия должна быть 1.0.10.
  • Если в следующем выпуске есть новая функция, новая версия должна быть 1.1.0.
  • Если в следующем выпуске есть критическое изменение, новая версия должна быть 2.0.0.

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

Как и описание, поле keywords также используется для SEO. Это набор слов, описывающих пакет. Если кто-то ищет любое из слов в поле keywords, вполне вероятно, что ваш пакет будет найден.

Обычно в этом поле вы размещаете ссылку на веб-сайт вашего проекта. Кроме того, вы также можете указать на проекты README или документацию.

Цель этого поля — указать на систему отслеживания проблем вашего проекта или любое электронное письмо службы поддержки. Это может быть форма

{ "url": "https://github.com/dyte-io/html-samples/issues", "email": "[email protected]" }

Если вы не хотите предоставлять поддержку по электронной почте, вы можете напрямую назначить URL свойству bugs.

Лицензия является важным полем, так как она описывает пользователям вашего пакета разрешения и ограничения, установленные вами при использовании этого пакета. В идеале для пакетов с открытым исходным кодом лицензия должна быть одобрена OSI. Если вы не хотите предоставлять какие-либо разрешения пользователям пакета ни на каких условиях, вы можете установить в этом поле значение UNLICENSED. Вы должны установить для поля private в файле package.json значение true, чтобы не допустить случайной публикации пакета.

Поле автора используется для предоставления информации о разработчике пакета. Он состоит из поля name и необязательных полей email и url. Вот пример:

{ "name": "Rohan Mukherjee", "email": "[email protected]", "url": "https://dyte.io" }

Вся информация также может быть уменьшена до одной строки следующего формата:

{ "author": "Name <Email> (Site)" }

Например, вы можете указать того же автора, что и выше, в таком формате:

{ "author": "Rohan Mukherjee <[email protected]> (https://dyte.io)" }

Как и поле author, поле contributors предоставляет информацию о разработчиках пакета. Он содержит множество авторов.

В этом поле вы можете указать любые ссылки для финансирования вашего пакета с открытым исходным кодом. Например, если у вас есть ссылка Patreon или купить кофе для финансирования вашего проекта, вы можете добавить ее в это поле. Это также может принимать массив из нескольких URL-адресов для финансирования. Это URL-адрес, который открывается, когда пользователь запускает npm fund <projectname>.

Поле files содержит массив файлов, которые необходимо загрузить в реестр, когда ваш пакет будет опубликован. Шаблоны файлов имеют синтаксис, аналогичный .gitignore. Единственное отличие состоит в том, что файлы, указанные в .gitignore, исключаются, тогда как эти файлы включаются. Вы также можете использовать шаблоны глобусов, такие как * и **/*, как и в файлах .gitignore. Поле files по умолчанию равно ["*"], если не указано иное.

Обратите внимание, что файлы package.json, README и LICENSE/LICENCE включаются всегда, независимо от ваших настроек. Файлы README и LICENSE/LICENCE могут иметь любое расширение.

Точка входа в вашу программу определяется в свойстве main. Когда вам требуется пакет, вы фактически импортируете файл, описанный в свойстве main. Существует альтернатива Node.js 12+ для этого поля, известная как exports, которая описана ниже.

Вы можете определить точки входа в свой пакет, используя поле exports в качестве альтернативы полю main. В отличие от main, exports позволяет определить экспорт подпутей и условный экспорт.

Например, вы можете экспортировать файл submodule.js вашего проекта, используя следующее свойство exports:

{ "exports": { ".": "./index.js", "./submodule.js": "./src/submodule.js" } }

Также возможен экспорт условно — в зависимости от того, использует ли пользователь пакета require или import.

{ "exports": { "import": "./index-module.js", "require": "./index-require.cjs" }, "type": "module" }

Условный экспорт часто используется для пакетов для обратной совместимости, так как ключевое слово import может использоваться только в ESM.

Это описывает, должны ли файлы .js в текущем пакете обрабатываться как ESM или commonjs. Вы можете установить тип module для пакетов ESM и commonjs для пакетов без ESM. Кроме того, вы можете явно указать, должен ли файл интерпретироваться как ESM или commonjs, используя расширение .mjs для файлов ESM и расширение .cjs для файлов commonjs.

{ "type": "module" }

По состоянию на февраль 2023 года packageManager является экспериментальным полем, которое определяет, какой менеджер пакетов будет использоваться для текущего пакета. Также следует указать версию используемого менеджера пакетов. Это поле может содержать значения, присутствующие в этом списке.

Это поле используется вместо main, чтобы указать, предназначен ли пакет для использования в браузере, а не в проекте Node.js. Это используется, когда ваш пакет использует такие примитивы, как window, которые недоступны в средах Node.js.

В некоторых случаях пакеты npm необходимо устанавливать в , чтобы они могли запускаться непосредственно операционной системой из любого каталога. Поле bin определяет эти исполняемые файлы. Например, вы можете иметь следующую конфигурацию в вашем свойстве bin.

{ "bin": { "dyte": "./dyte.js", "myapp": "./cli.js" } }

После глобальной установки этого пакета (используя npm install -g) вы сможете запускать такие команды, как dyte и myapp, прямо с вашего терминала. Это внутренне создает символическую ссылку для файла с dyte.js на /usr/local/bin/dyte и символическую ссылку для файла с cli.js на /usr/local/bin/myapp в unix-подобных ОС. В Windows создается файл C:\\Users\\{Username}\\AppData\\Roaming\\npm\\dyte.cmd, который запускает сценарий dyte.js. Следует отметить, что каждый из файлов, упомянутых как значения в свойстве bin, начинается с шебанга #!/usr/bin/env node, иначе ваша операционная система не поймет, что файл должен запускаться в среде Node.js.

Вы можете связать документ или список документов, которые программа man найдет в этом поле. Когда вы запустите man <package-name>, он должен показать этот документ.

Вы можете использовать объект directories, если хотите показать папку, полную двоичных файлов, или папку, полную справочных страниц. Если вы используете эту опцию, вам не нужно указывать все справочные страницы в массиве или все двоичные файлы в объекте. Вы можете просто добавить следующую конфигурацию:

{ "directories": { "man": "./man", "bin": "./bin" } }

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

{ "repository": { "type": "git", "url": "https://github.com/dyte-in/docs.git" } }

Свойство scripts — это словарь, содержащий команды сценариев, которые можно запускать с помощью интерфейса командной строки npm. Вы также можете указать сценарии, которые запускаются в разное время в течение жизненного цикла вашего пакета. Например, вы можете добавить скрипт prepublish, который запускается непосредственно перед публикацией пакета (когда вы запускаете npm publish).

Это позволяет вам указать конфигурацию для вашего пакета, которая сохраняется в разных версиях пакета. Например, вы можете указать такую ​​​​конфигурацию, как:

{ "config": { "port": "8080" } }

Теперь вы можете использовать переменную среды npm_package_config_port в файле scripts.

Все зависимости вашего пакета указаны в dependencies. Всякий раз, когда вы npm install создаете пакет, имя пакета добавляется в словарь dependencies в качестве ключа, а версия пакета добавляется в качестве значения. Вы также можете указать диапазоны версий вместо одной версии в соответствии со спецификацией semver. Вы также можете использовать URL-адреса GitHub и локальные каталоги для указания зависимостей вместе с пакетами npm.

Если у вас есть зависимости, которые вам нужны только во время разработки пакета, вы можете указать их как файл devDependency. Вы можете установить пакет как devDependency, используя npm install -D <package-name>. Как правило, такие пакеты, как typescript, ts-node и т. д., устанавливаются как devDependencies.

Часто для вашего пакета требуется другой пакет, но вы не хотите добавлять фиксированную версию в свои зависимости. Например, если вы создаете пакет react, вы не хотите добавлять react к своим зависимостям, потому что, если вы это сделаете, установка вашего пакета также установит указанную версию react из вашего package.json. В этом случае вы можете указать react как peerDependency, что указывает на то, что вашему пакету требуется определенный диапазон версий react для правильной работы. Ваш пакет называется плагином, если он имеет такое поведение. Например, посмотрите этот peerDependencies config.

В последних версиях npm все peerDependencies пакета автоматически устанавливаются вместе с пакетом.

Иногда желательно иметь peerDependency, но это не обязательно. Если ваш пакет работает без какой-либо из peerDependencies, вы можете указать эту зависимость как необязательную в ключе peerDependenciesMeta в package.json.

С помощью этой опции вы можете указать массив зависимостей, которые должны быть связаны с вашим пакетом. Когда ваш пакет будет подготовлен к публикации, зависимости, упомянутые в bundleDependencies или bundledDependencies, также будут упакованы вместе с исходным кодом в архив.

Если зависимость не найдена или ее установка не удалась, команда npm install завершает работу с ошибкой. Вы можете предотвратить это для определенного пакета, если этот пакет присутствует в optionalDependencies вместо любых других списков/словарей зависимостей.

С помощью этой опции вы можете указать, должен ли ваш пакет быть общедоступным, и какой тег выпускает пакет. По умолчанию пакет является частным, а тег по умолчанию — latest. Используя другой тег, например beta, пользователи могут устанавливать конкретную версию пакета, используя npm install <package-name>@beta.

Эта опция действительно полезна в монорепозиториях. Вы можете указать список рабочих пространств следующим образом:

{ "workspaces": [ "./packages/client", "./packages/server" ] }

Вы можете иметь отдельные файлы package.json в каталоге клиента и сервера, которые имеют отдельные сценарии. Запуск npm install --workspaces приведет к запуску npm install в обоих каталогах. Фактически вы можете запустить любой скрипт во всех рабочих областях, указанных с помощью команды --workspaces. Например, если у вас есть отдельные сценарии lint в packages/client и packages/server, в корневом каталоге package.json вы можете иметь сценарий lint, который запускает npm run lint --workspaces --if-present. Теперь, если вы запустите npm run lint в корне, он запустит сценарий lint во всех рабочих областях, в которых присутствует сценарий lint. Вот пример: пакет sockrates имеет 2 подрабочих области @dyte-in/sockrates-client и @dyte-in/sockrates-server.

«файл блокировки»

Существует таинственный файл package-lock.json, который появляется всякий раз, когда вы устанавливаете пакеты в свой проект npm.

Как следует из названия, package-lock.json — это файл блокировки, т. е. файл, в котором хранятся точные номера версий используемых пакетов и всех зависимых от него пакетов. Сюда входят все пакеты, присутствующие в вашем каталоге node_modules.

Цель этого файла — убедиться, что все зависимости установлены одинаково на разных машинах, что гарантирует согласованную работу проекта в разных средах.

Файл package-lock.json также содержит криптографический хэш содержимого каждого пакета, что гарантирует, что установленные пакеты не будут подделаны и что это те же самые пакеты, которые были опубликованы автором пакета.

Когда вы запускаете npm install, npm использует информацию из package-lock.json для определения точных версий устанавливаемых пакетов и устанавливает их в том же порядке и с теми же зависимостями, что и при исходной установке.

Если вы просмотрите файл package-lock.json, вы можете обнаружить случайные пакеты, существующие в вашем node_modules, о которых вы даже не знали. Например, это пакет, который я нашел в файле блокировки одного из проектов Dyte.

"node_modules/why-is-node-running": { "version": "2.2.2", "dev": true, "license": "MIT", "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" }, "engines": { "node": ">=8" } },

Пакет why-is-node-running присутствует в файле блокировки.

На самом деле вы можете запустить его с npx и увидеть, что пакет присутствует в вашем node_modules.

Другие менеджеры пакетов

Несмотря на то, что npm является одним из самых популярных менеджеров пакетов, многие люди используют другие менеджеры пакетов, такие как yarn, pnpm или turbo. Файл package.json по-прежнему существует для всех них, но файл блокировки может называться по-разному для разных менеджеров пакетов. Например, файл блокировки, созданный yarn, называется yarn.lock и выглядит примерно так:

# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 package-1@^1.0.0: version "1.0.3" resolved "https://registry.npmjs.org/package-1/-/package-1-1.0.3.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" package-2@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/package-2/-/package-2-2.0.1.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" dependencies: package-4 "^4.0.0" package-3@^3.0.0: version "3.1.9" resolved "https://registry.npmjs.org/package-3/-/package-3-3.1.9.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0" dependencies: package-4 "^4.5.0" package-4@^4.0.0, package-4@^4.5.0: version "4.6.3" resolved "https://registry.npmjs.org/package-4/-/package-4-2.6.3.tgz#a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0"

Точно так же pnpm создает файл блокировки с именем pnpm-lock.yaml. Однако назначение всех этих файлов блокировки такое же, как у файла package-lock.json, сгенерированного npm.

Заключение

В целом, package.json — это жизненно важный файл метаданных, используемый при разработке Node.js. Он помогает управлять зависимостями, автоматизировать задачи и настраивать проект. Файл содержит важную информацию, такую ​​как название проекта, номер версии, автора, лицензию, зависимости и многое другое.

Используя package.json, разработчики могут легко управлять зависимостями, необходимыми для их проекта, гарантируя, что установлена ​​правильная версия каждого пакета. Это упрощает поддержку проекта и обновление зависимостей при необходимости.

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

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

Надеюсь, вы нашли этот пост информативным и интересным. Если у вас есть какие-либо мысли или отзывы, не стесняйтесь обращаться ко мне в Twitter или LinkedIn 😄. Следите за новостями в блогах по теме в будущем!

Первоначально опубликовано на https://dyte.io 21 марта 2023 г.