mirror of
https://github.com/Ku6epXBOCTuK/easy-png-tools.git
synced 2026-09-14 21:46:35 +00:00
docs: update plans, backlog and other docs
This commit is contained in:
@@ -5,68 +5,23 @@
|
|||||||
## Стек
|
## Стек
|
||||||
|
|
||||||
- **SvelteKit** (`web/`) — основное приложение, Svelte 5 (runes).
|
- **SvelteKit** (`web/`) — основное приложение, Svelte 5 (runes).
|
||||||
- Менеджер пакетов: **pnpm**. Запускать команды из `web/`, либо через
|
- Менеджер пакетов: **pnpm**. Запускать команды из `web/`, либо через обёртки в
|
||||||
обёртки в корне: `pnpm build`, `pnpm dev` (это `pnpm --dir web ...`).
|
корне: `pnpm build`, `pnpm dev` (это `pnpm --dir web ...`). Не использовать
|
||||||
Не использовать npm.
|
npm.
|
||||||
- Сборка: `pnpm --dir web build`, проверка типов: `pnpm --dir web exec svelte-check --tsconfig ./tsconfig.json`,
|
- Сборка: `pnpm --dir web build`, проверка типов:
|
||||||
тесты: `pnpm --dir web test` (Vitest), e2e: `pnpm --dir web test:e2e` (Playwright,
|
`pnpm --dir web exec svelte-check --tsconfig ./tsconfig.json`, тесты:
|
||||||
`web/e2e/`, свой webServer на `pnpm build` + `scripts/serve-static.mjs`, порт 4173).
|
`pnpm --dir web test` (Vitest), e2e: `pnpm --dir web test:e2e` (Playwright,
|
||||||
- Форматирование: два независимых прогона. Корневой prettier форматирует
|
`web/e2e/`, свой webServer на `pnpm build` + `scripts/serve-static.mjs`, порт
|
||||||
только markdown `docs/` (`.prettierrc` в корне, `pnpm format:docs`, проверка —
|
4173).
|
||||||
`pnpm check:docs`). Корневой `.prettierignore` — только точечные исключения
|
- Форматирование: `pnpm format` в корне (оба прогона) или
|
||||||
(node_modules, refs-html…), без `/*`-allowlist: он глушит формат-он-сейв
|
`pnpm --dir web format` (только код web/). Проверка: `pnpm check:docs`
|
||||||
для всего `web/` (файлы резолвятся как ignored и дочерние конфиги не
|
(docs) + `pnpm --dir web exec prettier --check .` (код).
|
||||||
применяются). Код `web/` форматируется
|
- Линтинг: `pnpm --dir web lint` (ESLint). Полный прогон дизайн-проверок:
|
||||||
отдельно: `pnpm --dir web format` (Prettier + `prettier-plugin-svelte`,
|
`pnpm --dir web lint:all` (ESLint + stylelint + токен-аудит). Каждый шаг
|
||||||
конфиг `web/.prettierrc`, игнор `web/.prettierignore`). Проверка без записи:
|
выполняется, даже если предыдущий упал. Текущие «ожидаемые долги» (hardcoded
|
||||||
`pnpm --dir web exec prettier --check .`. Оба прогона сразу: `pnpm format`
|
размеры в kit до C19, не-Prefix токены в `app_v1.css`) — техдолг: чинить
|
||||||
в корне.
|
|
||||||
- Линтинг: `pnpm --dir web lint` (ESLint, flat-конфиг `web/eslint.config.js`).
|
|
||||||
Устроен инкрементально:
|
|
||||||
- На **весь код** — парсинг TS/Svelte + правило
|
|
||||||
`@typescript-eslint/consistent-type-imports` (запрет инлайн-тип-импортов).
|
|
||||||
- Полные `recommended`-наборы (`eslint` + `typescript-eslint` + `eslint-plugin-svelte`)
|
|
||||||
навешены **только на новый код** (`src/lib/components/kit/**`,
|
|
||||||
`src/routes/preview/**`), чтобы старый код не засыпался предсуществующими
|
|
||||||
ошибками. Когда старый дизайн удалён (C19), scoped-блок убирается и
|
|
||||||
`recommended` включается на весь код.
|
|
||||||
- **Scoped-пути двигаются вместе с папками** (см. `plan-redesign.md` §10):
|
|
||||||
на C17 `src/routes/preview/**` → `src/routes/**` (preview переезжает на
|
|
||||||
реальные маршруты), на C19 при переименовании `kit/`→`ui/` — и компонентный
|
|
||||||
glob. Не оставлять устаревшие пути в `eslint.config.js`.
|
|
||||||
- Для `*.svelte` выключен `prefer-const` (пропсы в Svelte 5 пишутся через `let`).
|
|
||||||
- **Единый запуск всей дизайн-проверки: `pnpm --dir web lint:all`**
|
|
||||||
(`web/scripts/lint-all.mjs`) — гоняет по очереди: ESLint (все правила,
|
|
||||||
включая design-tokens), stylelint (`lint:css`) и токен-аудит
|
|
||||||
(`lint:tokens`). Каждый шаг выполняется, даже если предыдущий упал; выход
|
|
||||||
ненулевой, если хоть один не прошёл. Текущие «ожидаемые долги» (hardcoded
|
|
||||||
размеры в kit, не-Prefix токены в preview.css и т.п.) — техдолг: чинить
|
|
||||||
только по заведённым tasks, не игнорировать правилом.
|
только по заведённым tasks, не игнорировать правилом.
|
||||||
|
|
||||||
### Тесты кастомных линт-правил
|
|
||||||
|
|
||||||
Линт-правила в `web/eslint-plugins/` покрыты юнит-тестами (Vitest):
|
|
||||||
|
|
||||||
- `web/eslint-plugins/__tests__/design-tokens.test.ts` — три чистых правила
|
|
||||||
(`no-hardcoded-in-svelte`, `no-category-mismatch`,
|
|
||||||
`no-token-definition-in-svelte`) через `RuleTester` со строковыми кейсами;
|
|
||||||
`no-undefined-in-svelte` — через `Linter` API, т.к. читает словарь токенов из
|
|
||||||
`__fixtures__/src/app.css` (не из реального `src/app.css`).
|
|
||||||
- `web/eslint-plugins/__tests__/no-mixed-imports.test.ts` — isolation-правило
|
|
||||||
через `Linter` API с `cwd` на `__fixtures__`: правило резолвит импорты по
|
|
||||||
реальным файлам, поэтому цели импортов обязаны существовать на диске.
|
|
||||||
- Хелпер `__tests__/helpers.ts` собирает `Linter` с `cwd = __fixtures__` —
|
|
||||||
`process.cwd()` не трогается, реальные `src/` не читаются.
|
|
||||||
|
|
||||||
Запуск: `pnpm --dir web test:rules` (`vitest run eslint-plugins/__tests__`).
|
|
||||||
Полный `pnpm --dir web test` тоже их гоняет.
|
|
||||||
|
|
||||||
Фикстуры живут в `web/eslint-plugins/__fixtures__/` и сами прогоняются линтом
|
|
||||||
(`eslint .`), поэтому добавление/правка стабов — тоже работа с валидным кодом.
|
|
||||||
Помнить: `allowDefaultProject` в `eslint.config.js` перечисляет test-файлы и
|
|
||||||
фикстуры точечно (glob'ы с `**` там запрещены tseslint) — при добавлении
|
|
||||||
файлов в `__tests__/`/`__fixtures__/` дописать их туда же.
|
|
||||||
|
|
||||||
## Правила кода
|
## Правила кода
|
||||||
|
|
||||||
### Svelte 5: типизация props через `interface Props`
|
### Svelte 5: типизация props через `interface Props`
|
||||||
@@ -91,111 +46,44 @@
|
|||||||
Не использовать инлайн-дженерик `$props<{ ... }>()` — он тяжело читается и
|
Не использовать инлайн-дженерик `$props<{ ... }>()` — он тяжело читается и
|
||||||
разносит тип и деструктуризацию по разным местам. Также **не использовать
|
разносит тип и деструктуризацию по разным местам. Также **не использовать
|
||||||
инлайн-импорты в типах** (`children?: import('svelte').Snippet;`) — все
|
инлайн-импорты в типах** (`children?: import('svelte').Snippet;`) — все
|
||||||
`import type` поднимаются наверх файла. Это правило **включено в ESLint**
|
`import type` поднимаются наверх файла.
|
||||||
(`@typescript-eslint/consistent-type-imports`, `prefer: 'type-imports'`).
|
|
||||||
|
|
||||||
> Правило «всегда `interface Props` + `let {...}: Props = $props()`» стандартным
|
> Правило «всегда `interface Props` + `let {...}: Props = $props()`» стандартным
|
||||||
> ESLint-правилом не покрывается — остаётся конвенцией. Кастомное правило
|
> ESLint-правилом не покрывается — остаётся конвенцией.
|
||||||
> (eslint-plugin-svelte / свой visitor) — TODO, позже.
|
|
||||||
|
|
||||||
### Дизайн: новый визуальный язык
|
### Дизайн: новый визуальный язык
|
||||||
|
|
||||||
Переезд на новый дизайн описан в `docs/plan-redesign.md`. Ключевые
|
Описание дизайна — в `docs/plan-redesign.md`. Общие правила:
|
||||||
ограничения при работе с веткой `feat/redesign`:
|
|
||||||
|
|
||||||
- Глобальные стили старого и нового дизайна разнесены по layout без
|
- Новый дизайн живёт в `web/src/app.css` (корневые маршруты), старый — в
|
||||||
наследования: старое в `web/src/app.css` (группа `(old)/`), новое в
|
`web/src/app_v1.css` (маршруты `/v1/*`).
|
||||||
`web/src/lib/styles/design2.css` (ветка `preview/*`). Префикс `.ds2` не
|
|
||||||
используется.
|
|
||||||
- Все повторяющиеся визуальные элементы — отдельные компоненты в
|
- Все повторяющиеся визуальные элементы — отдельные компоненты в
|
||||||
`web/src/lib/components/kit/`, даже «просто div с двумя стилями».
|
`web/src/lib/components/`, даже «просто div с двумя стилями».
|
||||||
|
|
||||||
### Линтинг дизайн-токенов («единая точка правды» дизайна)
|
### Линтинг дизайн-токенов
|
||||||
|
|
||||||
Запрещено «захардкоживать» дизайн: цвета, размеры, длительности и z-index
|
Запрещено «захардкоживать» дизайн: цвета, размеры, длительности и z-index
|
||||||
обязаны приходить из CSS-переменных. Стек: **stylelint** прогоняется по всем
|
обязаны приходить из CSS-переменных. Прогон: `pnpm --dir web lint:all`.
|
||||||
CSS-файлам (`web/stylelint.config.js`), **ESLint** лезет в `<style>`-блоки
|
|
||||||
svelte-компонентов через локальный плагин `web/eslint-plugins/design-tokens/`
|
|
||||||
(постпрефес postcss AST от svelte-eslint-parser). Правила применяются к тем же
|
|
||||||
scoped-путям (`kit/**`, `preview/**`).
|
|
||||||
|
|
||||||
Команды:
|
- **Цвета**: только `hct(...)` в `app.css`, seed-токены
|
||||||
|
(`--brand-main`/`--brand-alt`) и `color-mix(...)` — исключения.
|
||||||
- `pnpm --dir web lint:css` — stylelint по всем `src/**/*.css` (`old.css` и
|
|
||||||
`node_modules/build/.svelte-kit/static` игнорируются).
|
|
||||||
- `pnpm --dir web exec node scripts/check-tokens.mjs` — токен-аудит по
|
|
||||||
preview.css (лёгкий оркестратор поверх `web/scripts/token-audit/*`);
|
|
||||||
проверяет три вещи. Каждый цветовой
|
|
||||||
токен из `:root` обязан иметь пару в `[data-theme="dark"]` и наоборот.
|
|
||||||
Производные токены (значение содержит `var()`, напр. `hct(from var(--...))`)
|
|
||||||
из пары исключены — они наследуют тему автоматически. Выводит **варнинг** о
|
|
||||||
неиспользуемых токенах preview.css (определены, но нигде не используются) —
|
|
||||||
выход он не меняет. Провалом (exit 1) считаются только parity и hct-авторство.
|
|
||||||
- Тот же скрипт проверяет **hct-only для цветов**: ЛЮБОЕ цветовое значение в
|
|
||||||
preview.css обязано быть `hct(...)` — и литерал, и производное
|
|
||||||
`hct(from var(...) h c t)` (вся палитра считается через hct-каналы; эммит в
|
|
||||||
sRGB-hex делает postcss-плагин `web/scripts/postcss-hct.mjs`). Исключения:
|
|
||||||
только `--brand-main` / `--brand-alt` (seed-токены, любая форма) и
|
|
||||||
`color-mix(...)` (единственный легальный способ смешать два токена).
|
|
||||||
`oklch()/rgb()/#hex` в `--color-*` запрещены.
|
`oklch()/rgb()/#hex` в `--color-*` запрещены.
|
||||||
|
- **Размеры**: `--space-*`, `--text-*`, `--radius-*`, `--size-*`.
|
||||||
|
- **Breakpoints**: `@custom-media --bp-*` (объявления в `app.css`, используются
|
||||||
|
как `@media (--bp-*)`).
|
||||||
|
- **z-index**: `--z-*`; **длительности**: `--duration-*`, `--ease-*`.
|
||||||
|
- В `<style>` svelte-компонентов: нельзя хардкодить цвета/размеры/длительности,
|
||||||
|
нельзя использовать необъявленные `var(--x)`, нельзя путать категории
|
||||||
|
(color-токен в size-свойстве).
|
||||||
|
|
||||||
Токены-префиксы (целевой словарь дизайна):
|
Детали: полный список правил плагина, токены-префиксы, настройка stylelint — см.
|
||||||
|
`web/eslint-plugins/README.md`.
|
||||||
|
|
||||||
- Цвета: `--color-*`, бренд `--brand-main` / `--brand-alt` — единственные
|
### Изоляция веток old ↔ new
|
||||||
две переменные, которым разрешено быть hex/rgb, остальные цвета — только
|
|
||||||
`hct()` (эммит в sRGB-hex делает postcss-плагин `web/scripts/postcss-hct.mjs`)
|
|
||||||
и только в preview.css.
|
|
||||||
- Размеры: `--space-*`, `--text-*` (font-size), `--radius-*`, `--size-*`.
|
|
||||||
- Брейкпоинты: `@custom-media --bp-mobile (max-width: 640px)` /
|
|
||||||
`--bp-tablet (800px)` / `--bp-desktop (1100px)` — объявляются в preview.css,
|
|
||||||
используются как `@media (--bp-*)`. CSS-переменные в `@media` не работают,
|
|
||||||
поэтому отдельных `--bp-*` токенов нет; раскрытие делает postcss-плагин
|
|
||||||
`postcss-custom-media` (конфиг `web/postcss.config.js`, определения
|
|
||||||
подтягиваются через `@csstools/postcss-global-data`).
|
|
||||||
- z-index: `--z-*`; длительности/анимации: `--duration-*`, `--ease-*`.
|
|
||||||
- Правило `custom-property-pattern` в stylelint тестирует паттерн **без** `--`
|
|
||||||
(`--x` → `x`), а `declaration-property-value-disallowed-list` — целиком с `--`.
|
|
||||||
- `custom-property-empty-line-before` перенастроен (`after-custom-property` в
|
|
||||||
`ignore`, не в `except`): пустые строки между подряд идущими токенами
|
|
||||||
**свободны** и `--fix` их не удаляет — можно группировать цветовые и размерные
|
|
||||||
токены в preview.css отдельными блоками.
|
|
||||||
|
|
||||||
Правила плагина (компоненты, `eslint.config.js`):
|
Старый (`v1/`) и новый UI полностью изолированы: ESLint-правило
|
||||||
|
`isolation/no-mixed-imports` резолвит каждый импорт до файла и запрещает
|
||||||
|
смешивание.
|
||||||
|
|
||||||
- `design-tokens/no-hardcoded-in-svelte` — запрет прямых цветов/размеров/
|
- Trunk-based: коммиты делает разработчик после ревью, самому не коммитить.
|
||||||
длительностей/z-index и `color-mix()` (результат токенизировать в CSS).
|
Изменения делать небольшими (< ~500 строк), атомарными.
|
||||||
Исключения размеров: `0`, `0px`, `1px`, проценты и unitless (`line-height`).
|
|
||||||
- `design-tokens/no-category-mismatch` — size-свойство не может использовать
|
|
||||||
color-токен (`--color-*`/`--brand-*`) и наоборот.
|
|
||||||
- `design-tokens/no-token-definition-in-svelte` — определение `--x:` в
|
|
||||||
компоненте не может содержать примитив (hex/rgb/oklch/px/rem); допустимы
|
|
||||||
производные от токенов (`var()`, `calc()`, unitless-числа).
|
|
||||||
- `design-tokens/no-undefined-in-svelte` — `var(--x)` в `<style>` должен быть
|
|
||||||
определён в `src/preview.css` (словарь токенов) или локально в компоненте.
|
|
||||||
Файл читается один раз и кэшируется (не `glob`-зависим).
|
|
||||||
|
|
||||||
### Изоляция веток old ↔ preview
|
|
||||||
|
|
||||||
`isolation/no-mixed-imports` (`web/eslint-plugins/isolation/no-mixed-imports.js`,
|
|
||||||
включён на весь `**/*.{ts,svelte}`) — **полная взаимная изоляция** старого UI и
|
|
||||||
новой preview-ветки. В отличие от `no-restricted-imports`, правило **резолвит**
|
|
||||||
каждый импорт (и `$lib/...`, и относительные `./`/`../`) до реального файла и
|
|
||||||
классифицирует стороны по фактическому пути, поэтому относительным импортом
|
|
||||||
правило не обойти.
|
|
||||||
|
|
||||||
- Старое: `routes/(old)/**`, `lib/old/**`.
|
|
||||||
- Новое: `routes/preview/**`, `lib/registry-new/**`, `lib/preview/**`,
|
|
||||||
`lib/registry-schema.ts`, `lib/registry-schema.test.ts`, `lib/components/kit/**`.
|
|
||||||
- Общее (разрешено обоим): `core/`, `i18n/`, `theme`, `assets/`, корневой `lib`.
|
|
||||||
- Плагин **конфигурируем** (опции `old`/`new` + `root`/`alias` в
|
|
||||||
`eslint.config.js`): перенос старых файлов в папки `old/` — это правка
|
|
||||||
glob-паттернов в настройке, а не код правила.
|
|
||||||
- Изоляция уже достигнута: старый `registry/` не тянет `registry-schema`
|
|
||||||
(пилоты add-border/add-stroke работают через `params`), `registry.ts` не
|
|
||||||
импортирует `ToolSchema`; preview-`list-tools` использует копию
|
|
||||||
`preview/tool-icons.ts`, а не старый `tools/tool-icons.ts`.
|
|
||||||
|
|
||||||
Линтер только показывает ошибки, старый код (непрефиксованные токены, hex в
|
|
||||||
preview.css) — известный техдолг, его НЕ чинить и не игнорировать правилами.
|
|
||||||
|
|
||||||
- Коммиты мелкие (< ~500 строк), атомарные
|
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
# План C17: перенос preview → корень + (old) → /v1/
|
# План C17: перенос preview → корень + (old) → /v1/
|
||||||
|
|
||||||
> Статус: план к выполнению. Предпосылки: tech debt закрыт, backlog №14 (старые
|
> **СТАТУС: ВЫПОЛНЕН (2026-09-11) — устарел.** Перенос завершён:
|
||||||
> файлы → lib/old/) выполнен. Домен НЕ блокер (см. plan-redesign.md §10,
|
> preview-страницы на реальных маршрутах, старый дизайн на `/v1/*`,
|
||||||
> plan-seo.md §6).
|
> `app.css`/`app_v1.css`, isolation-плагин на v1-путях, аудит-скрипты в
|
||||||
|
> `scripts/archive/`. C18 (правки импортов/редиректов, проверка билда) тоже
|
||||||
|
> выполнен. Следующий этап — C19–C21 (`plan-redesign.md`, §10).
|
||||||
|
>
|
||||||
|
> Оригинальный статус до выполнения: план к выполнению. Предпосылки: tech debt
|
||||||
|
> закрыт, backlog №14 (старые файлы → lib/old/) выполнен. Домен НЕ блокер (см.
|
||||||
|
> plan-redesign.md §10, plan-seo.md §6).
|
||||||
|
|
||||||
## Цель
|
## Цель
|
||||||
|
|
||||||
@@ -64,9 +64,9 @@
|
|||||||
|
|
||||||
**Вариант Б (страховка): руками, пакетами по компонентам.** Если скрипт не
|
**Вариант Б (страховка): руками, пакетами по компонентам.** Если скрипт не
|
||||||
удался — чистим/заводим токены вручную, пакетами по компонентам (1–2 на задачу,
|
удался — чистим/заводим токены вручную, пакетами по компонентам (1–2 на задачу,
|
||||||
дифф < ~500 строк). Отсутствующие токены (например `--space-2xl`/`--space-panel`,
|
дифф < ~500 строк). Отсутствующие токены (например
|
||||||
уже дважды обходились в SchemaTextSource) добавляем в словарь `preview.css`
|
`--space-2xl`/`--space-panel`, уже дважды обходились в SchemaTextSource)
|
||||||
осознанно.
|
добавляем в словарь `preview.css` осознанно.
|
||||||
|
|
||||||
Гейт этапа: `pnpm --dir web lint:all` — только допустимый остаток
|
Гейт этапа: `pnpm --dir web lint:all` — только допустимый остаток
|
||||||
(неиспользуемые токены — варнинг), плюс регресс-прогон тестов и svelte-check.
|
(неиспользуемые токены — варнинг), плюс регресс-прогон тестов и svelte-check.
|
||||||
|
|||||||
+7
-3
@@ -26,12 +26,14 @@
|
|||||||
паспортом, семейства с кириллицей)
|
паспортом, семейства с кириллицей)
|
||||||
- [ ] Посмотреть темную тему - слишком темная???
|
- [ ] Посмотреть темную тему - слишком темная???
|
||||||
- [ ] на картинке результате при работе дергается высота надписи (высота иконки)
|
- [ ] на картинке результате при работе дергается высота надписи (высота иконки)
|
||||||
- [ ] Глянуть что за ошибка (воспроизвелось на blur-png, в том числе и повторно)
|
- [x] Глянуть что за ошибка (воспроизвелось на blur-png, в том числе и повторно)
|
||||||
[баг в хроме](https://issues.chromium.org/issues/556160936)
|
[баг в хроме](https://issues.chromium.org/issues/556160936)
|
||||||
`Uncaught TypeError: Cannot read properties of undefined (reading 'startTime')`
|
`Uncaught TypeError: Cannot read properties of undefined (reading 'startTime')`
|
||||||
`at et.reportAllChanges (anonymous:2:19429)`
|
`at et.reportAllChanges (anonymous:2:19429)`
|
||||||
- [ ] сделать загрузку картинки исходника по ctrl+v и drag-and-drop (dropzone)
|
- [ ] сделать загрузку картинки исходника по ctrl+v и drag-and-drop (dropzone)
|
||||||
- [ ] не работают генераторы картинок (create-empty-png)
|
- [x] не работают генераторы картинок (create-empty-png)
|
||||||
|
- [ ] генераторы работают не автоматически - требуется нажать кнопку generate
|
||||||
|
(create-empty-png)
|
||||||
- [ ] square-mask-png - проверить работу - параметры неверно считаются. width -
|
- [ ] square-mask-png - проверить работу - параметры неверно считаются. width -
|
||||||
100, height - 100, x - 100, y - 100, обрезает 50% на 50% картинку
|
100, height - 100, x - 100, y - 100, обрезает 50% на 50% картинку
|
||||||
- [ ] прикрутить кастомный color picker (есть в old), добавить прозрачность
|
- [ ] прикрутить кастомный color picker (есть в old), добавить прозрачность
|
||||||
@@ -55,11 +57,13 @@
|
|||||||
## Переезд
|
## Переезд
|
||||||
|
|
||||||
- [x] составить план переноса preview -> корень, старый корень -> old
|
- [x] составить план переноса preview -> корень, старый корень -> old
|
||||||
|
- [x] сам перенос (C17) — `docs/archive/plan-c17.md`
|
||||||
- [ ] добавить i18n в preview
|
- [ ] добавить i18n в preview
|
||||||
|
|
||||||
## SEO/GEO
|
## SEO/GEO
|
||||||
|
|
||||||
Общий план: `docs/plan-seo.md` (S0–S4). Старт — после C17.
|
Общий план: `docs/plan-seo.md` (S0–S4). C17 выполнен; старт возможен — пока
|
||||||
|
только domain-free часть S1.
|
||||||
|
|
||||||
- [ ] переименование бренда + домен: чек-лист в локальном плане
|
- [ ] переименование бренда + домен: чек-лист в локальном плане
|
||||||
`docs/plan-domain.local.md` (вне git, имя домена в репо не публикуется;
|
`docs/plan-domain.local.md` (вне git, имя домена в репо не публикуется;
|
||||||
|
|||||||
+32
-30
@@ -1,12 +1,11 @@
|
|||||||
# План: переезд на новый дизайн (refs) — параллельная сборка
|
# План: переезд на новый дизайн (refs) — параллельная сборка
|
||||||
|
|
||||||
> Статус: план к выполнению. Шаги 5–6 (C17–C21) остаются **следующим этапом**.
|
> Статус: C17 **выполнен** (`docs/archive/plan-c17.md`). Остались C18–C21 (шаг
|
||||||
> Они больше не привязаны к «верности против рефа»:
|
> 5–6) — **следующий этап**. Они больше не привязаны к «верности против рефа»:
|
||||||
> `docs/archive/plan-design-fix.md` закрыт, аудит против рефа прекращён.
|
> `docs/archive/plan-design-fix.md` закрыт, аудит против рефа прекращён. Tech
|
||||||
> Tech debt закрыт (`docs/archive/plan-tech-debt.md`). Backlog №14 (переезд
|
> debt закрыт (`docs/archive/plan-tech-debt.md`). Backlog №14 (переезд старых
|
||||||
> старых файлов в `old/`) — **выполнено**. Покупка домена НЕ блокер C17:
|
> файлов в `old/`) — **выполнено**. Покупка домена НЕ была блокером C17: домен
|
||||||
> домен нужен для S1 (`plan-seo.md`) — canonical, sitemap, снятие `noindex`.
|
> нужен для S1 (`plan-seo.md`) — canonical, sitemap, снятие `noindex`.
|
||||||
> C17 можно запускать без домена (см. §10).
|
|
||||||
>
|
>
|
||||||
> Источники: `refs/` — Next/React-референс (правда по пикселям и интерактиву),
|
> Источники: `refs/` — Next/React-референс (правда по пикселям и интерактиву),
|
||||||
> `refs-html/` — статические HTML-снимки для быстрого просмотра в браузере.
|
> `refs-html/` — статические HTML-снимки для быстрого просмотра в браузере.
|
||||||
@@ -241,11 +240,11 @@ src/routes/preview/tools/[id]/+page.svelte
|
|||||||
маршруты отдают новый дизайн, тесты зелёные, билд без предупреждений.
|
маршруты отдают новый дизайн, тесты зелёные, билд без предупреждений.
|
||||||
|
|
||||||
- **Фаза 5 — Удалить старый дизайн.** Удаление старых страниц, старого `ui/`,
|
- **Фаза 5 — Удалить старый дизайн.** Удаление старых страниц, старого `ui/`,
|
||||||
старого `app.css` (и его токенов), неиспользуемых классов; переименование
|
старого `app.css` (и его токенов), неиспользуемых классов; наведение порядка в
|
||||||
`kit/` → `ui/` (финальное имя) либо оставление `kit/` как основного — решается
|
`components/` (имя каталога и подпапок — решается на фазе 1; к C17 `kit/` уже
|
||||||
на фазе 1. Финальный проход по брейкпоинтам 1200/1100/800/480. Критерий:
|
разложен в `components/`). Финальный проход по брейкпоинтам 1200/1100/800/480.
|
||||||
`grep` по старым токенам/классам пуст, build чистый, старый дизайн нигде не
|
Критерий: `grep` по старым токенам/классам пуст, build чистый, старый дизайн
|
||||||
упоминается.
|
нигде не упоминается.
|
||||||
|
|
||||||
Оценка объёма: Фаза 1 — фундамент+kit (~1 день), Фаза 2 — страницы (~1–2 дня),
|
Оценка объёма: Фаза 1 — фундамент+kit (~1 день), Фаза 2 — страницы (~1–2 дня),
|
||||||
Фаза 3 — проверка (~полдня), Фаза 4–5 — переключение и чистка (~полдня).
|
Фаза 3 — проверка (~полдня), Фаза 4–5 — переключение и чистка (~полдня).
|
||||||
@@ -265,9 +264,9 @@ src/routes/preview/tools/[id]/+page.svelte
|
|||||||
|
|
||||||
## 9. Открытые вопросы
|
## 9. Открытые вопросы
|
||||||
|
|
||||||
- **Имя финального каталога компонентов:** оставить `kit/` как основное имя или
|
- **Имя финального каталога компонентов:** решено на C1 — писали в `kit/`, C17
|
||||||
переименовать в `ui/` на фазе 5 (удалив старое `ui/`). Рекомендую сразу писать
|
разложил его в `components/` (подпапки fields/layout/ui). На C19 — только
|
||||||
в `kit/`, на фазе 5 переименовать в `ui/`. Решить на фазе 1.
|
навести порядок внутри `components/`, переименование не требуется.
|
||||||
- **Изоляция через layout:** `app.css` вынесен из корневого layout в
|
- **Изоляция через layout:** `app.css` вынесен из корневого layout в
|
||||||
`(old)/+layout.svelte`; новый `design2.css` — только в `preview/+layout`.
|
`(old)/+layout.svelte`; новый `design2.css` — только в `preview/+layout`.
|
||||||
Проверить на фазе 1, что на `/preview/*` не попадают стили старого дизайна
|
Проверить на фазе 1, что на `/preview/*` не попадают стили старого дизайна
|
||||||
@@ -358,15 +357,14 @@ src/routes/preview/tools/[id]/+page.svelte
|
|||||||
неконсистентен. (доказательство отсутствия регресса). Фиксы по результатам
|
неконсистентен. (доказательство отсутствия регресса). Фиксы по результатам
|
||||||
— отдельными мелкими коммитами.
|
— отдельными мелкими коммитами.
|
||||||
|
|
||||||
### Шаг 5. Сделать новый дизайн основным — СЛЕДУЮЩИЙ ЭТАП
|
### Шаг 5. Сделать новый дизайн основным
|
||||||
|
|
||||||
> Tech debt закрыт. Backlog №14 (переезд старых файлов в `lib/old/`) — **выполнено**.
|
> C17 — **выполнен** (`docs/archive/plan-c17.md`): старый `(old)/` → `/v1/`,
|
||||||
> Покупка домена — НЕ блокер C17. C17 переносит маршруты и делает новый дизайн
|
> preview-страницы переехали в корень, `app.css` стал корневым. C18 (правки
|
||||||
> основным; `noindex` остаётся до покупки домена и запуска S1 (`plan-seo.md`).
|
> импортов/редиректов после переноса, проверка билда) — **выполнен**. Остались:
|
||||||
> Детальный план см. в `docs/plan-c17.md` (старый `(old)/` → `/v1/`, удаление
|
> C19 (удаление старого, раскрытие `recommended` на весь код), C20, C21.
|
||||||
> отложено до C19).
|
|
||||||
|
|
||||||
- [ ] **[C17]** Перенести `preview/*` → реальные маршруты (move), удалить
|
- [x] **[C17]** Перенести `preview/*` → реальные маршруты (move), удалить
|
||||||
`(old)/`, корневой `+layout.svelte` импортирует `design2.css` + новый
|
`(old)/`, корневой `+layout.svelte` импортирует `design2.css` + новый
|
||||||
shell. Убрать флаг/кнопку. **Снять `noindex, nofollow`** со страницы
|
shell. Убрать флаг/кнопку. **Снять `noindex, nofollow`** со страницы
|
||||||
инструмента (`preview/tools/[id]/+page.svelte`) — страницы становятся
|
инструмента (`preview/tools/[id]/+page.svelte`) — страницы становятся
|
||||||
@@ -376,17 +374,21 @@ src/routes/preview/tools/[id]/+page.svelte
|
|||||||
вместе с подключением canonical в S1. **И обновить `newCode` в
|
вместе с подключением canonical в S1. **И обновить `newCode` в
|
||||||
`web/eslint.config.js`:** заменить `src/routes/preview/**` на
|
`web/eslint.config.js`:** заменить `src/routes/preview/**` на
|
||||||
`src/routes/**` (после переезда все маршруты — новый дизайн), компонентный
|
`src/routes/**` (после переезда все маршруты — новый дизайн), компонентный
|
||||||
glob `src/lib/components/kit/**` пока оставить.
|
glob `src/lib/components/kit/**` пока оставить. _(Итог: `newCode` обновлён
|
||||||
- [ ] **[C18]** Правки импортов/редиректов после переноса, проверка билда.
|
(`components/**`, `routes/**`), `noindex` оставлен до S1 — домен ещё не
|
||||||
|
куплен. Детали: `docs/archive/plan-c17.md`.)_
|
||||||
|
- [x] **[C18]** Правки импортов/редиректов после переноса, проверка билда.
|
||||||
|
_(Билд собирается без ошибок, линт/свелт-чека нет — выполнен.)_
|
||||||
|
|
||||||
### Шаг 6. Удаление старого
|
### Шаг 6. Удаление старого
|
||||||
|
|
||||||
- [ ] **[C19]** Удалить старое `ui/`, прочие старые компоненты, старый
|
- [ ] **[C19]** Удалить старое: маршруты `routes/v1/**`, код `lib/v1/**`,
|
||||||
`app.css`, неиспользуемые классы. Переименовать `kit/` → `ui/` (если
|
`app_v1.css` (и его токены), неиспользуемые классы. К этому моменту `kit/`
|
||||||
решено на C1 — тогда в `eslint.config.js` обновить и компонентный glob).
|
уже упразднён (C17 разложил его в `components/`), навести порядок в
|
||||||
**И раскрыть ESLint `recommended` на весь код** — убрать scoped-блок
|
`components/` (согласовать подпапки, финальное имя каталога). **И раскрыть
|
||||||
`newCode` в `web/eslint.config.js` (см. AGENTS.md), прогнать `lint` и
|
ESLint `recommended` на весь код** — убрать scoped-блок `newCode` в
|
||||||
починить всплывшие ошибки в оставшемся коде. _Примечание: доменные модули
|
`web/eslint.config.js` (см. AGENTS.md), прогнать `lint` и починить
|
||||||
|
всплывшие ошибки в оставшемся коде. _Примечание: доменные модули
|
||||||
(`lib/core`, `lib/registry`, тесты и т.п.) — это не «старый дизайн», у них
|
(`lib/core`, `lib/registry`, тесты и т.п.) — это не «старый дизайн», у них
|
||||||
свой линт-долг; расширение `recommended` на них может выдать много ошибок.
|
свой линт-долг; расширение `recommended` на них может выдать много ошибок.
|
||||||
Решить на C19: либо чиним сразу, либо расширяем scoped только на
|
Решить на C19: либо чиним сразу, либо расширяем scoped только на
|
||||||
|
|||||||
+15
-15
@@ -1,9 +1,9 @@
|
|||||||
# План: SEO + GEO — индексация и конкуренция в image tools
|
# План: SEO + GEO — индексация и конкуренция в image tools
|
||||||
|
|
||||||
> **Статус (2026-09-10):** план к выполнению. Старт — после C17
|
> **Статус (2026-09-11):** план к выполнению. C17 выполнен — `preview/*`
|
||||||
> (`plan-redesign.md`): до переезда `preview/*` на реальные маршруты страницы
|
> переехали на реальные маршруты (в `noindex` сохраняются до запуска S1, см.
|
||||||
> инструментов в `noindex`, применять техминимум некуда.
|
> `plan-redesign.md` C17). Сейчас применять можно domain-free часть S1.
|
||||||
> **Уточнено:** домен НЕ блокер C17 (см. §6). Задачи S1 разбиты на
|
> **Уточнено:** домен не был блокером C17 (см. §6). Задачи S1 разбиты на
|
||||||
> domain-free (можно делать параллельно с покупкой домена) и domain-required
|
> domain-free (можно делать параллельно с покупкой домена) и domain-required
|
||||||
> (после покупки и настройки домена на Vercel).
|
> (после покупки и настройки домена на Vercel).
|
||||||
>
|
>
|
||||||
@@ -136,8 +136,8 @@
|
|||||||
|
|
||||||
### S1. Технический минимум (сразу после C17)
|
### S1. Технический минимум (сразу после C17)
|
||||||
|
|
||||||
Разбито на две группы: **domain-free** (делаем параллельно с покупкой домена)
|
Разбито на две группы: **domain-free** (делаем параллельно с покупкой домена) и
|
||||||
и **domain-required** (после покупки и DNS-настройки).
|
**domain-required** (после покупки и DNS-настройки).
|
||||||
|
|
||||||
#### S1-free: без домена (параллельно с C18–C19)
|
#### S1-free: без домена (параллельно с C18–C19)
|
||||||
|
|
||||||
@@ -167,16 +167,16 @@
|
|||||||
из `pnpm build` после `vite build` (adapter-static пишет в `build/`,
|
из `pnpm build` после `vite build` (adapter-static пишет в `build/`,
|
||||||
дописываем файл там же). Origin — из env (`PUBLIC_SITE_ORIGIN`), учёт
|
дописываем файл там же). Origin — из env (`PUBLIC_SITE_ORIGIN`), учёт
|
||||||
опционального `BASE_PATH`.
|
опционального `BASE_PATH`.
|
||||||
- [ ] **[S1c]** `robots.txt`: `Sitemap:` + закрыть служебные маршруты
|
- [ ] **[S1c]** `robots.txt`: `Sitemap:` + закрыть служебные маршруты (`/kit` до
|
||||||
(`/kit` до C19; после — вернуть минимальный allow-all).
|
C19; после — вернуть минимальный allow-all).
|
||||||
- [ ] **[S1d]** Компонент `Seo.svelte` (kit): canonical (абсолютный URL по
|
- [ ] **[S1d]** Компонент `Seo.svelte` (kit): canonical (абсолютный URL по
|
||||||
актуальному маршруту), OG-теги (title/description/type/url/image),
|
актуальному маршруту), OG-теги (title/description/type/url/image),
|
||||||
twitter:card. Подключить в root-layout. **Требует `PUBLIC_SITE_ORIGIN`**
|
twitter:card. Подключить в root-layout. **Требует `PUBLIC_SITE_ORIGIN`**
|
||||||
для абсолютных URL в canonical и OG:url.
|
для абсолютных URL в canonical и OG:url.
|
||||||
|
|
||||||
> **Порядок S1:** S1e/S1f/S1g → покупка домена → S1a+S1b+S1c+S1d.
|
> **Порядок S1:** S1e/S1f/S1g → покупка домена → S1a+S1b+S1c+S1d. Гейты: build
|
||||||
> Гейты: build зелёный; в `build/` лежит корректный `sitemap.xml` со всеми
|
> зелёный; в `build/` лежит корректный `sitemap.xml` со всеми инструментами;
|
||||||
> инструментами; canonical/OG присутствуют на всех страницах; e2e зелёные.
|
> canonical/OG присутствуют на всех страницах; e2e зелёные.
|
||||||
|
|
||||||
### S2. Контентный слой (параллельно C18–C19, волнами по категориям)
|
### S2. Контентный слой (параллельно C18–C19, волнами по категориям)
|
||||||
|
|
||||||
@@ -329,10 +329,10 @@ with PNG, JPG, WebP» в тексте + селектор формата в Downl
|
|||||||
- [ ] **[R2]** Купить выбранный домен (кандидаты и пробы — в локальном плане) и
|
- [ ] **[R2]** Купить выбранный домен (кандидаты и пробы — в локальном плане) и
|
||||||
настроить деплой на Vercel без `BASE_PATH` (§0). Домен нужен до S1-domain:
|
настроить деплой на Vercel без `BASE_PATH` (§0). Домен нужен до S1-domain:
|
||||||
canonical и sitemap генерятся от `PUBLIC_SITE_ORIGIN`.
|
canonical и sitemap генерятся от `PUBLIC_SITE_ORIGIN`.
|
||||||
- **Порядок:** R1+R2 можно делать параллельно с C17/C18/C19 (не блокируют).
|
- **Порядок:** R1+R2 можно делать параллельно с C17/C18/C19 (не блокируют). R2
|
||||||
R2 (домен) — блокер только для S1-domain (canonical + sitemap + снятие
|
(домен) — блокер только для S1-domain (canonical + sitemap + снятие
|
||||||
`noindex`). После индексации переименование = переезд с 301, потеря позиций
|
`noindex`). После индексации переименование = переезд с 301, потеря позиций и
|
||||||
и ссылок — не делать. Поэтому R1/R2 — до S1-domain, но не обязательно до C17.
|
ссылок — не делать. Поэтому R1/R2 — до S1-domain, но не обязательно до C17.
|
||||||
|
|
||||||
## 7. Что осознанно НЕ делаем
|
## 7. Что осознанно НЕ делаем
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -1,12 +1,12 @@
|
|||||||
# План разработки: easy-png-tools
|
# План разработки: easy-png-tools
|
||||||
|
|
||||||
> **Статус:** Фаза 1 (полноценный TS-сайт) в основном выполнена —
|
> **Статус:** Фаза 1 (полноценный TS-сайт) в основном выполнена — сайт живёт,
|
||||||
> сайт живёт, каталог переведён в типизированный `registry-new` (121/125), идёт
|
> каталог переведён в типизированный `registry` (121/125), новый дизайн по
|
||||||
> редизайн на `preview/*` по `plan-redesign.md` (параллельная ветка, старый UI
|
> `plan-redesign.md` стал основным на корневых маршрутах (C17 выполнен), старый
|
||||||
> на `(old)/`). Tech debt закрыт. Переезд старых файлов в `old/` выполнен.
|
> UI — на `/v1/*` (удаление отложено до C19). Tech debt закрыт. Следующий этап:
|
||||||
> Следующий этап: C17 (перенос preview → корневые маршруты). Домен НЕ блокер
|
> C18–C21 (`plan-redesign.md`, §10). Домен нужен для SEO (`plan-seo.md`,
|
||||||
> C17, нужен для SEO (`plan-seo.md`, S1-domain). Фазы 2–8 — будущие. Фазы 9–10
|
> S1-domain). Фазы 2–8 — будущие. Фазы 9–10 спланированы в
|
||||||
> спланированы в `docs/plan-platform.md`.
|
> `docs/plan-platform.md`.
|
||||||
|
|
||||||
## 0. Решения
|
## 0. Решения
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
# ESLint-плагины и дизайн-токены: детали
|
||||||
|
|
||||||
|
Эта часть репозитория подробно описана здесь, а не в корневом `AGENTS.md`:
|
||||||
|
рутина-разработка её не требует, детали нужны только при правке самих
|
||||||
|
линт-правил или конфигов.
|
||||||
|
|
||||||
|
## Структура
|
||||||
|
|
||||||
|
- `web/eslint-plugins/` — локальные ESLint-плагины.
|
||||||
|
- `design-tokens/` — правила для `<style>`-блоков svelte-компонентов.
|
||||||
|
- `isolation/` — правило изоляции old/new UI.
|
||||||
|
- `__tests__/` — юнит-тесты (Vitest + `RuleTester`/`Linter`).
|
||||||
|
- `__fixtures__/` — фикстуры для тестов: миниатюрный `src/app.css` (словарь
|
||||||
|
токенов) и файлы для изоляционных тестов.
|
||||||
|
- `web/scripts/`:
|
||||||
|
- `lint-all.mjs` — оркестратор `lint:all`.
|
||||||
|
- `check-tokens.mjs` + `token-audit/` — токен-аудит по `app.css`.
|
||||||
|
- `postcss-hct.mjs` — постcss-плагин эммита `hct()` в sRGB-hex.
|
||||||
|
- `web/stylelint.config.js`, `web/postcss.config.js` — конфиги stylelint/css.
|
||||||
|
|
||||||
|
## Конфигурация ESLint (`web/eslint.config.js`)
|
||||||
|
|
||||||
|
Устроена инкрементально:
|
||||||
|
|
||||||
|
- На **весь код** — парсинг TS/Svelte + правило
|
||||||
|
`@typescript-eslint/consistent-type-imports` (запрет инлайн-тип-импортов,
|
||||||
|
`prefer: 'type-imports'`).
|
||||||
|
- Полные `recommended`-наборы (`eslint` + `typescript-eslint` +
|
||||||
|
`eslint-plugin-svelte`) навешаны **только на новый код**
|
||||||
|
(`src/lib/components/**`, `src/routes/**`), чтобы старый код не засыпался
|
||||||
|
предсуществующими ошибками. Старый код (C17) — `src/lib/v1/**`,
|
||||||
|
`src/routes/v1/**`. Когда старый дизайн удалён (C19), scoped-блок убирается и
|
||||||
|
`recommended` включается на весь код.
|
||||||
|
- **Scoped-пути двигаются вместе с папками** (см. `plan-redesign.md` §10): не
|
||||||
|
оставлять устаревшие пути в `eslint.config.js` (C17 уже двигал
|
||||||
|
`routes/preview/**` → `routes/**` и `components/kit/**` → `components/**`; на
|
||||||
|
C19 при наведении порядка в `components/` paths останутся `components/**` +
|
||||||
|
`routes/**`).
|
||||||
|
- Для `*.svelte` выключен `prefer-const` (пропсы в Svelte 5 пишутся через
|
||||||
|
`let`).
|
||||||
|
- `allowDefaultProject` перечисляет test-файлы и фикстуры точечно (glob'ы с `**`
|
||||||
|
там запрещены tseslint) — при добавлении файлов в `__tests__/` /
|
||||||
|
`__fixtures__/` дописать их туда же.
|
||||||
|
- design-tokens правила применяются к тем же scoped-путям
|
||||||
|
(`src/lib/components/**`, `src/routes/**`).
|
||||||
|
- isolation-правило включено на весь `**/*.{ts,svelte}`.
|
||||||
|
|
||||||
|
## Правила плагина design-tokens
|
||||||
|
|
||||||
|
- `design-tokens/no-hardcoded-in-svelte` — запрет прямых цветов/размеров/
|
||||||
|
длительностей/z-index и `color-mix()` (результат токенизировать в CSS).
|
||||||
|
Исключения размеров: `0`, `0px`, `1px`, проценты и unitless (`line-height`).
|
||||||
|
- `design-tokens/no-category-mismatch` — size-свойство не может использовать
|
||||||
|
color-токен (`--color-*`/`--brand-*`) и наоборот.
|
||||||
|
- `design-tokens/no-token-definition-in-svelte` — определение `--x:` в
|
||||||
|
компоненте не может содержать примитив (hex/rgb/oklch/px/rem); допустимы
|
||||||
|
производные от токенов (`var()`, `calc()`, unitless-числа).
|
||||||
|
- `design-tokens/no-undefined-in-svelte` — `var(--x)` в `<style>` должен быть
|
||||||
|
определён в `src/app.css` (словарь токенов) или локально в компоненте. Файл
|
||||||
|
читается один раз и кэшируется (не `glob`-зависим).
|
||||||
|
|
||||||
|
(ESLint лезет в `<style>`-блоки через постпрефес postcss AST от
|
||||||
|
`svelte-eslint-parser`; stylelint прогоняется по всем CSS-файлам.)
|
||||||
|
|
||||||
|
## Токены-префиксы (целевой словарь дизайна)
|
||||||
|
|
||||||
|
- Цвета: `--color-*`, бренд `--brand-main` / `--brand-alt` — единственные две
|
||||||
|
переменные, которым разрешено быть hex/rgb, остальные цвета — только `hct()` и
|
||||||
|
только в app.css.
|
||||||
|
- Размеры: `--space-*`, `--text-*` (font-size), `--radius-*`, `--size-*`.
|
||||||
|
- Брейкпоинты: `@custom-media --bp-mobile (max-width: 640px)` /
|
||||||
|
`--bp-tablet (800px)` / `--bp-desktop (1100px)` — объявляются в app.css,
|
||||||
|
используются как `@media (--bp-*)`. CSS-переменные в `@media` не работают,
|
||||||
|
поэтому отдельных `--bp-*` токенов нет; раскрытие делает postcss-плагин
|
||||||
|
`postcss-custom-media` (конфиг `web/postcss.config.js`, определения
|
||||||
|
подтягиваются через `@csstools/postcss-global-data`).
|
||||||
|
- z-index: `--z-*`; длительности/анимации: `--duration-*`, `--ease-*`.
|
||||||
|
|
||||||
|
Нюансы stylelint:
|
||||||
|
|
||||||
|
- Правило `custom-property-pattern` тестирует паттерн **без** `--` (`--x` →
|
||||||
|
`x`), а `declaration-property-value-disallowed-list` — целиком с `--`.
|
||||||
|
- `custom-property-empty-line-before` перенастроен (`after-custom-property` в
|
||||||
|
`ignore`, не в `except`): пустые строки между подряд идущими токенами
|
||||||
|
**свободны** и `--fix` их не удаляет — можно группировать цветовые и размерные
|
||||||
|
токены в app.css отдельными блоками.
|
||||||
|
|
||||||
|
## Токен-аудит (`check-tokens.mjs`)
|
||||||
|
|
||||||
|
Лёгкий оркестратор поверх `web/scripts/token-audit/*`. Проверяет:
|
||||||
|
|
||||||
|
- **Parity**: каждый цветовой токен из `:root` обязан иметь пару в
|
||||||
|
`[data-theme="dark"]` и наоборот. Производные токены (значение содержит
|
||||||
|
`var()`, напр. `hct(from var(--...))`) из пары исключены — они наследуют тему
|
||||||
|
автоматически.
|
||||||
|
- **hct-only для цветов**: ЛЮБОЕ цветовое значение в app.css обязано быть
|
||||||
|
`hct(...)` — и литерал, и производное `hct(from var(...) h c t)`. Исключения:
|
||||||
|
только `--brand-main` / `--brand-alt` (seed-токены, любая форма) и
|
||||||
|
`color-mix(...)` (единственный легальный способ смешать два токена).
|
||||||
|
`oklch()/rgb()/#hex` в `--color-*` запрещены.
|
||||||
|
|
||||||
|
Выводит **варнинг** о неиспользуемых токенах app.css (определены, но нигде не
|
||||||
|
используются) — выход он не меняет. Провалом (exit 1) считаются только parity и
|
||||||
|
hct-авторство.
|
||||||
|
|
||||||
|
## Тесты кастомных линт-правил
|
||||||
|
|
||||||
|
Линт-правила покрыты юнит-тестами (Vitest, `test:rules`):
|
||||||
|
|
||||||
|
- `__tests__/design-tokens.test.ts` — три чистых правила
|
||||||
|
(`no-hardcoded-in-svelte`, `no-category-mismatch`,
|
||||||
|
`no-token-definition-in-svelte`) через `RuleTester` со строковыми кейсами;
|
||||||
|
`no-undefined-in-svelte` — через `Linter` API, т.к. читает словарь токенов из
|
||||||
|
`__fixtures__/src/app.css` (не из реального `src/app.css`).
|
||||||
|
- `__tests__/no-mixed-imports.test.ts` — isolation-правило через `Linter` API с
|
||||||
|
`cwd` на `__fixtures__`: правило резолвит импорты по реальным файлам, поэтому
|
||||||
|
цели импортов обязаны существовать на диске.
|
||||||
|
- Хелпер `__tests__/helpers.ts` собирает `Linter` с `cwd = __fixtures__` —
|
||||||
|
`process.cwd()` не трогается, реальные `src/` не читаются.
|
||||||
|
|
||||||
|
Запуск: `pnpm --dir web test:rules` (`vitest run eslint-plugins/__tests__`).
|
||||||
|
Полный `pnpm --dir web test` тоже их гоняет.
|
||||||
|
|
||||||
|
Фикстуры живут в `__fixtures__/` и сами прогоняются линтом (`eslint .`), поэтому
|
||||||
|
добавление/правка стабов — тоже работа с валидным кодом.
|
||||||
|
|
||||||
|
## Isolation-правило (`isolation/no-mixed-imports`)
|
||||||
|
|
||||||
|
**Полная взаимная изоляция** старого (v1) и нового (корень) UI. В отличие от
|
||||||
|
`no-restricted-imports`, правило **резолвит** каждый импорт (и `$lib/...`, и
|
||||||
|
относительные `./`/`../`) до реального файла и классифицирует стороны по
|
||||||
|
фактическому пути, поэтому относительным импортом правило не обойти.
|
||||||
|
|
||||||
|
- Старое: `routes/v1/**`, `lib/v1/**`.
|
||||||
|
- Новое: `routes/**` без `v1/**`, `lib/components/**`, `lib/registry/**`,
|
||||||
|
`lib/catalog.ts`, `lib/categories.ts`, `lib/tool-icons.ts`,
|
||||||
|
`lib/registry-schema.ts`, `lib/registry-schema.test.ts`.
|
||||||
|
- Общее (разрешено обоим): `core/`, `i18n/`, `theme`, `assets/`, корневой `lib`.
|
||||||
|
- Плагин **конфигурируем** (опции `old`/`new` + `root`/`alias` в
|
||||||
|
`eslint.config.js`): перенос старых файлов в папки `old/` — это правка
|
||||||
|
glob-паттернов в настройке, а не код правила.
|
||||||
|
- Изоляция уже достигнута: старый `lib/v1/registry.ts`/`registry-helpers.ts` не
|
||||||
|
тянут `registry-schema` (пилоты add-border/add-stroke работают через
|
||||||
|
`params`), `registry.ts` не импортирует `ToolSchema`; новый `list-tools`
|
||||||
|
использует `$lib/tool-icons.ts`, а не старые `lib/v1/tools/tool-icons.ts`.
|
||||||
|
|
||||||
|
Линтер только показывает ошибки, старый код (непрефиксованные токены, hex в
|
||||||
|
`app_v1.css`) — известный техдолг, его НЕ чинить и не игнорировать правилами.
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
/**
|
/**
|
||||||
* Категории инструментов для нового registry/preview.
|
* Категории инструментов для нового registry.
|
||||||
*
|
*
|
||||||
* Единая точка правды: тип `CategoryId` и порядок отображения в каталоге
|
* Единая точка правды: тип `CategoryId` и порядок отображения в каталоге
|
||||||
* выводятся из одного `as const`-объекта, чтобы не рассинхронизироваться.
|
* выводятся из одного `as const`-объекта, чтобы не рассинхронизироваться.
|
||||||
* Этот файл — локальная копия подхода для нового UI; старый `../categories`
|
* Этот файл — локальная копия подхода для нового UI; старый
|
||||||
* (массив + union вручную) не трогаем — он обслуживает старый UI в `(old)/`.
|
* `lib/v1/categories` (массив + union вручную) не трогаем — он обслуживает
|
||||||
|
* старый UI в `/v1/*`.
|
||||||
*
|
*
|
||||||
* Человекочитаемые названия живут в словарях i18n: секция categories,
|
* Человекочитаемые названия живут в словарях i18n: секция categories,
|
||||||
* ключ = CategoryId.
|
* ключ = CategoryId.
|
||||||
|
|||||||
Reference in New Issue
Block a user