Как обновить проект
Обновление проекта — переход на новый выпуск по журналу изменений: механические замены делает инструмент, остальное — человек. Здесь — порядок обновления и разбор записей журнала на примерах.
Как обновить проект
Обновление — это переход проекта с одного выпуска Framework на другой (вместе
с пакетами продуктов, если они подключены). Страница описывает порядок перехода,
инструмент sf-upgrade и работу с ИИ.
Порядок
- Узнайте, на каком выпуске проект сейчас (см. ниже).
- Выберите новый выпуск Framework и запишите его идентификатор и полный
коммит, а для подключения с CDN — версию пакета npm
@simai-studio/uiэтого выпуска; до запроса с CDN убедитесь, что npm её уже показывает (см. Пакет npm). Если подключён пакет продукта, возьмите его сборку, квитанция которой называет этот выпуск Framework. - Прогоните
sf-upgradeпо проекту и исправьте всё, что он нашёл (Инструментsf-upgrade). Исходники после--applyрассчитаны на новый выпуск, поэтому замены и новую поставку выкладывайте вместе. - Замените поставку целиком в тестовом окружении: свою копию
distr/— каталогом нового выпуска, а при подключении с CDN — версию пакета во всех трёх адресах. - Проверьте основные страницы на мобильной и настольной ширине, в обеих темах,
Network и Console; на страницах со Smart-компонентами — пустой
SF.deprecations.list(). - Запишите новый идентификатор выпуска, его полный коммит, версию пакета npm (при подключении с CDN) и квитанции пакетов.
- Перенесите в рабочее окружение те же проверенные файлы.
Как узнать текущий выпуск проекта. Страница не сообщает свой выпуск: идентификатора выпуска в поставке нет. Выпуск определяют так:
-
по файлу блокировки проекта (см. Версии: как закрепить выпуск), если он есть;
-
по адресу поставки: при подключении с CDN в адресе стоит версия пакета npm, и
npm view @simai-studio/ui@<версия> simaiназывает её выпуск и коммит; записи о выпусках (contracts/releases/вui) называют коммит каждого выпуска в полеruntime_sources.core.commit; -
для своей копии без записи — сравнением файлов: копия
distr/совпадает с каталогомdistr/в коммите выпуска репозиторияui. Совпавший целиком выпуск — текущий. Сравнить копию с выпуском можно так; пустой выводdiffозначает совпадение:mkdir -p /tmp/release git -C simai-ui archive <коммит выпуска> distr | tar -x -C /tmp/release diff -r /tmp/release/distr public/assets/simai-framework/ui/distr
Откат — тоже целиком: вернуть прежний выпуск Framework вместе с прежними сборками пакетов, не смешивая файлы разных выпусков.
Инструмент sf-upgrade
scripts/sf-upgrade.mjs в репозитории ui-source читает журнал и ищет
устаревшие имена в исходниках проекта: HTML, PHP и Blade, Twig, Vue,
JavaScript и TypeScript, CSS и SCSS, Markdown, JSON. Инструмент и журнал
лежат в открытом репозитории simai/ui-source;
в поставке ui их нет. Ревизию
ui-source, из которой собран выпуск, называет поле
build_inputs.source.commit записи о выпуске (см. Выпуск и запись о выпуске).
Инструмент и журнал берут из ревизии ui-source целевого выпуска — того,
на который проект переходит: git checkout <build_inputs.source.commit>. Более
новый журнал может назвать каноническое имя, которого в целевом выпуске ещё
нет, и --apply записал бы в проект имя, которое этот выпуск не знает. Для
выпуска ui-106d5901f8c3 это ревизия
3029d8abda3899f055d91e1599e1c19a969e4369.
Инструменту нужен только Node.js: зависимостей у него нет, устанавливать ничего не нужно.
node scripts/sf-upgrade.mjs ../my-product # только отчёт
node scripts/sf-upgrade.mjs ../my-product --apply # механические замены
node scripts/sf-upgrade.mjs ../my-product --json # отчёт для ИИ и скриптов
node scripts/sf-upgrade.mjs ../my-product --since 2026-10-01 # только записи с этой даты
node scripts/sf-upgrade.mjs ../my-product --journal ./journal.json # другой файл журнала
| Параметр | Что делает |
|---|---|
<папка проекта> |
Обязателен: каталог, который просматривается вместе со всеми вложенными |
--apply |
Выполняет механические замены и переписывает файлы |
--json |
Печатает отчёт одним JSON-документом; с --apply в нём applied: true |
--since ГГГГ-ММ-ДД |
Берёт только записи, устаревшие в этот день или позже (deprecated_on) |
--journal <путь> |
Читает журнал из указанного файла вместо contracts/changes/journal.json рядом с инструментом |
Параметры сочетаются: --apply --json делает замены и печатает отчёт.
Отчёт --json — один JSON-объект:
{
"summary": {"project": "/abs/my-product", "files": 214, "findings": 3,
"mechanical": 2, "applied": false, "manual": 1},
"findings": [
{"file": "templates/cart.html", "line": 12, "record": "attribute-type-to-appearance",
"stage": "compat", "from": "type", "to": "appearance", "mechanical": true}
],
"manual": [
{"id": "…", "summary": "…", "manual": "…", "examples": {"before": "…", "after": "…"},
"places": ["templates/cart.html:40 …"]}
]
}
| Поле | Что в нём |
|---|---|
summary |
Просмотренный каталог, число файлов, число находок, из них механических, были ли замены (applied) и число записей с ручной работой (manual) |
findings |
Каждая находка: файл относительно каталога проекта, строка, запись журнала, её этап, устаревшее и каноническое имя (to равно null, если замены нет), механическая ли замена. С --apply выполненные замены остаются в списке, а summary.applied равно true |
manual |
Каждая запись с ручной работой: id, summary, инструкция manual, пример examples и места places в виде файл:строка имя |
Коды завершения: 0 — находок нет или с --apply остались только
выполненные механические замены, 1 — остались находки (без --apply —
любые, с --apply — ручные), 2 — не указана папка проекта.
Какие папки пропускаются: node_modules, vendor, .git, dist, build,
build_production, storage, .artifacts. Папка distr не
пропускается. Если проект хранит свою копию поставки внутри проверяемого
каталога (например, public/assets/simai-framework/ui/distr/), инструмент
находит устаревшие имена и в файлах самого Framework, а --apply их
переписывает. Запускайте инструмент на каталогах с исходниками проекта или
держите копию поставки вне проверяемого каталога.
- Без
--applyинструмент только сообщает: файл, строку, устаревшее и каноническое имя, запись журнала. Пока находки остаются, он завершается с кодом 1, поэтому его можно ставить в проверки проекта. - С
--applyон сам делает механические замены: атрибут, значение, событие, класс, токен, утилиту. Если в теге уже есть каноническое имя, устаревшее просто удаляется — каноническое действует и во время работы страницы. - Инструмент читает файлы
.html,.htm,.php(в том числе.blade.php),.twig,.vue,.js,.mjs,.ts,.jsx,.tsx,.css,.scss,.mdи.json. Прежнее имя события заменяется в любой строке в одинарных, двойных или обратных кавычках в любом из этих файлов, в том числе в JavaScript, Vue и Blade:addEventListener('sf-range-slider-change', …)становитсяaddEventListener('change', …). - Где инструмент ищет каждое имя: атрибут компонента — только в открывающем
теге этого компонента, значение — в открывающем теге
<sf-…>, событие — в кавычках в любой строке, класс и токен — как отдельное слово в любом месте. Поэтому находки бывают и в.jsonи.md, например ключ"type"; проверьте их перед--apply. - Остальное он выводит списком для ручной работы — с инструкцией и примером из записи.
- Инструкцию
manualмеханической записи инструмент не выводит. Она печатается только для ручных мест. Если у механической записи есть полеmanual, прочитайте его в журнале сами: оно описывает, чем замена меняет поведение (пример — в Запись с механической заменой). --sinceограничивает проверку записями, устаревшими в указанный день или позже. В записи о выпуске даты нет. Если неизвестно, на каком выпуске проект и когда он обновлялся, запускайте инструмент без--since: он проверит все записи журнала.- Инструмент читает и удалённые записи, поэтому обновить можно проект, отставший на любое число выпусков.
- Компонент пакета приходит на страницу только из объявленного пакета. Если
запись журнала переносит компонент в пакет (
toравноSF_BOOT_CONFIG.packages.<имя>), в проекте есть его тег, а этот пакет нигде не объявлен (packages: {<имя>: …},packages.<имя> = …или вызовSF.RuleLoader.registerPackage('<имя>', …)), инструмент сообщает об этом как о ручной работе и называет, что добавить: объявление пакета (см. Пакеты компонентов). Так перенесены в пакет продуктаlarena(исходник —simai/larena-ui) четыре элемента:sf-table,sf-data-view,sf-property-viewиsf-admin-menu; запись журнала —component-larena-package-move. В поставкеuiих нет; на странице они работают, только если она объявила пакетlarena, напримерSF_BOOT_CONFIG.packages.larena = {base: '/assets/larena/5f33ba576aebde377d5195ec71836eb4430aa84d/smart'}(путь — по правилу из Пакеты компонентов), и подключила его сборку, квитанция которой называет выпуск Framework на странице. Проект без Larena берёт сборку из выпускаsimai/larena-ui(см. Пакетlarenaдля проекта без Larena).
После ручных правок инструмент запускают снова и повторяют, пока он ничего не находит: ручная правка одного места может открыть следующую механическую замену, например значение атрибута, который правили вручную.
Проект обновлён, когда выполнены три условия:
sf-upgradeничего не находит;- проверки проекта проходят;
SF.deprecations.list()пуст на затронутых страницах.
Обновление с помощью ИИ
Журнал устроен так, чтобы по нему мог работать ИИ, не угадывая соответствий. ИИ получает три вещи:
- отчёт
sf-upgrade --json— найденные места и список ручной работы; - записи журнала, на которые этот отчёт ссылается;
- проверки самого продукта.
Правила для ИИ:
- сначала выполняются механические замены через
--apply, затем каждое ручное место — строго по инструкции и примеру из его записи; - соответствие, которого нет в журнале, придумывать нельзя;
- работа закончена при тех же трёх условиях, что и у человека (Инструмент
sf-upgrade).
Общие правила работы с ИИ — см. Работа с ИИ.
Пример: запись журнала и её применение
Этот раздел показывает на одном примере, как запись журнала превращается в правку проекта. Записи бывают двух видов: с механической заменой и с ручной работой.
Запись с механической заменой
Возьмём запись attribute-type-to-appearance из Журнал изменений: rewrite равно
rename-attribute, у sf-button атрибут type заменяется на appearance.
Пусть в шаблоне проекта есть такая разметка:
<sf-button type="outline" text="Отмена"></sf-button>
Что происходит на каждом шаге:
-
На странице. Пока запись на этапе совместимости, кнопка работает: Smart-компонент читает
typeкакappearance.SF.deprecations.list()показывает строку с компонентомsf-button, устаревшим именемtypeи каноническимappearance. -
Отчёт.
node scripts/sf-upgrade.mjs ../my-productсообщает файл, строку, заменуtype→appearanceи записьattribute-type-to-appearance, отмечая её как механическую. -
Замена. С
--applyинструмент сам переписывает разметку:<sf-button appearance="outline" text="Отмена"></sf-button>Если в теге уже были оба атрибута,
typeудаляется, аappearanceостаётся. -
Проверка. Повторный запуск
sf-upgradeничего не находит, списокSF.deprecations.list()на этой странице пуст.
Что важно заметить: ни человек, ни ИИ не выбирают новое имя сами — его даёт
поле to записи.
Одна запись — несколько имён. Запись event-change-aliases заменяет на
change все прежние имена события изменения: sf-button-group-change у
sf-button-group, sf-datepicker-change и sf-change у sf-datepicker,
sf-dropdown:change у sf-dropdown, sf-range-slider-change и
onSliderChange у sf-range-slider. У sf-button-group методы select() и
clear() тоже отправляют change, как и прежнее имя. Пока запись на этапе совместимости,
прежние имена приходят вместе с change, поэтому код на прежних именах
работает до замены. У sf-datepicker прежнее sf-change несёт другой
detail: все данные компонента, а не состав change (см. События).
Механическая замена может менять поведение. Запись event-change-aliases
механически заменяет прежние имена события изменения на change, в том числе
sf-range-slider-change у sf-range-slider. Её поле manual предупреждает:
sf-range-slider-change приходит и при установке значения из кода, а change
— только при действии человека (см. Соглашение change). Код, которому нужно событие после
установки значения из кода, после замены вызывает свой обработчик сам.
sf-upgrade --apply это предупреждение не печатает (см. Инструмент sf-upgrade), поэтому поле manual
каждой применённой записи читают в журнале.
Запись с ручной работой
Если в записи rewrite равно manual, механической замены нет: устаревшее
имя нельзя заменить одним каноническим без решения автора проекта.
- Отчёт.
sf-upgradeнаходит места с устаревшим именем, но не меняет их даже с--apply. Каждое такое место попадает в список ручной работы вместе с полемmanualи примеромexamplesиз записи. - Правка. Человек или ИИ правит каждое место строго по инструкции из
manual. - Проверка. Проект обновлён при тех же трёх условиях (Инструмент
sf-upgrade).
Изменение поведения (запись с kind равным behaviour) и переименование
свойства JavaScript (kind равен property) sf-upgrade не ищет. Для таких
изменений запись журнала описывает в поле manual, что проверить и как
поправить; места находит человек или ИИ по полям from и to, а результат
подтверждают проверки проекта.
Связь с другими частями
- Из чего состоит поставка по смыслу (ядро, утилиты, компоненты) — Уровни Framework и Из чего состоит Framework.
- Пакеты продуктов: где живут, как собираются и закрепляются, лицензия — Пакеты компонентов продуктов.
- Тема на
<html>— Темы; токены, которые приноситcore.css, — Дизайн-токены. - Лестница размеров и адаптивная типографика — Адаптивные размеры, Одна лестница размеров и Типографика.
- Классы и условия, по которым Loader выбирает модули, — Как устроено имя утилиты, Условия действия и Как утилиты попадают на страницу.
- Smart-компоненты, их база, шаблоны и события — раздел Smart-компоненты (Устройство компонента, Шаблоны, События).
- Проверки качества и работа с ИИ — Как Framework проверяет себя–Работа с ИИ.