mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-28 19:01:34 +00:00
Карточка и редакторский рантайм держали ≈380 неиспользуемых импортов, 56 мёртвых объявлений и дублей типов (warm-boot, LS_*, GLOW_*, debounce, navigate, lruRead — копии карточки в рантайме) и 112 приватных членов карточки, которых не читал никто — делегаты `_editorRuntimeOrThrow()._x()`, оставшиеся от выноса #425, и аксессоры glow-состояния. Всё это снято; в 9 других файлах — по одиночной ошибке. Делегаты и поля, которых касаются браузерные смоки (`card._x(...)`), оставлены и посчитаны отдельно. Гейт `npm run lint:unused` (scripts/unused-locals-gate.mjs, в gate:small и Validate после сборки): `tsc --noUnusedLocals` чист, кроме приватных членов карточки из порта HouseplanEditorHostPort / `host.` (portPrivates) и членов, которых зовёт харнесс (harnessPrivates); храповик по шести числам scripts/monolith-metrics.mjs против scripts/monolith-baseline.json — delegates 260→159, portMembers 350, hostRefs 4948, portPrivates 96, harnessPrivates 107, bundleBytes 2 510 141→2 500 387. `npm run inventory` печатает те же числа. Заморозка 54 тестов, читающих монолит как текст (test/monolith-text-anchors.test.mjs); PROCESS.md §2.7 — правило. Логический исходник для контрактных тестов (test/houseplan-source.mjs) дописывает члены рантайма без делегата в карточке — контракт продукта не зависит от наличия заглушки. Потолки ядер и initial gzip опущены на выигрыш (292 000 → 290 400). Бандл пересобран, три копии синхронны. Issue: #624 User-Visible: no
71 lines
5.2 KiB
JavaScript
71 lines
5.2 KiB
JavaScript
// #624: заморозка текстовых якорей монолита.
|
|
//
|
|
// 54 тест-файла читают `src/houseplan-card.ts` / `src/houseplan-editor-runtime.ts`
|
|
// как текст (напрямую или через `test/houseplan-source.mjs`). Каждый такой
|
|
// тест красится на переносе метода из карточки в модуль без единой регрессии
|
|
// — и это главная причина, по которой монолит не разбирается: перенос стоит
|
|
// дороже, чем оставить как есть. Новые утверждения о монолите доказываются
|
|
// экспортом и `test-build` (PROCESS.md §2.7), поэтому список ниже может
|
|
// только уменьшаться. Новое имя здесь — не «добавьте в список», а вопрос
|
|
// «почему контракт нельзя проверить исполнением».
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readdirSync, readFileSync } from 'node:fs';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { join } from 'node:path';
|
|
|
|
/** Признак якоря: имя монолита или общий хелпер-реконструктор в тексте теста. */
|
|
export const MONOLITH_ANCHOR_RE = /houseplan-source\.mjs|houseplan-(?:card|editor-runtime)\.ts/;
|
|
|
|
/** Замороженный список — 2026-09-23, #624. Уменьшать свободно. */
|
|
export const FROZEN_TEXT_ANCHOR_TESTS = [
|
|
'bundle-assets.test.mjs', 'card-version.test.mjs', 'check-inputs.test.mjs', 'classify-changes.test.mjs',
|
|
'coincident-partitions.test.mjs', 'color-picker.test.mjs', 'config-adoption-ownership.test.mjs',
|
|
'coordinate-canonicalization.test.mjs', 'coordinate-write-barrier-guard.test.mjs', 'core-file-budget.test.mjs',
|
|
'danger-confirmation.test.mjs', 'data-hp-contract.test.mjs', 'device-hit-owner-contract.test.mjs',
|
|
'device-marker-polish-contract.test.mjs', 'device-position-echo.test.mjs', 'device-presentation-policy.test.mjs',
|
|
'device-presentation.test.mjs', 'devices.test.mjs', 'draft-live-preflight.test.mjs', 'editor-dialog-modules.test.mjs',
|
|
'editor-runtime-loader.test.mjs', 'fixed-floor-contract.test.mjs', 'furniture-assets.test.mjs',
|
|
'furniture-stroke-contract.test.mjs', 'furniture-transform-contract.test.mjs', 'golden-matrix.test.mjs',
|
|
'houseplan-panel.test.mjs', 'hp-dialog-contract.test.mjs', 'i18n.test.mjs', 'isometric-contract.test.mjs',
|
|
'junction-limits.test.mjs', 'mutation-gate.test.mjs', 'native-select-contract.test.mjs', 'near-axis.test.mjs',
|
|
'open-passage-contract.test.mjs', 'optional-space-model-contract.test.mjs', 'paper-scene-contract.test.mjs',
|
|
'performance-contract.test.mjs', 'process-gate.test.mjs', 'rebase-on-dev.test.mjs', 'release-contract.test.mjs',
|
|
'render-device-snapshot.test.mjs', 'resize-production-path.test.mjs', 'review-doc-guard.test.mjs',
|
|
'room-temperature-renderers.test.mjs', 'source-fingerprint.test.mjs', 'summary-panel.test.mjs',
|
|
'support-feedback.test.mjs', 'unified-wall-tool-source.test.mjs', 'version-recovery-card-contract.test.mjs',
|
|
'view-accessibility.test.mjs', 'wall-segment-model.test.mjs', 'wall-union-isolation.test.mjs',
|
|
'writer-fixed-point.test.mjs',
|
|
];
|
|
|
|
/** Файлы, которых нет в замороженном списке, но которые читают монолит текстом. */
|
|
export function newTextAnchors(actual, frozen = FROZEN_TEXT_ANCHOR_TESTS) {
|
|
const allowed = new Set(frozen);
|
|
return actual.filter((name) => !allowed.has(name)).sort();
|
|
}
|
|
|
|
export function textAnchorTests(dir) {
|
|
return readdirSync(dir)
|
|
.filter((name) => name.endsWith('.test.mjs') && name !== 'monolith-text-anchors.test.mjs')
|
|
.filter((name) => MONOLITH_ANCHOR_RE.test(readFileSync(join(dir, name), 'utf8')))
|
|
.sort();
|
|
}
|
|
|
|
test('#624 AC4: ни одного нового теста, читающего монолит как текст', () => {
|
|
const dir = fileURLToPath(new URL('./', import.meta.url));
|
|
const actual = textAnchorTests(dir);
|
|
assert.deepEqual(newTextAnchors(actual), [],
|
|
'новый контракт по монолиту доказывается экспортом и test-build, не regex по src/houseplan-card.ts (PROCESS.md §2.7, #624)');
|
|
// Список — верхняя граница, не инвентарь: ушедшие имена не возвращаются.
|
|
assert.ok(actual.length <= FROZEN_TEXT_ANCHOR_TESTS.length);
|
|
});
|
|
|
|
test('#624 AC4: подмена имени и новый файл видны как новые якоря, а не как «столько же»', () => {
|
|
const frozen = ['a.test.mjs', 'b.test.mjs'];
|
|
assert.deepEqual(newTextAnchors(['a.test.mjs', 'b.test.mjs'], frozen), []);
|
|
assert.deepEqual(newTextAnchors(['a.test.mjs'], frozen), [], 'уменьшение свободно');
|
|
// Та же длина, другое имя: сравнение по множеству, не по числу.
|
|
assert.deepEqual(newTextAnchors(['a.test.mjs', 'c.test.mjs'], frozen), ['c.test.mjs']);
|
|
assert.deepEqual(newTextAnchors(['a.test.mjs', 'b.test.mjs', 'c.test.mjs'], frozen), ['c.test.mjs']);
|
|
});
|