mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
## Summary
- Breaks `hyperframeLinter.ts` from 1,314 lines (one massive function) into a plugin system of focused rule modules
- Introduces `LintContext` — HTML is parsed once and shared across all rules
- Adds `LintRule<TContext>` type as the formal contract for rules
- Public API unchanged: `lintHyperframeHtml`, `lintMediaUrls`, `lintScriptUrls` signatures identical
## New file structure
```
src/lint/
utils.ts — shared types (OpenTag, ExtractedBlock), regex constants, helpers
context.ts — LintContext type + buildLintContext() factory
rules/
core.ts — structural rules (root attrs, timeline registry, script syntax)
media.ts — media element rules (duplicate ids, video pitfalls, placeholder URLs, etc.)
gsap.ts — GSAP rules + GSAP-specific parsing utils
captions.ts — caption rules
composition.ts — timed element, deprecated attrs, template literal selector, external scripts
adapters.ts — Lottie + Three.js missing-script rules (from PR #149)
hyperframeLinter.ts — orchestrator only (~200 lines, down from 1,314)
```
## Adding a new adapter rule going forward
1. Create `src/lint/rules/my-adapter.ts` exporting `myAdapterRules: LintRule[]`
2. Import and spread into `ALL_RULES` in `hyperframeLinter.ts`
## Test plan
- [x] All 402 core tests pass unchanged
- [x] Full workspace build clean (`pnpm build`)
- [x] TypeScript strict mode clean (`pnpm tsc --noEmit`)
52 lines
1.4 KiB
TypeScript
52 lines
1.4 KiB
TypeScript
import type { HyperframeLintFinding, HyperframeLinterOptions } from "./types";
|
|
import {
|
|
extractBlocks,
|
|
extractOpenTags,
|
|
findRootTag,
|
|
collectCompositionIds,
|
|
readAttr,
|
|
STYLE_BLOCK_PATTERN,
|
|
SCRIPT_BLOCK_PATTERN,
|
|
} from "./utils";
|
|
import type { OpenTag, ExtractedBlock } from "./utils";
|
|
|
|
export type { OpenTag, ExtractedBlock };
|
|
|
|
export type LintContext = {
|
|
source: string;
|
|
tags: OpenTag[];
|
|
styles: ExtractedBlock[];
|
|
scripts: ExtractedBlock[];
|
|
compositionIds: Set<string>;
|
|
rootTag: OpenTag | null;
|
|
rootCompositionId: string | null;
|
|
options: HyperframeLinterOptions;
|
|
};
|
|
|
|
// Re-export for convenience so rule modules only need one import for the finding type
|
|
export type { HyperframeLintFinding };
|
|
|
|
export function buildLintContext(html: string, options: HyperframeLinterOptions = {}): LintContext {
|
|
let source = html || "";
|
|
const templateMatch = source.match(/<template[^>]*>([\s\S]*)<\/template>/i);
|
|
if (templateMatch?.[1]) source = templateMatch[1];
|
|
|
|
const tags = extractOpenTags(source);
|
|
const styles = extractBlocks(source, STYLE_BLOCK_PATTERN);
|
|
const scripts = extractBlocks(source, SCRIPT_BLOCK_PATTERN);
|
|
const compositionIds = collectCompositionIds(tags);
|
|
const rootTag = findRootTag(source);
|
|
const rootCompositionId = readAttr(rootTag?.raw || "", "data-composition-id");
|
|
|
|
return {
|
|
source,
|
|
tags,
|
|
styles,
|
|
scripts,
|
|
compositionIds,
|
|
rootTag,
|
|
rootCompositionId,
|
|
options,
|
|
};
|
|
}
|