How the broken-ref Rule Detects Unresolved Token References in DESIGN.md
The broken-ref rule detects unresolved token references by collecting failed reference lookups during component parsing and then reporting them as lint errors after the model is fully built.
The broken-ref rule in the google-labs-code/design.md linter validates that all token references within component definitions resolve to actual design tokens. When a property value like {color.primary} cannot be mapped to a defined token in the symbol table, the rule generates a specific lint error indicating the exact reference path and component location.
How Reference Resolution Works During Parsing
During the linting process, the ModelHandler.execute method in packages/cli/src/linter/model/handler.ts processes the components section of the DESIGN.md file. For each property value that matches the token reference pattern ({…}), the handler invokes resolveReference to traverse the reference chain against the symbol table.
When resolveReference encounters a missing token or detects a circular dependency, it returns null. The handler captures these failures by pushing the raw reference string onto the component's unresolvedRefs array, which is stored alongside the component's properties in the internal model.
Tracking Unresolved References in ModelHandler
The following TypeScript implementation from model/handler.ts demonstrates how unresolved references are collected during component processing:
// Inside ModelHandler.execute – building component definitions
for (const [compName, props] of Object.entries(input.components)) {
const properties = new Map<string, ResolvedValue>();
const unresolvedRefs: string[] = [];
for (const [propName, rawValue] of Object.entries(props)) {
if (isTokenReference(rawValue)) {
const refPath = rawValue.slice(1, -1);
const resolved = resolveReference(symbolTable, refPath, new Set());
if (resolved !== null) {
properties.set(propName, resolved);
} else {
// <-- reference cannot be resolved → track it
unresolvedRefs.push(rawValue);
properties.set(propName, rawValue);
}
}
// … handling of numbers, booleans, colors, dimensions …
}
components.set(compName, { properties, unresolvedRefs });
}
Reporting Broken References
After the model is constructed, the brokenRef function defined in packages/cli/src/linter/linter/rules/broken-ref.ts receives the DesignSystemState containing the populated components map. The rule iterates through each component's unresolvedRefs array and generates a RuleFinding for every entry.
Each finding includes the specific path components.<componentName> and a descriptive message identifying the unresolvable reference. This approach ensures that detection is decoupled from reporting—the parsing stage captures all resolution failures, while the rule simply surfaces them as lint errors.
The brokenRef Rule Implementation
The implementation in broken-ref.ts processes the pre-collected unresolved references:
export function brokenRef(state: DesignSystemState): RuleFinding[] {
const findings: RuleFinding[] = [];
for (const [compName, comp] of state.components) {
// Unresolved references → generate error findings
for (const ref of comp.unresolvedRefs) {
findings.push({
path: `components.${compName}`,
message: `Reference ${ref} does not resolve to any defined token.`,
});
}
// … additional check for unknown component sub‑tokens …
}
return findings;
}
Key Files and Functions
The detection and reporting logic spans two primary files in the google-labs-code/design.md repository:
packages/cli/src/linter/model/handler.ts: Parses DESIGN.md components, resolves token references viaresolveReference, and populates theunresolvedRefsarrays when references fail to resolve.packages/cli/src/linter/linter/rules/broken-ref.ts: Contains thebrokenReffunction that transforms unresolved references into lint findings.packages/cli/src/linter/linter/rules/index.ts: Registers thebroken-refrule within the default linting configuration.
Summary
- The
broken-refrule detects unresolved token references by analyzing pre-collected data from the parsing phase rather than performing live lookups. - During component processing in
ModelHandler.execute, failed reference resolutions (returningnullfromresolveReference) are stored in component-specificunresolvedRefsarrays. - The
brokenRefrule inpackages/cli/src/linter/linter/rules/broken-ref.tsiterates these arrays to generate lint errors with paths likecomponents.Buttonand messages identifying the specific broken reference. - This architecture separates reference resolution from error reporting, ensuring consistent and accurate detection of missing tokens in DESIGN.md files.
Frequently Asked Questions
What triggers the broken-ref rule to report an error?
The broken-ref rule reports an error when a component property contains a token reference string (enclosed in curly braces) that cannot be resolved to a defined token in the symbol table. This occurs when resolveReference returns null during the parsing phase, indicating either a missing token definition or a circular reference chain.
How does the linter distinguish between valid and broken references?
The linter validates references in ModelHandler.execute by calling resolveReference with the reference path and symbol table. If the function successfully traverses the reference chain and returns a resolved value, the reference is valid. If it returns null, the reference is considered broken and is added to the unresolvedRefs collection for later reporting.
Where are broken references stored before the rule reports them?
Broken references are stored in the unresolvedRefs array property of each component object within the DesignSystemState.components map. This data structure is populated during the model building phase in packages/cli/src/linter/model/handler.ts and consumed by the brokenRef function after the entire model has been constructed.
Can circular token references trigger the broken-ref rule?
Yes. The resolveReference function tracks visited references using a Set to detect cycles. When a circular reference is detected, the function returns null, causing the reference to be added to unresolvedRefs and subsequently reported by the broken-ref rule as an unresolved token reference.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →