@vernikr/size-report 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -18,6 +18,62 @@
18
18
  где за основу взяты `fixtures/synthetic/config.json`, метрики — `raw`, `min`, `tok`, а
19
19
  способ минификации — `strip` или `esbuild`.
20
20
 
21
+ ## 1.3.0 — 2026-09-15
22
+
23
+ Выпуск выведенных настроек: чтобы получить отчёт, проект больше не обязан себя
24
+ описывать.
25
+
26
+ - **Файла настроек может не быть.** Их выводит сам инструмент — по проекту: колонками
27
+ крупнейшие файлы, по одному от каждого расширения (иначе отчёт состоял бы из одних
28
+ крупных `.md`, и ни один `.js` не попал бы под настоящее сжатие), журналом — первый
29
+ знакомый (`WORKLOG.md`, `CHANGELOG.md`, …), файлом отчёта — `docs/`, если каталог есть,
30
+ командой починки — объявленный скрипт `sizes`, а без него путь к установленному
31
+ пакету внутри проекта, ссылкой на коммит — адрес `origin` (GitHub или GitLab: у
32
+ остальных хозяев пусто, догадка вела бы не туда), метриками — `raw`, `min`, `tok`.
33
+ Команда починки и ссылка берутся готовыми, а не выдуманными: её цитируют подпись
34
+ отчёта и отказы, и зов скрипта, которого в проекте ещё нет, ответил бы «нет такого
35
+ скрипта» ровно там, где человеку нужна работающая команда.
36
+ - **Всё, что колонкой быть не может или в неё не поместилось, названо в `skip`** (сам
37
+ отчёт, замки зависимостей, карты, собранное): поэтому первый же `size check` полон, а не
38
+ красный, — «пути мимо колонок» появляются от новых правок, а не от того, что проект ещё
39
+ не настраивали. Колонка — это файл: список путей колонки движок читает как её
40
+ переименования, поэтому «папка целиком» колонкой не бывает.
41
+ - **О выведенных настройках сказано строкой** в stderr, с готовой командой `--init` —
42
+ она их закрепляет, и дальше их правят как обычные настройки. Закрепляется то же,
43
+ чем проект работает без файла (вывод поверх умолчаний), поэтому смена умолчаний в
44
+ новой версии пакета не поедет по уже настроенному проекту молча. Без закрепления
45
+ профиль выводится на каждом запуске: числа не «поехали», но повторить прежний замер
46
+ можно только закрепив его.
47
+ - **Отказ остался у названного файла:** `--config <файл>`, которого нет, — код 2 с той же
48
+ починкой `--init <файл>` (опечатку в пути покрывать догадкой нельзя). У умолчательного
49
+ имени отказа нет вовсе; коды выхода ни в одном другом случае не изменились.
50
+ - Номер **1.3.0** — по SemVer: появилась возможность, которой не было; схема данных
51
+ (`schema: 1`) та же.
52
+
53
+ ### Что изменится в числах
54
+
55
+ **У кого настройки есть — ничего.** Этот выпуск не трогает ни форму отчёта, ни счёт:
56
+ таблица ниже та же, что у 1.2.0.
57
+
58
+ **У кого настроек нет** — числа появятся там, где был отказ (код 2): их даст выведенный
59
+ профиль. Выведенное и закреплённое (`--init`) друг от друга не отличаются: файл — это тот
60
+ же профиль, только записанный. Повторить замер можно только по закреплённому: без файла
61
+ профиль выводится заново каждый запуск.
62
+
63
+ | Файл | raw | min со strip | min с esbuild | tok |
64
+ |---|---|---|---|---|
65
+ | code.js | 735 | 276 | 185 | 168 |
66
+ | modern.js | 246 | 51 | 45 | 44 |
67
+ | config.mjs | 172 | 45 | 40 | 40 |
68
+ | заметки.md | 306 | 303 | 303 | 53 |
69
+ | crlf.txt | 63 | 60 | 60 | 10 |
70
+ | package.json | 87 | 69 | 69 | 34 |
71
+ | style.css | 156 | 55 | 43 | 38 |
72
+ | table.toml | 300 | 299 | 299 | 52 |
73
+ | empty.js | 0 | 0 | 0 | 0 |
74
+ | WORKLOG.md | 446 | 439 | 439 | 101 |
75
+ | **ИТОГО** | **2511** | **1597** | **1483** | **540** |
76
+
21
77
  ## 1.2.0 — 2026-09-15
22
78
 
23
79
  Выпуск причины, а не измерения: проверка («таблица совпадает с историей?») теперь
package/README.md CHANGED
@@ -10,8 +10,11 @@
10
10
 
11
11
  ## Статус
12
12
 
13
- **Выпуск 1.1.1 (2026-09-15).** Инструмент живёт отдельным пакетом: имя в
14
- реестре — `@vernikr/size-report` (выпуск переименования, числа не изменились).
13
+ **Выпуск 1.3.0 (2026-09-15).** Инструмент живёт отдельным пакетом: имя в
14
+ реестре — `@vernikr/size-report` (публикуется тегом из CI, без секрета). Настроек
15
+ проект может не заводить вовсе: без файла инструмент выводит их из самого проекта и
16
+ говорит об этом строкой, а `--init` закрепляет выведенное файлом (чем этот шаг
17
+ отличается от прежнего — `CHANGELOG.md` 1.3.0).
15
18
  Версия — в манифесте, а у выпуска есть `CHANGELOG.md` с разделом «Что изменится
16
19
  в числах»:
17
20
  таблица чисел в нём не пересказ, а замер на фикстуре, который сверяется с живым
@@ -79,12 +82,17 @@
79
82
  дальше он не упоминается без имени проекта.
80
83
  **Пункт R-1.2 волны 1** (`REFACTOR.md`): в пакете есть линтер — правила те же, что у
81
84
  проекта-потребителя, плюс запрет склейки операторов в одну строку; всё настоящее
82
- дерево (37 файлов) даёт ноль замечаний, `fixtures/` не линтуются — там данные.
85
+ дерево (102 файла) даёт ноль замечаний, `fixtures/` не линтуются — там данные.
83
86
 
84
87
  **Пункты R-1.1 и R-2.1 волн 1–2** (`REFACTOR.md`): вычислительная часть отчёта одна
85
88
  (`src/derived.js`) — страница исполняет тот же код, что считает статическую таблицу,
86
- и разметку для неё строит обычный исходник `src/page/app.js`, а не строка внутри
89
+ и разметку для неё строят обычные исходники (`src/page/*.js`), а не строки внутри
87
90
  движка; артефакт и разметка страницы при этом совпали со старым выводом побайтово.
91
+ Главы программы страницы разделены по предметам — состояние выбора, узлы, панель,
92
+ таблица, сборка (`WORKLOG.md` §62): вклейка склеивает их подряд, поэтому собранная
93
+ страница осталась той же побайтово, а у глав одна область видимости — это записано
94
+ в `eslint.config.js`, потому что `import` между зовущими друг друга главами завёл
95
+ бы кольцо связей.
88
96
 
89
97
  **Отчёт собирается и в проекте с модулями в `.js`** (`REFACTOR.md` R-4.6): гард
90
98
  стриппера понимает обе формы — скрипт и модуль, — поэтому подключение не требует
@@ -208,10 +216,10 @@
208
216
 
209
217
  | Прогон | Команда | Проверок |
210
218
  |---|---|---|
211
- | Быстрый — каждая правка | `pnpm test` | **57 из 135** |
212
- | Полный — выкладка и CI | `pnpm test:all` | **135** |
219
+ | Быстрый — каждая правка | `pnpm test` | **65 из 167** |
220
+ | Полный — выкладка и CI | `pnpm test:all` | **167** |
213
221
 
214
- Ни одна проверка не потеряна и не ослаблена: полный прогон запускает все 135 теми же
222
+ Ни одна проверка не потеряна и не ослаблена: полный прогон запускает все 167 теми же
215
223
  файлами, а быстрый берёт их часть. Умолчание — полный: файл становится быстрым только
216
224
  явно и с причиной, поэтому новое дорогое не может тихо уехать в быстрый. Стерегут это
217
225
  объявление `test/suites.test.js` (полнота классификации и причина у каждого файла) и
@@ -238,10 +246,15 @@ fixture`) и больше не зависит ни от того, держит
238
246
  пакета `npx size-table --write`).
239
247
 
240
248
  **Проверки идут сами (шаг 6 плана, `.github/workflows/ci.yml`).** На каждый пуш и
241
- на каждый запрос правки один job проходит семь шагов теми же командами, что и у
242
- себя локально: строгий линтер, набор проверок, тот же набор в среде, где настроек
243
- машины нет вовсе (`GIT_CONFIG_GLOBAL=/dev/null`), работу из собранного тарболла,
244
- сверку с историей проекта-потребителя и воспроизводимость обоих эталонов.
249
+ на каждый запрос правки один job `verify` зовёт **одну команду** `pnpm run verify`;
250
+ список шагов живёт в одном месте (`tools/gates/run.js`) и совпадает с локальным,
251
+ поэтому проверки, которой нет в профиле, в CI быть не может (это стережёт
252
+ `test/gates-verify.test.js`). В профиле: строгий линтер, датчики раздувания,
253
+ набор проверок, тот же набор в среде, где настроек машины нет вовсе
254
+ (`GIT_CONFIG_GLOBAL=/dev/null`), работу из собранного тарболла, сверку с историей
255
+ проекта-потребителя и воспроизводимость обоих эталонов. Покрытие под c8 дороже
256
+ (полный набор под ним) и живёт в slow-профиле — `pnpm run verify:slow`,
257
+ `.github/workflows/verify-slow.yml` по расписанию.
245
258
  Секретов job не требует: история потребителя лежит в репозитории бандлом на той
246
259
  же ревизии, что записана в эталоне (`fixtures/live/`), а пересъём идёт во временный
247
260
  каталог и сверяется с закоммиченным — рабочее дерево остаётся чистым. Матрицы по
@@ -251,16 +264,27 @@ fixture`) и больше не зависит ни от того, держит
251
264
  прогоняет тот же полный набор, сверяет версию манифеста с тегом, проверяет работу
252
265
  из собранного пакета и отправляет его в реестр — без секрета и без кода из
253
266
  аутентификатора: публикация идёт по удостоверению GitHub Actions (trusted
254
- publishing), которое npm принимает вместо токена. Одна настройка делается человеком
255
- и один раз: на npmjs.com в настройках пакета (Trusted Publisher) нужно назвать
256
- владельца, репозиторий и файл `release.yml`. Черновой прогон из Actions («Run
257
- workflow»: по умолчанию он ничего не публикует) проходит весь список до самого пути
258
- публикации гоняет полный набор, проверяет работу из тарболла и собирает пакет на
259
- черновой версии (`1.1.1` → `1.1.2-draft.0`, чтобы реестр не отказал в уже выпущенном
260
- номере), и это же стережёт `test/release.test.js` со стороны текста. Только настроен
261
- ли издатель, он не проверяет: `--dry-run` не обменивается удостоверением и проходит
262
- вообще без учётных данных (проверено в пустом каталоге: код 0 без токена). Это делает
263
- первый настоящий тег.
267
+ publishing), которое npm принимает вместо токена. Издатель заведён один раз и живёт
268
+ на стороне npmjs.com, а не в репозитории: `npm trust github @vernikr/size-report
269
+ --file release.yml --repo vernikr/size-report --allow-publish` (то же самое кнопка
270
+ Trusted Publisher в настройках пакета), права **publish** и stage publish; проверить,
271
+ что связь есть, `npm trust list @vernikr/size-report`. Выпуск `1.2.0` прошёл именно
272
+ так: `v1.2.0` → 44 с, `+ @vernikr/size-report@1.2.0`, удостоверение подписано и
273
+ записано в журнал прозрачности.
274
+
275
+ Одна ловушка раннера стоила отдельной правки, и она не про этот пакет, а про
276
+ `setup-node`: с `registry-url` действие пишет в `.npmrc` строку
277
+ `_authToken=${NODE_AUTH_TOKEN}`, npm считает учётные данные заданными и за
278
+ удостоверением OIDC **не идёт** — публикация падает 404 при верно заведённом
279
+ издателе. Поэтому `registry-url` здесь не указан (реестр и так по умолчанию тот же, а
280
+ явный адрес живёт в `publishConfig`), и это стережёт `test/release.test.js`. Черновой
281
+ прогон из Actions («Run workflow»: по умолчанию он ничего не публикует) проходит весь
282
+ список до самого пути публикации: гоняет полный набор, проверяет работу из тарболла и
283
+ собирает пакет на черновой версии (`1.2.0` → `1.2.1-draft.0`, чтобы реестр не отказал
284
+ в уже выпущенном номере). Настроен ли издатель, черновой прогон не показывает:
285
+ `--dry-run` не обменивается удостоверением и проходит вообще без учётных данных
286
+ (проверено в пустом каталоге: код 0 без токена) — правду об этом даёт только настоящий
287
+ тег, и он её дал.
264
288
 
265
289
  Первым же прогоном CI окупился: шаг живого паритета упал не на расхождении чисел,
266
290
  а на самой проверке — вывод процессов собирался как строка, и многобайтовый символ,
@@ -358,8 +382,8 @@ deльт задан один раз и по артефакту: рост зел
358
382
  `src/metrics.js`), поэтому разойтись не могут. Минификатора нет (установка без необязательных
359
383
  зависимостей, платформа без него) — метрика отступает к упрощению, способ говорит
360
384
  об этом словами, а прогон отдаёт **код 4**, а не молчание: числа при этом те же, что
361
- у прежнего способа, — побайтово со эталоном. Черновик `--init` ведёт новые проекты
362
- сразу на сжатие; цена названа прямо в его подсказке. Файл, который минификатор не
385
+ у прежнего способа, — побайтово со эталоном. Выведенный профиль ведёт новые проекты
386
+ сразу на сжатие (и `--init` закрепляет то же самое); цена названа прямо в его подсказке. Файл, который минификатор не
363
387
  разобрал (разметка в `.js`, чужой синтаксис), — отказ кодом 2 с причиной от него
364
388
  самого и двумя готовыми выходами.
365
389
 
@@ -393,13 +417,13 @@ deльт задан один раз и по артефакту: рост зел
393
417
  живой истории (95 строк × 27 колонок, 1,23 МБ текста) тот же отчёт идёт
394
418
  **1,55 → 6,35 с** — умножается именно сбор истории, а не таблица: токенов в
395
419
  «сейчас» — **303 705**, то есть 4,05 Б на токен. Отсюда и цена набора проверок:
396
- **7,3–7,9 → 10,4 с** при 66 → 73 проверках (запас и новый бюджет — ниже). Черновик
397
- `--init` ведёт новые проекты сразу на токены.
420
+ **7,3–7,9 → 10,4 с** при 66 → 73 проверках (запас и новый бюджет — ниже). Выведенный
421
+ профиль ведёт новые проекты сразу на токены.
398
422
 
399
423
  **Волна 0 чистки пройдена** (`REFACTOR.md`): у отказов командной строки появились
400
424
  коды выхода и справка вместо стека, `--help` отвечает, `--page` и `--write`
401
425
  создают недостающий каталог, подсказка в отказе ведёт к работающей команде, а
402
- черновик `--init` больше не предлагает колонкой саму таблицу — иначе первая же
426
+ вывод настроек больше не предлагает колонкой саму таблицу — иначе первая же
403
427
  проверка настроек его отвергала.
404
428
 
405
429
  **Появились две команды: полнота и объяснение** (шаг 5 плана). `size check`
@@ -448,10 +472,24 @@ deльт задан один раз и по артефакту: рост зел
448
472
  | `CHANGELOG.md` | История выпусков и, у каждого выпуска, раздел «Что изменится в числах»: у кого числа поедут и почему |
449
473
  | `tools/parity-freeze.js` | Снимает эталон паритета (`pnpm run parity`): замороженной копией, на ревизии проекта из манифеста — `--json`, конфиг, хеш артефакта, хеш инструмента |
450
474
  | `tools/make-fixture.js` | Собирает синтетическую фикстуру (`pnpm run fixture`): детерминированную историю с ловушками плюс эталонные числа |
475
+ | `tools/synthetic/` | Сюжеты той сборки по предметам: `repo.js` — как говорим с git (закреплённые время, автор, настройки), `content.js` — что лежит в файлах, `history.js` — какие коммиты из этого получаются, `note.js` — записка к фикстуре со списком ловушек |
451
476
  | `tools/parity-live.js` | Сверяет движок с живым проектом на клоне: числа и артефакт (`pnpm run parity:live`) |
452
477
  | `tools/pack-check.js` | Собирает тарболл и проверяет, что из него всё работает: все исходники доехали, числа, артефакт и страница — как из репозитория (`pnpm run pack:check`) |
453
478
  | `tools/check-standards.js` | Проверяет, что оба эталона воспроизводятся: пересъём идёт в никуда и сверяется с закоммиченным (наши файлы — побайтово, бандл — по содержимому) и что бандл живой истории несёт `HEAD` (`pnpm run check:standards`) |
454
- | `.github/workflows/ci.yml` | CI: семь шагов на каждый пуш и запрос правки — те же команды, что локально, без секретов и матриц |
479
+ | `.github/workflows/ci.yml` | CI: работа `verify` на каждый пуш и запрос правки зовёт `pnpm run verify` тот же профиль, что локально; действия закреплены по SHA коммита |
480
+ | `.github/workflows/verify-slow.yml` | Slow-профиль по расписанию: то же плюс покрытие под c8 — дорогое не в каждом прогоне |
481
+ | `tools/gates/run.js` | Профили проверок — единственный список шагов: `fast` (каждая правка), `full` (перед отправкой и в CI), `slow` (+ покрытие); `--list` печатает команды |
482
+ | `tools/gates/metrics.js` | Датчик раздувания: правила размера и сложности, вес проверок, пометки долга — с храповиком подавлений ESLint (`.eslint-suppressions.json`) |
483
+ | `tools/gates/dup.js` | Датчик дублей: отпечатки клонов по содержимому (`dup-baseline.json`), взгляд против файла базы и против дерева `origin/main` |
484
+ | `tools/gates/deps.js` | Датчик связей: циклы, сироты, направление слоёв и неразрешимые импорты (`dependency-cruiser`) |
485
+ | `tools/gates/coverage.js` | Датчик покрытия: храповик по файлам против `coverage-baseline.json`, а не процент по репозиторию |
486
+ | `tools/gates/gatefiles.js` | Защита гейт-файлов: правка порогов, баз и обвязки без трейлера `Gate-Change:` — красный (хук `commit-msg` и CI по диапазону) |
487
+ | `tools/gates/common.js`, `tools/gate-probe.js` | Общее у датчиков (корень, разбор ключей, отчёты) и обвязка их проб: датчик зовётся командой, а не импортом |
488
+ | `.githooks/commit-msg`, `.githooks/pre-commit`, `.githooks/pre-push` | Хуки: защита гейт-файлов, быстрый профиль на правку и перед отправкой; ставятся `pnpm run hooks:install` (свой менеджер хуков не заводится) |
489
+ | `eslint.metrics.config.js`, `.eslint-suppressions.json` | Правила датчика раздувания и его база: пороги из замеров, всё, что выше, — в базе и разбирается постепенно |
490
+ | `.jscpd.json`, `dup-baseline.json` | Настройки и база датчика дублей: отпечаток считается по содержимому клона, поэтому база переносима |
491
+ | `.dependency-cruiser.cjs`, `.c8rc.json`, `coverage-baseline.json` | Правила графа связей, настройки снятия покрытия и его база по файлам |
492
+ | `AGENTS.md` | Короткая инструкция агенту репозитория: что запускать, что делать при красном, что нельзя менять |
455
493
  | `.github/workflows/release.yml` | Выпуск по тегу: тот же полный набор, сверка версии манифеста с тегом и публикация в реестр по удостоверению GitHub Actions — без секрета и без кода из аутентификатора |
456
494
  | `templates/` | То, что проект берёт как есть: `size-report.config.json` (черновик настроек), `ci.yml` (описание проверки) и `README.md` (куда что кладётся и что в них менять); едут в поставке и стерегутся `pack:check` и `test/templates.test.js` |
457
495
  | `fixtures/parity/` | Эталон с `safe-resets` на коммите `bd6ef9d`: 95 строк × 27 колонок. Копия реализации, которой он снят, в дереве не лежит — её байты живут в истории и берутся оттуда по требованию (`REFACTOR.md` R-1.5) |
@@ -466,10 +504,17 @@ deльт задан один раз и по артефакту: рост зел
466
504
  | `src/table.css` | Общая часть таблицы: геометрия клеток, липкие шапка и колонка, цвет дельт — одна на артефакт и страницу |
467
505
  | `src/artifact.css` | Оформление статического артефакта сверх общей части |
468
506
  | `src/page/app.css` | Оформление страницы сверх общей части: панель с деревом файлов, состояния пустоты, узкое окно |
469
- | `src/page/app.js` | Программа страницы: дерево файлов, разметка, состояние галочек, память выбора и ссылка; вклеивается в собранную страницу |
507
+ | `src/page/state.js` | Состояние страницы: данные отчёта, вид галочек, паспорт записи, память браузера и обмен ссылкой глава программы страницы |
508
+ | `src/page/dom.js` | Узлы страницы: мелкие помощники разметки (`appEl`, `appBox`) — одни на панель и таблицу |
509
+ | `src/page/panel.js` | Панель выбора: галочки метрик и файлов, категории, дерево путей, легенда; перерисовку просит у главы сборки |
510
+ | `src/page/table.js` | Таблица страницы: клетка, подпись коммита, шапка и состояния пустоты — разметка поверх общего расчёта |
511
+ | `src/page/app.js` | Сборка и запуск страницы: таблица целиком, перерисовка по выбору читателя, первая отрисовка и смена якоря; вклеивается в собранную страницу
470
512
  | `src/page/build.js` | Сборка страницы: данные, оформление и программа в одном файле без внешних ссылок |
471
513
  | `src/git.js` | Единственная граница вызова git: закрепления настроек, блобы пачкой, история, сверка с диском |
472
- | `src/strip.js` | Снятие балласта: стрипперы комментариев и отступов и правила, какая форма к какому файлу, гард компиляции |
514
+ | `src/strip.js` | Снятие балласта: какая форма к какому файлу (расширение, стратегия) и что считать точным числом вход разбора форм |
515
+ | `src/strip/js.js` | Снятие комментариев и отступов в JS: проход по случаям (комментарий, регексп, строка, символ) — строки и шаблоны насквозь |
516
+ | `src/strip/forms.js` | Формы текста со своим снятием балласта: разметка, стили, строки файла и JSON |
517
+ | `src/strip/guard.js` | Гард стриппера: снятое обязано компилироваться — скриптом в процессе или модулем в рабочем потоке |
473
518
  | `src/parse.js` | Разбор модуля: рабочий поток на прогон и отступление к `node --check`, способ разбора последнего модуля |
474
519
  | `src/parse-worker.js` | Сам разбор внутри потока: разбирает текст без исполнения, сообщает, что модулей vm в Node нет |
475
520
  | `src/metrics.js` | Реестр метрик: что измеряется, нужен ли текст и насколько честна цифра; описание метрики для читателя — в одном месте |
@@ -486,11 +531,16 @@ deльт задан один раз и по артефакту: рост зел
486
531
  | `src/data.js` | Категории файлов и контракт со страницей (`--data`) |
487
532
  | `src/render.js` | Статический артефакт: клетки, таблица, примечание (стили — в `src/css.js`) |
488
533
  | `src/config.js` | Настройки проекта-потребителя: умолчания, чтение, проверка |
534
+ | `src/project.js` | Настройки, выведенные из самого проекта (дерево и история): колонки, журнал, исключения. Без файла настроек он и есть настройки; `--init` закрепляет его файлом |
489
535
  | `src/locales.js`, `src/refusal.js`, `src/tool.js` | Тексты отчёта; коды выхода и справка; имя и версия пакета |
490
- | `src/cli.js` | Режимы командной строки и разбор ключей; главный файл пакета |
536
+ | `src/cli.js` | Вход инструмента: разбор строки, чтение проекта и доставка запроса режиму; главный файл пакета |
537
+ | `src/args.js` | Грамматика командной строки: режимы, ключи и команды плюс проверки их сочетаний — отказ называет виновника и готовую команду |
538
+ | `src/modes.js` | Режимы: собрать таблицу, сверить её с историей, отдать данные или страницу, полноту покрытия и диагностику |
539
+ | `src/init.js` | Закрепление настроек файлом (`--init`): то, что проект вывел о себе сам, ложится файлом — и проходит ту же проверку, что первый запуск |
491
540
  | `test/api.test.js` | Публичный API пакета: список имён заморожен, разбиение не имеет права его менять |
492
541
  | `eslint.config.js` | Правила оформления: те же, что у проекта-потребителя, плюс запрет склейки операторов в строке (`pnpm run lint`, `pnpm run lint:strict`) |
493
542
  | `tools/harness.js` | Обвязка проверок: пути, клоны фикстуры (в том числе общий на набор и с CRLF), запуск инструмента, разбор отказов, хеши |
543
+ | `tools/page-harness.js` | Обвязка проверок контракта и страницы: данные контракта, собранная страница, чтение её в настоящем DOM, переключатели панели — одна на четыре набора |
494
544
  | `tools/suites.js` | Разделение набора: какие файлы идут в быстрый прогон (с причиной для каждого), почему каждый дорогой — в полном |
495
545
  | `tools/run-tests.js` | Прогон набора (`pnpm test`, `pnpm test:all`, `pnpm run suites:measure`): длительность каждого файла своим замером и сверка числа проверок |
496
546
  | `tools/docs-facts.js` | Чтение фактов из документации — один слой на четыре проверки сторожа: что документ называет (пути, зовы, адреса разделов) против того, что есть в репозитории |
@@ -504,7 +554,10 @@ deльт задан один раз и по артефакту: рост зел
504
554
  | `test/cli.test.js`, `test/cli-paths.test.js` | Отказы командной строки: справка, настройки, коды выхода — и куда инструмент пишет |
505
555
  | `test/refusals.test.js` | Отказы исполняются: каждый вызван прогоном, сверены код выхода и обещанные фразы (свои клоны — для чужого хука, обрезанной истории и ветки мимо отчёта), и **совет выполняется** — команда даёт обещанный код, не падает стеком, а где объявлено «отказ ушёл», тот же зов после неё отвечает другим |
506
556
  | `test/refusals-catalog.test.js` | Сторож каталога отказов: у каждого места отказа в исходниках есть пункт, у каждого пункта — объявленный совет, а отказы, отданные другой проверке, ею в самом деле утверждаются (названные файл и строка проверяются) |
507
- | `test/contract.test.js` | Контракт данных и страница: числа против эталона, производные против чисел артефакта, дерево файлов против путей, память выбора и ссылка против перезахода, чужого отчёта и чужого адреса, пометки приближения против подписи метрики |
557
+ | `test/contract-data.test.js` | Контракт данных: числа против эталона, состав полей против производных, пометки приближения против подписи метрики |
558
+ | `test/contract-derived.test.js` | Производные против чисел артефакта: итоги строки, дельты клетки и дельта итога — на коде, который лежит в дереве |
559
+ | `test/page-view.test.js` | Собранная страница: вклейка без копий расчёта, самодостаточность, дерево файлов, состояния пустоты, оформление и переключатели |
560
+ | `test/page-choice.test.js` | Память выбора и обмен ссылкой: перезаход, чужой отчёт, чужая и битая запись, смена адреса на открытой странице |
508
561
  | `test/module.test.js` | Модуль в расширении `.js`: измеряется без правок настроек; гард стриппера жив (доказано мутацией) и не обвиняет невиновного |
509
562
  | `test/guard.test.js` | Разбор модуля: идёт потоком, оба пути дают один вердикт, отступление работает без файла потока, сотни разборов дешевле запуска |
510
563
  | `test/runner.test.js` | Чтение вывода процесса: куски склеиваются буферами, а не приклеиваются к строке — многобайтовый символ на границе кусков не превращается в два символа-заменителя |
@@ -513,12 +566,14 @@ deльт задан один раз и по артефакту: рост зел
513
566
  | `test/changelog.test.js` | Сторож выпуска: версия в `CHANGELOG.md` — версия манифеста, а таблица «что изменится в числах» — это замер на фикстуре, сверенный с живым прогоном |
514
567
  | `test/release.test.js` | Сторож выпуска из CI: он начинается тегом, версия берётся из манифеста, секрета и одноразового кода не требует, prerelease не уезжает в `latest`, перед публикацией идёт полный набор — и подсказка на npmjs.com называет этот же файл |
515
568
  | `test/suites.test.js` | Сторож разделения набора: полнота классификации (быстрый — явно, полный — с причиной), причина у каждого файла, что быстрый прогон остаётся частью набора |
569
+ | `test/gates-metrics.test.js`, `test/gates-dup.test.js`, `test/gates-deps.test.js`, `test/gates-coverage.test.js`, `test/gates-files.test.js` | Пробы датчиков: искусственное нарушение → датчик красный, снятие → снова зелёный; прогон зовёт датчик командой, а не импортом, поэтому доказывает и код возврата |
570
+ | `test/gates-verify.test.js` | Сторож единственного списка: команды профиля против рабочих процессов, хуков и `templates/ci.yml` — проверки, которой нет в профиле, в CI быть не может |
516
571
  | `test/check.test.js` | Полнота и объяснение на настоящих коммитах фикстуры: непокрытый путь, «только отчёт», «число не сдвинулось», «мимо колонок», слияние — и что починка настроек не двигает числа |
517
572
  | `test/doctor.test.js` | Диагностика на пяти состояниях проекта: без настроек (2), полное покрытие (0), неполное (1), обрезанная история (3), нет датчика (4) — и блок покрытия равен ответу `size check`, а не считается вторым разом |
518
573
  | `test/hook.test.js` | Хуки на свежем клоне: ставятся только командой, дают отдельный коммит отчёта (в том числе после слияния), повторный запуск молчит, чужая работа и индекс не тронуты, в CI и при отказе инструмента ничего не делают, снятие возвращает проект к прежнему |
519
574
  | `test/templates.test.js` | Шаблоны: черновик настроек проходит проверку инструмента и собирает настоящий отчёт; описание проверки разбирается и зовёт только существующие команды и ключи |
520
575
  | `test/minify.test.js`, `test/tokens.test.js` | Настоящее сжатие и токены: числа против упрощения, кодировка как часть числа, честность подписи, работа без необязательной зависимости (код 4) и шов `SIZE_REPORT_NO_OPTIONAL` |
521
- | `package.json` | Манифест пакета: имя `@vernikr/size-report`, версия `1.1.1`, список поставки — только существующее |
576
+ | `package.json` | Манифест пакета: имя `@vernikr/size-report`, версия `1.2.0`, список поставки — только существующее |
522
577
 
523
578
  Оба каталога эталонов снимаются заново теми же инструментами: `pnpm run parity` и
524
579
  `pnpm run fixture` дают те же файлы. Побайтово сверяется наше — конфиг, эталонные
@@ -550,7 +605,7 @@ JSX и TSX выход зависит от настройки jsx сам
550
605
  уже собранных значениях (`data`, `render`, `page`), а настройки, тексты и отказ —
551
606
  по краям, потому что их знает любой и они не знают никого. Оба отчёта считаются на
552
607
  сборке: страница получает исходники общего расчёта и своей программы вклеенными
553
- (`src/derived.js`, `src/page/app.js`), потому что открывается она с диска, без
608
+ (`src/derived.js`, `src/page/*.js`), потому что открывается она с диска, без
554
609
  сервера и без сети. Остальное — по шагам 2–6 (`PLAN.md` §5).
555
610
 
556
611
  ## Как подключить к своему проекту
@@ -573,7 +628,7 @@ pnpm add -D @vernikr/size-report
573
628
  ```
574
629
 
575
630
  Пакет **опубликован в реестре**, и публично: `npm view @vernikr/size-report
576
- version` отвечает `1.1.1`, `npm access get status @vernikr/size-report` — `public`,
631
+ version` отвечает `1.3.0`, `npm access get status @vernikr/size-report` — `public`,
577
632
  а анонимный запрос тарболла — код 200. `npm i -D` и `yarn add -D` принимают то же
578
633
  имя; ни ключа, ни ссылки на репозиторий не нужно.
579
634
 
@@ -581,11 +636,11 @@ version` отвечает `1.1.1`, `npm access get status @vernikr/size-report`
581
636
  реестра, но остаётся привязанной к ревизии:
582
637
 
583
638
  ```bash
584
- pnpm add -D github:vernikr/size-report#v1.2.0
639
+ pnpm add -D github:vernikr/size-report#v1.3.0
585
640
  ```
586
641
 
587
642
  Без сети (или если тянуть из codeload нечем) — тарболл: `pnpm pack` в клоне
588
- пакета, затем `pnpm add -D ./vernikr-size-report-1.2.0.tgz`.
643
+ пакета, затем `pnpm add -D ./vernikr-size-report-1.3.0.tgz`.
589
644
 
590
645
  **Почему тег, а не sha.** Короткий sha pnpm разрешает только через видимые рефы, а
591
646
  `git ls-remote` отдаёт одни верхушки веток: пока ревизия — верхушка, короткий sha
@@ -593,7 +648,7 @@ pnpm add -D github:vernikr/size-report#v1.2.0
593
648
  <sha> to a commit`. Это не рассуждение, а проба: короткий пин `6530237` ставился,
594
649
  пока `main` стоял на нём, и перестал — на следующем же коммите, а тот же sha
595
650
  целиком поставился. Имя ветки (`#main`) или тег принимаются оба, но ветка —
596
- движущаяся цель, а тег постоянен: этот выпуск стоит на теге `v1.1.1`, он же и в
651
+ движущаяся цель, а тег постоянен: этот выпуск стоит на теге `v1.3.0`, он же и в
597
652
  примере (сорок знаков тоже годятся, но их придётся брать глазами из истории).
598
653
 
599
654
  Ревизия в примере — не украшение, а часть утверждения: она закреплена за тем, что
@@ -618,30 +673,39 @@ GIT_SSH_COMMAND=false`): установка 3,4 с, дальше `size --write`
618
673
  области владельца (`@vernikr/size-report`), а не только отправка архива; что
619
674
  затронуло переименование — `PLAN.md` §10, чем доказана выкладка — `WORKLOG.md` §53.
620
675
 
621
- ### 2. Черновик настроек
676
+ ### 2. Настройки: их можно не заводить
622
677
 
623
678
  ```bash
624
- pnpm exec size --init # создаёт size-table.config.json
679
+ pnpm exec size --write # таблица; настроек нет — их выведет сам инструмент
680
+ pnpm exec size --init # закрепить выведенное в size-table.config.json
625
681
  ```
626
682
 
627
- Черновик собирается по самому проекту: колонки крупнейшие файлы по списку
628
- расширений исходников (список печатается тут же «расширения в проекте»), журнал
629
- по знакомым именам (`WORKLOG.md`, `CHANGELOG.md`, …), вывод в `docs/`, если он
630
- есть, `fixCommand` под ваш менеджер пакетов.
631
-
632
- > **Документации в черновике нет, и это заметно сразу.** Список расширений
633
- > исходники (`.js`, `.css`, `.json`, …), а `README.md` и журнал в него не входят:
634
- > колонкой доки делает человек, потому что «важный документ» — не про расширение.
635
- > Поэтому первый же `size check` на проекте с README и журналом честно скажет, что
636
- > эти пути не отслеживаются и не исключены (код 1), — это и есть тот шаг, где доки
637
- > становятся колонками или попадают в `skip` (`§5`). Проверено покомандно на
638
- > живом проекте: `WORKLOG.md` §54. Он же печатает
639
- следующие три шага. Черновик ведёт метрику `min` на настоящее сжатие
640
- (`"minify": {"engine": "esbuild"}`) и сразу включает метрику `tok`
641
- (`"tokens": {"family": "openai", "encoding": "o200k_base"}`): и минификатор, и
642
- словарь едут необязательными зависимостями и ставятся обычной установкой, а без
643
- них инструмент работает и сам скажет об этом (код 4) правки настроек не
644
- требуются.
683
+ Начинать с настроек не нужно: без файла инструмент выводит их из проекта — колонками
684
+ берёт крупнейшие файлы, по одному от каждого расширения (иначе отчёт состоял бы из
685
+ одних крупных `.md`, и ни один `.js` не попал бы под настоящее сжатие), журналом —
686
+ первый знакомый (`WORKLOG.md`, `CHANGELOG.md`, …), файлом отчёта `docs/`, если
687
+ каталог есть, командой починки — объявленный скрипт `sizes`, а без него — путь
688
+ к установленному пакету (его цитируют подпись отчёта и отказы, поэтому он обязан
689
+ работать уже сейчас), ссылкой на коммит адрес `origin`, метриками `raw`, `min`,
690
+ `tok`. Метрика `min` считается настоящим сжатием (`"minify": {"engine": "esbuild"}`),
691
+ а `tok` словарём (`"tokens": {"family": "openai", "encoding": "o200k_base"}`): без
692
+ этих необязательных зависимостей метрика честно отступает к другому счёту и прогон
693
+ отдаёт код 4 правки настроек и тут не требуются.
694
+
695
+ Всё, что колонкой быть не может или в неё не поместилось (сам отчёт, замки
696
+ зависимостей, карты, собранное), называется в `skip` поэтому первый же `size check`
697
+ полон, а не красен: «пути мимо колонок» появляются от новых правок, а не от того, что
698
+ проект ещё не описан. О том, что настройки выведены, инструмент говорит строкой в
699
+ stderr и называет команду, которая их закрепляет,`--init`; закреплённое проходит
700
+ ту же проверку, что любой файл настроек, и дальше его правят глазами (сам `--init`
701
+ печатает, что закрепил, и что делать дальше — скрипты и проверку в CI). Без
702
+ закрепления профиль выводится заново на каждом запуске: числа не «поедут», но
703
+ повторить прежний замер — в том числе хуком и проверкой — можно только по файлу.
704
+
705
+ Закрепляется **то же, чем проект работает без файла**: вывод из проекта поверх
706
+ умолчаний. Поэтому в закреплённом файле видны и значения, которых в проекте никто не
707
+ писал, — тогда смена умолчаний в новой версии пакета не поедет по уже настроенному
708
+ проекту молча.
645
709
 
646
710
  > Subкоманды `size init` пока нет — CLI знает только флаги (`--init`, `--write`,
647
711
  > `--page`, `--data`, `--json`, без флага — проверка); полный список даёт `size --help`.
@@ -649,20 +713,21 @@ pnpm exec size --init # создаёт size-table.config.json
649
713
 
650
714
  ### 3. Что правится в конфиге
651
715
 
652
- Черновик знает про проект только размеры файлов — какие колонки важны, знает
653
- человек. Чаще всего правят:
716
+ Вывод знает про проект только то, что видно в дереве и истории, — какие колонки важны,
717
+ знает человек. Чаще всего правят:
654
718
 
655
719
  | Ключ | Что это |
656
720
  |---|---|
657
- | `columns` | колонки таблицы: `{label, paths: [...]}`; пути в одной колонке складываются (например, `src` целиком), `label` — то, что увидит человек |
721
+ | `columns` | колонки таблицы: `{label, paths: [...]}`; **колонка это файл**: список путей — её переименования (в ревизии берётся тот путь, который в ней есть), а не несколько файлов разом; `label` — то, что увидит человек |
658
722
  | `metrics` | из чего состоит число: `raw` (размер объекта git), `min` (минифицированная форма — какая именно, решает `minify.engine`), `tok` (токены), `gzip` |
659
723
  | `tokens.family`, `tokens.encoding` | словарь для `tok`: семейство (`openai`) и кодировка (`o200k_base` или `cl100k_base`) — кодировка меняет число, поэтому она и в настройках, и в подписи метрики |
660
724
  | `minify.engine` | чем считается `min`: `strip` (комментарии и отступы, точность не обещается) или `esbuild` (настоящее сжатие; форматы без минификатора — упрощение, и это видно в подписи метрики) |
661
- | `output` | файл таблицы (по черновику — `docs/size-table.html`) |
725
+ | `output` | файл таблицы (в выведенном профиле — `docs/size-table.html`, если каталог `docs/` есть, иначе в корне) |
662
726
  | `journal` | где искать разделы журнала, на которые ссылаются строки |
663
- | `links.commitUrl` | шаблон ссылки на коммит, например `https://github.com/org/repo/commit/{sha}` |
664
- | `skip` | пути, которые колонками быть не могут |
665
- | `fixCommand` | команда, которую цитирует подпись отчёта и подсказывает отказ; в черновике уже ваша |
727
+ | `links.commitUrl` | шаблон ссылки на коммит, например `https://github.com/org/repo/commit/{sha}`; выводится из адреса `origin` у GitHub и GitLab (у остальных хозяев — пусто, а не догадка) |
728
+ | `skip` | пути, которые колонкой не стали: и те, что ею быть не могут (сам отчёт, замки зависимостей), и те, что в колонки не поместились (выведенный профиль объявляет исключениями всё остальное — поэтому первый `check` полон) |
729
+ | `fixCommand` | команда, которую цитирует подпись отчёта и подсказывает отказ; в выведенном профиле ваш скрипт `sizes`, если он объявлен, иначе путь к установленному пакету внутри проекта (зов по имени пакета уходит в реестр — `REFACTOR.md` R-4.21) |
730
+ | `locale`, `title`, `heading` | язык текстов отчёта и его заголовки; пустые `title`/`heading` значат «взять из локали» |
666
731
  | `minify.guard` | расширения, где результат стриппера проверяется разбором; модуль в `.js` гард понимает сам, трогать его не нужно |
667
732
  | `hooks.enabled` | выключатель хука автообновления (`false` — хук остаётся на месте, но молчит; убирается он только `size uninstall-hook`) |
668
733
 
@@ -740,9 +805,10 @@ pnpm exec size doctor # 0 — делать нечего; иначе перв
740
805
  `.github/workflows/size-report.yml` без правок — сборка таблицы, сверка с файлом
741
806
  на диске, два снимка чисел (обычный и в среде без настроек git) и их сравнение.
742
807
  Секретов оно не требует. Для `npm`/`yarn` в самом файле сказано, какие две строки
743
- заменить. Рядом — `templates/size-report.config.json`, черновик настроек в дополнение
744
- к `pnpm exec size --init`: колонки в нём примерные (`README.md`, `package.json`),
745
- они есть почти в любом проекте, поэтому первый отчёт собирается сразу.
808
+ заменить. Рядом — `templates/size-report.config.json`, образец настроек: колонки в нём
809
+ примерные (`README.md`, `package.json`), они есть почти в любом проекте, поэтому
810
+ первый отчёт собирается сразу. Нужен он, только если хочется начать с правленого
811
+ файла: без файла настройки выводятся из проекта (`--init` закрепляет выведенное).
746
812
 
747
813
  Свой CI у пакета — `.github/workflows/ci.yml`: он гоняет у себя тот же список
748
814
  команд, что описан ниже, и его можно взять за образец для шага потребителя.
@@ -871,8 +937,43 @@ R-4.1): пути, таблица файлов, зовы и ключи инстр
871
937
  починки взяты из конфига) не проверяет никто: если это важно, это одна проверка
872
938
  поверх `--data` в проекте.
873
939
 
940
+ ## Гейт против раздувания
941
+
942
+ **Список проверок — один, и он же в CI.** Профиль проверок задан в одном месте
943
+ (`tools/gates/run.js`): `pnpm run verify:fast` (десятки секунд — каждая правка),
944
+ `pnpm run verify` (полный — перед отправкой и в CI) и `pnpm run verify:slow`
945
+ (по расписанию — то же плюс покрытие). CI зовёт эту же команду, а не свой список:
946
+ работа `verify` (`.github/workflows/ci.yml`) на каждый пуш и запрос правки, работа
947
+ `verify-slow` — по расписанию. Совпадение стережёт `test/gates-verify.test.js`:
948
+ проверка, которой нет в профиле, в CI не пройдёт.
949
+
950
+ **Датчики ловят раздувание, а не стиль** (стиль — у линтера): размер и сложность
951
+ функций, размер модулей, дубли веток и функций (`sonarjs`), вес проверок (проверка
952
+ без утверждения, утверждение без сравнения, выключенная проверка), пометки долга,
953
+ клоны по токенам (`jscpd`), циклы и сироты связей (`dependency-cruiser`), просадка
954
+ покрытия против своей же базы (`c8`).
955
+
956
+ **Порог взят из замера, а не из головы, и он храповик.** По исходному замеру:
957
+ сложность функции p50 1 / p90 4 / p99 11 / max 27 — порог 12 (в базе осталось 5
958
+ функций); длина функции p50 7 / p90 27 / p99 73 / max 118 — порог 60 (9 в базе);
959
+ модуль p90 381 строка / max 907 — порог 450 (в базе не осталось ни одного: три
960
+ толстых файла — контракт, программа страницы и сборка фикстуры — разделены,
961
+ `WORKLOG.md` §59–§61). Всё, что выше порога
962
+ сегодня, лежит в базе (`.eslint-suppressions.json`) и работе не мешает; новое валит
963
+ прогон. Дубли — 13 клонов / 84 строки (0,67 %), связи — 112 модулей / 468 связей и ни
964
+ одной находки.
965
+
966
+ **Базы обновляет человек.** `pnpm run baseline:metrics`, `baseline:dup`,
967
+ `baseline:coverage` — и только с трейлером `Gate-Change:` в сообщении коммита: правка
968
+ гейт-файла без него красна и локально (хук `commit-msg`), и в CI (по каждому коммиту
969
+ диапазона). Иначе гейт ослаблялся бы тем же коммитом, который он останавливает.
970
+ Таблица замеров, отвергнутые инструменты (knip, ast-grep, size-limit, gitleaks) и
971
+ действия человека — в `WORKLOG.md` §58.
972
+
874
973
  ## Для ИИ-агента
875
974
 
975
+ - `pnpm run verify:fast` — перед каждой правкой, `pnpm run verify` — перед отправкой;
976
+ что не так и что нельзя трогать при красном — `AGENTS.md`.
876
977
  - `size check --json` — готово ли всё: какая часть истории покрыта, какие пути
877
978
  мимо колонок (с коммитом-первопричиной) и какие коммиты выпали без строки.
878
979
  - `size explain <коммит> --json` — почему у конкретного коммита нет строки: причина,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vernikr/size-report",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "author": "vernikr",
5
5
  "repository": {
6
6
  "type": "git",
@@ -41,7 +41,21 @@
41
41
  "parity:live": "node tools/parity-live.js",
42
42
  "pack:check": "node tools/pack-check.js",
43
43
  "check:standards": "node tools/check-standards.js",
44
- "fixture": "node tools/make-fixture.js"
44
+ "fixture": "node tools/make-fixture.js",
45
+ "verify:fast": "node tools/gates/run.js fast",
46
+ "verify": "node tools/gates/run.js full",
47
+ "verify:slow": "node tools/gates/run.js slow",
48
+ "metrics": "node tools/gates/metrics.js",
49
+ "dup": "node tools/gates/dup.js",
50
+ "dup:ci": "node tools/gates/dup.js --ref origin/main",
51
+ "dup:baseline": "node tools/gates/dup.js --update",
52
+ "deps": "node tools/gates/deps.js",
53
+ "cover": "node tools/gates/coverage.js",
54
+ "gate:files": "node tools/gates/gatefiles.js",
55
+ "baseline:metrics": "eslint --config eslint.metrics.config.js --suppressions-location .eslint-suppressions.json --prune-suppressions src bin tools test && eslint --config eslint.metrics.config.js --suppressions-location .eslint-suppressions.json --suppress-all src bin tools test",
56
+ "baseline:dup": "node tools/gates/dup.js --update",
57
+ "baseline:coverage": "node tools/gates/coverage.js --update",
58
+ "hooks:install": "git config core.hooksPath .githooks"
45
59
  },
46
60
  "keywords": [
47
61
  "size",
@@ -51,7 +65,12 @@
51
65
  "report"
52
66
  ],
53
67
  "devDependencies": {
68
+ "@vernikr/size-report": "1.2.0",
69
+ "c8": "10",
70
+ "dependency-cruiser": "17",
54
71
  "eslint": "^9.18.0",
72
+ "eslint-plugin-sonarjs": "4",
73
+ "jscpd": "5.2.0",
55
74
  "jsdom": "~26"
56
75
  },
57
76
  "optionalDependencies": {