mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
feat(daemon): consume component manifests (#2053)
* feat(design-systems): extract component manifests * feat(daemon): consume component manifests --------- Co-authored-by: chaoxiaoche <chaoxiaoche@chaoxiaochedeMacBook-Pro.local>
This commit is contained in:
parent
46a64edce3
commit
1f66c53203
8 changed files with 221 additions and 15 deletions
|
|
@ -6,6 +6,11 @@
|
|||
import { readdir, readFile, stat } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
|
||||
import {
|
||||
extractComponentsManifest,
|
||||
summarizeComponentsManifestForPrompt,
|
||||
} from '@open-design/contracts';
|
||||
|
||||
export type DesignSystemSurface = 'web' | 'image' | 'video' | 'audio';
|
||||
|
||||
export type DesignSystemSummary = {
|
||||
|
|
@ -73,10 +78,14 @@ export async function readDesignSystem(root: string, id: string): Promise<string
|
|||
*
|
||||
* - `tokensCss` — verbatim content of `<brand>/tokens.css`.
|
||||
* - `fixtureHtml` — verbatim content of `<brand>/components.html`.
|
||||
* - `componentsManifest` — concise summary derived from components.html
|
||||
* for prompt injection; when absent, callers
|
||||
* can fall back to `fixtureHtml`.
|
||||
*/
|
||||
export type DesignSystemAssets = {
|
||||
tokensCss?: string | undefined;
|
||||
fixtureHtml?: string | undefined;
|
||||
componentsManifest?: string | undefined;
|
||||
};
|
||||
|
||||
export async function readDesignSystemAssets(
|
||||
|
|
@ -87,7 +96,7 @@ export async function readDesignSystemAssets(
|
|||
readFileOptional(path.join(root, id, 'tokens.css')),
|
||||
readFileOptional(path.join(root, id, 'components.html')),
|
||||
]);
|
||||
return { tokensCss, fixtureHtml };
|
||||
return withComponentsManifest(id, { tokensCss, fixtureHtml });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -154,10 +163,42 @@ export async function resolveDesignSystemAssets(
|
|||
}
|
||||
|
||||
const userInstalled = await readDesignSystemAssets(userInstalledRoot, designSystemId);
|
||||
return {
|
||||
return withComponentsManifest(designSystemId, {
|
||||
tokensCss: builtIn.tokensCss ?? userInstalled.tokensCss,
|
||||
fixtureHtml: builtIn.fixtureHtml ?? userInstalled.fixtureHtml,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function withComponentsManifest(
|
||||
designSystemId: string,
|
||||
assets: Pick<DesignSystemAssets, 'tokensCss' | 'fixtureHtml'>,
|
||||
): DesignSystemAssets {
|
||||
const componentsManifest = buildComponentsManifestSummary(
|
||||
designSystemId,
|
||||
assets.fixtureHtml,
|
||||
assets.tokensCss,
|
||||
);
|
||||
return { ...assets, componentsManifest };
|
||||
}
|
||||
|
||||
function buildComponentsManifestSummary(
|
||||
designSystemId: string,
|
||||
fixtureHtml: string | undefined,
|
||||
tokensCss: string | undefined,
|
||||
): string | undefined {
|
||||
if (fixtureHtml === undefined || fixtureHtml.trim().length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const manifest =
|
||||
tokensCss === undefined
|
||||
? extractComponentsManifest({ brandId: designSystemId, fixtureHtml })
|
||||
: extractComponentsManifest({ brandId: designSystemId, fixtureHtml, tokensCss });
|
||||
return summarizeComponentsManifestForPrompt(manifest);
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async function readFileOptional(file: string): Promise<string | undefined> {
|
||||
|
|
|
|||
|
|
@ -185,10 +185,13 @@ export interface ComposeInput {
|
|||
// - `designSystemTokensCss` — verbatim `tokens.css` :root contract
|
||||
// that the agent pastes into the
|
||||
// artifact's <style>.
|
||||
// - `designSystemFixtureHtml` — verbatim `components.html` reference
|
||||
// fixture demonstrating button / card /
|
||||
// type-scale shapes wired to the tokens.
|
||||
// - `designSystemComponentsManifest` — concise structured summary
|
||||
// derived from components.html.
|
||||
// - `designSystemFixtureHtml` — verbatim `components.html`
|
||||
// fallback when no manifest can
|
||||
// be derived.
|
||||
designSystemTokensCss?: string | undefined;
|
||||
designSystemComponentsManifest?: string | undefined;
|
||||
designSystemFixtureHtml?: string | undefined;
|
||||
// Craft references the active skill opted into via `od.craft.requires`.
|
||||
// The daemon resolves the slug list to file contents and concatenates
|
||||
|
|
@ -271,6 +274,7 @@ export function composeSystemPrompt({
|
|||
designSystemBody,
|
||||
designSystemTitle,
|
||||
designSystemTokensCss,
|
||||
designSystemComponentsManifest,
|
||||
designSystemFixtureHtml,
|
||||
craftBody,
|
||||
craftSections,
|
||||
|
|
@ -348,18 +352,23 @@ export function composeSystemPrompt({
|
|||
// sets voice and intent; the tokens.css block below is the SAME
|
||||
// contract in machine-readable form — names + values the agent pastes
|
||||
// verbatim instead of re-deriving from prose. The components.html
|
||||
// fixture grounds the token vocabulary in worked component shapes
|
||||
// (button / card / type roles) so the agent can copy fragments
|
||||
// directly. Both blocks are individually gated: missing files (today,
|
||||
// every brand except `default` and `kami`) skip silently, preserving
|
||||
// the legacy DESIGN.md-only behaviour for the other ~138 brands.
|
||||
// manifest grounds the token vocabulary in worked component shapes
|
||||
// (button / card / type roles) without injecting the full HTML fixture.
|
||||
// If manifest extraction fails or is unavailable, the composer falls
|
||||
// back to the verbatim components.html fixture. Both blocks are
|
||||
// individually gated: missing files skip silently, preserving the
|
||||
// legacy DESIGN.md-only behaviour for prose-only brands.
|
||||
if (designSystemTokensCss && designSystemTokensCss.trim().length > 0) {
|
||||
parts.push(
|
||||
`\n\n## Active design system tokens${designSystemTitle ? ` — ${designSystemTitle}` : ''}\n\nThe block below is this brand's tokens.css contract — every \`:root\` custom property and any scoped override (e.g. \`:root[lang=...]\`) the brand defines. **Paste the unscoped \`:root { ... }\` block verbatim into the artifact's first \`<style>\`** so every \`var(--*)\` reference resolves at runtime.\n\nDo not invent new tokens. Do not redefine these values. Do not write raw hex outside this :root block. The DESIGN.md above is prose; this is the binding contract.\n\n\`\`\`css\n${designSystemTokensCss.trim()}\n\`\`\``,
|
||||
);
|
||||
}
|
||||
|
||||
if (designSystemFixtureHtml && designSystemFixtureHtml.trim().length > 0) {
|
||||
if (designSystemComponentsManifest && designSystemComponentsManifest.trim().length > 0) {
|
||||
parts.push(
|
||||
`\n\n## Reference component manifest${designSystemTitle ? ` — ${designSystemTitle}` : ''}\n\nA compact structured summary derived from this brand's components.html fixture. Use it as the component inventory for generated artifacts: match the listed selectors, component groups, class names, token references, focus behavior, and spacing cadence. Prefer these manifest entries over inventing new component shapes.\n\n\`\`\`text\n${designSystemComponentsManifest.trim()}\n\`\`\``,
|
||||
);
|
||||
} else if (designSystemFixtureHtml && designSystemFixtureHtml.trim().length > 0) {
|
||||
parts.push(
|
||||
`\n\n## Reference fixture${designSystemTitle ? ` — ${designSystemTitle}` : ''}\n\nA self-contained worked artifact in this design system. Match its component shapes (button structure, card structure, type-scale rhythm, focus ring, spacing cadence) when generating new artifacts. Copying fragments is encouraged as long as you keep the \`var(--*)\` references intact — they are already wired to the tokens above.\n\n\`\`\`html\n${designSystemFixtureHtml.trim()}\n\`\`\``,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7759,7 +7759,8 @@ export async function startServer({
|
|||
|
||||
let designSystemBody;
|
||||
let designSystemTitle;
|
||||
// Compiled (tokens.css + components.html) form of the active brand.
|
||||
// Compiled (tokens.css + components manifest / components.html)
|
||||
// form of the active brand.
|
||||
// Default-on as of PR-D — every chat that picks a brand with
|
||||
// `tokens.css` + `components.html` siblings (today: `default` and
|
||||
// `kami`; every other brand falls through silently because the
|
||||
|
|
@ -7772,6 +7773,7 @@ export async function startServer({
|
|||
// `true`, etc.) keeps the new default. Drift on prose-only brands
|
||||
// is pinned by `scripts/check-design-system-flag-parity.ts`.
|
||||
let designSystemTokensCss;
|
||||
let designSystemComponentsManifest;
|
||||
let designSystemFixtureHtml;
|
||||
if (effectiveDesignSystemId) {
|
||||
const systems = await listAllDesignSystems();
|
||||
|
|
@ -7791,6 +7793,7 @@ export async function startServer({
|
|||
USER_DESIGN_SYSTEMS_DIR,
|
||||
);
|
||||
designSystemTokensCss = assets.tokensCss;
|
||||
designSystemComponentsManifest = assets.componentsManifest;
|
||||
designSystemFixtureHtml = assets.fixtureHtml;
|
||||
}
|
||||
|
||||
|
|
@ -7946,6 +7949,7 @@ export async function startServer({
|
|||
designSystemBody,
|
||||
designSystemTitle,
|
||||
designSystemTokensCss,
|
||||
designSystemComponentsManifest,
|
||||
designSystemFixtureHtml,
|
||||
craftBody,
|
||||
craftSections,
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ describe('readDesignSystemAssets', () => {
|
|||
const assets = await readDesignSystemAssets(root, 'sample');
|
||||
expect(assets.tokensCss).toContain('--bg: #fff');
|
||||
expect(assets.fixtureHtml).toContain('fixture');
|
||||
expect(assets.componentsManifest).toContain('components.manifest schema v1 for sample');
|
||||
});
|
||||
|
||||
it('returns the single field that exists when its sibling is missing (per-file independence)', async () => {
|
||||
|
|
@ -60,6 +61,7 @@ describe('readDesignSystemAssets', () => {
|
|||
const fixtureOnly = await readDesignSystemAssets(root, 'fixture-only');
|
||||
expect(fixtureOnly.tokensCss).toBeUndefined();
|
||||
expect(fixtureOnly.fixtureHtml).toBe('<p>only</p>');
|
||||
expect(fixtureOnly.componentsManifest).toContain('components.manifest schema v1 for fixture-only');
|
||||
});
|
||||
|
||||
it('returns an empty object when the brand directory has neither file', async () => {
|
||||
|
|
@ -179,6 +181,7 @@ describe('resolveDesignSystemAssets (PR-D server-layer asset resolution)', () =>
|
|||
const assets = await resolveDesignSystemAssets('sample', builtInRoot, userRoot, {});
|
||||
expect(assets.tokensCss).toBe(':root { --bg: #fff; }');
|
||||
expect(assets.fixtureHtml).toBe('<button>btn</button>');
|
||||
expect(assets.componentsManifest).toContain('Buttons and calls to action');
|
||||
});
|
||||
|
||||
it('returns empty (kill switch) when OD_DESIGN_TOKEN_CHANNEL is `0`, even if files are on disk', async () => {
|
||||
|
|
@ -193,6 +196,7 @@ describe('resolveDesignSystemAssets (PR-D server-layer asset resolution)', () =>
|
|||
});
|
||||
expect(assets.tokensCss).toBeUndefined();
|
||||
expect(assets.fixtureHtml).toBeUndefined();
|
||||
expect(assets.componentsManifest).toBeUndefined();
|
||||
});
|
||||
|
||||
it('still returns the assets under the legacy explicit opt-in `OD_DESIGN_TOKEN_CHANNEL=1`', async () => {
|
||||
|
|
@ -207,6 +211,7 @@ describe('resolveDesignSystemAssets (PR-D server-layer asset resolution)', () =>
|
|||
});
|
||||
expect(assets.tokensCss).toContain('--bg: #fff');
|
||||
expect(assets.fixtureHtml).toContain('<button>');
|
||||
expect(assets.componentsManifest).toContain('Buttons and calls to action');
|
||||
});
|
||||
|
||||
it('falls back to user-installed root for files missing in built-in (per-file independence)', async () => {
|
||||
|
|
@ -220,6 +225,7 @@ describe('resolveDesignSystemAssets (PR-D server-layer asset resolution)', () =>
|
|||
const assets = await resolveDesignSystemAssets('split', builtInRoot, userRoot, {});
|
||||
expect(assets.tokensCss).toBe(':root { --bg: built-in; }');
|
||||
expect(assets.fixtureHtml).toBe('<from-user-installed/>');
|
||||
expect(assets.componentsManifest).toContain('components.manifest schema v1 for split');
|
||||
});
|
||||
|
||||
it('returns the built-in assets verbatim when both files are present built-in (skips the user-installed roundtrip)', async () => {
|
||||
|
|
@ -237,6 +243,7 @@ describe('resolveDesignSystemAssets (PR-D server-layer asset resolution)', () =>
|
|||
const assets = await resolveDesignSystemAssets('sample', builtInRoot, userRoot, {});
|
||||
expect(assets.tokensCss).toBe(':root { --bg: built-in; }');
|
||||
expect(assets.fixtureHtml).toBe('<from-built-in/>');
|
||||
expect(assets.componentsManifest).toContain('components.manifest schema v1 for sample');
|
||||
});
|
||||
|
||||
it('returns undefined for both fields when the brand ships neither file in either root (legacy ~138-brand fallback)', async () => {
|
||||
|
|
@ -247,6 +254,7 @@ describe('resolveDesignSystemAssets (PR-D server-layer asset resolution)', () =>
|
|||
const assets = await resolveDesignSystemAssets('prose-only', builtInRoot, userRoot, {});
|
||||
expect(assets.tokensCss).toBeUndefined();
|
||||
expect(assets.fixtureHtml).toBeUndefined();
|
||||
expect(assets.componentsManifest).toBeUndefined();
|
||||
});
|
||||
|
||||
it('returns undefined for both fields when the brand directory does not exist in either root', async () => {
|
||||
|
|
@ -256,5 +264,6 @@ describe('resolveDesignSystemAssets (PR-D server-layer asset resolution)', () =>
|
|||
const assets = await resolveDesignSystemAssets('nonexistent', builtInRoot, userRoot, {});
|
||||
expect(assets.tokensCss).toBeUndefined();
|
||||
expect(assets.fixtureHtml).toBeUndefined();
|
||||
expect(assets.componentsManifest).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -305,6 +305,8 @@ describe('composeSystemPrompt', () => {
|
|||
describe('design-system token + fixture injection (#PR-C)', () => {
|
||||
const sampleTokensCss = ':root {\n --bg: #ffffff;\n --fg: #111111;\n --accent: #0050d8;\n}';
|
||||
const sampleFixtureHtml = '<!doctype html>\n<html lang="en">\n <body><button class="btn btn-primary">Subscribe</button></body>\n</html>';
|
||||
const sampleComponentsManifest =
|
||||
'components.manifest schema v1 for default\nAvailable component groups:\n- Buttons and calls to action: selectors .btn, .btn-primary; tokens --accent';
|
||||
|
||||
it('appends BOTH a tokens block and a fixture block when both inputs are present', () => {
|
||||
const prompt = composeSystemPrompt({
|
||||
|
|
@ -323,6 +325,22 @@ describe('composeSystemPrompt', () => {
|
|||
expect(prompt).toContain('class="btn btn-primary"');
|
||||
});
|
||||
|
||||
it('prefers the component manifest over the full fixture when both are present', () => {
|
||||
const prompt = composeSystemPrompt({
|
||||
designSystemTitle: 'default',
|
||||
designSystemBody: '# Neutral Modern\n\n> Category: Utility\n\nProse description.',
|
||||
designSystemTokensCss: sampleTokensCss,
|
||||
designSystemComponentsManifest: sampleComponentsManifest,
|
||||
designSystemFixtureHtml: sampleFixtureHtml,
|
||||
});
|
||||
|
||||
expect(prompt).toContain('## Reference component manifest — default');
|
||||
expect(prompt).toContain('components.manifest schema v1 for default');
|
||||
expect(prompt).toContain('Buttons and calls to action');
|
||||
expect(prompt).not.toContain('## Reference fixture — default');
|
||||
expect(prompt).not.toContain('class="btn btn-primary"');
|
||||
});
|
||||
|
||||
it('keeps the prompt byte-equivalent to the legacy path when both inputs are omitted', () => {
|
||||
const baseline = composeSystemPrompt({
|
||||
designSystemTitle: 'default',
|
||||
|
|
@ -332,11 +350,13 @@ describe('composeSystemPrompt', () => {
|
|||
designSystemTitle: 'default',
|
||||
designSystemBody: '# Neutral Modern\n\nProse only.',
|
||||
designSystemTokensCss: undefined,
|
||||
designSystemComponentsManifest: undefined,
|
||||
designSystemFixtureHtml: undefined,
|
||||
});
|
||||
|
||||
expect(withFlagOffEquivalent).toBe(baseline);
|
||||
expect(withFlagOffEquivalent).not.toContain('## Active design system tokens');
|
||||
expect(withFlagOffEquivalent).not.toContain('## Reference component manifest');
|
||||
expect(withFlagOffEquivalent).not.toContain('## Reference fixture');
|
||||
});
|
||||
|
||||
|
|
@ -356,18 +376,27 @@ describe('composeSystemPrompt', () => {
|
|||
});
|
||||
expect(fixtureOnly).not.toContain('## Active design system tokens');
|
||||
expect(fixtureOnly).toContain('## Reference fixture — default');
|
||||
|
||||
const manifestOnly = composeSystemPrompt({
|
||||
designSystemTitle: 'default',
|
||||
designSystemBody: '# x\n\nbody',
|
||||
designSystemComponentsManifest: sampleComponentsManifest,
|
||||
});
|
||||
expect(manifestOnly).not.toContain('## Active design system tokens');
|
||||
expect(manifestOnly).toContain('## Reference component manifest — default');
|
||||
});
|
||||
|
||||
it('places the tokens + fixture blocks AFTER the DESIGN.md prose block (prose sets voice, structured form binds names)', () => {
|
||||
it('places the tokens + component manifest blocks AFTER the DESIGN.md prose block (prose sets voice, structured form binds names)', () => {
|
||||
const prompt = composeSystemPrompt({
|
||||
designSystemTitle: 'default',
|
||||
designSystemBody: 'PROSE_BODY_MARKER',
|
||||
designSystemTokensCss: sampleTokensCss,
|
||||
designSystemComponentsManifest: sampleComponentsManifest,
|
||||
designSystemFixtureHtml: sampleFixtureHtml,
|
||||
});
|
||||
const proseAt = prompt.indexOf('PROSE_BODY_MARKER');
|
||||
const tokensAt = prompt.indexOf('## Active design system tokens');
|
||||
const fixtureAt = prompt.indexOf('## Reference fixture');
|
||||
const fixtureAt = prompt.indexOf('## Reference component manifest');
|
||||
expect(proseAt).toBeGreaterThan(0);
|
||||
expect(tokensAt).toBeGreaterThan(proseAt);
|
||||
expect(fixtureAt).toBeGreaterThan(tokensAt);
|
||||
|
|
@ -378,9 +407,11 @@ describe('composeSystemPrompt', () => {
|
|||
designSystemTitle: 'default',
|
||||
designSystemBody: '# x\n\nbody',
|
||||
designSystemTokensCss: ' \n \t ',
|
||||
designSystemComponentsManifest: '\n\t',
|
||||
designSystemFixtureHtml: '\n\n',
|
||||
});
|
||||
expect(prompt).not.toContain('## Active design system tokens');
|
||||
expect(prompt).not.toContain('## Reference component manifest');
|
||||
expect(prompt).not.toContain('## Reference fixture');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
109
scripts/check-components-manifest-extraction.ts
Normal file
109
scripts/check-components-manifest-extraction.ts
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import { readFile, readdir } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import {
|
||||
extractComponentsManifest,
|
||||
summarizeComponentsManifestForPrompt,
|
||||
} from '../packages/contracts/src/design-systems/components-manifest.ts';
|
||||
|
||||
const repoRoot = path.resolve(import.meta.dirname, '..');
|
||||
const designSystemsRoot = path.join(repoRoot, 'design-systems');
|
||||
const skippedDesignSystemDirectories = new Set(['_schema']);
|
||||
|
||||
type BrandSources = {
|
||||
id: string;
|
||||
fixturePath: string;
|
||||
tokensCss: string;
|
||||
fixtureHtml: string;
|
||||
};
|
||||
|
||||
export async function checkComponentsManifestExtraction(): Promise<boolean> {
|
||||
const sources = await discoverBrandSources();
|
||||
const violations: string[] = [];
|
||||
let selectorCount = 0;
|
||||
let groupCount = 0;
|
||||
|
||||
for (const source of sources) {
|
||||
try {
|
||||
const manifest = extractComponentsManifest({
|
||||
brandId: source.id,
|
||||
fixtureHtml: source.fixtureHtml,
|
||||
tokensCss: source.tokensCss,
|
||||
});
|
||||
const summary = summarizeComponentsManifestForPrompt(manifest);
|
||||
|
||||
selectorCount += manifest.fixture.selectorCount;
|
||||
groupCount += manifest.groups.filter((group) => group.present).length;
|
||||
|
||||
if (manifest.fixture.styleBlockCount === 0) {
|
||||
violations.push(`[${source.id}] ${toRepositoryPath(source.fixturePath)} has no <style> blocks to summarize.`);
|
||||
}
|
||||
if (manifest.fixture.selectorCount === 0) {
|
||||
violations.push(`[${source.id}] ${toRepositoryPath(source.fixturePath)} produced a manifest with zero CSS selectors.`);
|
||||
}
|
||||
if (!summary.includes(`components.manifest schema v${manifest.schemaVersion} for ${source.id}`)) {
|
||||
violations.push(`[${source.id}] manifest summary is missing its schema/id header.`);
|
||||
}
|
||||
} catch (err) {
|
||||
violations.push(
|
||||
`[${source.id}] failed to extract manifest from ${toRepositoryPath(source.fixturePath)}: ${
|
||||
err instanceof Error ? err.message : String(err)
|
||||
}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (violations.length > 0) {
|
||||
console.error('Design system component manifest extraction violations:');
|
||||
for (const violation of violations) {
|
||||
console.error(`- ${violation}`);
|
||||
}
|
||||
console.error('Every compiled design system must remain consumable through the structured component manifest path.');
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Design system component manifest extraction passed: ${sources.length} fixtures summarized (${selectorCount} selectors across ${groupCount} present component groups).`,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function discoverBrandSources(): Promise<BrandSources[]> {
|
||||
const entries = await readdir(designSystemsRoot, { withFileTypes: true });
|
||||
const sources: BrandSources[] = [];
|
||||
|
||||
for (const entry of entries) {
|
||||
if (!entry.isDirectory() || skippedDesignSystemDirectories.has(entry.name)) continue;
|
||||
|
||||
const brandRoot = path.join(designSystemsRoot, entry.name);
|
||||
const tokensPath = path.join(brandRoot, 'tokens.css');
|
||||
const fixturePath = path.join(brandRoot, 'components.html');
|
||||
const [tokensCss, fixtureHtml] = await Promise.all([
|
||||
readFile(tokensPath, 'utf8'),
|
||||
readFile(fixturePath, 'utf8'),
|
||||
]);
|
||||
sources.push({
|
||||
id: entry.name,
|
||||
fixturePath,
|
||||
tokensCss,
|
||||
fixtureHtml,
|
||||
});
|
||||
}
|
||||
|
||||
return sources.sort((a, b) => a.id.localeCompare(b.id));
|
||||
}
|
||||
|
||||
function toRepositoryPath(filePath: string): string {
|
||||
return path.relative(repoRoot, filePath).split(path.sep).join('/');
|
||||
}
|
||||
|
||||
const isMain = process.argv[1] === fileURLToPath(import.meta.url);
|
||||
if (isMain) {
|
||||
checkComponentsManifestExtraction().then((passed) => {
|
||||
process.exitCode = passed ? 0 : 1;
|
||||
}, (err: unknown) => {
|
||||
console.error(err);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
}
|
||||
|
|
@ -182,6 +182,7 @@ export async function checkDesignSystemFlagParity(): Promise<boolean> {
|
|||
designSystemBody: brand.designMd,
|
||||
designSystemTitle: brand.title,
|
||||
designSystemTokensCss: brand.assets.tokensCss,
|
||||
designSystemComponentsManifest: brand.assets.componentsManifest,
|
||||
designSystemFixtureHtml: brand.assets.fixtureHtml,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import path from "node:path";
|
|||
|
||||
import { checkDesignSystemComponentFixtureReport } from "./check-components-fixtures.ts";
|
||||
import { checkDesignSystemFlagParity } from "./check-design-system-flag-parity.ts";
|
||||
import { checkComponentsManifestExtraction } from "./check-components-manifest-extraction.ts";
|
||||
import {
|
||||
checkDesignSystemA1RequiredTokens,
|
||||
checkDesignSystemA2DefaultsParity,
|
||||
|
|
@ -713,6 +714,7 @@ const checks: GuardCheck[] = [
|
|||
{ name: "design system unknown token allowlist", run: checkDesignSystemUnknownTokens },
|
||||
{ name: "design system A2 defaults parity", run: checkDesignSystemA2DefaultsParity },
|
||||
{ name: "design system flag parity", run: checkDesignSystemFlagParity },
|
||||
{ name: "design system component manifest extraction", run: checkComponentsManifestExtraction },
|
||||
];
|
||||
|
||||
const results: boolean[] = [];
|
||||
|
|
|
|||
Loading…
Reference in a new issue