Приятно видеть, как множество библиотек начинают внедрять flow
, чтобы добавить в свой код типобезопасность ...
НО… многие люди забывают, что npm
пакеты обычно поставляют код ES5 без какой-либо информации о типе, и, в конце концов, потребитель этой библиотеки получит any
только как импортированные типы.
Работая над набором текста в styled-components
, я искал руководство о том, как правильно предоставлять flow
типы для пользователя, и понял, что его еще не было.
Таким образом, эта статья направлена на то, чтобы дать разработчикам библиотек некоторые рекомендации / идеи о том, как продавать flow
определения в процессе npm
сборки.
В качестве эталонного проекта я создал репозиторий с некоторыми настройками пакетов и некоторыми примерами кода.
Мы рассмотрим настройку шаг за шагом, а также немного поговорим о некоторых flow
конкретных механизмах, например как flow
импортирует файлы.
Итак, приступим!
Наша библиотека примеров my-lib
состоит из каталога src
с некоторым исходным кодом ES6:
$ tree src src ├── index.js └── util ├── __tests__ │ └── reduceChainPromises-test.js └── reduceChainPromises.js <-- showcasing a nested subdir
Как сопровождающие, мы обычно хотим распространять наш код как…
- … Веб-совместимый формат (UMD, AMD,…) в
dist
- … Исходный код, скомпилированный ES5 в
lib
(сохраняя файловую структуруsrc
)
Мы полностью проигнорируем регистр 1
, поскольку на данный момент нет простого способа предоставить информацию о типе для одного скомпилированного файла (кроме записи файла libdef
). В этой статье мы сосредоточимся на 2
, как заставить flow
распознавать типы файлов, отправленных в lib
.
На первом этапе мы установим все необходимое devDependencies
для запуска нашей цепочки сборки:
npm install babel-cli babel-core babel-preset-es2015 babel-plugin-transform-flow-strip-types flow-copy-source rimraf --save-dev
Если вы уже использовали babel
, то вы, вероятно, знакомы с большинством этих зависимостей. Плагин babel babel-plugin-transform-flow-strip-types
позаботится о том, чтобы весь flow
связанный код был удален из нашего скомпилированного кода ES5.
Кроме того, мы устанавливаем rimraf
для выполнения rm -rf
команд и flow-copy-source
для простого создания так называемых «потоковых файлов», которые будут объяснены позже.
Самое интересное то, что все наши инструменты работают на node
, поэтому нам не нужно беспокоиться о каких-либо настройках разработки для Windows / OSX / Linux.
Для полноты картины вот наш .babelrc
:
{ "presets": ["es2015"], "plugins": ["transform-flow-strip-types"] }
Хорошо, теперь babel
понимает синтаксис ES6 + flow
! Давайте теперь рассмотрим наши package.json
скрипты:
{ "name": "my-lib", ... "scripts": { "test": "jest", "build": "npm run build:clean && npm run build:lib && npm run build:flow", "build:clean": "rimraf lib", "build:lib": "babel -d lib src --ignore '**/__tests__/**'", "build:flow": "flow-copy-source -v -i '**/__tests__/**' src lib" } ... }
В нашем разделе scripts
мы можем найти инструкциюbuild
, которая выполняет три действия:
npm run build:clean
: удаляет текущийlib
каталог.npm run build:lib
: Создаетlib
с нуля путем компиляцииsrc
через babelnpm run build:flow
: Создает наши потоковые файлы, которые также помещаются вlib
Шаг 3) имеет отношение к нашей цели ... мы вызываем flow-copy-source
, который (по умолчанию) копирует все *.js
файлы в src
и копирует их в целевой каталог lib
, сохраняя исходную иерархию каталогов. Эти файлы будут иметь другое окончание, называемое *.js.flow
.
Примечание.
Я используюjest
для тестирования, поэтому добавил правила игнорирования для**/__tests__/**
глобусов. Я не хочу, чтобы тестовые файлы попадали вlib
.
Вот список наших результатов lib
после запуска npm run build
:
$ tree lib lib ├── index.js ├── index.js.flow <-- Exact same content as src/index.js └── util ├── reduceChainPromises.js └── reduceChainPromises.js.flow
Представляем «потоковые файлы» (* .js.flow)
Итак, если есть файл src/util/reduceChainPromises.js
, весь файл будет скопирован в lib/util/reduceChainPromises.js.flow
… но зачем он нам?
Flowtype разрешает модули так же, как node
. Если вы импортируете my-lib/lib/util/reduceChainPromises
, вы, вероятно, сделаете это так:
import reduceChainP from 'my-lib/lib/util/reduceChainPromises';
По умолчанию flow
просматривает node_modules/my-lib
и пытается найти файл lib/util/reduceChainPromises
. Но ждать! Файлы в lib
содержат только чистый код ES5, поэтому мы теряем информацию о нашем типе (flow
, скорее всего, будет рассматривать reduceChainP
как тип any
).
К счастью, если flow
находит файлы с окончанием *.js.flow
, он предпочтет его, а не реальный *.js
. Поэтому мы поставляем дополнительные файлы, которые только flow
сообщают, какие типы представлены в прилагаемом файле ES5. Поскольку эти файлы создаются в процессе сборки, мы всегда можем быть уверены, что наши потоковые файлы синхронизированы с реальным кодом.
А что насчет раздувания файлов?
Вы, вероятно, возразите, что копирование всех src
файлов в lib
приведет к излишнему раздуванию результирующего npm
пакета, верно? Команда flow
знает об этой проблеме и работает над экспериментальной командой flow gen-flow-files
:
$ flow gen-flow-files --help Usage (EXPERIMENTAL): flow gen-flow-files [OPTIONS] [FILE] e.g. flow gen-flow-files ./src/foo.js > ./dist/foo.js.flow
Как только эта функция станет стабильной, мы сможем извлекать чистую информацию о типе из нашего кода ES6 и помещать эту информацию о типе в наш *.js.flow
, в конечном итоге удаляя код реализации (что-то вроде инвертирования того, что babel
делает с нашими src
файлами) .
Хорошо, теперь у нас есть система сборки, которая будет генерировать обратно совместимый код ES5 И добавлять определения типов для нашей библиотеки, готовые к использованию потребителями.
Это было всего лишь базовое введение в эту тему.
Есть еще много открытых вопросов:
- А что насчет зависимостей для нашей библиотеки?
- А как насчет конфликтов зависимостей с нашей библиотекой (например,
lodash
)? - А как насчет различных
flow
версий для потребителя и моей библиотеки?
Как только у меня будет время, я напишу несколько последующих статей, в которых мы глубоко погрузимся в эти проблемы и узнаем, как их решать!
Вы можете ознакомиться с нашей документацией по поддержке потока по стилизованным компонентам, чтобы увидеть, как более сложная библиотека набирается и поставляется с flow
.
Если у вас возникнут вопросы или другие новости о Flowtype, оставьте мне твит или подпишитесь на меня в Twitter @ryyppy