You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/scripts/packs-check.ts

115 lines
3.6 KiB

feat(packs+text): incorporate the animation collection — Ambient pack + text-effects family + docs Two streams, split by what the animation touches: STREAM A — decorative backgrounds → the pack tier - arts/scene: a consolidated scene runtime ($scene) that owns, once, the citizenship every ad-hoc background reinvented or skipped (frame loop, off-view pause, DPR cap, mandatory reduced-motion, WebGL context loss/restore, scene budget, teardown). SceneDom port (adom satisfies it), webgl/webgl2/canvas2d drivers + a custom-pipeline extension (vertexShader + draw + glContext.depth/dprCap) for real geometry (beam, particles, dither, grid, eter, pixel-blast, hyperspeed). 32 effects as shared resources. - src/packs/ambient: the first pack — <Ambient effect="…"> mounts a registered effect; the P contract (P-1..P-6) guarded by scripts/packs-check.ts; colors are token-aware (P-4). One-way dependency, removable-by-construction. - resolveToken extended to semantic color slots (--color-{role}-{slot}) so consumers resolve theme tokens to concrete colors (the P-4 half). STREAM B — animations over real text → canon - Six components (count-up + text-{gradient,circular,blur,focus,scramble}): each a morfo + eidos recipe (where there's styling) + demo. CountUp is a service component (counts through uix.format.numbers). The five Text* are passive decoratives. Upgrades over the seeds: SR hardening (real text visually-hidden + aria-hidden decoration), a11y fix (no fake role=button), measurement discipline (cached rects via dom.measure, no reflow storm), reduced-motion, ecosystem citizenship (eidos.dom/timers, no raw platform). - MorfoElement gains 'p'. DOCS - docs/architecture/packs.md (pack tier, admission rule, P contract, Aura promotion path); docs/decisions/design-text-effects.md (the family design record) + indexed in decisions.md / README.md; glossary entries (scene/Ambient/Aura/text effects); scene README custom-pipeline + authoring bridge; motion-guide content-effects note; strata tables acknowledge packs. Gates: component:audit 141/0/0 · docs:check 0/0 · scene tests 9/9 · packs:check 0/36 · check 0 own errors. Verified in browser (32 effects mount+compile; 6 text components SSR+hydrate, CountUp re-formats by locale live, TextGradient resolves token stops to OKLCH via var()). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 months ago
/**
* packs-check — the mechanical guard of the pack tier's P contract
* (docs/architecture/packs.md). Small on purpose: packs are exempt from the
* acceptance matrix, NOT from discipline.
*
* Checks (all error-level):
* P-3 no raw platform APIs in `src/packs/` — everything goes through the
* injected ports (`SceneDom` etc.). Raw `requestAnimationFrame`,
* `new ResizeObserver/IntersectionObserver/MutationObserver`,
* `addEventListener`, `setTimeout/setInterval`, `matchMedia` = error.
* S-1 no scoped `<style>` blocks in pack `.svelte` files — the consumer
* styles the host via class/style; the pack never competes with eidos.
* R-1 every `effects/*.ts` module registers its effect AND merges its
* params type (`registerAmbientEffect` + `declare module`), so the
* typed registry can't drift from the runtime one.
*
* P-1/P-2/P-5/P-6 are enforced structurally by `EngineScene` (type-required
* `reduce`, handle teardown, aria-hidden canvas, IO pause + budget) — no
* static check needed here.
*/
import { readdirSync, readFileSync, statSync } from 'node:fs';
import { join, relative } from 'node:path';
const REPO = process.cwd();
const PACKS_DIR = join(REPO, 'src', 'packs');
interface Violation {
rule: string;
file: string;
line: number;
detail: string;
}
const RAW_API_RE =
/\brequestAnimationFrame\s*\(|\bcancelAnimationFrame\s*\(|new\s+(ResizeObserver|IntersectionObserver|MutationObserver)\b|\baddEventListener\s*\(|\bsetTimeout\s*\(|\bsetInterval\s*\(|\bmatchMedia\s*\(/;
function walk(dir: string, out: string[] = []): string[] {
for (const entry of readdirSync(dir)) {
const path = join(dir, entry);
if (statSync(path).isDirectory()) walk(path, out);
else if (/\.(ts|svelte)$/.test(entry) && !/\.test\./.test(entry)) out.push(path);
}
return out;
}
function main(): void {
const violations: Violation[] = [];
let files: string[] = [];
try {
files = walk(PACKS_DIR);
} catch {
console.log('packs-check: no src/packs/ tree — nothing to check');
return;
}
for (const file of files) {
const rel = relative(REPO, file);
const src = readFileSync(file, 'utf8');
const lines = src.split('\n');
lines.forEach((line, i) => {
// Comments may NAME the raw APIs (docs of what the port replaces).
const code = line.replace(/\/\/.*$/, '').replace(/^\s*\*.*$/, '');
if (RAW_API_RE.test(code)) {
violations.push({
rule: 'P-3',
file: rel,
line: i + 1,
detail: `raw platform API — use the injected port (${code.trim().slice(0, 60)}…)`
});
}
});
if (file.endsWith('.svelte') && /<style\b/.test(src)) {
violations.push({
rule: 'S-1',
file: rel,
line: lines.findIndex((l) => /<style\b/.test(l)) + 1,
detail: 'scoped <style> in a pack component — style the host via class/style'
});
}
if (/[\\/]effects[\\/][^\\/]+\.ts$/.test(file)) {
if (!src.includes('registerAmbientEffect(')) {
violations.push({
rule: 'R-1',
file: rel,
line: 1,
detail: 'effect module does not register its effect (registerAmbientEffect)'
});
}
if (!src.includes('declare module')) {
violations.push({
rule: 'R-1',
file: rel,
line: 1,
detail: 'effect module does not merge its params type (declare module …/registry)'
});
}
}
}
if (violations.length > 0) {
for (const v of violations) {
console.error(`ERROR [${v.rule}] ${v.file}:${v.line} — ${v.detail}`);
}
console.error(`\npacks-check: ${violations.length} error(s) across ${files.length} files`);
process.exit(1);
}
console.log(`packs-check: 0 error(s) across ${files.length} files`);
}
main();

Powered by TurnKey Linux.