feat(opts): M1 — 49 interfaces de opts se funden en las bases

Las 49 redeclaraciones verbatim de id/ref en 7 providers pasan a heredar de
WithRefOpts (44) / ProviderOpts (5 raices con ref opcional). Guard de censo
por linea cruda CASE-SENSITIVE en opts-census.test.ts (un parser de llaves se
traga los extends con genericos inline; menubar tiene `ref: state<...>` en un
call-site que un scan insensible flagearia), verificado en rojo y en verde.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
alpha-0.1-dir-prefs
dev 2 months ago
parent 30e2a027fe
commit 337dde8575

@ -1,5 +1,5 @@
import { context } from '../../provider';
import { state, type Active, type ActiveProps, type State, type StateProps } from '$libs/reactive';
import { context, type WithRefOpts } from '../../provider';
import { state, type ActiveProps, type StateProps } from '$libs/reactive';
import type { SomaMouseEvent } from '../../types';
import { Soma } from '../../core/soma.svelte';
import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
@ -8,10 +8,7 @@ import { collapsibleMorfo } from '../../../morfo/components/collapsible';
// ── Provider ─────────────────────────────────────────────────────────────────
interface CollapsibleOpts
extends StateProps<{ open: boolean }>, ActiveProps<{ disabled: boolean }> {
id: Active<string>;
ref: State<HTMLElement | null>;
}
extends WithRefOpts, StateProps<{ open: boolean }>, ActiveProps<{ disabled: boolean }> {}
/**
* Collapsible root — runtime-direct (no `Provider` base class).
@ -95,10 +92,7 @@ export class CollapsibleProvider {
// ── Trigger ──────────────────────────────────────────────────────────────────
interface CollapsibleTriggerOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface CollapsibleTriggerOpts extends WithRefOpts {}
export class CollapsibleTriggerProvider {
static create(opts: CollapsibleTriggerOpts) {
@ -138,10 +132,7 @@ export class CollapsibleTriggerProvider {
// ── Content ──────────────────────────────────────────────────────────────────
interface CollapsibleContentOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface CollapsibleContentOpts extends WithRefOpts {}
export class CollapsibleContentProvider {
static create(opts: CollapsibleContentOpts) {

@ -1,12 +1,5 @@
import { context } from '../../provider';
import {
readableActive,
state,
type Active,
type ActiveProps,
type State,
type StateProps
} from '$libs/reactive';
import { context, type ProviderOpts, type WithRefOpts } from '../../provider';
import { readableActive, state, type ActiveProps, type StateProps } from '$libs/reactive';
import type { OnChangeFn, SomaMouseEvent } from '../../types';
import { Soma } from '../../core/soma.svelte';
import { watch } from '$libs/reactive';
@ -66,6 +59,7 @@ const DISMISS_CAUSES: Record<
interface DialogOpts
extends
ProviderOpts,
StateProps<{ open: boolean }>,
ActiveProps<{
disabled: boolean;
@ -73,10 +67,7 @@ interface DialogOpts
variant: DialogVariant;
intent: 'neutral' | 'risk' | 'threat';
onOpenChangeComplete: OnChangeFn<boolean>;
}> {
id: Active<string>;
ref?: State<HTMLElement | null>;
}
}> {}
/**
* Dialog root — runtime-direct (no `Provider` base inheritance).
@ -294,10 +285,7 @@ export class DialogProvider {
// ── Trigger ────────────────────────────────────────────────────────────────
interface DialogTriggerOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface DialogTriggerOpts extends WithRefOpts {}
export class DialogTriggerProvider {
static create(opts: DialogTriggerOpts) {
@ -343,7 +331,7 @@ export class DialogTriggerProvider {
// ── Content ─────────────────────────────────────────────────────────────────
interface DialogContentOpts extends ActiveProps<{
interface DialogContentOpts extends WithRefOpts, ActiveProps<{
trapFocus: boolean | undefined;
loop: boolean;
preventScroll: boolean | undefined;
@ -355,10 +343,7 @@ interface DialogContentOpts extends ActiveProps<{
onInteractOutside: (e: PointerEvent) => void;
interactOutsideBehavior: DismissalBehavior | undefined;
onFocusOutside: (e: FocusEvent) => void;
}> {
id: Active<string>;
ref: State<HTMLElement | null>;
}
}> {}
export class DialogContentProvider {
static create(opts: DialogContentOpts) {
@ -477,10 +462,7 @@ export class DialogContentProvider {
// ── Overlay ────────────────────────────────────────────────────────────────
interface DialogOverlayOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface DialogOverlayOpts extends WithRefOpts {}
export class DialogOverlayProvider {
static create(opts: DialogOverlayOpts) {
@ -520,10 +502,7 @@ export class DialogOverlayProvider {
// ── Title ──────────────────────────────────────────────────────────────────
interface DialogTitleOpts extends ActiveProps<{ level: number }> {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface DialogTitleOpts extends WithRefOpts, ActiveProps<{ level: number }> {}
export class DialogTitleProvider {
static create(opts: DialogTitleOpts) {
@ -556,10 +535,7 @@ export class DialogTitleProvider {
// ── Description ────────────────────────────────────────────────────────────
interface DialogDescriptionOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface DialogDescriptionOpts extends WithRefOpts {}
export class DialogDescriptionProvider {
static create(opts: DialogDescriptionOpts) {
@ -589,10 +565,7 @@ export class DialogDescriptionProvider {
// ── Close ──────────────────────────────────────────────────────────────────
interface DialogCloseOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface DialogCloseOpts extends WithRefOpts {}
export class DialogCloseProvider {
static create(opts: DialogCloseOpts) {

@ -1,14 +1,7 @@
import { context } from '../../provider';
import { context, type ProviderOpts, type WithRefOpts } from '../../provider';
import { resolveDir } from '../../direction';
import { boolToEmptyStrOrUndef, getDataOpenClosed } from '$adom';
import {
readableActive,
state,
type Active,
type ActiveProps,
type State,
type StateProps
} from '$libs/reactive';
import { readableActive, state, type ActiveProps, type StateProps } from '$libs/reactive';
import type { SomaMouseEvent, SomaKeyboardEvent, Direction } from '../../types';
import { KEYS } from '../../keyboard';
import { Soma } from '../../core/soma.svelte';
@ -125,10 +118,8 @@ function defaultMinSize(refSize: number): number {
// ── Provider (root) ─────────────────────────────────────────────────────────
interface DrawerOpts extends StateProps<DrawerStateFields>, ActiveProps<DrawerActiveFields> {
id: Active<string>;
ref?: State<HTMLElement | null>;
}
interface DrawerOpts
extends ProviderOpts, StateProps<DrawerStateFields>, ActiveProps<DrawerActiveFields> {}
/**
* Drawer root — runtime-direct (no `Provider` base inheritance).
@ -397,10 +388,7 @@ export class DrawerProvider {
// ── Trigger ─────────────────────────────────────────────────────────────────
interface DrawerTriggerOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface DrawerTriggerOpts extends WithRefOpts {}
export class DrawerTriggerProvider {
static create(opts: DrawerTriggerOpts) {
@ -460,10 +448,7 @@ export class DrawerTriggerProvider {
// ── Content ─────────────────────────────────────────────────────────────────
interface DrawerContentOpts extends ActiveProps<DrawerContentActiveFields> {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface DrawerContentOpts extends WithRefOpts, ActiveProps<DrawerContentActiveFields> {}
export class DrawerContentProvider {
static create(opts: DrawerContentOpts) {
@ -1094,10 +1079,7 @@ export class DrawerContentProvider {
// ── Overlay ─────────────────────────────────────────────────────────────────
interface DrawerOverlayOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface DrawerOverlayOpts extends WithRefOpts {}
export class DrawerOverlayProvider {
static create(opts: DrawerOverlayOpts) {
@ -1146,10 +1128,7 @@ export class DrawerOverlayProvider {
// ── Handle ──────────────────────────────────────────────────────────────────
interface DrawerHandleOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface DrawerHandleOpts extends WithRefOpts {}
export class DrawerHandleProvider {
static create(opts: DrawerHandleOpts) {
@ -1187,10 +1166,7 @@ export class DrawerHandleProvider {
// ── Title ───────────────────────────────────────────────────────────────────
interface DrawerTitleOpts extends ActiveProps<{ level: number }> {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface DrawerTitleOpts extends WithRefOpts, ActiveProps<{ level: number }> {}
export class DrawerTitleProvider {
static create(opts: DrawerTitleOpts) {
@ -1223,10 +1199,7 @@ export class DrawerTitleProvider {
// ── Description ─────────────────────────────────────────────────────────────
interface DrawerDescriptionOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface DrawerDescriptionOpts extends WithRefOpts {}
export class DrawerDescriptionProvider {
static create(opts: DrawerDescriptionOpts) {
@ -1256,10 +1229,7 @@ export class DrawerDescriptionProvider {
// ── Close ───────────────────────────────────────────────────────────────────
interface DrawerCloseOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface DrawerCloseOpts extends WithRefOpts {}
export class DrawerCloseProvider {
static create(opts: DrawerCloseOpts) {

@ -1,12 +1,11 @@
import { untrack } from 'svelte';
import { resolveDir } from '../../direction';
import { context } from '../../provider';
import { context, type ProviderOpts, type WithRefOpts } from '../../provider';
import {
state,
readableActive,
type Active,
type ActiveProps,
type State,
type StateProps
} from '$libs/reactive';
import type { OnChangeFn, SomaMouseEvent, SomaKeyboardEvent, Direction } from '../../types';
@ -86,6 +85,7 @@ const KB_ANNOUNCE_DEBOUNCE_MS = 300;
interface FloatPanelOpts
extends
ProviderOpts,
StateProps<{
open: boolean;
position: FloatPanelPosition | undefined;
@ -115,10 +115,7 @@ interface FloatPanelOpts
zIndex: number | undefined;
onOpenChangeComplete: OnChangeFn<boolean> | undefined;
dir: Direction | undefined;
}> {
id: Active<string>;
ref?: State<HTMLElement | null>;
}
}> {}
export class FloatPanelProvider {
/**
@ -960,10 +957,7 @@ export class FloatPanelProvider {
// ── Trigger ─────────────────────────────────────────────────────────────────
interface FloatPanelTriggerOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface FloatPanelTriggerOpts extends WithRefOpts {}
export class FloatPanelTriggerProvider {
static create(opts: FloatPanelTriggerOpts) {
@ -1007,10 +1001,7 @@ export class FloatPanelTriggerProvider {
// ── Content ───────────────────────────────────────────────────────────────────
interface FloatPanelContentOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface FloatPanelContentOpts extends WithRefOpts {}
export class FloatPanelContentProvider {
static create(opts: FloatPanelContentOpts) {
@ -1187,10 +1178,7 @@ export class FloatPanelContentProvider {
// ── Header (drag handle) ──────────────────────────────────────────────────────
interface FloatPanelHeaderOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface FloatPanelHeaderOpts extends WithRefOpts {}
export class FloatPanelHeaderProvider {
static create(opts: FloatPanelHeaderOpts) {
@ -1238,10 +1226,7 @@ export class FloatPanelHeaderProvider {
// ── Title ──────────────────────────────────────────────────────────────────
interface FloatPanelTitleOpts extends ActiveProps<{ level: number }> {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface FloatPanelTitleOpts extends WithRefOpts, ActiveProps<{ level: number }> {}
export class FloatPanelTitleProvider {
static create(opts: FloatPanelTitleOpts) {
@ -1270,9 +1255,7 @@ export class FloatPanelTitleProvider {
// ── Close ────────────────────────────────────────────────────────────────────
interface FloatPanelCloseOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
interface FloatPanelCloseOpts extends WithRefOpts {
action: Active<'save' | 'cancel' | 'dismiss'>;
}
@ -1310,10 +1293,7 @@ export class FloatPanelCloseProvider {
// ── Body ───────────────────────────────────────────────────────────────────
interface FloatPanelBodyOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface FloatPanelBodyOpts extends WithRefOpts {}
export class FloatPanelBodyProvider {
static create(opts: FloatPanelBodyOpts) {
@ -1340,10 +1320,7 @@ export class FloatPanelBodyProvider {
// ── Controls (header button group) ──────────────────────────────────────────
interface FloatPanelControlsOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface FloatPanelControlsOpts extends WithRefOpts {}
export class FloatPanelControlsProvider {
static create(opts: FloatPanelControlsOpts) {
@ -1370,10 +1347,7 @@ export class FloatPanelControlsProvider {
// ── Action (generic header button) ──────────────────────────────────────────
interface FloatPanelActionOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface FloatPanelActionOpts extends WithRefOpts {}
export class FloatPanelActionProvider {
static create(opts: FloatPanelActionOpts) {
@ -1405,10 +1379,8 @@ export class FloatPanelActionProvider {
// ── StageTrigger (minimize / maximize / restore) ─────────────────────────────
interface FloatPanelStageTriggerOpts extends ActiveProps<{ stage: FloatPanelStage }> {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface FloatPanelStageTriggerOpts
extends WithRefOpts, ActiveProps<{ stage: FloatPanelStage }> {}
export class FloatPanelStageTriggerProvider {
static create(opts: FloatPanelStageTriggerOpts) {
@ -1443,10 +1415,8 @@ export class FloatPanelStageTriggerProvider {
// ── ResizeHandle ──────────────────────────────────────────────────────────────
interface FloatPanelResizeHandleOpts extends ActiveProps<{ edge: FloatPanelResizeEdge }> {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface FloatPanelResizeHandleOpts
extends WithRefOpts, ActiveProps<{ edge: FloatPanelResizeEdge }> {}
export class FloatPanelResizeHandleProvider {
static create(opts: FloatPanelResizeHandleOpts) {
@ -1481,10 +1451,7 @@ export class FloatPanelResizeHandleProvider {
// ── ResizeGrip (keyboard resize affordance) ─────────────────────────────────
interface FloatPanelResizeGripOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface FloatPanelResizeGripOpts extends WithRefOpts {}
export class FloatPanelResizeGripProvider {
static create(opts: FloatPanelResizeGripOpts) {

@ -1,10 +1,9 @@
import { context } from '../../provider';
import { context, type ProviderOpts, type WithRefOpts } from '../../provider';
import {
readableActive,
state,
type Active,
type ActiveProps,
type State,
type StateProps
} from '$libs/reactive';
import type {
@ -76,13 +75,12 @@ const DISMISS_CAUSES: Record<
interface PopoverOpts
extends
ProviderOpts,
StateProps<{ open: boolean }>,
ActiveProps<{
modal: boolean;
onOpenChangeComplete: OnChangeFn<boolean>;
}> {
id: Active<string>;
ref?: State<HTMLElement | null>;
/**
* The direction this popover asserts, raw (`activeDir` result). The root
* renders no element, so nothing is stamped in place — the value exists to
@ -335,7 +333,7 @@ export class PopoverProvider {
// ── Trigger ─────────────────────────────────────────────────────────────────
interface PopoverTriggerOpts extends ActiveProps<{
interface PopoverTriggerOpts extends WithRefOpts, ActiveProps<{
openOnHover: boolean;
openDelay: number;
closeDelay: number;
@ -347,10 +345,7 @@ interface PopoverTriggerOpts extends ActiveProps<{
* to honour `disabled` / `readonly` without losing focusability.
*/
disabled?: boolean;
}> {
id: Active<string>;
ref: State<HTMLElement | null>;
}
}> {}
export class PopoverTriggerProvider {
static create(opts: PopoverTriggerOpts) {
@ -456,7 +451,7 @@ export class PopoverTriggerProvider {
// ── Content ─────────────────────────────────────────────────────────────────
interface PopoverContentOpts extends ActiveProps<{
interface PopoverContentOpts extends WithRefOpts, ActiveProps<{
side: Side;
sideOffset: number;
align: Align;
@ -489,10 +484,7 @@ interface PopoverContentOpts extends ActiveProps<{
* from the portal host instead of forcing `ltr`. */
dir: Direction | undefined;
style: StyleProperties | null | undefined | string;
}> {
id: Active<string>;
ref: State<HTMLElement | null>;
}
}> {}
export class PopoverContentProvider {
static create(opts: PopoverContentOpts) {
@ -640,10 +632,7 @@ export class PopoverContentProvider {
// ── Title ──────────────────────────────────────────────────────────────────
interface PopoverTitleOpts extends ActiveProps<{ level: number }> {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface PopoverTitleOpts extends WithRefOpts, ActiveProps<{ level: number }> {}
export class PopoverTitleProvider {
static create(opts: PopoverTitleOpts) {
@ -674,10 +663,7 @@ export class PopoverTitleProvider {
// ── Description ────────────────────────────────────────────────────────────
interface PopoverDescriptionOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface PopoverDescriptionOpts extends WithRefOpts {}
export class PopoverDescriptionProvider {
static create(opts: PopoverDescriptionOpts) {
@ -707,10 +693,7 @@ export class PopoverDescriptionProvider {
// ── Arrow ────────────────────────────────────────────────────────────────────
interface PopoverArrowOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface PopoverArrowOpts extends WithRefOpts {}
export class PopoverArrowProvider {
static create(opts: PopoverArrowOpts) {
@ -742,9 +725,7 @@ export class PopoverArrowProvider {
// ── Close ────────────────────────────────────────────────────────────────────
interface PopoverCloseOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
interface PopoverCloseOpts extends WithRefOpts {
action: Active<'save' | 'cancel' | 'dismiss'>;
}
@ -784,10 +765,7 @@ export class PopoverCloseProvider {
// ── Anchor ───────────────────────────────────────────────────────────────────
interface PopoverAnchorOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface PopoverAnchorOpts extends WithRefOpts {}
export class PopoverAnchorProvider {
static create(opts: PopoverAnchorOpts) {
@ -822,10 +800,7 @@ export class PopoverAnchorProvider {
// ── Overlay ──────────────────────────────────────────────────────────────────
interface PopoverOverlayOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface PopoverOverlayOpts extends WithRefOpts {}
export class PopoverOverlayProvider {
static create(opts: PopoverOverlayOpts) {

@ -1,8 +1,8 @@
import { untrack } from 'svelte';
import type { Direction } from '../../types';
import { context } from '../../provider';
import { readableActive, state, type Active, type ActiveProps, type State } from '$libs/reactive';
import { context, type ProviderOpts, type WithRefOpts } from '../../provider';
import { readableActive, state, type ActiveProps } from '$libs/reactive';
import type { SomaMouseEvent } from '../../types';
import { Soma } from '../../core/soma.svelte';
import type { SomaRuntime, SomaRuntimePart } from '../../runtime.svelte';
@ -15,13 +15,13 @@ import { toastMorfo } from '../../../morfo/components/toast';
// ── Provider (root, virtual) ────────────────────────────────────────────────
interface ToastProviderOpts extends ActiveProps<{
toaster: Toaster;
label: string | undefined;
}> {
id: Active<string>;
ref?: State<HTMLElement | null>;
}
interface ToastProviderOpts
extends
ProviderOpts,
ActiveProps<{
toaster: Toaster;
label: string | undefined;
}> {}
/**
* Toast root — runtime-direct (no `Provider` base inheritance).
@ -71,10 +71,7 @@ export class ToastProvider {
// ── Viewport ────────────────────────────────────────────────────────────────
interface ToastViewportOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface ToastViewportOpts extends WithRefOpts {}
export class ToastViewportProvider {
static create(opts: ToastViewportOpts) {
@ -172,12 +169,12 @@ export class ToastViewportProvider {
// ── Item (individual toast) ─────────────────────────────────────────────────
interface ToastItemOpts extends ActiveProps<{
toast: ToastData;
}> {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface ToastItemOpts
extends
WithRefOpts,
ActiveProps<{
toast: ToastData;
}> {}
export class ToastItemProvider {
static readonly ctx = context<ToastItemProvider>('ToastItem');
@ -518,10 +515,7 @@ export class ToastItemProvider {
// ── Title ───────────────────────────────────────────────────────────────────
interface ToastTitleOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface ToastTitleOpts extends WithRefOpts {}
export class ToastTitleProvider {
static create(opts: ToastTitleOpts) {
@ -551,10 +545,7 @@ export class ToastTitleProvider {
// ── Description ─────────────────────────────────────────────────────────────
interface ToastDescriptionOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface ToastDescriptionOpts extends WithRefOpts {}
export class ToastDescriptionProvider {
static create(opts: ToastDescriptionOpts) {
@ -584,12 +575,12 @@ export class ToastDescriptionProvider {
// ── Action ──────────────────────────────────────────────────────────────────
interface ToastActionOpts extends ActiveProps<{
altText: string;
}> {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface ToastActionOpts
extends
WithRefOpts,
ActiveProps<{
altText: string;
}> {}
export class ToastActionProvider {
static create(opts: ToastActionOpts) {
@ -624,10 +615,7 @@ export class ToastActionProvider {
// ── Close ───────────────────────────────────────────────────────────────────
interface ToastCloseOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface ToastCloseOpts extends WithRefOpts {}
export class ToastCloseProvider {
static create(opts: ToastCloseOpts) {
@ -667,10 +655,7 @@ export class ToastCloseProvider {
// ── Status ──────────────────────────────────────────────────────────────────
interface ToastStatusOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface ToastStatusOpts extends WithRefOpts {}
/**
* Decorative status indicator (the leading-edge icon disc). Pure
@ -706,10 +691,7 @@ export class ToastStatusProvider {
// ── Main ────────────────────────────────────────────────────────────────────
interface ToastMainOpts {
id: Active<string>;
ref: State<HTMLElement | null>;
}
interface ToastMainOpts extends WithRefOpts {}
/**
* Layout-only stack container for Title + Description + Action. Declared

@ -0,0 +1,75 @@
import { readdirSync, readFileSync } from 'node:fs';
import { join, relative } from 'node:path';
import { describe, expect, it } from 'vitest';
/**
* The census guard of the opts axis: no opts interface inside
* `soma/components` re-declares `id` / `ref` in its own body — those two
* keys come from `WithRefOpts` / `ProviderOpts` (`provider.svelte.ts`),
* the single place that says what they are. History: 49 interfaces across
* 7 files had drifted into verbatim copies before the 2026-08-05 merge.
*
* The scan is a raw-line, CASE-SENSITIVE match — deliberately not a
* parser. The brace-matching parser that produced the first census count
* missed 16 interfaces whose `extends` clause carries inline literal
* generics (`StateProps<{ open: boolean }>`); a line match has no such
* blind spot, and it also covers `type XOpts = { … }` literal bodies.
* Case matters: `menubar-provider.svelte.ts` composes an internal menu
* with `ref: state<HTMLElement | null>(null)` — the FUNCTION `state` at a
* call-site — which a case-insensitive scan would flag as a false positive.
*/
const COMPONENTS_DIR = join(__dirname, '..', 'components');
/**
* Signed exceptions — relative path → reason. Growing this list is a
* decision, not a convenience.
*/
const EXCEPTIONS: Record<string, string> = {};
// Prefix matches on purpose: a widened `id: Active<string | undefined>` or
// a narrowed `ref: State<HTMLElementX …>` is also a deviation from the base.
const ID_DECL = /^\s*id\s*:\s*Active<string>/;
const REF_DECL = /^\s*ref\s*\??\s*:\s*State<HTMLElement/;
function scanTsFiles(dir: string): string[] {
const out: string[] = [];
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const path = join(dir, entry.name);
if (entry.isDirectory()) out.push(...scanTsFiles(path));
else if (entry.name.endsWith('.ts') && !entry.name.endsWith('.test.ts')) out.push(path);
}
return out;
}
describe('opts census — id/ref come from the base, never re-declared', () => {
const files = scanTsFiles(COMPONENTS_DIR);
it('finds the catalogue (sanity: the census is not scanning an empty tree)', () => {
expect(files.length).toBeGreaterThan(200);
});
it('no file in components/ re-declares the canonical id/ref lines', () => {
const offenders: string[] = [];
for (const file of files) {
const rel = relative(COMPONENTS_DIR, file).replace(/\\/g, '/');
if (rel in EXCEPTIONS) continue;
const lines = readFileSync(file, 'utf-8').split('\n');
for (let i = 0; i < lines.length; i++) {
if (ID_DECL.test(lines[i]) || REF_DECL.test(lines[i])) {
offenders.push(`${rel}:${i + 1}`);
}
}
}
expect(
offenders,
'id/ref re-declared verbatim — extend WithRefOpts / ProviderOpts instead'
).toEqual([]);
});
it('the exceptions are real files that still exist', () => {
const relSet = new Set(files.map((f) => relative(COMPONENTS_DIR, f).replace(/\\/g, '/')));
const stale = Object.keys(EXCEPTIONS).filter((rel) => !relSet.has(rel));
expect(stale, 'these exceptions point at files that no longer exist — retire them').toEqual([]);
});
});
Loading…
Cancel
Save

Powered by TurnKey Linux.