Files
gstack/lib/design-lint/context.ts
T
garrytanandcapy-ai[bot] 6100d6e94d design-lint: third review cycle fixes
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-26 08:21:13 +00:00

61 lines
2.5 KiB
TypeScript

/**
* The lint context: one page with the stylesheets it links (and anything
* they @import), or one directory of component files. Rules read it, the
* caches on it memoise the shared indexes.
*/
import { buildStyleIndex, declValue, fontPxOf, mergeBySelector, stylesFor, trackingEmOf, type CssDecl, type CssRule, type MergedRule, type StyleIndex, type Tokens } from './css';
import type { Doc } from './html';
export interface CssFile {
file: string;
raw: string;
}
/** Project-wide facts shared across a directory of components, so a focus ring in globals.css counts for Hero.tsx. */
export interface SharedStyles {
focus: boolean;
active: boolean;
disabled: boolean;
reducedMotion: boolean;
}
export interface LintContext {
/** The page file, or the directory for a group of components. */
key: string;
rules: CssRule[];
tokens: Tokens;
docs: Doc[];
cssFiles: CssFile[];
/** Every stylesheet's text joined, for regex questions. */
allCss: string;
scope: 'page' | 'component';
/** The visitor's job (book, buy, subscribe, …) when the caller named one. */
job: string | null;
/** A stylesheet-only context whose rules are all tokens: nothing to lint but the tokens. */
tokensOnly: boolean;
shared: SharedStyles | null;
/** Tokens visible to Tailwind utilities in components: the project's merged tokens. */
twTokens: Tokens | null;
cache: Record<string, unknown>;
}
export function memo<T>(ctx: LintContext, key: string, make: () => T): T {
if (!(key in ctx.cache)) ctx.cache[key] = make();
return ctx.cache[key] as T;
}
export const mergedRules = (ctx: LintContext): MergedRule[] => memo(ctx, 'merged', () => mergeBySelector(ctx.rules));
export const styleIndex = (ctx: LintContext): StyleIndex => memo(ctx, 'styleIndex', () => buildStyleIndex(ctx.rules));
/** Declarations applying to an element by its class attribute and tag; restingOnly drops state and pseudo-element rules. */
export const styleFor = (ctx: LintContext, classAttr: string | null | undefined, tag: string, restingOnly = false): CssDecl[] =>
stylesFor(styleIndex(ctx), classAttr, tag, restingOnly);
export const valueOf = (ctx: LintContext, decls: CssDecl[], prop: string): string | null => declValue(decls, prop, ctx.tokens);
export const fontPx = (ctx: LintContext, decls: CssDecl[]): number | null => fontPxOf(decls, ctx.tokens);
export const trackingEm = (ctx: LintContext, decls: CssDecl[], px: number | null): number | null => trackingEmOf(decls, ctx.tokens, px);