9. Компиляция и генерация

Пайплайн
Saving .sharq to Assets/
│
▼ AssetPostprocessor.OnPostprocessAllAssets
├── SharqFileImporter.ProcessSharq()
│ ├── SharqFileParser → SharqFileModel (template, script, style)
│ ├── SharqSectionCache → incremental regeneration
│ ├── SharqValidator → diagnostics (warnings)
│ ├── BuildMethodGenerator → .g.cs
│ ├── ScopedCssGenerator → _scoped.g.uss
│ └── StyleParser → .g.uss (global)
│
▼ Result:
Assets/SusUI/Generated/ComponentName.g.cs
Assets/SusUI/Generated/ComponentName_scoped.g.uss
Assets/SusUI/Generated/ComponentName.g.uss (global)Инкрементальная компиляция
- Content-hash: если
.sharqне изменился — пропускается - Section-level: если изменился только
<style>— перегенерируется только.uss(hot reload без C# domain reload) - SharqAutoRegenerate: при Domain Reload проверяется актуальность
SharqValidator — диагностика
| Проверка | Уровень |
|---|---|
v-for без :key (при StrictVForKey: true) | Warning |
Неиспользуемые поля в <script> | Info |
Сгенерированные файлы
Assets/SusUI/
├── Component.sharq ← source (you can edit here, in any subfolder)
├── Resources/SusRuntime/ ← author's runtime resources (in git): Icons/, Fonts/, theme-overrides
└── Generated/ ← auto-generated (.gitignored)
├── Component.g.cs
├── Component_scoped.g.uss
├── Component.g.uss
├── Component_static.g.uss
├── Component.sections.json
├── Component.sharq.hash
├── .gitignore
└── Resources/SusRuntime/ ← synced USS components for runtime (auto)Базовые тема/иконки/шрифты (
_theme,_icon,_font,design-tokens) поставляются в самом пакете:sus-core/Runtime/Resources/SusRuntime/.SusRuntime— виртуальное пространство Resources (Resources.Load("SusRuntime/..")), а не "вывод компилятора"; Unity объединяет все папкиResources/SusRuntime/(пакет + проект) в один путь.
Важно: Всегда редактируйте .sharq, а не Generated/* — сгенерированные файлы перезаписываются.
Очистка: при удалении
.sharqвсе 6 сгенерированных файлов +.meta+ копии в Resources удаляются автоматически. При переименовании старые артефакты удаляются, новые создаются.
Загрузка USS в рантайме
Компоненты загружают стили через Resources/SusRuntime/ (в билде). У редактора есть fallback — если файл не найден в Resources, он загружается напрямую из Generated/ через AssetDatabase.
Batch-контур (для библиотечных пакетов)
Помимо project-scoped импортёра (Assets/SusUI), есть декларативный batch-контур — для библиотек, которые поставляют уже сгенерированные .cs/.uss внутри самого пакета (опциональные downstream UI-пакеты).
Пакет декларирует, ЧТО генерировать, в дескрипторе sharq.gen.json (рядом с package.json):
{
"displayName": "My UI Package",
"sources": ["Components"],
"generated": "Runtime/Generated",
"resources": "Runtime/Resources/SusRuntime",
"watch": true,
"namespace": "MyCompany.UI"
}Опционально namespace: составной C#-идентификатор, оборачивающий каждый сгенерированный .g.cs-тип этого пакета. Пусто / опущено → глобальное пространство имён (legacy). Downstream UI-пакетам стоит задавать здесь свой корневой namespace пакета, чтобы короткие имена типов не конфликтовали между пакетами.
Опционально usings: массив строк с дополнительными C#-пространствами имён, эмитируемыми как using-директивы в каждом сгенерированном .g.cs (при композиции типов из другого UI-пакета).
Инфраструктура в sus-core/Editor/Packaging/:
SusPackageRegistry— находит дескрипторы для всех разрешённых пакетов; учитываются только мутируемые пакеты (file:-ссылки / embedded) — registry/git-пакеты уже поставляются готовыми артефактами.SusPackageGenerator— менюSharq/Generate All Packages(все пакеты разом) и окноSharq/Generate Package…(по одному). Ядро —SharqBatchCompiler.CompileDirectory(sus-core/Editor/AssetPipeline/), тот жеBuildMethodGenerator, что и у импортёра.SusPackageAutoCompile— FileSystemWatcher для каждой исходной директории пакета сwatch: true(пакеты внеAssets/не видны AssetPostprocessor'у): сохранение.sharq→ автоперегенерация. В Play: только-style / только-template →SharqBatchMode.HotReloadSafe(USS + интерпретатор, без.g.cs/ domain reload);<script>→ откладывается до выхода из Play, затем полный Generate. В Edit mode — полный Generate пакета как раньше.- Результат коммитится в пакет (в отличие от project-scoped
Generated/, который в.gitignore).
Три контура генерации
| Контур | Триггер | Область | Кто |
|---|---|---|---|
| Project | сохранение .sharq в Assets/ (AssetPostprocessor) + Window → SUS → Sharq → Regenerate All Prototype Components | sus.config.json → SharqDirectory | SharqFileImporter |
| Packages | сохранение .sharq в пакете (watcher) + проверка актуальности при domain reload + меню | все пакеты с sharq.gen.json | SusPackage* (см. выше) |
| CLI | вручную / CI | Assets/ без Unity (только .g.cs) | SharqBootstrap |
Потребитель пакета не может полагаться на runtime-Roslyn source generator — поэтому
.csгенерируется заранее и поставляется в пакете как обычные скрипты.
Как генератор транслирует авторский .sharq → C#
BuildMethodGenerator конвертирует синтаксис DSL в валидный C#:
В .sharq | В .g.cs |
|---|---|
[CreateProperty(default:0)] public Prop<int> Damage = new(0); | [CreateProperty] (параметры DSL default:/validate: отбрасываются) + backing-поле public Prop<int> Damage = new(0); + companion-свойство public int damage { get => Damage.Value; set => Damage.Value = value; } |
наличие [CreateProperty] | автоматический using Unity.Properties; |
Vue-кавычки в выражениях: v-if="Mode.Value != 'delete'", != '' | C#-кавычки: Mode.Value != "delete", != "" |
$using System.Linq; | using System.Linq; |
Всегда добавляются using System; и using System.Collections.Generic;.
Соглашения для компонента <script>
SusComponent : VisualElement— корень — сам компонент: используйтеthis.Q<…>(),this.style,this.schedule(неElement.…).- Базовые хуки жизненного цикла:
Created(),BeforeMounted(),Mounted(),Updated(),BeforeUnmounted(),Unmounted()(всеprotected override void). МетодаInit()нет — инициализацию элементов (this.Q<…>(),Watch(...)) делайте вCreated().
Hot reload (Editor + DEVELOPMENT_BUILD)
USS (E1 / E4)
- Изменение только
<style>→ регенерация.g.ussбез domain reload →UssHotReloadServiceперезагружает сопутствующие таблицы стилей на живых компонентах (<1с в Editor Play). - Remote (E4):
SusRuntimeHotReload.ApplyUss+ Runtime MCPui.hotreload.uss/ Session MCPclient.hotreload_uss. StyleSheet-from-text — через EditorStyleSheetImporterImpl(SusUssFromString). На standalone-плеере без фабрики USS-parse недоступен; template hot reload работает. - Editor push:
RemoteHotReloadPushService(отключение: EditorPrefsSharq.RemoteHotReload.Enabled= 0).
Templates (E3)
- Изменение только
<template>→SharqCompileEvents.OnTemplateChanged→TemplateHotReloadService/SharqTemplateInterpreter.TryApply. - Remote:
ui.hotreload.template/client.hotreload_template.
Матрица поддержки интерпретатора шаблонов
| Фича | Статус | Поведение |
|---|---|---|
$MainElement / root-атрибуты → this | ✅ | Как у BuildMethodGenerator: class / :class / name — на хост-компонент, дети — внутрь |
class, :class (объект), name, :text | ✅ | |
v-if / v-show (литерал, !, ==/!=, ||/&&, Prop.Value) | ✅ | Сложный C# (string.IsNullOrEmpty, вызовы) → fallback |
Prop="lit" / :Prop="expr" | ✅ | Простые строки/числа/bool/enum |
<slot> / именованный slot | ✅ | Через GetSlotContainer / BuildSlot |
@click / @* события | ⚠️ пропуск | Лог [SharqInterp] Skipping unsupported event…; дерево применяется без обработчиков |
transition= на v-if | ⚠️игнор | Атрибут пропускается; анимация — после полной перекомпиляции |
v-for | ❌ fallback | TryApply → false, нужен domain reload / полный Build |
| Вложенные произвольные C#-выражения | ❌fallback |
Критерий: простые downstream UI-шаблоны (Divider / Badge / упрощённый Alert) — TryApply == true; заведомо сложные (v-for, тяжёлые выражения) — предсказуемый fallback + предупреждение.
State-preserving domain reload (E2b)
При редактировании <script> Unity делает domain reload. Чтобы UI не сбрасывался:
- Edit → Preferences → General → Script Changes While Playing = Recompile And Continue Playing (иначе Play остановится). Без этой настройки Unity останавливает Play при каждом изменении скрипта — снапшот бесполезен.
HotReloadStatePreserveServiceснимаетSusComponentSnapshotс деревьев Sus в UIDocument и EditorWindow перед reload и восстанавливает послеdelayCall.- Сериализуются примитивы + enum в
Prop<T>; остальные типы игнорируются. - Опция в
Assets/sus.config.json:"HotReloadStatePreserve": true(по умолчанию). Отключить:false.
Правило «не компилировать в бою» остаётся приоритетным для ручной PvP-сессии — E2b это Editor-удобство, а не замена этому правилу.