Files
hyperframes/packages/core/src/lint/context.ts
T
Vance IngallsandClaude Opus 4.6 fc973ee2e8 feat(lint): add rules for missing data-start, template wrapper, and DOCTYPE
Three new lint rules that catch structural issues causing compositions
to fail silently in preview:

- root_composition_missing_data_start: Root composition needs data-start="0"
  for the runtime to begin playback
- standalone_composition_wrapped_in_template: index.html should not be
  wrapped in <template> (only sub-compositions use that)
- root_composition_missing_html_wrapper: index.html needs <!DOCTYPE html>
  and <html> wrapper for the bundler

Also adds rawSource to LintContext so rules can inspect pre-template-stripped
HTML, and isSubComposition to linter options so rules can distinguish root
from sub-composition files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:45:25 -07:00

55 lines
1.5 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;
rawSource: 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 {
const rawSource = html || "";
let source = rawSource;
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,
rawSource,
tags,
styles,
scripts,
compositionIds,
rootTag,
rootCompositionId,
options,
};
}