Версионирование
Что означает 0.x на практике, как пакеты семейства связаны версиями и чего библиотека пока не обещает.
Вопрос внедренца звучит не «какая сейчас версия», а «сколько эта версия проживёт». Честный ответ на сегодня: окно поддержки не объявлено. Ниже — что вместо него есть, что из этого проверяемо и что делать, пока обещания нет.
Пока 0.x
Библиотека следует Semantic Versioning, и до 1.0
это значит ровно одно: минорный выпуск может сломать совместимость. Не
«теоретически» — это уже происходило. Версия 0.41.0 вынесла GrCodeBlock из
ядра в @feugene/granularity-code, и подпуть @feugene/granularity/components/GrCodeBlock
исчез:
- import { GrCodeBlock } from '@feugene/granularity'
+ import { GrCodeBlock } from '@feugene/granularity-code/components/GrCodeBlock'Причина названа в журнале изменений и она про 1.0: вынести что-то из замороженного ядра стоит мажора, поэтому переезды такого рода делаются до заморозки, а не после.
На нулевом мажоре ^ и ~ означают одно и то же. ^0.41.0 разворачивается в
>=0.41.0 <0.42.0 — тот же диапазон, что у ~0.41.0. То есть менеджер пакетов
и так закрепляет минор за вас: обновление до 0.42 требует осознанного действия,
а не приезжает с install.
Каждый пакет версионируется сам
Общей версии у семейства нет, и это не недосмотр. У ядра и спутников разный
цикл: @feugene/granularity-code живёт на 0.1.x, пока ядро идёт к 0.42, — и
лишний мажор в спутнике ради выпуска ядра означал бы обновление, за которым не
стоит ни одного изменения.
Из этого следует три вещи, и все три видны в репозитории:
- У каждого пакета свой журнал изменений. Общего журнала в корне нет намеренно — журналы на портале собраны из них.
- У каждого пакета свой тег. Ядро выпускается тегом
vX.Y.Z, спутник —<имя-каталога>-vX.Y.Z, напримерgranularity-charts-v0.11.0. - Публикация идёт из тега. Пуш тега запускает публикацию в npm с
--provenanceи в GitHub Packages: у выпущенного артефакта есть прослеживаемое происхождение, и это проверяет ваш сканер, а не мы.
Матрица совместимости
Спутник объявляет, с какой версией ядра и пресета он работает, — не словами, а
диапазоном в peerDependencies. Ваш менеджер пакетов прочитает его сам и
откажется собрать несовместимую пару. Здесь тот же диапазон в читаемом виде:
| Пакет | Ядро @feugene/granularity | Пресет @feugene/unocss-preset-granular | Vue |
|---|---|---|---|
@feugene/granularity-charts | >=0.38.0 <1.0.0 | >=0.13.0 <1.0.0 | ^3.5.0 |
@feugene/granularity-chrono | >=0.38.0 <1.0.0 | >=0.13.0 <1.0.0 | ^3.5.0 |
@feugene/granularity-code | >=0.40.0 <1.0.0 | >=0.16.0 <1.0.0 | ^3.5.0 |
@feugene/granularity-dashboard | >=0.38.0 <1.0.0 | >=0.13.0 <1.0.0 | ^3.5.0 |
@feugene/granularity-datasource | — | — | ^3.5.0 |
@feugene/granularity-devtools | >=0.38.0 <1.0.0 | — | ^3.5.0 |
@feugene/granularity-editor | >=0.38.0 <1.0.0 | >=0.13.0 <1.0.0 | ^3.5.0 |
@feugene/granularity-forms-schema | >=0.38.0 <1.0.0 | >=0.13.0 <1.0.0 | ^3.5.0 |
@feugene/granularity-media | >=0.38.0 <1.0.0 | >=0.13.0 <1.0.0 | ^3.5.0 |
@feugene/granularity-test-kit | — | >=0.13.0 <1.0.0 | ^3.5.0 |
@feugene/unplugin-granularity | >=0.38.0 <1.0.0 | — | — |
Прочерк означает, что пакет этой зависимости не объявляет вовсе:
granularity-datasource не знает ни о ядре, ни о пресете — он про данные, а не
про разметку, — а test-kit работает с любым ядром, потому что проверяет CSS и
DOM, а не импортирует компоненты.
Верхняя граница у всех одна и та же — <1.0.0. Она говорит не «мы совместимы
со всем до единицы», а «дальше границы обещаний нет»: 1.0 переопределит контракт,
и диапазоны будут переписаны вместе с ним.
Таблица не набрана руками, а сверяется с манифестами на каждой сборке: диапазон,
разошедшийся с package.json спутника, роняет сборку портала. Но сверяется она
с тем тегом, на который закреплён подмодуль библиотеки, — а не с тем, что лежит
в npm сию секунду. Источник истины — peerDependencies установленного пакета.
Требования к окружению
| Что | Версия | Откуда |
|---|---|---|
| Node | >=22 | engines во всех пакетах семейства |
| Vue | ^3.5.0 | peer-зависимость |
| Формат модулей | только ESM | "type": "module", CommonJS-сборки нет |
Эти три строки — тоже часть совместимости, и меняются они по тем же правилам: поднять нижнюю границу Node — ломающее изменение, и до 1.0 оно может приехать минором.
Что произойдёт на 1.0
До 1.0 версионирования документации нет: есть только /docs, без префиксов.
Преждевременное версионирование удваивает работу и путает поиск — архив, у
которого нет читателей, стоит ровно столько же, сколько живой раздел.
С выпуском 1.0 включается следующее:
- текущее дерево копируется в
/v0/, помечается баннером «архив» и получаетrel="canonical"на актуальный аналог; - страницы архива, у которых актуальный аналог есть, уходят из индекса;
- в шапке документации появляется переключатель версий рядом с версией пакета.
Версия в шапке читается из манифеста ядра при сборке и руками не пишется — это правило действует уже сейчас, до всякого 1.0.
Чего библиотека пока не обещает
Раздел существует потому, что молчание здесь читается как обещание. Ни одного из перечисленного сегодня нет:
- срок жизни минора. Сколько 0.41 будет получать исправления после выхода 0.42 — не объявлено;
- период депрекации. Подпуть может исчезнуть в том же выпуске, в котором
появилась замена: так и вышло с
GrCodeBlock; - бэкпорт исправлений безопасности в предыдущие миноры;
- метаданные
sinceиdeprecatedу компонентов. Страница компонента не говорит, в какой версии появился проп: этих данных в библиотеке пока нет, и портал не станет их выдумывать.
Что делать вместо этого:
- Закрепляйте минор. На 0.x это происходит само — см. выноску выше, — но
проверьте, что в
package.jsonстоит диапазон, а неlatest. - Читайте журнал пакета, который поднимаете. Не общий: у каждого свой, и ломающее изменение описано там с диффом.
- Поднимайте по одному. Спутник и ядро связаны диапазоном, а не общей версией: обновление ядра не требует обновления спутников, пока диапазон сходится.
Где смотреть изменения
Журналы изменений на портале собраны из журналов пакетов в репозитории библиотеки — по одному на пакет, с диффами и объяснениями. Это тот же текст, что в репозитории: портал его не пересказывает.