Трудно решить, использовать ли поле package.json # files или файлы .npmignore. Может есть альтернатива.

ПРИМЕЧАНИЕ. Этот пост содержит информацию, которая не является на 100% точной, как мне указал добрый читатель. Я опубликовал новый рассказ об исправлении моей ошибки. Вы можете прочитать это здесь.

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

Был там, сделал это.

С npm всегда неплохо запустить команду npm pack, чтобы точно увидеть, что находится внутри вашего пакета:

npm pack --dry-run

Новые версии npm также поддерживают --dry-run в команде npm publish. Кроме того, эти более новые версии достаточно любезны, чтобы выводить содержимое пакета во время публикации. Запомните мои слова: вы не хотите обнаруживать, что ваш пакет неправильно настроен во время публикации. Вы должны взять под свой контроль содержимое пакета с самого начала.

package.json # files field и файлы .npmignore

Итак, как npm знает, что упаковать? Согласно документам здесь:

  • Вы можете использовать поле files для определения шаблонов включаемых файлов.
  • Вы можете пропустить files, чтобы включить все, и использовать .npmignore для определения шаблонов файлов, которые не следует включать. Вы можете поместить .npmignore в корневую папку или в любой подкаталог.
  • Если .npmignore нет, npm будет искать .gitignore файл.
  • Одни файлы всегда включаются (package.json,…), а другие всегда игнорируются (node_modules,…) независимо от того, что вы говорите с files и .npmignore.

В документации также говорится о том, что вызывает недоумение:

Файлы, включенные в поле «package.json # files», нельзя исключить через .npmignore или .gitignore.

Постичь эти правила непросто, а критические случаи плохо документированы. Лично мне такой подход не нравится. Я не могу открыть package.json и сразу увидеть, что будет включено. Если я хочу быть точным, я должен отредактировать свои files или разбросать несколько .npmignore файлов. Отсутствие возможности исключить то, что было добавлено через files, просто раздражает.

Взгляните на следующий пример: библиотека React с размещенными файлами.

.
├── component
│   ├── component.js
│   └── component.test.js
└── package.json

Я хочу опубликовать component, но исключить тесты, мои варианты:

  • Напишите исчерпывающее files поле, в котором перечислены все файлы, которые я хочу добавить (в данном случае только component/component.js).
  • Напишите .npmignore, игнорирующее **/*.test.js.

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

Если бы можно было комбинировать files и .npmignore, было бы лучше, но реальность сурова. Итак, у меня есть два предложения, как это исправить.

Предложение 1: поддержка включает и исключает через поле файлов

Поле files может поддерживать как включения, так и исключения с четкой семантикой: определите, что включено, если вы хотите сделать исключение, используйте исключения.

"files": {
  "include": ["component"],
  "exclude": ["**/*.sample.js"]
}

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

Предложение 2: поддержка файлов package.js

Вы когда-нибудь видели спецификацию драгоценного камня рубиновый камень? Если нет, вот один (для краткости я опустил некоторые части):

Обратите внимание на строки с 10 по 12. Эта спецификация gem определяет файлы, которые должны быть включены, с помощью исполняемой инструкции: получить все исходные файлы, контролируемые git, и исключить все виды тестов. Спецификации Gem - это просто рубиновый код, поэтому код расширит этот код до исчерпывающего списка файлов. Это действительно гибко и позволяет нам точно определить включаемый файл. Красивый.

Итак, почему у нас не может быть package.js вместо утомительного package.json? Большинство инструментов JavaScript перемещается в файлы конфигурации JavaScript вместо файлов JSON. Файлы JavaScript можно компоновать, создавать линки и тестировать, и это очень полезно.

Я понимаю, что npm использует JSON как способ предоставления метаданных из пакета без необходимости запускать сам пакет. Я понимаю логику этого рассуждения, но можно было бы использовать JavaScript в нашем коде и упаковать сгенерированную информацию о пакете в формате JSON во время публикации, имея лучшее из обоих миров.

Я не думаю, что это произойдет в ближайшее время 😔.

Что мы можем сделать сегодня?

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

Мы делаем это в наших проектах. Мы пишем пользовательское свойство filesGlob в package.json, используя синтаксис, поддерживаемый библиотекой fast-glob:

"filesGlob": {
  "include": ["component/*"],
  "exclude": ["**/*.test.js"]
}

А затем expand-files скрипт преобразует наши выражения в исчерпывающий files список с помощью библиотеки. Мы можем увидеть расширенные поля перед публикацией, если мы запустим npm pack:

expand-files npm pack --dry-run

Сценарий изменяет package.json и выполняет предоставленную команду. После этого восстанавливается исходный файл. Мы используем другое имя свойства, чтобы избежать проблем с другими инструментами, которые могут ожидать свойство files, соответствующее спецификации. Это не идеально, но работает.

Вот полный сценарий:

Подводя итоги

Я не думаю, что модель npm в корне ошибочна. Это работает в подавляющем большинстве случаев, и когда мы опубликовали пакет с большим или меньшим количеством файлов, чем следовало бы, это произошло из-за нашего собственного невнимания 😅. Однако, имея дело с размещенными в одном месте файлами, мы хотим, чтобы у нас было что-то другое.

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