Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .fallowrc.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@
// page.addScriptTag (see layout.ts / validate.ts) — referenced by file
// path, never imported, so they have no import-graph referrer.
"packages/cli/src/commands/layout-audit.browser.js",
"packages/cli/src/commands/contrast-audit.browser.js",
"packages/cli/src/commands/motion-sample.browser.js",
// @hyperframes/validate contrast audit injects it via page.addScriptTag (raw string, no import referrer).
"packages/validate/src/contrast-audit.browser.js",
// Worker entry points loaded dynamically by their *Pool.ts companions.
"packages/producer/src/services/pngDecodeBlitWorker.ts",
"packages/producer/src/services/shaderTransitionWorker.ts",
Expand Down Expand Up @@ -305,6 +306,13 @@
// new package makes fallow see it as a fresh finding; the underlying clone
// predates this refactor.
"packages/parsers/src/hfIds.ts",
// @hyperframes/validate vendors these from cli/src/utils/* so it needn't import @hyperframes/cli (which depends on it); a shared low-level package is a follow-up.
"packages/validate/src/staticProjectServer.ts",
"packages/validate/src/compositionViewport.ts",
"packages/validate/src/dom.ts",
"packages/validate/src/errorMessage.ts",
// browserValidate.ts: raceMediaReady vs auditClipDurations' in-page closure share the race wiring; intentional — the closure is Puppeteer-serialized into a separate realm (see raceMediaReady doc).
"packages/validate/src/browserValidate.ts",
// Parser test files: parallel arrange/act/assert test cases — pre-existing
// duplication moved from packages/core/src/parsers/.
"packages/parsers/src/gsapParser.test.ts",
Expand Down Expand Up @@ -573,6 +581,8 @@
"packages/studio/src/player/components/TimelineCanvas.tsx",
"packages/studio/src/player/components/TimelineClip.tsx",
"packages/studio/src/player/components/TimelineClipDiamonds.tsx",
// WCAG contrast sampler ported wholesale into @hyperframes/validate; high cyclomatic is inherent to the pixel scan (no new logic).
"packages/validate/src/contrast-audit.browser.js",
],
},
}
54 changes: 41 additions & 13 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"type": "module",
"scripts": {
"dev": "bun run studio",
"build": "bun run --filter '@hyperframes/{parsers,lint,studio-server}' build && bun run --filter @hyperframes/core build && bun run --filter '@hyperframes/{core,engine,producer,player,studio,shader-transitions,aws-lambda,gcp-cloud-run,sdk}' build && bun run --filter @hyperframes/cli build",
"build": "bun run --filter '@hyperframes/{parsers,lint,studio-server}' build && bun run --filter @hyperframes/core build && bun run --filter '@hyperframes/{core,engine,producer,player,studio,shader-transitions,aws-lambda,gcp-cloud-run,sdk}' build && bun run --filter @hyperframes/validate build && bun run --filter @hyperframes/cli build",
"build:producer": "bun run --filter @hyperframes/producer build",
"studio": "bun run --filter @hyperframes/studio dev",
"build:hyperframes-runtime": "bun run --filter @hyperframes/core build:hyperframes-runtime",
Expand Down
1 change: 1 addition & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"@hyperframes/producer": "workspace:*",
"@hyperframes/studio": "workspace:*",
"@hyperframes/studio-server": "workspace:*",
"@hyperframes/validate": "workspace:*",
"@types/adm-zip": "^0.5.7",
"@types/fontkit": "^2.0.9",
"@types/mime-types": "^3.0.1",
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/scripts/build-copy.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ async function main() {
cpSync(layoutAuditScript, join(DIST, "commands", "layout-audit.browser.js"));
}

const contrastAuditScript = join(CLI_ROOT, "src", "commands", "contrast-audit.browser.js");
// Owned by @hyperframes/validate; bundled CLI loads it from dist/commands via page.addScriptTag.
const contrastAuditScript = join(CLI_ROOT, "..", "validate", "src", "contrast-audit.browser.js");
if (existsSync(contrastAuditScript)) {
cpSync(contrastAuditScript, join(DIST, "commands", "contrast-audit.browser.js"));
}
Expand Down
75 changes: 0 additions & 75 deletions packages/cli/src/commands/layout-audit.browser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { fileURLToPath } from "node:url";

const __dirname = dirname(fileURLToPath(import.meta.url));
const script = readFileSync(join(__dirname, "layout-audit.browser.js"), "utf-8");
const contrastScript = readFileSync(join(__dirname, "contrast-audit.browser.js"), "utf-8");

interface RectInput {
left: number;
Expand Down Expand Up @@ -196,47 +195,6 @@ describe("layout-audit.browser content overlap", () => {
});
});

describe("contrast-audit.browser clip-path visibility", () => {
afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllGlobals();
document.body.innerHTML = "";
delete (document as unknown as { elementFromPoint?: unknown }).elementFromPoint;
delete (window as unknown as { __contrastAudit?: unknown }).__contrastAudit;
});

it("excludes text clipped to nothing by clip-path from contrast reports", async () => {
document.body.innerHTML = `
<div id="root" data-composition-id="main" data-width="640" data-height="360">
<div id="headline">Hidden text</div>
</div>
`;

vi.spyOn(window, "getComputedStyle").mockImplementation((element) => {
const id = (element as Element).id;
return {
display: "block",
visibility: "visible",
opacity: "1",
color: "rgb(0, 0, 0)",
fontSize: "32px",
fontWeight: "400",
clipPath: id === "headline" ? "inset(0px 100% 0px 0px)" : "none",
} as unknown as CSSStyleDeclaration;
});

vi.spyOn(document.getElementById("headline")!, "getBoundingClientRect").mockReturnValue(
rect({ left: 100, top: 100, width: 400, height: 80 }),
);
(document as unknown as { elementFromPoint: () => Element | null }).elementFromPoint = () =>
null;

installContrastScript();

expect(await runContrastAudit()).toEqual([]);
});
});

// Both blocks overlap heavily; only the exemption on block A should suppress
// the finding, so a missing exemption would surface as a failure here.
function expectExemptFromOverlap(aOverrides: { color?: string; attrs?: string }): void {
Expand Down Expand Up @@ -446,39 +404,6 @@ function installAuditScript(): void {
window.eval(script);
}

function installContrastScript(): void {
class MockImage {
onload: (() => void) | null = null;
onerror: (() => void) | null = null;
naturalWidth = 640;
naturalHeight = 360;

set src(_value: string) {
this.onload?.();
}
}

vi.stubGlobal("Image", MockImage);
const getContextSpy = vi.spyOn(HTMLCanvasElement.prototype, "getContext") as unknown as {
mockReturnValue(value: CanvasRenderingContext2D): void;
};
getContextSpy.mockReturnValue({
drawImage() {},
getImageData() {
return { data: new Uint8ClampedArray(640 * 360 * 4).fill(255) };
},
} as unknown as CanvasRenderingContext2D);
window.eval(contrastScript);
}

async function runContrastAudit(): Promise<Array<Record<string, unknown>>> {
return (
window as unknown as {
__contrastAudit: (imgBase64: string, time: number) => Promise<Array<Record<string, unknown>>>;
}
).__contrastAudit("stub", 0);
}

function runAudit(): Array<{
code: string;
selector: string;
Expand Down
Loading
Loading