@fragments-sdk/cli 0.5.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +996 -79
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-ICAIQ57V.js → chunk-6JBGU74P.js} +5 -3
- package/dist/chunk-6JBGU74P.js.map +1 -0
- package/dist/chunk-7OPWMLOE.js +1625 -0
- package/dist/chunk-7OPWMLOE.js.map +1 -0
- package/dist/{chunk-2H2JAA3U.js → chunk-CVXKXVOY.js} +3 -3
- package/dist/{chunk-2H2JAA3U.js.map → chunk-CVXKXVOY.js.map} +1 -1
- package/dist/{chunk-IOJE35DZ.js → chunk-NWQ4CJOQ.js} +3 -3
- package/dist/{chunk-2DJH4F4P.js → chunk-RVRTRESS.js} +3 -3
- package/dist/{chunk-V7YLRR4C.js → chunk-TJ34N7C7.js} +41 -4
- package/dist/{chunk-V7YLRR4C.js.map → chunk-TJ34N7C7.js.map} +1 -1
- package/dist/{chunk-XNWDI6UT.js → chunk-XHUDJNN3.js} +5 -5
- package/dist/{core-DKHB7FYV.js → core-W2HYIQW6.js} +4 -4
- package/dist/{generate-KL24VZVD.js → generate-LMTISDIJ.js} +5 -5
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15 -7
- package/dist/index.js.map +1 -1
- package/dist/{init-NION5S3M.js → init-7CHRKQ7P.js} +5 -5
- package/dist/mcp-bin.js +8 -220
- package/dist/mcp-bin.js.map +1 -1
- package/dist/scan-WY23TJCP.js +12 -0
- package/dist/{service-RWUMZ3EW.js → service-T2L7VLTE.js} +5 -5
- package/dist/static-viewer-GBR7YNF3.js +12 -0
- package/dist/{test-ECPEXFDN.js → test-OJRXNDO2.js} +4 -4
- package/dist/{tokens-ITADYVPF.js → tokens-3BWDESVM.js} +6 -6
- package/dist/viewer-SUFOISZM.js +1822 -0
- package/dist/viewer-SUFOISZM.js.map +1 -0
- package/package.json +6 -5
- package/src/bin.ts +31 -0
- package/src/build.ts +147 -13
- package/src/cli-commands.ts +18 -0
- package/src/commands/__tests__/a11y-scoring.test.ts +278 -0
- package/src/commands/a11y-report.ts +625 -0
- package/src/commands/a11y.ts +168 -14
- package/src/commands/build.ts +16 -0
- package/src/commands/graph.ts +274 -0
- package/src/core/auto-props.ts +464 -0
- package/src/core/composition.ts +64 -1
- package/src/core/graph-extractor.test.ts +542 -0
- package/src/core/graph-extractor.ts +601 -0
- package/src/core/importAnalyzer.ts +5 -0
- package/src/core/schema.ts +2 -0
- package/src/core/types.ts +3 -1
- package/src/index.ts +4 -0
- package/src/mcp/server.ts +13 -220
- package/src/theme/__tests__/component-contrast.test.ts +338 -0
- package/src/theme/__tests__/contrast-validation.test.ts +326 -0
- package/src/theme/contrast.test.ts +331 -0
- package/src/theme/contrast.ts +246 -0
- package/src/theme/generator.ts +213 -1
- package/src/theme/index.ts +16 -0
- package/src/theme/types.ts +51 -0
- package/src/viewer/__tests__/a11y-fixes.test.ts +358 -0
- package/src/viewer/__tests__/viewer-integration.test.ts +2 -7
- package/src/viewer/components/AccessibilityPanel.tsx +493 -433
- package/src/viewer/components/ActionCapture.tsx +1 -1
- package/src/viewer/components/ActionsPanel.tsx +142 -183
- package/src/viewer/components/App.tsx +276 -183
- package/src/viewer/components/BottomPanel.tsx +40 -80
- package/src/viewer/components/CodePanel.tsx +9 -87
- package/src/viewer/components/CommandPalette.tsx +117 -74
- package/src/viewer/components/ComponentGraph.tsx +143 -126
- package/src/viewer/components/ComponentHeader.tsx +46 -43
- package/src/viewer/components/ContractPanel.tsx +124 -117
- package/src/viewer/components/ErrorBoundary.tsx +47 -35
- package/src/viewer/components/FigmaEmbed.tsx +18 -13
- package/src/viewer/components/FragmentEditor.tsx +126 -63
- package/src/viewer/components/HealthDashboard.tsx +146 -171
- package/src/viewer/components/HmrStatusIndicator.tsx +31 -41
- package/src/viewer/components/Icons.tsx +151 -98
- package/src/viewer/components/InteractionsPanel.tsx +317 -264
- package/src/viewer/components/IsolatedPreviewFrame.tsx +52 -27
- package/src/viewer/components/IsolatedRender.tsx +12 -6
- package/src/viewer/components/KeyboardShortcutsHelp.tsx +34 -70
- package/src/viewer/components/LandingPage.tsx +285 -305
- package/src/viewer/components/Layout.tsx +12 -10
- package/src/viewer/components/LeftSidebar.tsx +103 -155
- package/src/viewer/components/MultiViewportPreview.tsx +254 -63
- package/src/viewer/components/PreviewArea.tsx +113 -44
- package/src/viewer/components/PreviewFrameHost.tsx +36 -6
- package/src/viewer/components/PreviewPane.tsx +2 -3
- package/src/viewer/components/PreviewToolbar.tsx +109 -105
- package/src/viewer/components/PropsEditor.tsx +154 -74
- package/src/viewer/components/PropsTable.tsx +95 -82
- package/src/viewer/components/RelationsSection.tsx +71 -40
- package/src/viewer/components/ResizablePanel.tsx +158 -55
- package/src/viewer/components/RightSidebar.tsx +46 -56
- package/src/viewer/components/ScreenshotButton.tsx +12 -12
- package/src/viewer/components/SkeletonLoader.tsx +99 -83
- package/src/viewer/components/StoryRenderer.tsx +4 -11
- package/src/viewer/components/Toast.tsx +3 -67
- package/src/viewer/components/TokenStylePanel.tsx +136 -118
- package/src/viewer/components/UsageSection.tsx +26 -26
- package/src/viewer/components/VariantMatrix.tsx +140 -47
- package/src/viewer/components/VariantTabs.tsx +24 -68
- package/src/viewer/components/ViewportSelector.tsx +121 -114
- package/src/viewer/constants/ui.ts +23 -22
- package/src/viewer/entry.tsx +8 -3
- package/src/viewer/index.ts +3 -6
- package/src/viewer/preview-frame.html +43 -18
- package/src/viewer/server.ts +7 -16
- package/src/viewer/styles/globals.css +46 -85
- package/src/viewer/utils/a11y-fixes.ts +53 -30
- package/dist/chunk-ICAIQ57V.js.map +0 -1
- package/dist/chunk-U4GQ2JTD.js +0 -832
- package/dist/chunk-U4GQ2JTD.js.map +0 -1
- package/dist/scan-ESEXV7LF.js +0 -12
- package/dist/static-viewer-O37MJ5B6.js +0 -12
- package/dist/viewer-YDGFDTK5.js +0 -11104
- package/dist/viewer-YDGFDTK5.js.map +0 -1
- package/src/viewer/postcss.config.js +0 -6
- package/src/viewer/tailwind.config.js +0 -37
- /package/dist/{chunk-IOJE35DZ.js.map → chunk-NWQ4CJOQ.js.map} +0 -0
- /package/dist/{chunk-2DJH4F4P.js.map → chunk-RVRTRESS.js.map} +0 -0
- /package/dist/{chunk-XNWDI6UT.js.map → chunk-XHUDJNN3.js.map} +0 -0
- /package/dist/{core-DKHB7FYV.js.map → core-W2HYIQW6.js.map} +0 -0
- /package/dist/{generate-KL24VZVD.js.map → generate-LMTISDIJ.js.map} +0 -0
- /package/dist/{init-NION5S3M.js.map → init-7CHRKQ7P.js.map} +0 -0
- /package/dist/{scan-ESEXV7LF.js.map → scan-WY23TJCP.js.map} +0 -0
- /package/dist/{service-RWUMZ3EW.js.map → service-T2L7VLTE.js.map} +0 -0
- /package/dist/{static-viewer-O37MJ5B6.js.map → static-viewer-GBR7YNF3.js.map} +0 -0
- /package/dist/{test-ECPEXFDN.js.map → test-OJRXNDO2.js.map} +0 -0
- /package/dist/{tokens-ITADYVPF.js.map → tokens-3BWDESVM.js.map} +0 -0
package/dist/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { createRequire } from 'module'; const require =
|
|
2
|
+
import { createRequire as __banner_createRequire } from 'module'; const require = __banner_createRequire(import.meta.url);
|
|
3
3
|
import {
|
|
4
4
|
buildFragmentsDir,
|
|
5
5
|
buildSegments,
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
validateAll,
|
|
10
10
|
validateCoverage,
|
|
11
11
|
validateSchema
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-7OPWMLOE.js";
|
|
13
13
|
import {
|
|
14
14
|
scan
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-XHUDJNN3.js";
|
|
16
16
|
import {
|
|
17
17
|
FigmaClient,
|
|
18
18
|
StorageManager,
|
|
@@ -28,22 +28,22 @@ import {
|
|
|
28
28
|
renderAllComponentVariants,
|
|
29
29
|
scanCodebase,
|
|
30
30
|
shutdownSharedPool
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-NWQ4CJOQ.js";
|
|
32
32
|
import {
|
|
33
33
|
discoverSegmentFiles,
|
|
34
34
|
loadConfig,
|
|
35
35
|
loadSegmentFile
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-CVXKXVOY.js";
|
|
37
37
|
import {
|
|
38
38
|
generateContext
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-TJ34N7C7.js";
|
|
40
40
|
import {
|
|
41
41
|
BRAND
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-6JBGU74P.js";
|
|
43
43
|
|
|
44
44
|
// src/bin.ts
|
|
45
45
|
import { Command } from "commander";
|
|
46
|
-
import
|
|
46
|
+
import pc21 from "picocolors";
|
|
47
47
|
import { readFileSync } from "fs";
|
|
48
48
|
import { fileURLToPath } from "url";
|
|
49
49
|
import { dirname as dirname4, join as join10 } from "path";
|
|
@@ -145,6 +145,13 @@ ${BRAND.name} Build
|
|
|
145
145
|
}
|
|
146
146
|
console.log();
|
|
147
147
|
}
|
|
148
|
+
if (result.warnings.length > 0) {
|
|
149
|
+
console.log(pc2.yellow("Build warnings:\n"));
|
|
150
|
+
for (const warning of result.warnings) {
|
|
151
|
+
console.log(` ${pc2.yellow("\u26A0")} ${warning.file}: ${warning.warning}`);
|
|
152
|
+
}
|
|
153
|
+
console.log();
|
|
154
|
+
}
|
|
148
155
|
segmentCount = result.segmentCount;
|
|
149
156
|
outputPath = result.outputPath;
|
|
150
157
|
console.log(pc2.green(`\u2713 Built ${result.segmentCount} fragment(s)`));
|
|
@@ -162,6 +169,13 @@ ${BRAND.name} Build
|
|
|
162
169
|
}
|
|
163
170
|
console.log();
|
|
164
171
|
}
|
|
172
|
+
if (fragmentsResult.warnings.length > 0) {
|
|
173
|
+
console.log(pc2.yellow("Registry warnings:\n"));
|
|
174
|
+
for (const warning of fragmentsResult.warnings) {
|
|
175
|
+
console.log(` ${pc2.yellow("\u26A0")} ${warning.file}: ${warning.warning}`);
|
|
176
|
+
}
|
|
177
|
+
console.log();
|
|
178
|
+
}
|
|
165
179
|
componentCount = fragmentsResult.componentCount;
|
|
166
180
|
registryPath = fragmentsResult.registryPath;
|
|
167
181
|
contextPath = fragmentsResult.contextPath;
|
|
@@ -1614,19 +1628,19 @@ async function discoverStoryFiles(projectRoot, patterns) {
|
|
|
1614
1628
|
|
|
1615
1629
|
// src/setup.ts
|
|
1616
1630
|
async function isSegmentsJsonStale(configDir, outFile) {
|
|
1617
|
-
const
|
|
1631
|
+
const fs2 = await import("fs/promises");
|
|
1618
1632
|
const path = await import("path");
|
|
1619
1633
|
const fg4 = await import("fast-glob");
|
|
1620
1634
|
const segmentsJsonPath = path.join(configDir, outFile);
|
|
1621
1635
|
try {
|
|
1622
|
-
const segmentsJsonStat = await
|
|
1636
|
+
const segmentsJsonStat = await fs2.stat(segmentsJsonPath);
|
|
1623
1637
|
const segmentFiles = await fg4.default(`**/*${BRAND.fileExtension}`, {
|
|
1624
1638
|
cwd: configDir,
|
|
1625
1639
|
ignore: ["**/node_modules/**"],
|
|
1626
1640
|
absolute: true
|
|
1627
1641
|
});
|
|
1628
1642
|
for (const file of segmentFiles) {
|
|
1629
|
-
const stat3 = await
|
|
1643
|
+
const stat3 = await fs2.stat(file);
|
|
1630
1644
|
if (stat3.mtimeMs > segmentsJsonStat.mtimeMs) {
|
|
1631
1645
|
return { stale: true, missing: false };
|
|
1632
1646
|
}
|
|
@@ -1637,11 +1651,11 @@ async function isSegmentsJsonStale(configDir, outFile) {
|
|
|
1637
1651
|
}
|
|
1638
1652
|
}
|
|
1639
1653
|
async function loadSegmentInfo(segmentFiles) {
|
|
1640
|
-
const
|
|
1654
|
+
const fs2 = await import("fs/promises");
|
|
1641
1655
|
const segments = [];
|
|
1642
1656
|
for (const file of segmentFiles) {
|
|
1643
1657
|
try {
|
|
1644
|
-
const content = await
|
|
1658
|
+
const content = await fs2.readFile(file.absolutePath, "utf-8");
|
|
1645
1659
|
const nameMatch = content.match(/name:\s*['"]([^'"]+)['"]/);
|
|
1646
1660
|
const hasFigma = /meta:\s*\{[^}]*figma:\s*['"]https?:/.test(content);
|
|
1647
1661
|
if (nameMatch) {
|
|
@@ -1657,7 +1671,7 @@ async function loadSegmentInfo(segmentFiles) {
|
|
|
1657
1671
|
return segments;
|
|
1658
1672
|
}
|
|
1659
1673
|
async function runSetup(options = {}) {
|
|
1660
|
-
const
|
|
1674
|
+
const fs2 = await import("fs/promises");
|
|
1661
1675
|
const path = await import("path");
|
|
1662
1676
|
const result = {
|
|
1663
1677
|
segmentFilesCreated: 0,
|
|
@@ -1685,8 +1699,8 @@ async function runSetup(options = {}) {
|
|
|
1685
1699
|
try {
|
|
1686
1700
|
const parsed = await parseStoryFile(storyFile);
|
|
1687
1701
|
const segmentResult = convertToSegment(parsed);
|
|
1688
|
-
await
|
|
1689
|
-
await
|
|
1702
|
+
await fs2.mkdir(path.dirname(segmentResult.outputFile), { recursive: true });
|
|
1703
|
+
await fs2.writeFile(segmentResult.outputFile, segmentResult.code);
|
|
1690
1704
|
converted++;
|
|
1691
1705
|
} catch {
|
|
1692
1706
|
}
|
|
@@ -1771,7 +1785,7 @@ ${BRAND.name} Dev Server
|
|
|
1771
1785
|
}
|
|
1772
1786
|
}
|
|
1773
1787
|
}
|
|
1774
|
-
const { createDevServer } = await import("./viewer-
|
|
1788
|
+
const { createDevServer } = await import("./viewer-SUFOISZM.js");
|
|
1775
1789
|
console.log(pc7.dim("\nStarting dev server..."));
|
|
1776
1790
|
const parsedPort = typeof port === "string" ? parseInt(port, 10) : port;
|
|
1777
1791
|
try {
|
|
@@ -2402,13 +2416,636 @@ ${BRAND.name} Design System Audit
|
|
|
2402
2416
|
}
|
|
2403
2417
|
|
|
2404
2418
|
// src/commands/a11y.ts
|
|
2419
|
+
import fs from "fs";
|
|
2405
2420
|
import pc12 from "picocolors";
|
|
2421
|
+
|
|
2422
|
+
// src/commands/a11y-report.ts
|
|
2423
|
+
function generateA11yReport(summary) {
|
|
2424
|
+
const score = summary.score ?? calculateA11yScore(summary);
|
|
2425
|
+
return `<!DOCTYPE html>
|
|
2426
|
+
<html lang="en">
|
|
2427
|
+
<head>
|
|
2428
|
+
<meta charset="UTF-8">
|
|
2429
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2430
|
+
<title>${BRAND.name} Accessibility Report</title>
|
|
2431
|
+
<style>
|
|
2432
|
+
${getStyles()}
|
|
2433
|
+
</style>
|
|
2434
|
+
</head>
|
|
2435
|
+
<body>
|
|
2436
|
+
<div class="container">
|
|
2437
|
+
${renderHeader(score)}
|
|
2438
|
+
${renderSummaryCards(summary, score)}
|
|
2439
|
+
${renderComponentGrid(summary.components)}
|
|
2440
|
+
${renderFooter()}
|
|
2441
|
+
</div>
|
|
2442
|
+
<script>
|
|
2443
|
+
${getScripts()}
|
|
2444
|
+
</script>
|
|
2445
|
+
</body>
|
|
2446
|
+
</html>`;
|
|
2447
|
+
}
|
|
2448
|
+
function getStyles() {
|
|
2449
|
+
return `
|
|
2450
|
+
:root {
|
|
2451
|
+
--bg: #0a0a0a;
|
|
2452
|
+
--bg-card: #141414;
|
|
2453
|
+
--bg-hover: #1a1a1a;
|
|
2454
|
+
--border: #262626;
|
|
2455
|
+
--text: #f2f2f2;
|
|
2456
|
+
--text-secondary: #a1a1aa;
|
|
2457
|
+
--text-muted: #71717a;
|
|
2458
|
+
--accent: #10b981;
|
|
2459
|
+
--accent-light: #34d399;
|
|
2460
|
+
--danger: #ef4444;
|
|
2461
|
+
--warning: #eab308;
|
|
2462
|
+
--success: #22c55e;
|
|
2463
|
+
--radius: 12px;
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
@media (prefers-color-scheme: light) {
|
|
2467
|
+
:root:not(.dark) {
|
|
2468
|
+
--bg: #f8f8f8;
|
|
2469
|
+
--bg-card: #ffffff;
|
|
2470
|
+
--bg-hover: #f0f0f0;
|
|
2471
|
+
--border: #e0e0e0;
|
|
2472
|
+
--text: #171717;
|
|
2473
|
+
--text-secondary: #525252;
|
|
2474
|
+
--text-muted: #737373;
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
.dark {
|
|
2479
|
+
--bg: #0a0a0a;
|
|
2480
|
+
--bg-card: #141414;
|
|
2481
|
+
--bg-hover: #1a1a1a;
|
|
2482
|
+
--border: #262626;
|
|
2483
|
+
--text: #f2f2f2;
|
|
2484
|
+
--text-secondary: #a1a1aa;
|
|
2485
|
+
--text-muted: #71717a;
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
.light {
|
|
2489
|
+
--bg: #f8f8f8;
|
|
2490
|
+
--bg-card: #ffffff;
|
|
2491
|
+
--bg-hover: #f0f0f0;
|
|
2492
|
+
--border: #e0e0e0;
|
|
2493
|
+
--text: #171717;
|
|
2494
|
+
--text-secondary: #525252;
|
|
2495
|
+
--text-muted: #737373;
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
* {
|
|
2499
|
+
margin: 0;
|
|
2500
|
+
padding: 0;
|
|
2501
|
+
box-sizing: border-box;
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2504
|
+
body {
|
|
2505
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
2506
|
+
background: var(--bg);
|
|
2507
|
+
color: var(--text);
|
|
2508
|
+
line-height: 1.6;
|
|
2509
|
+
-webkit-font-smoothing: antialiased;
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
.container {
|
|
2513
|
+
max-width: 1200px;
|
|
2514
|
+
margin: 0 auto;
|
|
2515
|
+
padding: 48px 24px;
|
|
2516
|
+
}
|
|
2517
|
+
|
|
2518
|
+
/* Header */
|
|
2519
|
+
.header {
|
|
2520
|
+
display: flex;
|
|
2521
|
+
justify-content: space-between;
|
|
2522
|
+
align-items: flex-start;
|
|
2523
|
+
margin-bottom: 48px;
|
|
2524
|
+
padding-bottom: 32px;
|
|
2525
|
+
border-bottom: 1px solid var(--border);
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2528
|
+
.header-left h1 {
|
|
2529
|
+
font-size: 32px;
|
|
2530
|
+
font-weight: 700;
|
|
2531
|
+
margin-bottom: 8px;
|
|
2532
|
+
background: linear-gradient(135deg, var(--text), var(--accent));
|
|
2533
|
+
-webkit-background-clip: text;
|
|
2534
|
+
-webkit-text-fill-color: transparent;
|
|
2535
|
+
}
|
|
2536
|
+
|
|
2537
|
+
.header-left p {
|
|
2538
|
+
color: var(--text-secondary);
|
|
2539
|
+
font-size: 14px;
|
|
2540
|
+
}
|
|
2541
|
+
|
|
2542
|
+
.header-right {
|
|
2543
|
+
display: flex;
|
|
2544
|
+
align-items: flex-start;
|
|
2545
|
+
gap: 16px;
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
.theme-toggle {
|
|
2549
|
+
background: var(--bg-card);
|
|
2550
|
+
border: 1px solid var(--border);
|
|
2551
|
+
border-radius: 8px;
|
|
2552
|
+
color: var(--text-secondary);
|
|
2553
|
+
cursor: pointer;
|
|
2554
|
+
padding: 8px 12px;
|
|
2555
|
+
font-size: 14px;
|
|
2556
|
+
line-height: 1;
|
|
2557
|
+
}
|
|
2558
|
+
|
|
2559
|
+
.theme-toggle:hover {
|
|
2560
|
+
border-color: var(--accent);
|
|
2561
|
+
color: var(--text);
|
|
2562
|
+
}
|
|
2563
|
+
|
|
2564
|
+
.grade-badge {
|
|
2565
|
+
display: flex;
|
|
2566
|
+
flex-direction: column;
|
|
2567
|
+
align-items: center;
|
|
2568
|
+
padding: 24px 32px;
|
|
2569
|
+
background: var(--bg-card);
|
|
2570
|
+
border: 1px solid var(--border);
|
|
2571
|
+
border-radius: var(--radius);
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2574
|
+
.grade-score-large {
|
|
2575
|
+
font-size: 56px;
|
|
2576
|
+
font-weight: 800;
|
|
2577
|
+
line-height: 1;
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
.grade-label {
|
|
2581
|
+
font-size: 14px;
|
|
2582
|
+
color: var(--text-secondary);
|
|
2583
|
+
margin-top: 8px;
|
|
2584
|
+
}
|
|
2585
|
+
|
|
2586
|
+
/* Summary Cards */
|
|
2587
|
+
.summary-grid {
|
|
2588
|
+
display: grid;
|
|
2589
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
2590
|
+
gap: 16px;
|
|
2591
|
+
margin-bottom: 48px;
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
.summary-card {
|
|
2595
|
+
background: var(--bg-card);
|
|
2596
|
+
border: 1px solid var(--border);
|
|
2597
|
+
border-radius: var(--radius);
|
|
2598
|
+
padding: 24px;
|
|
2599
|
+
}
|
|
2600
|
+
|
|
2601
|
+
.summary-card-label {
|
|
2602
|
+
font-size: 13px;
|
|
2603
|
+
color: var(--text-muted);
|
|
2604
|
+
text-transform: uppercase;
|
|
2605
|
+
letter-spacing: 0.05em;
|
|
2606
|
+
margin-bottom: 8px;
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
.summary-card-value {
|
|
2610
|
+
font-size: 36px;
|
|
2611
|
+
font-weight: 700;
|
|
2612
|
+
}
|
|
2613
|
+
|
|
2614
|
+
.summary-card-sub {
|
|
2615
|
+
font-size: 13px;
|
|
2616
|
+
color: var(--text-secondary);
|
|
2617
|
+
margin-top: 4px;
|
|
2618
|
+
}
|
|
2619
|
+
|
|
2620
|
+
/* Section */
|
|
2621
|
+
.section {
|
|
2622
|
+
margin-bottom: 48px;
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2625
|
+
.section-title {
|
|
2626
|
+
font-size: 20px;
|
|
2627
|
+
font-weight: 600;
|
|
2628
|
+
margin-bottom: 24px;
|
|
2629
|
+
display: flex;
|
|
2630
|
+
align-items: center;
|
|
2631
|
+
gap: 12px;
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
.section-title::before {
|
|
2635
|
+
content: '';
|
|
2636
|
+
width: 4px;
|
|
2637
|
+
height: 24px;
|
|
2638
|
+
background: var(--accent);
|
|
2639
|
+
border-radius: 2px;
|
|
2640
|
+
}
|
|
2641
|
+
|
|
2642
|
+
/* Component Grid */
|
|
2643
|
+
.component-grid {
|
|
2644
|
+
display: grid;
|
|
2645
|
+
grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
|
|
2646
|
+
gap: 16px;
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2649
|
+
.component-card {
|
|
2650
|
+
background: var(--bg-card);
|
|
2651
|
+
border: 1px solid var(--border);
|
|
2652
|
+
border-radius: var(--radius);
|
|
2653
|
+
padding: 20px;
|
|
2654
|
+
transition: border-color 0.2s;
|
|
2655
|
+
}
|
|
2656
|
+
|
|
2657
|
+
.component-card:hover {
|
|
2658
|
+
border-color: var(--accent);
|
|
2659
|
+
}
|
|
2660
|
+
|
|
2661
|
+
.component-card-header {
|
|
2662
|
+
display: flex;
|
|
2663
|
+
justify-content: space-between;
|
|
2664
|
+
align-items: center;
|
|
2665
|
+
margin-bottom: 12px;
|
|
2666
|
+
}
|
|
2667
|
+
|
|
2668
|
+
.component-name {
|
|
2669
|
+
font-weight: 600;
|
|
2670
|
+
font-size: 16px;
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
.status-badge {
|
|
2674
|
+
font-size: 12px;
|
|
2675
|
+
font-weight: 700;
|
|
2676
|
+
padding: 3px 10px;
|
|
2677
|
+
border-radius: 6px;
|
|
2678
|
+
text-transform: uppercase;
|
|
2679
|
+
letter-spacing: 0.03em;
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
.status-pass { background: rgba(34, 197, 94, 0.15); color: #22c55e; }
|
|
2683
|
+
.status-warn { background: rgba(234, 179, 8, 0.15); color: #eab308; }
|
|
2684
|
+
.status-fail { background: rgba(239, 68, 68, 0.15); color: #ef4444; }
|
|
2685
|
+
|
|
2686
|
+
.component-stats {
|
|
2687
|
+
display: flex;
|
|
2688
|
+
gap: 16px;
|
|
2689
|
+
font-size: 13px;
|
|
2690
|
+
color: var(--text-secondary);
|
|
2691
|
+
margin-bottom: 8px;
|
|
2692
|
+
}
|
|
2693
|
+
|
|
2694
|
+
.severity-bar {
|
|
2695
|
+
display: flex;
|
|
2696
|
+
gap: 8px;
|
|
2697
|
+
flex-wrap: wrap;
|
|
2698
|
+
margin-bottom: 12px;
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2701
|
+
.severity-chip {
|
|
2702
|
+
font-size: 12px;
|
|
2703
|
+
padding: 3px 10px;
|
|
2704
|
+
border-radius: 6px;
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
.severity-critical { background: rgba(239, 68, 68, 0.15); color: #ef4444; }
|
|
2708
|
+
.severity-serious { background: rgba(239, 68, 68, 0.10); color: #f87171; }
|
|
2709
|
+
.severity-moderate { background: rgba(234, 179, 8, 0.15); color: #eab308; }
|
|
2710
|
+
.severity-minor { background: rgba(113, 113, 122, 0.15); color: var(--text-muted); }
|
|
2711
|
+
|
|
2712
|
+
/* Expandable violation details */
|
|
2713
|
+
.violation-details {
|
|
2714
|
+
margin-top: 8px;
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
.violation-details summary {
|
|
2718
|
+
cursor: pointer;
|
|
2719
|
+
font-size: 13px;
|
|
2720
|
+
color: var(--text-secondary);
|
|
2721
|
+
padding: 4px 0;
|
|
2722
|
+
user-select: none;
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2725
|
+
.violation-details summary:hover {
|
|
2726
|
+
color: var(--text);
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2729
|
+
.violation-details[open] summary {
|
|
2730
|
+
margin-bottom: 8px;
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
.violation-list {
|
|
2734
|
+
display: flex;
|
|
2735
|
+
flex-direction: column;
|
|
2736
|
+
gap: 8px;
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2739
|
+
.violation-item {
|
|
2740
|
+
background: var(--bg);
|
|
2741
|
+
border-radius: 8px;
|
|
2742
|
+
padding: 12px;
|
|
2743
|
+
font-size: 13px;
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2746
|
+
.violation-item-header {
|
|
2747
|
+
display: flex;
|
|
2748
|
+
align-items: center;
|
|
2749
|
+
gap: 8px;
|
|
2750
|
+
margin-bottom: 4px;
|
|
2751
|
+
}
|
|
2752
|
+
|
|
2753
|
+
.violation-rule {
|
|
2754
|
+
font-weight: 600;
|
|
2755
|
+
color: var(--text);
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2758
|
+
.violation-impact {
|
|
2759
|
+
font-size: 11px;
|
|
2760
|
+
font-weight: 600;
|
|
2761
|
+
padding: 1px 6px;
|
|
2762
|
+
border-radius: 4px;
|
|
2763
|
+
text-transform: uppercase;
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2766
|
+
.violation-description {
|
|
2767
|
+
color: var(--text-secondary);
|
|
2768
|
+
line-height: 1.5;
|
|
2769
|
+
}
|
|
2770
|
+
|
|
2771
|
+
.violation-help {
|
|
2772
|
+
color: var(--accent);
|
|
2773
|
+
font-size: 12px;
|
|
2774
|
+
margin-top: 4px;
|
|
2775
|
+
}
|
|
2776
|
+
|
|
2777
|
+
/* Footer */
|
|
2778
|
+
.footer {
|
|
2779
|
+
margin-top: 64px;
|
|
2780
|
+
padding-top: 32px;
|
|
2781
|
+
border-top: 1px solid var(--border);
|
|
2782
|
+
text-align: center;
|
|
2783
|
+
color: var(--text-muted);
|
|
2784
|
+
font-size: 13px;
|
|
2785
|
+
}
|
|
2786
|
+
|
|
2787
|
+
.footer a {
|
|
2788
|
+
color: var(--accent);
|
|
2789
|
+
text-decoration: none;
|
|
2790
|
+
}
|
|
2791
|
+
|
|
2792
|
+
/* Responsive */
|
|
2793
|
+
@media (max-width: 768px) {
|
|
2794
|
+
.header {
|
|
2795
|
+
flex-direction: column;
|
|
2796
|
+
gap: 24px;
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2799
|
+
.header-right {
|
|
2800
|
+
align-self: flex-start;
|
|
2801
|
+
}
|
|
2802
|
+
|
|
2803
|
+
.component-grid {
|
|
2804
|
+
grid-template-columns: 1fr;
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
`;
|
|
2808
|
+
}
|
|
2809
|
+
function getScoreColor(score) {
|
|
2810
|
+
if (score >= 90) return "#22c55e";
|
|
2811
|
+
if (score >= 70) return "#eab308";
|
|
2812
|
+
return "#ef4444";
|
|
2813
|
+
}
|
|
2814
|
+
function escapeHtml(str) {
|
|
2815
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
2816
|
+
}
|
|
2817
|
+
function renderHeader(score) {
|
|
2818
|
+
const color = getScoreColor(score.score);
|
|
2819
|
+
const now = /* @__PURE__ */ new Date();
|
|
2820
|
+
const timestamp = now.toLocaleDateString("en-US", {
|
|
2821
|
+
weekday: "long",
|
|
2822
|
+
year: "numeric",
|
|
2823
|
+
month: "long",
|
|
2824
|
+
day: "numeric",
|
|
2825
|
+
hour: "2-digit",
|
|
2826
|
+
minute: "2-digit"
|
|
2827
|
+
});
|
|
2828
|
+
return `
|
|
2829
|
+
<header class="header">
|
|
2830
|
+
<div class="header-left">
|
|
2831
|
+
<h1>${BRAND.name} Accessibility Report</h1>
|
|
2832
|
+
<p>Generated ${escapeHtml(timestamp)}</p>
|
|
2833
|
+
</div>
|
|
2834
|
+
<div class="header-right">
|
|
2835
|
+
<button class="theme-toggle" id="theme-toggle" aria-label="Toggle light/dark theme">Toggle theme</button>
|
|
2836
|
+
<div class="grade-badge">
|
|
2837
|
+
<div class="grade-score-large" style="color: ${color}">${score.score}</div>
|
|
2838
|
+
<div class="grade-label">/ 100</div>
|
|
2839
|
+
</div>
|
|
2840
|
+
</div>
|
|
2841
|
+
</header>
|
|
2842
|
+
`;
|
|
2843
|
+
}
|
|
2844
|
+
function renderSummaryCards(summary, score) {
|
|
2845
|
+
return `
|
|
2846
|
+
<div class="summary-grid">
|
|
2847
|
+
<div class="summary-card">
|
|
2848
|
+
<div class="summary-card-label">Components</div>
|
|
2849
|
+
<div class="summary-card-value">${summary.totalComponents}</div>
|
|
2850
|
+
<div class="summary-card-sub">${summary.accessibleComponents} accessible (${summary.accessiblePercent}%)</div>
|
|
2851
|
+
</div>
|
|
2852
|
+
<div class="summary-card">
|
|
2853
|
+
<div class="summary-card-label">AA Compliance</div>
|
|
2854
|
+
<div class="summary-card-value" style="color: ${getScoreColor(score.aaPercent)}">${score.aaPercent}%</div>
|
|
2855
|
+
<div class="summary-card-sub">No critical/serious violations</div>
|
|
2856
|
+
</div>
|
|
2857
|
+
<div class="summary-card">
|
|
2858
|
+
<div class="summary-card-label">AAA Compliance</div>
|
|
2859
|
+
<div class="summary-card-value" style="color: ${getScoreColor(score.aaaPercent)}">${score.aaaPercent}%</div>
|
|
2860
|
+
<div class="summary-card-sub">Zero violations</div>
|
|
2861
|
+
</div>
|
|
2862
|
+
<div class="summary-card">
|
|
2863
|
+
<div class="summary-card-label">Total Violations</div>
|
|
2864
|
+
<div class="summary-card-value" style="color: ${summary.totalViolations === 0 ? "#22c55e" : "#ef4444"}">${summary.totalViolations}</div>
|
|
2865
|
+
<div class="summary-card-sub">${summary.totalCritical} critical, ${summary.totalSerious} serious, ${summary.totalModerate} moderate, ${summary.totalMinor} minor</div>
|
|
2866
|
+
</div>
|
|
2867
|
+
</div>
|
|
2868
|
+
`;
|
|
2869
|
+
}
|
|
2870
|
+
function renderComponentCard(comp) {
|
|
2871
|
+
const statusClass = comp.status === "PASS" ? "status-pass" : comp.status === "WARN" ? "status-warn" : "status-fail";
|
|
2872
|
+
const variantCount = comp.results.length || 1;
|
|
2873
|
+
let critical = 0;
|
|
2874
|
+
let serious = 0;
|
|
2875
|
+
let moderate = 0;
|
|
2876
|
+
let minor = 0;
|
|
2877
|
+
for (const r of comp.results) {
|
|
2878
|
+
critical += r.summary.critical;
|
|
2879
|
+
serious += r.summary.serious;
|
|
2880
|
+
moderate += r.summary.moderate;
|
|
2881
|
+
minor += r.summary.minor;
|
|
2882
|
+
}
|
|
2883
|
+
const severityChips = [];
|
|
2884
|
+
if (critical > 0) severityChips.push(`<span class="severity-chip severity-critical">${critical} critical</span>`);
|
|
2885
|
+
if (serious > 0) severityChips.push(`<span class="severity-chip severity-serious">${serious} serious</span>`);
|
|
2886
|
+
if (moderate > 0) severityChips.push(`<span class="severity-chip severity-moderate">${moderate} moderate</span>`);
|
|
2887
|
+
if (minor > 0) severityChips.push(`<span class="severity-chip severity-minor">${minor} minor</span>`);
|
|
2888
|
+
let violationDetailsHtml = "";
|
|
2889
|
+
const variantsWithViolations = comp.results.filter((r) => r.summary.total > 0);
|
|
2890
|
+
if (variantsWithViolations.length > 0) {
|
|
2891
|
+
const variantItems = variantsWithViolations.map((r) => {
|
|
2892
|
+
const counts = [];
|
|
2893
|
+
if (r.summary.critical > 0) counts.push(`${r.summary.critical} critical`);
|
|
2894
|
+
if (r.summary.serious > 0) counts.push(`${r.summary.serious} serious`);
|
|
2895
|
+
if (r.summary.moderate > 0) counts.push(`${r.summary.moderate} moderate`);
|
|
2896
|
+
if (r.summary.minor > 0) counts.push(`${r.summary.minor} minor`);
|
|
2897
|
+
return `
|
|
2898
|
+
<div class="violation-item">
|
|
2899
|
+
<div class="violation-item-header">
|
|
2900
|
+
<span class="violation-rule">${escapeHtml(r.variant)}</span>
|
|
2901
|
+
</div>
|
|
2902
|
+
<div class="violation-description">${counts.join(", ")}</div>
|
|
2903
|
+
</div>
|
|
2904
|
+
`;
|
|
2905
|
+
}).join("");
|
|
2906
|
+
violationDetailsHtml = `
|
|
2907
|
+
<details class="violation-details">
|
|
2908
|
+
<summary>View variant breakdown (${variantsWithViolations.length} variant${variantsWithViolations.length === 1 ? "" : "s"} with issues)</summary>
|
|
2909
|
+
<div class="violation-list">
|
|
2910
|
+
${variantItems}
|
|
2911
|
+
</div>
|
|
2912
|
+
</details>
|
|
2913
|
+
`;
|
|
2914
|
+
}
|
|
2915
|
+
return `
|
|
2916
|
+
<div class="component-card">
|
|
2917
|
+
<div class="component-card-header">
|
|
2918
|
+
<span class="component-name">${escapeHtml(comp.component)}</span>
|
|
2919
|
+
<span class="status-badge ${statusClass}">${comp.status}</span>
|
|
2920
|
+
</div>
|
|
2921
|
+
<div class="component-stats">
|
|
2922
|
+
<span>${variantCount} variant${variantCount === 1 ? "" : "s"}</span>
|
|
2923
|
+
<span>${comp.totalViolations} violation${comp.totalViolations === 1 ? "" : "s"}</span>
|
|
2924
|
+
</div>
|
|
2925
|
+
${severityChips.length > 0 ? `<div class="severity-bar">${severityChips.join("")}</div>` : ""}
|
|
2926
|
+
${violationDetailsHtml}
|
|
2927
|
+
</div>
|
|
2928
|
+
`;
|
|
2929
|
+
}
|
|
2930
|
+
function renderComponentGrid(components) {
|
|
2931
|
+
if (components.length === 0) {
|
|
2932
|
+
return `
|
|
2933
|
+
<section class="section">
|
|
2934
|
+
<h2 class="section-title">Components</h2>
|
|
2935
|
+
<p style="color: var(--text-secondary)">No components found.</p>
|
|
2936
|
+
</section>
|
|
2937
|
+
`;
|
|
2938
|
+
}
|
|
2939
|
+
const order = { FAIL: 0, WARN: 1, PASS: 2 };
|
|
2940
|
+
const sorted = [...components].sort((a, b) => order[a.status] - order[b.status]);
|
|
2941
|
+
return `
|
|
2942
|
+
<section class="section">
|
|
2943
|
+
<h2 class="section-title">Components</h2>
|
|
2944
|
+
<div class="component-grid">
|
|
2945
|
+
${sorted.map(renderComponentCard).join("")}
|
|
2946
|
+
</div>
|
|
2947
|
+
</section>
|
|
2948
|
+
`;
|
|
2949
|
+
}
|
|
2950
|
+
function renderFooter() {
|
|
2951
|
+
return `
|
|
2952
|
+
<footer class="footer">
|
|
2953
|
+
<p>Generated by <a href="#">${BRAND.name}</a> — AI-first design system documentation</p>
|
|
2954
|
+
</footer>
|
|
2955
|
+
`;
|
|
2956
|
+
}
|
|
2957
|
+
function getScripts() {
|
|
2958
|
+
return `
|
|
2959
|
+
// Theme toggle
|
|
2960
|
+
(function() {
|
|
2961
|
+
var toggle = document.getElementById('theme-toggle');
|
|
2962
|
+
var root = document.documentElement;
|
|
2963
|
+
|
|
2964
|
+
function setTheme(theme) {
|
|
2965
|
+
root.classList.remove('light', 'dark');
|
|
2966
|
+
root.classList.add(theme);
|
|
2967
|
+
toggle.textContent = theme === 'dark' ? 'Light mode' : 'Dark mode';
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
// Default to dark
|
|
2971
|
+
var preferred = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
|
|
2972
|
+
setTheme(preferred);
|
|
2973
|
+
|
|
2974
|
+
toggle.addEventListener('click', function() {
|
|
2975
|
+
var current = root.classList.contains('dark') ? 'dark' : 'light';
|
|
2976
|
+
setTheme(current === 'dark' ? 'light' : 'dark');
|
|
2977
|
+
});
|
|
2978
|
+
})();
|
|
2979
|
+
|
|
2980
|
+
// Animate bars on load
|
|
2981
|
+
document.querySelectorAll('.bar-fill, .coverage-bar-fill').forEach(function(el) {
|
|
2982
|
+
var width = el.style.width;
|
|
2983
|
+
el.style.width = '0';
|
|
2984
|
+
setTimeout(function() {
|
|
2985
|
+
el.style.width = width;
|
|
2986
|
+
}, 100);
|
|
2987
|
+
});
|
|
2988
|
+
`;
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2991
|
+
// src/commands/a11y.ts
|
|
2992
|
+
function calculateA11yScore(summary) {
|
|
2993
|
+
const deductions = summary.totalCritical * 10 + summary.totalSerious * 5 + summary.totalModerate * 2 + summary.totalMinor * 1;
|
|
2994
|
+
const score = Math.max(0, 100 - deductions);
|
|
2995
|
+
const aaComponents = summary.components.filter(
|
|
2996
|
+
(c) => c.totalCritical === 0 && c.totalSerious === 0
|
|
2997
|
+
).length;
|
|
2998
|
+
const aaPercent = summary.totalComponents > 0 ? Math.round(aaComponents / summary.totalComponents * 100) : 100;
|
|
2999
|
+
const aaaComponents = summary.components.filter(
|
|
3000
|
+
(c) => c.totalViolations === 0
|
|
3001
|
+
).length;
|
|
3002
|
+
const aaaPercent = summary.totalComponents > 0 ? Math.round(aaaComponents / summary.totalComponents * 100) : 100;
|
|
3003
|
+
return { score, aaPercent, aaaPercent };
|
|
3004
|
+
}
|
|
3005
|
+
function formatGitHub(summary) {
|
|
3006
|
+
const score = summary.score ?? calculateA11yScore(summary);
|
|
3007
|
+
const badge = summary.passed ? "passing" : "failing";
|
|
3008
|
+
const badgeColor = summary.passed ? "brightgreen" : "red";
|
|
3009
|
+
const lines = [];
|
|
3010
|
+
lines.push(`## ${BRAND.name} Accessibility Report`);
|
|
3011
|
+
lines.push("");
|
|
3012
|
+
lines.push(``);
|
|
3013
|
+
lines.push(`**Score:** ${score.score}/100 | **AA:** ${score.aaPercent}% | **AAA:** ${score.aaaPercent}%`);
|
|
3014
|
+
lines.push("");
|
|
3015
|
+
lines.push("| Component | Variants | Violations | Critical | Serious | Status |");
|
|
3016
|
+
lines.push("|-----------|----------|------------|----------|---------|--------|");
|
|
3017
|
+
for (const result of summary.components) {
|
|
3018
|
+
const statusIcon = result.status === "PASS" ? "PASS" : result.status === "WARN" ? "WARN" : "FAIL";
|
|
3019
|
+
const variantCount = result.results.length || 1;
|
|
3020
|
+
lines.push(
|
|
3021
|
+
`| ${result.component} | ${variantCount} | ${result.totalViolations} | ${result.totalCritical} | ${result.totalSerious} | ${statusIcon} |`
|
|
3022
|
+
);
|
|
3023
|
+
}
|
|
3024
|
+
lines.push("");
|
|
3025
|
+
lines.push(`**Summary:** ${summary.accessibleComponents}/${summary.totalComponents} components accessible (${summary.accessiblePercent}%)`);
|
|
3026
|
+
lines.push(`**Violations:** ${summary.totalViolations} total (${summary.totalCritical} critical, ${summary.totalSerious} serious, ${summary.totalModerate} moderate, ${summary.totalMinor} minor)`);
|
|
3027
|
+
if (summary.totalCritical + summary.totalSerious > 0) {
|
|
3028
|
+
lines.push("");
|
|
3029
|
+
lines.push(`**Blocking:** ${summary.totalCritical + summary.totalSerious} critical/serious issues must be fixed`);
|
|
3030
|
+
}
|
|
3031
|
+
lines.push("");
|
|
3032
|
+
return lines.join("\n");
|
|
3033
|
+
}
|
|
2406
3034
|
async function a11y(options = {}) {
|
|
2407
|
-
const {
|
|
3035
|
+
const {
|
|
3036
|
+
config: configPath,
|
|
3037
|
+
json = false,
|
|
3038
|
+
ci = false,
|
|
3039
|
+
component,
|
|
3040
|
+
port = 6006,
|
|
3041
|
+
format = json ? "json" : "table",
|
|
3042
|
+
standard = "AA"
|
|
3043
|
+
} = options;
|
|
2408
3044
|
await loadConfig(configPath);
|
|
2409
3045
|
const client = createDevServerClient(port);
|
|
2410
3046
|
const componentResults = [];
|
|
2411
|
-
|
|
3047
|
+
const isJsonOutput = format === "json" || json;
|
|
3048
|
+
if (!isJsonOutput && format !== "github") {
|
|
2412
3049
|
console.log(pc12.cyan(`
|
|
2413
3050
|
${BRAND.name} Accessibility Report
|
|
2414
3051
|
`));
|
|
@@ -2422,7 +3059,7 @@ ${BRAND.name} Accessibility Report
|
|
|
2422
3059
|
}
|
|
2423
3060
|
const segments = await client.getSegments();
|
|
2424
3061
|
if (segments.length === 0) {
|
|
2425
|
-
if (
|
|
3062
|
+
if (isJsonOutput) {
|
|
2426
3063
|
console.log(JSON.stringify({ error: "No fragments found", components: [] }));
|
|
2427
3064
|
} else {
|
|
2428
3065
|
console.log(pc12.yellow("No fragments found.\n"));
|
|
@@ -2443,14 +3080,14 @@ ${BRAND.name} Accessibility Report
|
|
|
2443
3080
|
const componentsToCheck = component ? segments.filter((s) => s.name.toLowerCase() === component.toLowerCase()) : segments;
|
|
2444
3081
|
if (component && componentsToCheck.length === 0) {
|
|
2445
3082
|
const error = `Component '${component}' not found. Available: ${segments.map((s) => s.name).join(", ")}`;
|
|
2446
|
-
if (
|
|
3083
|
+
if (isJsonOutput) {
|
|
2447
3084
|
console.log(JSON.stringify({ error }));
|
|
2448
3085
|
} else {
|
|
2449
3086
|
console.log(pc12.red(error));
|
|
2450
3087
|
}
|
|
2451
3088
|
throw new Error(error);
|
|
2452
3089
|
}
|
|
2453
|
-
if (!
|
|
3090
|
+
if (!isJsonOutput && format !== "github") {
|
|
2454
3091
|
console.log(pc12.dim(`Checking ${componentsToCheck.length} component(s) for accessibility issues...
|
|
2455
3092
|
`));
|
|
2456
3093
|
}
|
|
@@ -2471,6 +3108,9 @@ ${BRAND.name} Accessibility Report
|
|
|
2471
3108
|
} else if (totalViolations2 > 0) {
|
|
2472
3109
|
status = "WARN";
|
|
2473
3110
|
}
|
|
3111
|
+
if (standard === "AAA" && totalViolations2 > 0) {
|
|
3112
|
+
status = "FAIL";
|
|
3113
|
+
}
|
|
2474
3114
|
componentResults.push({
|
|
2475
3115
|
component: seg.name,
|
|
2476
3116
|
results: a11yResult.results,
|
|
@@ -2479,7 +3119,7 @@ ${BRAND.name} Accessibility Report
|
|
|
2479
3119
|
totalCritical: totalCritical2,
|
|
2480
3120
|
totalSerious: totalSerious2
|
|
2481
3121
|
});
|
|
2482
|
-
} catch
|
|
3122
|
+
} catch {
|
|
2483
3123
|
componentResults.push({
|
|
2484
3124
|
component: seg.name,
|
|
2485
3125
|
results: [],
|
|
@@ -2502,6 +3142,7 @@ ${BRAND.name} Accessibility Report
|
|
|
2502
3142
|
totalMinor += result.summary.minor;
|
|
2503
3143
|
}
|
|
2504
3144
|
}
|
|
3145
|
+
const passed = standard === "AAA" ? totalViolations === 0 : totalCritical === 0 && totalSerious === 0;
|
|
2505
3146
|
const summary = {
|
|
2506
3147
|
totalComponents: componentResults.length,
|
|
2507
3148
|
accessibleComponents,
|
|
@@ -2512,9 +3153,12 @@ ${BRAND.name} Accessibility Report
|
|
|
2512
3153
|
totalSerious,
|
|
2513
3154
|
totalModerate,
|
|
2514
3155
|
totalMinor,
|
|
2515
|
-
passed
|
|
3156
|
+
passed
|
|
2516
3157
|
};
|
|
2517
|
-
|
|
3158
|
+
summary.score = calculateA11yScore(summary);
|
|
3159
|
+
if (format === "github") {
|
|
3160
|
+
console.log(formatGitHub(summary));
|
|
3161
|
+
} else if (isJsonOutput) {
|
|
2518
3162
|
console.log(JSON.stringify(summary, null, 2));
|
|
2519
3163
|
} else {
|
|
2520
3164
|
console.log(pc12.bold(
|
|
@@ -2530,21 +3174,46 @@ ${BRAND.name} Accessibility Report
|
|
|
2530
3174
|
}
|
|
2531
3175
|
console.log(pc12.dim("\u2500".repeat(72)));
|
|
2532
3176
|
console.log();
|
|
3177
|
+
const categories = [
|
|
3178
|
+
{ label: "Critical", count: totalCritical, color: pc12.red },
|
|
3179
|
+
{ label: "Serious", count: totalSerious, color: pc12.red },
|
|
3180
|
+
{ label: "Moderate", count: totalModerate, color: pc12.yellow },
|
|
3181
|
+
{ label: "Minor", count: totalMinor, color: pc12.dim }
|
|
3182
|
+
];
|
|
3183
|
+
for (const cat of categories) {
|
|
3184
|
+
if (cat.count > 0) {
|
|
3185
|
+
const dots = ".".repeat(Math.max(1, 30 - cat.label.length));
|
|
3186
|
+
console.log(` ${cat.label} ${pc12.dim(dots)} ${cat.color(String(cat.count))}`);
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
const { score, aaPercent, aaaPercent } = summary.score;
|
|
3190
|
+
console.log();
|
|
3191
|
+
console.log(pc12.bold(` Score: ${score}/100`));
|
|
3192
|
+
console.log(` AA compliance .... ${aaPercent}%`);
|
|
3193
|
+
console.log(` AAA compliance ... ${aaaPercent}%`);
|
|
3194
|
+
console.log(` Standard ......... WCAG ${standard}`);
|
|
3195
|
+
console.log();
|
|
2533
3196
|
console.log(pc12.bold("Summary:"));
|
|
2534
3197
|
console.log(` ${accessibleComponents}/${componentResults.length} components accessible (${summary.accessiblePercent}%)`);
|
|
2535
3198
|
console.log(` Total violations: ${totalViolations} (${totalCritical} critical, ${totalSerious} serious, ${totalModerate} moderate, ${totalMinor} minor)`);
|
|
2536
3199
|
if (!summary.passed) {
|
|
2537
3200
|
console.log();
|
|
2538
|
-
console.log(pc12.red("
|
|
3201
|
+
console.log(pc12.red("x Accessibility check failed - critical/serious violations found"));
|
|
2539
3202
|
} else if (totalViolations > 0) {
|
|
2540
3203
|
console.log();
|
|
2541
|
-
console.log(pc12.yellow("
|
|
3204
|
+
console.log(pc12.yellow("! Minor/moderate violations found - consider fixing for better accessibility"));
|
|
2542
3205
|
} else {
|
|
2543
3206
|
console.log();
|
|
2544
|
-
console.log(pc12.green("
|
|
3207
|
+
console.log(pc12.green("v All components pass accessibility checks"));
|
|
2545
3208
|
}
|
|
2546
3209
|
console.log();
|
|
2547
3210
|
}
|
|
3211
|
+
if (options.report) {
|
|
3212
|
+
const outputPath = options.output ?? "a11y-report.html";
|
|
3213
|
+
const html = generateA11yReport(summary);
|
|
3214
|
+
fs.writeFileSync(outputPath, html, "utf-8");
|
|
3215
|
+
console.log(pc12.green("v Report generated: " + outputPath));
|
|
3216
|
+
}
|
|
2548
3217
|
if (ci && !summary.passed) {
|
|
2549
3218
|
throw new Error(`Accessibility check failed: ${totalCritical} critical, ${totalSerious} serious violations found`);
|
|
2550
3219
|
}
|
|
@@ -3825,8 +4494,8 @@ ${BRAND.name} AI Enhancement
|
|
|
3825
4494
|
];
|
|
3826
4495
|
for (const srcPath of possiblePaths) {
|
|
3827
4496
|
try {
|
|
3828
|
-
const
|
|
3829
|
-
if (
|
|
4497
|
+
const fs2 = await import("fs");
|
|
4498
|
+
if (fs2.existsSync(srcPath)) {
|
|
3830
4499
|
const extraction = await extractPropsFromFile(srcPath, {
|
|
3831
4500
|
propsTypeName: `${compName}Props`
|
|
3832
4501
|
});
|
|
@@ -4275,6 +4944,242 @@ ${whenNotItems}
|
|
|
4275
4944
|
}
|
|
4276
4945
|
}
|
|
4277
4946
|
|
|
4947
|
+
// src/commands/graph.ts
|
|
4948
|
+
import pc20 from "picocolors";
|
|
4949
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
4950
|
+
import { resolve as resolve6 } from "path";
|
|
4951
|
+
import {
|
|
4952
|
+
ComponentGraphEngine,
|
|
4953
|
+
deserializeGraph
|
|
4954
|
+
} from "@fragments-sdk/context/graph";
|
|
4955
|
+
async function graph(component, options) {
|
|
4956
|
+
const { config, configDir } = await loadConfig(options.config);
|
|
4957
|
+
const outputPath = resolve6(configDir, config.outFile ?? BRAND.outFile);
|
|
4958
|
+
let data;
|
|
4959
|
+
try {
|
|
4960
|
+
const content = await readFile6(outputPath, "utf-8");
|
|
4961
|
+
data = JSON.parse(content);
|
|
4962
|
+
} catch {
|
|
4963
|
+
console.error(
|
|
4964
|
+
pc20.red(`Error: Could not load ${BRAND.outFile}. Run \`${BRAND.cliCommand} build\` first.`)
|
|
4965
|
+
);
|
|
4966
|
+
process.exit(1);
|
|
4967
|
+
}
|
|
4968
|
+
if (!data.graph) {
|
|
4969
|
+
console.error(
|
|
4970
|
+
pc20.red(`Error: No graph data in ${BRAND.outFile}. Rebuild with the latest CLI.`)
|
|
4971
|
+
);
|
|
4972
|
+
process.exit(1);
|
|
4973
|
+
}
|
|
4974
|
+
const graph2 = deserializeGraph(data.graph);
|
|
4975
|
+
const blocks = data.blocks ? Object.fromEntries(
|
|
4976
|
+
Object.entries(data.blocks).map(([k, v]) => [k, { components: v.components }])
|
|
4977
|
+
) : void 0;
|
|
4978
|
+
const engine = new ComponentGraphEngine(graph2, blocks);
|
|
4979
|
+
const mode = options.mode ?? (component ? "dependencies" : "health");
|
|
4980
|
+
const format = options.format ?? "table";
|
|
4981
|
+
const edgeTypes = options.edgeTypes ? options.edgeTypes.split(",") : void 0;
|
|
4982
|
+
const needsComponent = ["dependencies", "dependents", "impact", "path", "composition", "alternatives"];
|
|
4983
|
+
if (needsComponent.includes(mode) && !component) {
|
|
4984
|
+
console.error(pc20.red(`Error: "${mode}" mode requires a component name.`));
|
|
4985
|
+
process.exit(1);
|
|
4986
|
+
}
|
|
4987
|
+
if (component && !engine.hasNode(component)) {
|
|
4988
|
+
console.error(pc20.red(`Error: Component "${component}" not found in graph.`));
|
|
4989
|
+
process.exit(1);
|
|
4990
|
+
}
|
|
4991
|
+
switch (mode) {
|
|
4992
|
+
case "health": {
|
|
4993
|
+
const health = engine.getHealth();
|
|
4994
|
+
if (format === "json") {
|
|
4995
|
+
console.log(JSON.stringify(health, null, 2));
|
|
4996
|
+
return;
|
|
4997
|
+
}
|
|
4998
|
+
console.log(pc20.bold("\nComponent Graph Health\n"));
|
|
4999
|
+
console.log(` ${pc20.cyan("Nodes:")} ${health.nodeCount}`);
|
|
5000
|
+
console.log(` ${pc20.cyan("Edges:")} ${health.edgeCount}`);
|
|
5001
|
+
console.log(` ${pc20.cyan("Avg degree:")} ${health.averageDegree}`);
|
|
5002
|
+
console.log(` ${pc20.cyan("Islands:")} ${health.connectedComponents.length}`);
|
|
5003
|
+
console.log(` ${pc20.cyan("Coverage:")} ${health.compositionCoverage}% in blocks`);
|
|
5004
|
+
console.log(` ${pc20.cyan("Orphans:")} ${health.orphans.length > 0 ? health.orphans.join(", ") : pc20.green("none")}`);
|
|
5005
|
+
if (health.hubs.length > 0) {
|
|
5006
|
+
console.log(`
|
|
5007
|
+
${pc20.bold("Top hubs:")}`);
|
|
5008
|
+
for (const hub of health.hubs.slice(0, 5)) {
|
|
5009
|
+
console.log(` ${pc20.yellow(hub.name)} \u2014 ${hub.degree} connections`);
|
|
5010
|
+
}
|
|
5011
|
+
}
|
|
5012
|
+
console.log();
|
|
5013
|
+
break;
|
|
5014
|
+
}
|
|
5015
|
+
case "dependencies": {
|
|
5016
|
+
const deps = engine.dependencies(component, edgeTypes);
|
|
5017
|
+
if (format === "json") {
|
|
5018
|
+
console.log(JSON.stringify({ component, dependencies: deps }, null, 2));
|
|
5019
|
+
return;
|
|
5020
|
+
}
|
|
5021
|
+
console.log(pc20.bold(`
|
|
5022
|
+
Dependencies of ${component}
|
|
5023
|
+
`));
|
|
5024
|
+
if (deps.length === 0) {
|
|
5025
|
+
console.log(" No outgoing dependencies.");
|
|
5026
|
+
} else {
|
|
5027
|
+
for (const dep of deps) {
|
|
5028
|
+
console.log(` ${pc20.yellow(dep.target)} ${pc20.dim(`(${dep.type})`)}${dep.note ? ` \u2014 ${dep.note}` : ""}`);
|
|
5029
|
+
}
|
|
5030
|
+
}
|
|
5031
|
+
console.log();
|
|
5032
|
+
break;
|
|
5033
|
+
}
|
|
5034
|
+
case "dependents": {
|
|
5035
|
+
const deps = engine.dependents(component, edgeTypes);
|
|
5036
|
+
if (format === "json") {
|
|
5037
|
+
console.log(JSON.stringify({ component, dependents: deps }, null, 2));
|
|
5038
|
+
return;
|
|
5039
|
+
}
|
|
5040
|
+
console.log(pc20.bold(`
|
|
5041
|
+
Dependents of ${component}
|
|
5042
|
+
`));
|
|
5043
|
+
if (deps.length === 0) {
|
|
5044
|
+
console.log(" No incoming dependents.");
|
|
5045
|
+
} else {
|
|
5046
|
+
for (const dep of deps) {
|
|
5047
|
+
console.log(` ${pc20.yellow(dep.source)} ${pc20.dim(`(${dep.type})`)}${dep.note ? ` \u2014 ${dep.note}` : ""}`);
|
|
5048
|
+
}
|
|
5049
|
+
}
|
|
5050
|
+
console.log();
|
|
5051
|
+
break;
|
|
5052
|
+
}
|
|
5053
|
+
case "impact": {
|
|
5054
|
+
const result = engine.impact(component, options.depth ?? 3);
|
|
5055
|
+
if (format === "json") {
|
|
5056
|
+
console.log(JSON.stringify(result, null, 2));
|
|
5057
|
+
return;
|
|
5058
|
+
}
|
|
5059
|
+
console.log(pc20.bold(`
|
|
5060
|
+
Impact analysis: ${component}
|
|
5061
|
+
`));
|
|
5062
|
+
console.log(` ${pc20.red(`${result.totalAffected}`)} affected components, ${pc20.red(`${result.affectedBlocks.length}`)} affected blocks
|
|
5063
|
+
`);
|
|
5064
|
+
if (result.affected.length > 0) {
|
|
5065
|
+
console.log(` ${pc20.bold("Affected components:")}`);
|
|
5066
|
+
for (const entry of result.affected) {
|
|
5067
|
+
const indent = " ".repeat(entry.depth + 1);
|
|
5068
|
+
console.log(`${indent}${pc20.yellow(entry.component)} ${pc20.dim(`(depth ${entry.depth}, via ${entry.edgeType})`)}`);
|
|
5069
|
+
}
|
|
5070
|
+
}
|
|
5071
|
+
if (result.affectedBlocks.length > 0) {
|
|
5072
|
+
console.log(`
|
|
5073
|
+
${pc20.bold("Affected blocks:")} ${result.affectedBlocks.join(", ")}`);
|
|
5074
|
+
}
|
|
5075
|
+
console.log();
|
|
5076
|
+
break;
|
|
5077
|
+
}
|
|
5078
|
+
case "path": {
|
|
5079
|
+
if (!options.target) {
|
|
5080
|
+
console.error(pc20.red("Error: --target is required for path mode."));
|
|
5081
|
+
process.exit(1);
|
|
5082
|
+
}
|
|
5083
|
+
const result = engine.path(component, options.target);
|
|
5084
|
+
if (format === "json") {
|
|
5085
|
+
console.log(JSON.stringify(result, null, 2));
|
|
5086
|
+
return;
|
|
5087
|
+
}
|
|
5088
|
+
console.log(pc20.bold(`
|
|
5089
|
+
Path: ${component} \u2192 ${options.target}
|
|
5090
|
+
`));
|
|
5091
|
+
if (!result.found) {
|
|
5092
|
+
console.log(` ${pc20.red("No path found.")}`);
|
|
5093
|
+
} else {
|
|
5094
|
+
console.log(` ${result.path.map((n) => pc20.yellow(n)).join(pc20.dim(" \u2192 "))}`);
|
|
5095
|
+
if (result.edges.length > 0) {
|
|
5096
|
+
console.log(` ${pc20.dim(`(${result.edges.map((e) => e.type).join(" \u2192 ")})`)}`);
|
|
5097
|
+
}
|
|
5098
|
+
}
|
|
5099
|
+
console.log();
|
|
5100
|
+
break;
|
|
5101
|
+
}
|
|
5102
|
+
case "composition": {
|
|
5103
|
+
const tree = engine.composition(component);
|
|
5104
|
+
if (format === "json") {
|
|
5105
|
+
console.log(JSON.stringify(tree, null, 2));
|
|
5106
|
+
return;
|
|
5107
|
+
}
|
|
5108
|
+
console.log(pc20.bold(`
|
|
5109
|
+
Composition: ${component}
|
|
5110
|
+
`));
|
|
5111
|
+
console.log(` ${pc20.cyan("Pattern:")} ${tree.compositionPattern ?? "unknown"}`);
|
|
5112
|
+
if (tree.parent) {
|
|
5113
|
+
console.log(` ${pc20.cyan("Parent:")} ${tree.parent}`);
|
|
5114
|
+
}
|
|
5115
|
+
if (tree.subComponents.length > 0) {
|
|
5116
|
+
console.log(` ${pc20.cyan("Sub-components:")}`);
|
|
5117
|
+
for (const sub of tree.subComponents) {
|
|
5118
|
+
const isRequired = tree.requiredChildren.includes(sub);
|
|
5119
|
+
console.log(` ${pc20.yellow(sub)}${isRequired ? pc20.red(" (required)") : ""}`);
|
|
5120
|
+
}
|
|
5121
|
+
}
|
|
5122
|
+
if (tree.siblings.length > 0) {
|
|
5123
|
+
console.log(` ${pc20.cyan("Siblings:")} ${tree.siblings.join(", ")}`);
|
|
5124
|
+
}
|
|
5125
|
+
if (tree.blocks.length > 0) {
|
|
5126
|
+
console.log(` ${pc20.cyan("In blocks:")} ${tree.blocks.join(", ")}`);
|
|
5127
|
+
}
|
|
5128
|
+
console.log();
|
|
5129
|
+
break;
|
|
5130
|
+
}
|
|
5131
|
+
case "alternatives": {
|
|
5132
|
+
const alts = engine.alternatives(component);
|
|
5133
|
+
if (format === "json") {
|
|
5134
|
+
console.log(JSON.stringify({ component, alternatives: alts }, null, 2));
|
|
5135
|
+
return;
|
|
5136
|
+
}
|
|
5137
|
+
console.log(pc20.bold(`
|
|
5138
|
+
Alternatives for ${component}
|
|
5139
|
+
`));
|
|
5140
|
+
if (alts.length === 0) {
|
|
5141
|
+
console.log(" No known alternatives.");
|
|
5142
|
+
} else {
|
|
5143
|
+
for (const alt of alts) {
|
|
5144
|
+
console.log(` ${pc20.yellow(alt.component)}${alt.note ? ` \u2014 ${alt.note}` : ""}`);
|
|
5145
|
+
}
|
|
5146
|
+
}
|
|
5147
|
+
console.log();
|
|
5148
|
+
break;
|
|
5149
|
+
}
|
|
5150
|
+
case "islands": {
|
|
5151
|
+
const islands = engine.islands();
|
|
5152
|
+
if (format === "json") {
|
|
5153
|
+
console.log(JSON.stringify({ islands }, null, 2));
|
|
5154
|
+
return;
|
|
5155
|
+
}
|
|
5156
|
+
console.log(pc20.bold(`
|
|
5157
|
+
Connected Islands (${islands.length})
|
|
5158
|
+
`));
|
|
5159
|
+
for (let i = 0; i < islands.length; i++) {
|
|
5160
|
+
console.log(` ${pc20.cyan(`Island ${i + 1}`)} (${islands[i].length} components): ${islands[i].join(", ")}`);
|
|
5161
|
+
}
|
|
5162
|
+
console.log();
|
|
5163
|
+
break;
|
|
5164
|
+
}
|
|
5165
|
+
default:
|
|
5166
|
+
console.error(pc20.red(`Unknown mode: "${mode}". Valid: health, dependencies, dependents, impact, path, composition, alternatives, islands`));
|
|
5167
|
+
process.exit(1);
|
|
5168
|
+
}
|
|
5169
|
+
if (format === "dot") {
|
|
5170
|
+
const lines = ["digraph ComponentGraph {", " rankdir=LR;", " node [shape=box, style=rounded];"];
|
|
5171
|
+
for (const node of graph2.nodes) {
|
|
5172
|
+
lines.push(` "${node.name}" [label="${node.name}\\n(${node.category})"];`);
|
|
5173
|
+
}
|
|
5174
|
+
for (const edge of graph2.edges) {
|
|
5175
|
+
const style = edge.type === "alternative-to" ? "dashed" : "solid";
|
|
5176
|
+
lines.push(` "${edge.source}" -> "${edge.target}" [label="${edge.type}", style=${style}];`);
|
|
5177
|
+
}
|
|
5178
|
+
lines.push("}");
|
|
5179
|
+
console.log(lines.join("\n"));
|
|
5180
|
+
}
|
|
5181
|
+
}
|
|
5182
|
+
|
|
4278
5183
|
// src/bin.ts
|
|
4279
5184
|
var __dirname = dirname4(fileURLToPath(import.meta.url));
|
|
4280
5185
|
var pkg = JSON.parse(readFileSync(join10(__dirname, "../package.json"), "utf-8"));
|
|
@@ -4287,7 +5192,7 @@ program.command("validate").description("Validate fragment files").option("-c, -
|
|
|
4287
5192
|
process.exit(1);
|
|
4288
5193
|
}
|
|
4289
5194
|
} catch (error) {
|
|
4290
|
-
console.error(
|
|
5195
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4291
5196
|
process.exit(1);
|
|
4292
5197
|
}
|
|
4293
5198
|
});
|
|
@@ -4307,7 +5212,7 @@ program.command("build").description(`Build compiled ${BRAND.outFile} and ${BRAN
|
|
|
4307
5212
|
process.exit(1);
|
|
4308
5213
|
}
|
|
4309
5214
|
} catch (error) {
|
|
4310
|
-
console.error(
|
|
5215
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4311
5216
|
process.exit(1);
|
|
4312
5217
|
}
|
|
4313
5218
|
});
|
|
@@ -4326,7 +5231,7 @@ program.command("context").description("Generate AI-ready context for your desig
|
|
|
4326
5231
|
process.exit(1);
|
|
4327
5232
|
}
|
|
4328
5233
|
} catch (error) {
|
|
4329
|
-
console.error(
|
|
5234
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4330
5235
|
process.exit(1);
|
|
4331
5236
|
}
|
|
4332
5237
|
});
|
|
@@ -4353,7 +5258,7 @@ program.command("ai").description("Generate context optimized for AI assistants
|
|
|
4353
5258
|
}
|
|
4354
5259
|
}
|
|
4355
5260
|
} catch (error) {
|
|
4356
|
-
console.error(
|
|
5261
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4357
5262
|
process.exit(1);
|
|
4358
5263
|
}
|
|
4359
5264
|
});
|
|
@@ -4361,7 +5266,7 @@ program.command("list").description("List all discovered fragment files").option
|
|
|
4361
5266
|
try {
|
|
4362
5267
|
await list(options);
|
|
4363
5268
|
} catch (error) {
|
|
4364
|
-
console.error(
|
|
5269
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4365
5270
|
process.exit(1);
|
|
4366
5271
|
}
|
|
4367
5272
|
});
|
|
@@ -4369,7 +5274,7 @@ program.command("reset").description("Reset to initial state (delete all generat
|
|
|
4369
5274
|
try {
|
|
4370
5275
|
await reset(options);
|
|
4371
5276
|
} catch (error) {
|
|
4372
|
-
console.error(
|
|
5277
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4373
5278
|
process.exit(1);
|
|
4374
5279
|
}
|
|
4375
5280
|
});
|
|
@@ -4383,7 +5288,7 @@ linkCommand.command("figma").argument("[figma-url]", "Figma file URL to link com
|
|
|
4383
5288
|
variants: options.variants
|
|
4384
5289
|
});
|
|
4385
5290
|
} catch (error) {
|
|
4386
|
-
console.error(
|
|
5291
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4387
5292
|
process.exit(1);
|
|
4388
5293
|
}
|
|
4389
5294
|
});
|
|
@@ -4398,7 +5303,7 @@ linkCommand.command("storybook").description("Bootstrap fragments from existing
|
|
|
4398
5303
|
exclude: options.exclude
|
|
4399
5304
|
});
|
|
4400
5305
|
} catch (error) {
|
|
4401
|
-
console.error(
|
|
5306
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4402
5307
|
process.exit(1);
|
|
4403
5308
|
}
|
|
4404
5309
|
});
|
|
@@ -4413,9 +5318,9 @@ program.command("dev").description("Start the development server with live compo
|
|
|
4413
5318
|
skipBuild: options.skipBuild
|
|
4414
5319
|
});
|
|
4415
5320
|
} catch (error) {
|
|
4416
|
-
console.error(
|
|
5321
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4417
5322
|
if (error instanceof Error && error.stack) {
|
|
4418
|
-
console.error(
|
|
5323
|
+
console.error(pc21.dim(error.stack));
|
|
4419
5324
|
}
|
|
4420
5325
|
process.exit(1);
|
|
4421
5326
|
}
|
|
@@ -4436,7 +5341,7 @@ program.command("screenshot").description("Capture screenshots of component vari
|
|
|
4436
5341
|
process.exit(1);
|
|
4437
5342
|
}
|
|
4438
5343
|
} catch (error) {
|
|
4439
|
-
console.error(
|
|
5344
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4440
5345
|
process.exit(1);
|
|
4441
5346
|
}
|
|
4442
5347
|
});
|
|
@@ -4455,7 +5360,7 @@ program.command("diff").argument("[component]", "Component name to diff (optiona
|
|
|
4455
5360
|
process.exit(1);
|
|
4456
5361
|
}
|
|
4457
5362
|
} catch (error) {
|
|
4458
|
-
console.error(
|
|
5363
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4459
5364
|
process.exit(1);
|
|
4460
5365
|
}
|
|
4461
5366
|
});
|
|
@@ -4474,8 +5379,8 @@ program.command("compare").argument("[component]", "Component name to compare").
|
|
|
4474
5379
|
process.exit(1);
|
|
4475
5380
|
}
|
|
4476
5381
|
} catch (error) {
|
|
4477
|
-
console.error(
|
|
4478
|
-
console.log(
|
|
5382
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
5383
|
+
console.log(pc21.dim(`
|
|
4479
5384
|
Make sure the dev server is running: ${BRAND.cliCommand} dev`));
|
|
4480
5385
|
process.exit(1);
|
|
4481
5386
|
}
|
|
@@ -4494,7 +5399,7 @@ program.command("analyze").description("Analyze design system and generate repor
|
|
|
4494
5399
|
process.exit(1);
|
|
4495
5400
|
}
|
|
4496
5401
|
} catch (error) {
|
|
4497
|
-
console.error(
|
|
5402
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4498
5403
|
process.exit(1);
|
|
4499
5404
|
}
|
|
4500
5405
|
});
|
|
@@ -4513,7 +5418,7 @@ program.command("verify").argument("[component]", "Component name to verify (opt
|
|
|
4513
5418
|
if (options.ci) {
|
|
4514
5419
|
console.log(JSON.stringify({ error: error instanceof Error ? error.message : "Verification failed" }));
|
|
4515
5420
|
} else {
|
|
4516
|
-
console.error(
|
|
5421
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4517
5422
|
}
|
|
4518
5423
|
process.exit(1);
|
|
4519
5424
|
}
|
|
@@ -4530,25 +5435,29 @@ program.command("audit").description("Scan all fragments and show compliance met
|
|
|
4530
5435
|
if (options.json) {
|
|
4531
5436
|
console.log(JSON.stringify({ error: error instanceof Error ? error.message : "Audit failed" }));
|
|
4532
5437
|
} else {
|
|
4533
|
-
console.error(
|
|
5438
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4534
5439
|
}
|
|
4535
5440
|
process.exit(1);
|
|
4536
5441
|
}
|
|
4537
5442
|
});
|
|
4538
|
-
program.command("a11y").description("Run accessibility checks on all component variants").option("-c, --config <path>", "Path to config file").option("--json", "Output results as JSON").option("--ci", "CI mode (exit code 1 if any critical/serious violations)").option("--component <name>", "Check specific component only").option("-p, --port <port>", "Dev server port", "6006").action(async (options) => {
|
|
5443
|
+
program.command("a11y").description("Run accessibility checks on all component variants").option("-c, --config <path>", "Path to config file").option("--json", "Output results as JSON").option("--ci", "CI mode (exit code 1 if any critical/serious violations)").option("--component <name>", "Check specific component only").option("-p, --port <port>", "Dev server port", "6006").option("--format <format>", "Output format (table, json, github)", "table").option("--standard <level>", "WCAG level (AA or AAA)", "AA").option("--report", "Generate standalone HTML compliance report").option("-o, --output <path>", "Output path for report", "a11y-report.html").action(async (options) => {
|
|
4539
5444
|
try {
|
|
4540
5445
|
await a11y({
|
|
4541
5446
|
config: options.config,
|
|
4542
5447
|
json: options.json,
|
|
4543
5448
|
ci: options.ci,
|
|
4544
5449
|
component: options.component,
|
|
4545
|
-
port: options.port
|
|
5450
|
+
port: options.port,
|
|
5451
|
+
format: options.format,
|
|
5452
|
+
standard: options.standard,
|
|
5453
|
+
report: options.report,
|
|
5454
|
+
output: options.output
|
|
4546
5455
|
});
|
|
4547
5456
|
} catch (error) {
|
|
4548
5457
|
if (options.json) {
|
|
4549
5458
|
console.log(JSON.stringify({ error: error instanceof Error ? error.message : "A11y check failed" }));
|
|
4550
5459
|
} else {
|
|
4551
|
-
console.error(
|
|
5460
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4552
5461
|
}
|
|
4553
5462
|
process.exit(1);
|
|
4554
5463
|
}
|
|
@@ -4571,7 +5480,7 @@ program.command("enhance").description("AI-powered documentation generation from
|
|
|
4571
5480
|
if (options.format === "json") {
|
|
4572
5481
|
console.log(JSON.stringify({ success: false, error: error instanceof Error ? error.message : "Enhance failed" }));
|
|
4573
5482
|
} else {
|
|
4574
|
-
console.error(
|
|
5483
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4575
5484
|
}
|
|
4576
5485
|
process.exit(1);
|
|
4577
5486
|
}
|
|
@@ -4592,7 +5501,7 @@ program.command("scan").description(`Zero-config ${BRAND.outFile} generation fro
|
|
|
4592
5501
|
process.exit(1);
|
|
4593
5502
|
}
|
|
4594
5503
|
} catch (error) {
|
|
4595
|
-
console.error(
|
|
5504
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4596
5505
|
process.exit(1);
|
|
4597
5506
|
}
|
|
4598
5507
|
});
|
|
@@ -4605,7 +5514,7 @@ program.command("storygen").description("Generate Storybook stories from fragmen
|
|
|
4605
5514
|
format: options.format
|
|
4606
5515
|
});
|
|
4607
5516
|
} catch (error) {
|
|
4608
|
-
console.error(
|
|
5517
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4609
5518
|
process.exit(1);
|
|
4610
5519
|
}
|
|
4611
5520
|
});
|
|
@@ -4617,7 +5526,7 @@ program.command("metrics").argument("[component]", "Component name (optional, sh
|
|
|
4617
5526
|
json: options.json
|
|
4618
5527
|
});
|
|
4619
5528
|
} catch (error) {
|
|
4620
|
-
console.error(
|
|
5529
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4621
5530
|
process.exit(1);
|
|
4622
5531
|
}
|
|
4623
5532
|
});
|
|
@@ -4631,9 +5540,9 @@ program.command("baseline").description("Manage visual regression baselines").ar
|
|
|
4631
5540
|
port: options.port
|
|
4632
5541
|
});
|
|
4633
5542
|
} catch (error) {
|
|
4634
|
-
console.error(
|
|
5543
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4635
5544
|
if (action === "update") {
|
|
4636
|
-
console.log(
|
|
5545
|
+
console.log(pc21.dim(`
|
|
4637
5546
|
Make sure the dev server is running: ${BRAND.cliCommand} dev`));
|
|
4638
5547
|
}
|
|
4639
5548
|
process.exit(1);
|
|
@@ -4641,27 +5550,27 @@ Make sure the dev server is running: ${BRAND.cliCommand} dev`));
|
|
|
4641
5550
|
});
|
|
4642
5551
|
program.command("view").description(`Generate a static HTML viewer for ${BRAND.outFile}`).option("-i, --input <path>", `Path to ${BRAND.outFile}`, BRAND.outFile).option("-o, --output <path>", "Output HTML file path", BRAND.viewerHtmlFile).option("--open", "Open in browser after generation").action(async (options) => {
|
|
4643
5552
|
try {
|
|
4644
|
-
const { generateViewerFromJson } = await import("./static-viewer-
|
|
4645
|
-
const
|
|
5553
|
+
const { generateViewerFromJson } = await import("./static-viewer-GBR7YNF3.js");
|
|
5554
|
+
const fs2 = await import("fs/promises");
|
|
4646
5555
|
const path = await import("path");
|
|
4647
5556
|
const inputPath = path.resolve(process.cwd(), options.input);
|
|
4648
5557
|
const outputPath = path.resolve(process.cwd(), options.output);
|
|
4649
|
-
console.log(
|
|
5558
|
+
console.log(pc21.cyan(`
|
|
4650
5559
|
${BRAND.name} Viewer Generator
|
|
4651
5560
|
`));
|
|
4652
5561
|
try {
|
|
4653
|
-
await
|
|
5562
|
+
await fs2.access(inputPath);
|
|
4654
5563
|
} catch {
|
|
4655
|
-
console.log(
|
|
4656
|
-
console.log(
|
|
4657
|
-
Run ${
|
|
5564
|
+
console.log(pc21.red(`Error: ${options.input} not found.`));
|
|
5565
|
+
console.log(pc21.dim(`
|
|
5566
|
+
Run ${pc21.cyan(`${BRAND.cliCommand} build`)} first to generate ${BRAND.outFile}
|
|
4658
5567
|
`));
|
|
4659
5568
|
process.exit(1);
|
|
4660
5569
|
}
|
|
4661
|
-
console.log(
|
|
5570
|
+
console.log(pc21.dim(`Reading: ${options.input}`));
|
|
4662
5571
|
const html = await generateViewerFromJson(inputPath);
|
|
4663
|
-
await
|
|
4664
|
-
console.log(
|
|
5572
|
+
await fs2.writeFile(outputPath, html);
|
|
5573
|
+
console.log(pc21.green(`
|
|
4665
5574
|
\u2713 Generated: ${options.output}
|
|
4666
5575
|
`));
|
|
4667
5576
|
if (options.open) {
|
|
@@ -4670,7 +5579,7 @@ Run ${pc20.cyan(`${BRAND.cliCommand} build`)} first to generate ${BRAND.outFile}
|
|
|
4670
5579
|
exec(`${openCmd} "${outputPath}"`);
|
|
4671
5580
|
}
|
|
4672
5581
|
} catch (error) {
|
|
4673
|
-
console.error(
|
|
5582
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4674
5583
|
process.exit(1);
|
|
4675
5584
|
}
|
|
4676
5585
|
});
|
|
@@ -4683,33 +5592,33 @@ program.command("add").argument("[name]", 'Component name (e.g., "Button", "Text
|
|
|
4683
5592
|
component: options.component
|
|
4684
5593
|
});
|
|
4685
5594
|
} catch (error) {
|
|
4686
|
-
console.error(
|
|
5595
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4687
5596
|
process.exit(1);
|
|
4688
5597
|
}
|
|
4689
5598
|
});
|
|
4690
5599
|
program.command("init").description("Initialize fragments in a project (interactive by default)").option("--force", "Overwrite existing config").option("-y, --yes", "Non-interactive mode - auto-detect and use defaults").action(async (options) => {
|
|
4691
5600
|
try {
|
|
4692
|
-
const { init } = await import("./init-
|
|
5601
|
+
const { init } = await import("./init-7CHRKQ7P.js");
|
|
4693
5602
|
const result = await init({
|
|
4694
5603
|
projectRoot: process.cwd(),
|
|
4695
5604
|
force: options.force,
|
|
4696
5605
|
yes: options.yes
|
|
4697
5606
|
});
|
|
4698
5607
|
if (!result.success) {
|
|
4699
|
-
console.error(
|
|
5608
|
+
console.error(pc21.red("\nInit failed with errors:"));
|
|
4700
5609
|
for (const error of result.errors) {
|
|
4701
|
-
console.error(
|
|
5610
|
+
console.error(pc21.red(` - ${error}`));
|
|
4702
5611
|
}
|
|
4703
5612
|
process.exit(1);
|
|
4704
5613
|
}
|
|
4705
5614
|
} catch (error) {
|
|
4706
|
-
console.error(
|
|
5615
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4707
5616
|
process.exit(1);
|
|
4708
5617
|
}
|
|
4709
5618
|
});
|
|
4710
5619
|
program.command("tokens").description("Discover and list design tokens from CSS/SCSS files").option("-c, --config <path>", "Path to config file").option("--json", "Output as JSON").option("--categories", "Group tokens by category").option("--theme <theme>", "Filter by theme name").option("--category <category>", "Filter by category (color, spacing, typography, etc.)").option("--verbose", "Show all tokens (no truncation)").action(async (options) => {
|
|
4711
5620
|
try {
|
|
4712
|
-
const { tokens } = await import("./tokens-
|
|
5621
|
+
const { tokens } = await import("./tokens-3BWDESVM.js");
|
|
4713
5622
|
const result = await tokens({
|
|
4714
5623
|
config: options.config,
|
|
4715
5624
|
json: options.json,
|
|
@@ -4722,13 +5631,13 @@ program.command("tokens").description("Discover and list design tokens from CSS/
|
|
|
4722
5631
|
process.exit(1);
|
|
4723
5632
|
}
|
|
4724
5633
|
} catch (error) {
|
|
4725
|
-
console.error(
|
|
5634
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4726
5635
|
process.exit(1);
|
|
4727
5636
|
}
|
|
4728
5637
|
});
|
|
4729
5638
|
program.command("generate").description("Generate fragment files from component source code").argument("[component]", "Specific component name to generate (optional)").option("--force", "Overwrite existing fragment files").option("--pattern <glob>", "Pattern for component files", "src/components/**/*.tsx").action(async (component, options) => {
|
|
4730
5639
|
try {
|
|
4731
|
-
const { generate } = await import("./generate-
|
|
5640
|
+
const { generate } = await import("./generate-LMTISDIJ.js");
|
|
4732
5641
|
const result = await generate({
|
|
4733
5642
|
projectRoot: process.cwd(),
|
|
4734
5643
|
component,
|
|
@@ -4736,18 +5645,26 @@ program.command("generate").description("Generate fragment files from component
|
|
|
4736
5645
|
componentPattern: options.pattern
|
|
4737
5646
|
});
|
|
4738
5647
|
if (!result.success) {
|
|
4739
|
-
console.error(
|
|
5648
|
+
console.error(pc21.red("\nGenerate completed with errors"));
|
|
4740
5649
|
process.exit(1);
|
|
4741
5650
|
}
|
|
4742
5651
|
} catch (error) {
|
|
4743
|
-
console.error(
|
|
5652
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
5653
|
+
process.exit(1);
|
|
5654
|
+
}
|
|
5655
|
+
});
|
|
5656
|
+
program.command("graph").description("Query the component relationship graph").argument("[component]", "Component name (optional)").option("-c, --config <path>", "Path to config file").option("-m, --mode <mode>", "Query mode: health, dependencies, dependents, impact, path, composition, alternatives, islands").option("-t, --target <component>", "Target component for path mode").option("--edge-types <types>", "Comma-separated edge types to filter by").option("--depth <number>", "Max traversal depth for impact mode", parseInt).option("--format <format>", "Output format: table, json, dot", "table").action(async (component, options) => {
|
|
5657
|
+
try {
|
|
5658
|
+
await graph(component, options);
|
|
5659
|
+
} catch (error) {
|
|
5660
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4744
5661
|
process.exit(1);
|
|
4745
5662
|
}
|
|
4746
5663
|
});
|
|
4747
5664
|
program.command("test").description("Run interaction tests for fragments with play functions").option("-c, --config <path>", "Path to config file").option("--component <name>", "Filter by component name").option("--tags <tags>", "Filter by tags (comma-separated)").option("--grep <pattern>", "Filter by variant name pattern").option("--exclude <pattern>", "Exclude tests matching pattern").option("--parallel <count>", "Number of parallel browser contexts", parseInt, 4).option("--timeout <ms>", "Timeout per test in milliseconds", parseInt, 3e4).option("--retries <count>", "Number of retries for failed tests", parseInt, 0).option("--bail", "Stop on first failure").option("--browser <name>", "Browser to use (chromium, firefox, webkit)", "chromium").option("--headed", "Run in headed mode (show browser)").option("--a11y", "Run accessibility checks with axe-core").option("--visual", "Capture screenshots for visual regression").option("--update-snapshots", "Update visual snapshots").option("--watch", "Watch mode - re-run on file changes").option("--reporters <names>", "Reporters to use (console, junit, json)", "console").option("-o, --output <dir>", "Output directory for results", "./test-results").option("--server-url <url>", "URL of running dev server (skips starting server)").option("-p, --port <port>", "Port for dev server", parseInt, 6006).option("--ci", "CI mode - non-interactive, exit with code 1 on failure").option("--list", "List available tests without running them").action(async (options) => {
|
|
4748
5665
|
try {
|
|
4749
5666
|
const { config, configDir } = await loadConfig(options.config);
|
|
4750
|
-
const { runTestCommand, listTests } = await import("./test-
|
|
5667
|
+
const { runTestCommand, listTests } = await import("./test-OJRXNDO2.js");
|
|
4751
5668
|
if (options.list) {
|
|
4752
5669
|
await listTests(config, configDir, {
|
|
4753
5670
|
component: options.component,
|
|
@@ -4780,7 +5697,7 @@ program.command("test").description("Run interaction tests for fragments with pl
|
|
|
4780
5697
|
});
|
|
4781
5698
|
process.exit(exitCode);
|
|
4782
5699
|
} catch (error) {
|
|
4783
|
-
console.error(
|
|
5700
|
+
console.error(pc21.red("Error:"), error instanceof Error ? error.message : error);
|
|
4784
5701
|
process.exit(1);
|
|
4785
5702
|
}
|
|
4786
5703
|
});
|