Распределите свои утилиты JavaScript по нескольким проектам

Недостатка в великолепных библиотеках JavaScript нет. Они невероятно полезны, но, если вы похожи на меня, вам часто нужны определенные функции или функции, которых не существует.

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

Нам нужна собственная служебная библиотека JavaScript, которую мы можем распространять через NPM.

TL;DR

Вот пример репозитория GitHub с моими личными вспомогательными функциями, после которого вы можете создать шаблон своей архитектуры:



Цели

  1. Иметь один исходный код, который преобразуется как в ES6, так и в CommonJS (ES5).
  2. Tree-shakable для ES6
  3. Реализуйте модульное тестирование с помощью Jest.
  4. Автоматическое создание README.md для документации

Давайте начнем!

Перво-наперво.

Скучный отказ от ответственности

Я решил использовать TypeScript для этого проекта, но вам это не обязательно. Это прекрасно, если вы хотите использовать обычный ES6 с Babel. Все по-прежнему будет принадлежать вам, за исключением раздела The tsconfig(s).

И теперь, когда мы избавились от этого…

Файловая структура

Вот схема основной файловой структуры, которую мы хотим настроить:

/dist
  └───/lib
    └───/es5
    └───/es6
  └───CHANGELOG.md
  └───LICENSE
  └───README.md
  └───package.json
/src
package.json
tsconfig.es5.json
tsconfig.json

Каталог dist будет содержать все, что мы хотим опубликовать как часть нашего пакета NPM. Мы создадим все в lib, а также в README.md.

tsconfig (ы)

Как указано в заявлении об отказе от ответственности, если вы не планируете использовать TypeScript и вместо этого будете использовать ES6 с Babel, вы можете пропустить этот шаг и замените его собственной настройкой Babel.

У нас будет два файла tsconfig.json в корне нашего проекта. Один будет для ES6, а другой CommonJS:

Каталог bin

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

Файл generateReadme.js будет:

  1. Просмотрите наш каталог src, чтобы найти все наши утилиты.
  2. Проанализируйте информацию JSDoc в каждом, чтобы определить, что выводить для README.md.
  3. Используйте файл readme.ejs в качестве шаблона для уценки для окончательного файла README.md.
  4. Поместите основной файл README.md в каталог dist и корень.
  5. Создайте файл README.md для каждой утилиты в каталоге src/[util].

Обратите внимание, что вы можете изменить поведение этого кода для элемента № 5 для вашего проекта, в котором вся документация содержится в одном файле README.md.

Я настроил репозиторий utils так, как он есть, потому что мне и другим легче найти то, что они ищут, и изолировать то, на что они смотрят.

Если вы хотите распространять один большой файл README.md, в этом нет ничего плохого, и вам нужно будет внести лишь несколько незначительных изменений в код в generateReadme.js и readme. файлы ejs.

Пакет .json(s)

У нас будет два файла package.json: один в корне и один в каталоге dist.

package.json (корень):

Обратите внимание, что сценарий compile использует два файла tsconfig и в конце выполняет сценарий readme.

package.json (расстояние):

Интересные места здесь следующие:

  1. Измените name на то, что вы хотите, чтобы ваш пакет npm был.
  2. Измените URL-адреса repository, bugs и homepage, чтобы они соответствовали вашему репозиторию.
  3. Измените author на свое имя.
  4. Увеличивайте version по мере публикации.
  5. "sideEffects": false, имеет решающее значение для сотрясения деревьев. Он сообщает Webpack (и подобным), что ваши отдельные модули автономны без каких-либо внешних побочных эффектов.
  6. main — это точка входа для нашего кода CommonJS.
  7. module — это точка входа для нашего кода ES6. Если код, использующий библиотеку, понимает module, он будет использовать ее. В противном случае будет использоваться наш код CommonJS в main.

Откуда он знает, что использовать ES6 или CommonJS?

Когда приложение, использующее нашу библиотеку, транспилируется в CommonJS (ES5), оно не распознает module в нашем package.json как свойство, которое знает, как использовать, поэтому оно проигнорируйте его и войдите через main. Когда приложение запускается как ES6, оно распознает module, использует его и игнорирует main.

Давайте создадим несколько утилит!

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

/src
  └───/myCategory
    └───/myUtil
      └───/index.ts
      └───/index.test.js
  └───/index.ts

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

Наша первая польза

Давайте создадим простую утилиту значений, которая проверяет, является ли значение null или undefined.

Мы создадим следующее дерево:

/src
  └───/value
    └───/isNil
      └───/index.ts
      └───/index.test.js
  └───/index.ts

Две ключевые вещи, на которые следует обратить внимание:

  1. Экспортируйте свою функцию как имя утилиты, в данном случае isNil
  2. Структурируйте JSDoc, как указано выше, с описанием, начиная с категории, параметрами, возвратами и примерами.

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

Модульный тест

Нам нужно убедиться, что мы тестируем наши функции, прежде чем отправлять их. Вот пример теста для isNil в виде файла index.test.js:

Определите экспорт

Следующее, что нам нужно сделать, это определить экспорт, чтобы облегчить нашим потребителям доступ к нашим утилитам. Для этого мы создадим файл index.ts в корне нашего источника и включим в него следующее:

export { isNil } from './value/isNil';

Следуйте этому же шаблону в том же файле, чтобы добавить любые другие утилиты, которые могут появиться в будущем. Например:

Сотрясение дерева

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

// Bad! Do NOT do anything like this!
import { isNil } from './value/isNil';
export { isNil };

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

Скомпилировать это

Давайте воплотим нашу новую библиотеку в жизнь! В корне проекта выполните одну из следующих команд:

npm run compile

Проверьте README.md

Вы помните, что мы включили наш скрипт для создания README.md как часть скрипта компиляции в наш корневой файл package.json. Если наша компиляция прошла без ошибок, мы должны увидеть новый автоматически сгенерированный файл README.md в нашем каталоге dist.

Попробуй это

В корне проекта выполните одну из следующих команд:

npm test

Или, если вам нужна статистика покрытия кода:

npm run test:coverage

Если вы никогда раньше не тестировали с помощью Jest, вы должны увидеть свои тесты в списке в терминале и, надеюсь, много зеленых тегов PASS:

Опубликовать

Перейдите в каталог dist, чтобы выполнить нашу команду публикации.

npm publish

Чтобы узнать обо всех тонкостях публикации в NPM, вам следует посетить страницу документации NPM для публикации.

Потребляйте это

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

npm i @username/utils

Поскольку все наши функции называются экспортами, вы можете импортировать их следующим образом:

import { isNil, myOtherUtil } from '@username/utils'

Выгода!

Теперь мы можем распространять наш пакет utils по нескольким проектам и нескольким командам, мы можем быть уверены, что он будет работать в приложении ES6 или CommonJS (ES5), мы протестировали каждой функции, и мы создали нашу документацию для каждой функции.

Идите вперед и сделайте мир JavaScript более удобным для вас и вашей команды!