Трудно решить, использовать ли поле 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, иногда оно движется со скоростью света, иногда придерживается старых правил и практик. Вы никогда не узнаете, что будет завтра.