Изображение заголовка генерируется ИИ ночного кафе кстати.

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

Недавно я пытался разобраться в спортивной аналитике и понял, что, как и большая часть экосистемы науки о данных, все инструменты написаны на питоне. Пока я переучивался, чтобы отточить свои ржавые навыки работы с Python (без каламбура 🦀), я также попытался сделать кое-что с JS для развлечения, что превратилось в отдельную библиотеку на основе D3. В этом сообщении в блоге я не буду объяснять саму библиотеку, потому что это отдельная история (PS смотрите это место). Но на этот раз я поделюсь очень простой настройкой, чтобы вы могли придумать свою собственную с минимумом суеты. Ссылка на гитхаб в конце 👇

Для библиотеки JS/TS вам понадобится транспайлер/связчик, модульная стратегия, опубликованная версия вашей библиотеки и, возможно, демонстрационное приложение.


Упаковщик (esbuild и tsup)

esbuild утверждал, что является самым быстрым в городе, когда дело доходит до упаковщиков, и, судя по тестам, так оно и есть. Похожий на Parcel, tsup — это библиотека с нулевой конфигурацией (на случай, если она вам не нужна), основанная на esbuild. Посылка основана на Rust основанный на СВК компилятор, в то время как esbuild основан на Go. Tsup адаптирован для бесперебойной работы с TS. Несмотря на то, что это нулевая конфигурация, вы можете изменить ее, если это необходимо, что мы и делаем. Вот конфигурация, которую я использовал для этой статьи.

tsup.config.ts

import type { Options } from 'tsup';

const env = process.env.NODE_ENV;

export const tsup: Options = {
  splitting: true,
  clean: true, // clean up the dist folder
  dts: true, // generate dts files
  format: ['cjs', 'esm'], // generate cjs and esm files
  minify: env === 'production',
  bundle: env === 'production',
  skipNodeModulesBundle: true,
  entryPoints: ['src/index.ts'],
  watch: env === 'development',
  target: 'es2020',
  outDir: env === 'production' ? 'dist' : 'lib',
  entry: ['src/**/*.ts'], //include all files under src
};
Войти в полноэкранный режим

Выйти из полноэкранного режима

Здесь мы переносим все наши файлы TS в JS с помощью tsupконфиг. После этого мы опубликуем его с npm в формате JS с объявлениями типа, чтобы его могли использовать приложения JS и TS.

Хотя мне нужно прояснить несколько вещей.



splitting

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



skipNodeModulesBundle

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



dts

Создает файлы объявлений машинописного текста (например, index.d.ts), полезно, когда вы используете библиотеки с машинописным текстом.



entry

Посмотрите, как мы позволяем tsup анализировать все файлы с помощью простого шаблона регулярного выражения. ['src/**/*.ts']

Остальные параметры в значительной степени делают то, что следует из названий.


Модульная стратегия

Работа с модулями долгое время была головной болью для JS-сообщества. Поэтому есть несколько разных стратегий, когда вы хотите распространять свои пакеты JS. 4 основных модуля вокруг:

  1. CommonJS
  2. AMD: определение асинхронного модуля
  3. UMD: определение универсального модуля
  4. Модули ЕС

В этом посте я не буду вдаваться в подробности об AMD и UMD. Но я хотел бы поговорить о CJS и ESM, которые являются наиболее распространенными. Существует четкое различие между модулями CommonJS и ES, которое Tree shaking. Модули CommonJS — это оригинальный способ упаковки кода JavaScript для Node.js. Последние версии node.js поддерживают оба варианта. Основное отличие здесь в том, что в модуле CommonJS все ваши красивые js-файлы объединены в один файл. Вы запомните синтаксис, если работали с обеих сторон.

ЕСМ (модули ЕС)

import {john} from "doe";

export jane;
Войти в полноэкранный режим

Выйти из полноэкранного режима

CJS (Общий JS)

const john = require("doe")

module.exports = jane;

Войти в полноэкранный режим

Выйти из полноэкранного режима


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

Tree-shaking устраняет мертвый код и неиспользуемые экспорты из вашего пакета. С CJS даже если вы собираетесь использовать простой Card компонент, скажем, если это не древовидная библиотека, вам нужно загрузить всю библиотеку и в конечном итоге добавить ее в чанк вашего поставщика. Например, одной из наиболее часто используемых служебных библиотек является lodash, и было много проблем из-за ее огромного размера пакета в мимо Если вы используете импорт/экспорт модулей es, которые по своей природе являются древовидными, упаковщики могут обнаружить неиспользуемые экспорты. Мертвые коды устраняются таким образом.

Вы можете выводить различные форматы модулей с помощью tsup но cjs файл не будет древовидным. Не забывайте, что вы просто делаете библиотеку древовидной. Это приложение, которое использует библиотеку, на самом деле встряхивает и устраняет мертвый код. Но в этой статье я хочу вывести два разных формата для двух разных платформ (веб и узел), первый из которых будет древовидным. Основной мотивацией этого было создание для меня библиотеки пользовательского интерфейса, но вы никогда не знаете, на какой платформе закончится ваша библиотека, если это не библиотека пользовательского интерфейса. Так почему бы не предоставить его в обоих форматах просто для того, чтобы сделать это 🃏 Строка ниже сделает это за нас.

format: ['cjs', 'esm']
Войти в полноэкранный режим

Выйти из полноэкранного режима

Мы также немного изменим package.json.

  "main": "lib/index.cjs",
  "module": "lib/index.js",
  "types": "lib/index.d.ts",
Войти в полноэкранный режим

Выйти из полноэкранного режима

Поле модуля используется для определения версии ES пакета. Кроме того, мы хотим пометить нашу библиотеку как es модуль. Для этого мы добавим строку ниже в package.json.

"type": "module",

что также влияет на компиляцию tsup с точки зрения расширений файлов.

├── index.js          # esm
└── index.cjs         # cjs
Войти в полноэкранный режим

Выйти из полноэкранного режима

index.js теперь является модулем es. Причина всего этого заключается в том, что для библиотеки пользовательского интерфейса мы хотим, чтобы ее компоненты/классы можно было импортировать отдельно, чтобы пользователь мог асинхронно импортировать компонент, подобный приведенному ниже.

изображение.png

script type="module" позволяет браузеру рассматривать этот фрагмент импорта как модуль


Выходные каталоги

Если вы когда-либо изучали такие библиотеки, как Ant Design, вы узнаете, что внутри пакета будет отдельная папка lib, из которой вы сможете импортировать определенные компоненты. Мы бы оба lib а также dist библиотеки. dist будет означать пакетную версию ваших файлов и, возможно, может использоваться, например, когда вы хотите распространять свою библиотеку через CDN.


Используемый и неиспользованный экспорт 🤔

Теперь попробуйте посмотреть на эту реализацию с другой стороны.

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

Но если вы объедините все свои js в один, весь пакет все равно будет импортирован. Существует еще одна концепция, называемая sideEffect выскакивает на этом этапе. Согласно веб-пакету

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

Webpack может определить, следует ли запускать эту оптимизацию, проверив настраиваемое поле с именем sideEffects внутри package.json. Сохранение модульной структуры и поддержка библиотеки с крошечными модулями позволяют сборщикам устранять мертвый код.
Если у вас есть конкретный модуль, который вы хотите объединить, независимо от того, используется он или нет, вы должны добавить его в список побочных эффектов. Это также включает в себя scss или другие файлы. Возможная конфигурация побочных эффектов, которую вы можете добавить в свой package.json:

  "sideEffects": [
    "dist/*",
    "lib/**/style/*",
    "*.scss"
  ],```



In the above example, you're telling webpack not to clean modules considered to be dead code and this makes sense because under a dist folder possibly you have a single minified bundle including all needed utility functions, etc. It can be even a CJS module in which tree-shaking is not possible. The same is true for your style files, you possibly need all of them, it might be misleading to tell webpack to skip the whole module/subtree of scss. 

### How to test if `sideEffects`actually works
Basically looking at the bundle. Let's assume a very basic library 

![Screen Shot 2022-09-08 at 23.38.40.png]( align="left")

`index.ts`


Войти в полноэкранный режим

Выйти из полноэкранного режима

импортировать {сумма} из ‘./Sum’;

константа sampleJson = [
{
id: ‘f5e7457f-467d-4e37-9652-f2fb1b51c712’,
first_name: ‘John’,
last_name: ‘Doe’,
},
];

экспорт {sampleJson, сумма};



and inside `Sum` we are just exporting a basic sum method
`Sum/index.ts`


Войти в полноэкранный режим

Выйти из полноэкранного режима

const sum = (a: число, b: число) => a + b;

экспорт {сумма};



After we transpile with `tsup` all the files will be added to the bundle. Note that we are preserving folder structure under lib.

![Screen Shot 2022-09-08 at 23.40.33.png]( align="left")

Let's jump into to the app and locally link the library for testing. I'll use this webpack starter 

### Testing with npm link

The easiest way to test your library is to register it locally with `npm link` or `yarn link`.




Войти в полноэкранный режим

Выйти из полноэкранного режима

пряжа ссылка v1.22.19
Успех Зарегистрирован «tsup-library-template».
информация Теперь вы можете запустить npm link "tsup-library-template" в проектах, где вы хотите использовать этот пакет, и он будет использоваться вместо него.




Then go to the app you want to use the library then `yarn link <your-library>`


Войти в полноэкранный режим

Выйти из полноэкранного режима

пряжа ссылка v1.22.19
успех Использование связанного пакета для «tsup-library-template».
✨ Выполнено за 0,02 с.




This is the quickest way to set you up. If you want to manage your lib and examples in the same monorepo have a look at the [yarn workspaces]( 

### Let's use the library
Inside the app we will use library like this 

`someApp.js`


Войти в полноэкранный режим

Выйти из полноэкранного режима

импортировать {sum} из ‘tsup-library-template’;

console.log(сумма);



even though library has two expected members we only used one method which is `sum` and that reflects on webpack bundler.


`someAppsBundle.js`


Войти в полноэкранный режим

Выйти из полноэкранного режима

!function(){«использовать строгое»;console.log(((o,c)=>o+c))}();
//# sourceMappingURL=app.5712c325.js.map




Now import the `sampleJson` and use it as well


Войти в полноэкранный режим

Выйти из полноэкранного режима

импортировать {sum, sampleJson} из ‘tsup-library-template’;

console.log(сумма, примерJson);




Look at the bundle generated by the webpack
 it is larger because it know includes two imported members `sum` and `sampleJson`.


Войти в полноэкранный режим

Выйти из полноэкранного режима

!function(){«использовать строго»;console.log(((e,f)=>e+f),[{id:»f5e7457f-467d-4e37-9652-f2fb1b51c712″,first_name:»John»,last_name:»Doe»}])}();
//# sourceMappingURL=app.b0051b82.js.map




We can even solely import a module inside using the below syntax


Войти в полноэкранный режим

Выйти из полноэкранного режима

импортировать {sum} из ‘tsup-library-template/lib/Sum’;

console.log(сумма);



output will be same with the first example.


Войти в полноэкранный режим

Выйти из полноэкранного режима

!function(){«использовать строгое»;console.log(((o,c)=>o+c))}();
//# sourceMappingURL=app.5712c325.js.map



Some of you might think at this stage "isn't it just regular module import/export". Yes, you are absolutely right, the only difference here is we let the bundler to do that inside of an npm package. We're done with the building, time to send it to the npm 🚀

### Versioning and Releasing


Войти в полноэкранный режим

Выйти из полноэкранного режима

Основная версия npm // увеличить основную версию

Младшая версия npm // увеличить младшую версию

патч версии npm // увеличить версию патча



All of the above comments set the new version, updates the package json and commits to git



Войти в полноэкранный режим

Выйти из полноэкранного режима

npm логин«`

чтобы войти в свою учетную запись npm, затем запустите

npm publish
Войти в полноэкранный режим

Выйти из полноэкранного режима

на терминале. Проверьте свою учетную запись npm, чтобы убедиться, что пакет готов с версией, которую вы указали вpackage.json


Резюме

tsup — это сборщик, основанный, пожалуй, на самом быстром сборщике: esbuild. Вам не нужно изменять конфиги, но вы можете составлять их так, как хотите для разных выходов. ESM является предпочтительной модульной стратегией для веб-библиотек и библиотек пользовательского интерфейса. Основное различие между модулем CJS и ES заключается в том, что последний является древовидным, что делает его более эффективным и требует меньше времени для загрузки, анализа и выполнения для браузеров. Мы предоставляем библиотеке объявления типов в формате JS. Добавлять sideEffects:false в ваш package.json для дальнейшей оптимизации, когда другие сборщики используют библиотеку. Быстро протестируйте с npm link


Дальнейшее чтение

Я нашел все эти ресурсы очень полезными. Приветствую всех 🙌


Ссылка на гитхаб

Если вы хотите использовать этот подход в качестве шаблона
👉


Ошибка веб-пакета

Если вы видите эту ошибку при использовании библиотеки

ModuleNotFoundError: Module not found: Error: Can't resolve './Sum' in '/Users/hunor/workspace-zhunor/js/tsup-template.js/lib'
Did you mean 'index.js'?
BREAKING CHANGE: The request './Sum' failed to resolve only because it was resolved as fully specified
Войти в полноэкранный режим

Выйти из полноэкранного режима

По умолчанию webpack ожидает, что вы предоставите расширение файла при импорте модуля в файлы .mjs или любые другие файлы .js, когда их ближайший родительский файл package.json содержит поле «тип» со значением «модуль», в противном случае веб-пакет завершится ошибкой. компиляция с ошибкой Module not found.

Чтобы пропустить это, вам может потребоваться установить fullySpecified: false в конфигурации веб-пакета, аналогичной приведенной ниже.

{
  test: /\.m?js/,
  resolve: {
    fullySpecified: false
  }
}
Войти в полноэкранный режим

Выйти из полноэкранного режима