@fragments-sdk/cli 0.2.2
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/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +4783 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-4FDQSGKX.js +786 -0
- package/dist/chunk-4FDQSGKX.js.map +1 -0
- package/dist/chunk-7H2MMGYG.js +369 -0
- package/dist/chunk-7H2MMGYG.js.map +1 -0
- package/dist/chunk-BSCG3IP7.js +619 -0
- package/dist/chunk-BSCG3IP7.js.map +1 -0
- package/dist/chunk-LY2CFFPY.js +898 -0
- package/dist/chunk-LY2CFFPY.js.map +1 -0
- package/dist/chunk-MUZ6CM66.js +6636 -0
- package/dist/chunk-MUZ6CM66.js.map +1 -0
- package/dist/chunk-OAENNG3G.js +1489 -0
- package/dist/chunk-OAENNG3G.js.map +1 -0
- package/dist/chunk-XHNKNI6J.js +235 -0
- package/dist/chunk-XHNKNI6J.js.map +1 -0
- package/dist/core-DWKLGY4N.js +68 -0
- package/dist/core-DWKLGY4N.js.map +1 -0
- package/dist/generate-4LQNJ7SX.js +249 -0
- package/dist/generate-4LQNJ7SX.js.map +1 -0
- package/dist/index.d.ts +775 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/init-EMVI47QG.js +416 -0
- package/dist/init-EMVI47QG.js.map +1 -0
- package/dist/mcp-bin.d.ts +1 -0
- package/dist/mcp-bin.js +1117 -0
- package/dist/mcp-bin.js.map +1 -0
- package/dist/scan-4YPRF7FV.js +12 -0
- package/dist/scan-4YPRF7FV.js.map +1 -0
- package/dist/service-QSZMZJBJ.js +208 -0
- package/dist/service-QSZMZJBJ.js.map +1 -0
- package/dist/static-viewer-MIPGZ4Z7.js +12 -0
- package/dist/static-viewer-MIPGZ4Z7.js.map +1 -0
- package/dist/test-SQ5ZHXWU.js +1067 -0
- package/dist/test-SQ5ZHXWU.js.map +1 -0
- package/dist/tokens-HSGMYK64.js +173 -0
- package/dist/tokens-HSGMYK64.js.map +1 -0
- package/dist/viewer-YRF4SQE4.js +11101 -0
- package/dist/viewer-YRF4SQE4.js.map +1 -0
- package/package.json +107 -0
- package/src/ai.ts +266 -0
- package/src/analyze.ts +265 -0
- package/src/bin.ts +916 -0
- package/src/build.ts +248 -0
- package/src/commands/a11y.ts +302 -0
- package/src/commands/add.ts +313 -0
- package/src/commands/audit.ts +195 -0
- package/src/commands/baseline.ts +221 -0
- package/src/commands/build.ts +144 -0
- package/src/commands/compare.ts +337 -0
- package/src/commands/context.ts +107 -0
- package/src/commands/dev.ts +107 -0
- package/src/commands/enhance.ts +858 -0
- package/src/commands/generate.ts +391 -0
- package/src/commands/init.ts +531 -0
- package/src/commands/link/figma.ts +645 -0
- package/src/commands/link/index.ts +10 -0
- package/src/commands/link/storybook.ts +267 -0
- package/src/commands/list.ts +49 -0
- package/src/commands/metrics.ts +114 -0
- package/src/commands/reset.ts +242 -0
- package/src/commands/scan.ts +537 -0
- package/src/commands/storygen.ts +207 -0
- package/src/commands/tokens.ts +251 -0
- package/src/commands/validate.ts +93 -0
- package/src/commands/verify.ts +215 -0
- package/src/core/composition.test.ts +262 -0
- package/src/core/composition.ts +255 -0
- package/src/core/config.ts +84 -0
- package/src/core/constants.ts +111 -0
- package/src/core/context.ts +380 -0
- package/src/core/defineSegment.ts +137 -0
- package/src/core/discovery.ts +337 -0
- package/src/core/figma.ts +263 -0
- package/src/core/fragment-types.ts +214 -0
- package/src/core/generators/context.ts +389 -0
- package/src/core/generators/index.ts +23 -0
- package/src/core/generators/registry.ts +364 -0
- package/src/core/generators/typescript-extractor.ts +374 -0
- package/src/core/importAnalyzer.ts +217 -0
- package/src/core/index.ts +149 -0
- package/src/core/loader.ts +155 -0
- package/src/core/node.ts +63 -0
- package/src/core/parser.ts +551 -0
- package/src/core/previewLoader.ts +172 -0
- package/src/core/schema/fragment.schema.json +189 -0
- package/src/core/schema/registry.schema.json +137 -0
- package/src/core/schema.ts +182 -0
- package/src/core/storyAdapter.test.ts +571 -0
- package/src/core/storyAdapter.ts +761 -0
- package/src/core/token-types.ts +287 -0
- package/src/core/types.ts +754 -0
- package/src/diff.ts +323 -0
- package/src/index.ts +43 -0
- package/src/mcp/__tests__/projectFields.test.ts +130 -0
- package/src/mcp/bin.ts +36 -0
- package/src/mcp/index.ts +8 -0
- package/src/mcp/server.ts +1310 -0
- package/src/mcp/utils.ts +54 -0
- package/src/mcp-bin.ts +36 -0
- package/src/migrate/__tests__/argTypes/argTypes.test.ts +189 -0
- package/src/migrate/__tests__/args/args.test.ts +452 -0
- package/src/migrate/__tests__/meta/meta.test.ts +198 -0
- package/src/migrate/__tests__/stories/stories.test.ts +278 -0
- package/src/migrate/__tests__/utils/utils.test.ts +371 -0
- package/src/migrate/__tests__/values/values.test.ts +303 -0
- package/src/migrate/bin.ts +108 -0
- package/src/migrate/converter.ts +658 -0
- package/src/migrate/detect.ts +196 -0
- package/src/migrate/index.ts +45 -0
- package/src/migrate/migrate.ts +163 -0
- package/src/migrate/parser.ts +1136 -0
- package/src/migrate/report.ts +624 -0
- package/src/migrate/types.ts +169 -0
- package/src/screenshot.ts +249 -0
- package/src/service/__tests__/ast-utils.test.ts +426 -0
- package/src/service/__tests__/enhance-scanner.test.ts +200 -0
- package/src/service/__tests__/figma/figma.test.ts +652 -0
- package/src/service/__tests__/metrics-store.test.ts +409 -0
- package/src/service/__tests__/patch-generator.test.ts +186 -0
- package/src/service/__tests__/props-extractor.test.ts +365 -0
- package/src/service/__tests__/token-registry.test.ts +267 -0
- package/src/service/analytics.ts +659 -0
- package/src/service/ast-utils.ts +444 -0
- package/src/service/browser-pool.ts +339 -0
- package/src/service/capture.ts +267 -0
- package/src/service/diff.ts +279 -0
- package/src/service/enhance/aggregator.ts +489 -0
- package/src/service/enhance/cache.ts +275 -0
- package/src/service/enhance/codebase-scanner.ts +357 -0
- package/src/service/enhance/context-generator.ts +529 -0
- package/src/service/enhance/doc-extractor.ts +523 -0
- package/src/service/enhance/index.ts +131 -0
- package/src/service/enhance/props-extractor.ts +665 -0
- package/src/service/enhance/scanner.ts +445 -0
- package/src/service/enhance/storybook-parser.ts +552 -0
- package/src/service/enhance/types.ts +346 -0
- package/src/service/enhance/variant-renderer.ts +479 -0
- package/src/service/figma.ts +1008 -0
- package/src/service/index.ts +249 -0
- package/src/service/metrics-store.ts +333 -0
- package/src/service/patch-generator.ts +349 -0
- package/src/service/report.ts +854 -0
- package/src/service/storage.ts +401 -0
- package/src/service/token-fixes.ts +281 -0
- package/src/service/token-parser.ts +504 -0
- package/src/service/token-registry.ts +721 -0
- package/src/service/utils.ts +172 -0
- package/src/setup.ts +241 -0
- package/src/shared/command-wrapper.ts +81 -0
- package/src/shared/dev-server-client.ts +199 -0
- package/src/shared/index.ts +8 -0
- package/src/shared/segment-loader.ts +59 -0
- package/src/shared/types.ts +147 -0
- package/src/static-viewer.ts +715 -0
- package/src/test/discovery.ts +172 -0
- package/src/test/index.ts +281 -0
- package/src/test/reporters/console.ts +194 -0
- package/src/test/reporters/json.ts +190 -0
- package/src/test/reporters/junit.ts +186 -0
- package/src/test/runner.ts +598 -0
- package/src/test/types.ts +245 -0
- package/src/test/watch.ts +200 -0
- package/src/validators.ts +152 -0
- package/src/viewer/__tests__/jsx-parser.test.ts +502 -0
- package/src/viewer/__tests__/render-utils.test.ts +232 -0
- package/src/viewer/__tests__/style-utils.test.ts +404 -0
- package/src/viewer/bin.ts +86 -0
- package/src/viewer/cli/health.ts +256 -0
- package/src/viewer/cli/index.ts +33 -0
- package/src/viewer/cli/scan.ts +124 -0
- package/src/viewer/cli/utils.ts +174 -0
- package/src/viewer/components/AccessibilityPanel.tsx +1404 -0
- package/src/viewer/components/ActionCapture.tsx +172 -0
- package/src/viewer/components/ActionsPanel.tsx +371 -0
- package/src/viewer/components/App.tsx +638 -0
- package/src/viewer/components/BottomPanel.tsx +224 -0
- package/src/viewer/components/CodePanel.tsx +589 -0
- package/src/viewer/components/CommandPalette.tsx +336 -0
- package/src/viewer/components/ComponentGraph.tsx +394 -0
- package/src/viewer/components/ComponentHeader.tsx +85 -0
- package/src/viewer/components/ContractPanel.tsx +234 -0
- package/src/viewer/components/ErrorBoundary.tsx +85 -0
- package/src/viewer/components/FigmaEmbed.tsx +231 -0
- package/src/viewer/components/FragmentEditor.tsx +485 -0
- package/src/viewer/components/HealthDashboard.tsx +452 -0
- package/src/viewer/components/HmrStatusIndicator.tsx +71 -0
- package/src/viewer/components/Icons.tsx +417 -0
- package/src/viewer/components/InteractionsPanel.tsx +720 -0
- package/src/viewer/components/IsolatedPreviewFrame.tsx +321 -0
- package/src/viewer/components/IsolatedRender.tsx +111 -0
- package/src/viewer/components/KeyboardShortcutsHelp.tsx +89 -0
- package/src/viewer/components/LandingPage.tsx +441 -0
- package/src/viewer/components/Layout.tsx +22 -0
- package/src/viewer/components/LeftSidebar.tsx +391 -0
- package/src/viewer/components/MultiViewportPreview.tsx +429 -0
- package/src/viewer/components/PreviewArea.tsx +404 -0
- package/src/viewer/components/PreviewFrameHost.tsx +310 -0
- package/src/viewer/components/PreviewPane.tsx +150 -0
- package/src/viewer/components/PreviewToolbar.tsx +176 -0
- package/src/viewer/components/PropsEditor.tsx +512 -0
- package/src/viewer/components/PropsTable.tsx +98 -0
- package/src/viewer/components/RelationsSection.tsx +57 -0
- package/src/viewer/components/ResizablePanel.tsx +328 -0
- package/src/viewer/components/RightSidebar.tsx +118 -0
- package/src/viewer/components/ScreenshotButton.tsx +90 -0
- package/src/viewer/components/Sidebar.tsx +169 -0
- package/src/viewer/components/SkeletonLoader.tsx +156 -0
- package/src/viewer/components/StoryRenderer.tsx +128 -0
- package/src/viewer/components/ThemeProvider.tsx +96 -0
- package/src/viewer/components/Toast.tsx +67 -0
- package/src/viewer/components/TokenStylePanel.tsx +708 -0
- package/src/viewer/components/UsageSection.tsx +95 -0
- package/src/viewer/components/VariantMatrix.tsx +350 -0
- package/src/viewer/components/VariantRenderer.tsx +131 -0
- package/src/viewer/components/VariantTabs.tsx +84 -0
- package/src/viewer/components/ViewportSelector.tsx +165 -0
- package/src/viewer/components/_future/CreatePage.tsx +836 -0
- package/src/viewer/composition-renderer.ts +381 -0
- package/src/viewer/constants/index.ts +1 -0
- package/src/viewer/constants/ui.ts +185 -0
- package/src/viewer/entry.tsx +299 -0
- package/src/viewer/hooks/index.ts +2 -0
- package/src/viewer/hooks/useA11yCache.ts +383 -0
- package/src/viewer/hooks/useA11yService.ts +498 -0
- package/src/viewer/hooks/useActions.ts +138 -0
- package/src/viewer/hooks/useAppState.ts +124 -0
- package/src/viewer/hooks/useFigmaIntegration.ts +132 -0
- package/src/viewer/hooks/useHmrStatus.ts +109 -0
- package/src/viewer/hooks/useKeyboardShortcuts.ts +222 -0
- package/src/viewer/hooks/usePreviewBridge.ts +347 -0
- package/src/viewer/hooks/useScrollSpy.ts +78 -0
- package/src/viewer/hooks/useUrlState.ts +330 -0
- package/src/viewer/hooks/useViewSettings.ts +125 -0
- package/src/viewer/index.html +28 -0
- package/src/viewer/index.ts +14 -0
- package/src/viewer/intelligence/healthReport.ts +505 -0
- package/src/viewer/intelligence/styleDrift.ts +340 -0
- package/src/viewer/intelligence/usageScanner.ts +309 -0
- package/src/viewer/jsx-parser.ts +485 -0
- package/src/viewer/postcss.config.js +6 -0
- package/src/viewer/preview-frame-entry.tsx +25 -0
- package/src/viewer/preview-frame.html +109 -0
- package/src/viewer/render-template.html +68 -0
- package/src/viewer/render-utils.ts +170 -0
- package/src/viewer/server.ts +276 -0
- package/src/viewer/style-utils.ts +414 -0
- package/src/viewer/styles/globals.css +355 -0
- package/src/viewer/tailwind.config.js +37 -0
- package/src/viewer/types/a11y.ts +197 -0
- package/src/viewer/utils/a11y-fixes.ts +471 -0
- package/src/viewer/utils/actionExport.ts +372 -0
- package/src/viewer/utils/colorSchemes.ts +201 -0
- package/src/viewer/utils/detectRelationships.ts +256 -0
- package/src/viewer/vite-plugin.ts +2143 -0
package/src/bin.ts
ADDED
|
@@ -0,0 +1,916 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* fragments CLI - Design system documentation and compliance tool
|
|
4
|
+
*
|
|
5
|
+
* This file registers all commands with Commander. Command implementations
|
|
6
|
+
* are in the commands/ directory.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Command } from 'commander';
|
|
10
|
+
import pc from 'picocolors';
|
|
11
|
+
import { BRAND } from './core/index.js';
|
|
12
|
+
import { loadConfig } from './core/node.js';
|
|
13
|
+
|
|
14
|
+
// Import command implementations
|
|
15
|
+
import { validate } from './commands/validate.js';
|
|
16
|
+
import { build } from './commands/build.js';
|
|
17
|
+
import { context } from './commands/context.js';
|
|
18
|
+
import { list } from './commands/list.js';
|
|
19
|
+
import { reset } from './commands/reset.js';
|
|
20
|
+
import { dev } from './commands/dev.js';
|
|
21
|
+
import { compare } from './commands/compare.js';
|
|
22
|
+
import { verify } from './commands/verify.js';
|
|
23
|
+
import { audit } from './commands/audit.js';
|
|
24
|
+
import { a11y } from './commands/a11y.js';
|
|
25
|
+
import { storygen } from './commands/storygen.js';
|
|
26
|
+
import { metrics } from './commands/metrics.js';
|
|
27
|
+
import { baseline } from './commands/baseline.js';
|
|
28
|
+
import { add } from './commands/add.js';
|
|
29
|
+
import { linkFigma, linkStorybook } from './commands/link/index.js';
|
|
30
|
+
import { enhance } from './commands/enhance.js';
|
|
31
|
+
import { scan } from './commands/scan.js';
|
|
32
|
+
|
|
33
|
+
// Import existing commands that were already extracted
|
|
34
|
+
import { runScreenshotCommand } from './screenshot.js';
|
|
35
|
+
import { runDiffCommand } from './diff.js';
|
|
36
|
+
import { runAnalyzeCommand } from './analyze.js';
|
|
37
|
+
|
|
38
|
+
const program = new Command();
|
|
39
|
+
|
|
40
|
+
program
|
|
41
|
+
.name(BRAND.cliCommand)
|
|
42
|
+
.description(`${BRAND.name} - Design system documentation and compliance tool`)
|
|
43
|
+
.version('0.1.0');
|
|
44
|
+
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// VALIDATE COMMAND
|
|
47
|
+
// ============================================================================
|
|
48
|
+
program
|
|
49
|
+
.command('validate')
|
|
50
|
+
.description('Validate fragment files')
|
|
51
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
52
|
+
.option('--schema', 'Validate fragment schema only')
|
|
53
|
+
.option('--coverage', 'Validate coverage only')
|
|
54
|
+
.action(async (options) => {
|
|
55
|
+
try {
|
|
56
|
+
const result = await validate(options);
|
|
57
|
+
if (!result.valid) {
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// ============================================================================
|
|
67
|
+
// BUILD COMMAND
|
|
68
|
+
// ============================================================================
|
|
69
|
+
program
|
|
70
|
+
.command('build')
|
|
71
|
+
.description(`Build compiled ${BRAND.outFile} and ${BRAND.dataDir}/ directory`)
|
|
72
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
73
|
+
.option('-o, --output <path>', 'Output file path')
|
|
74
|
+
.option('--registry', `Also generate ${BRAND.dataDir}/${BRAND.registryFile} and ${BRAND.contextFile}`)
|
|
75
|
+
.option('--registry-only', `Only generate ${BRAND.dataDir}/ directory (skip ${BRAND.outFile})`)
|
|
76
|
+
.option('--from-source', 'Build from source code (zero-config, no fragment files needed)')
|
|
77
|
+
.option('--skip-usage', 'Skip usage analysis when building from source')
|
|
78
|
+
.option('--skip-storybook', 'Skip Storybook parsing when building from source')
|
|
79
|
+
.option('-v, --verbose', 'Verbose output')
|
|
80
|
+
.action(async (options) => {
|
|
81
|
+
try {
|
|
82
|
+
const result = await build({
|
|
83
|
+
config: options.config,
|
|
84
|
+
output: options.output,
|
|
85
|
+
registry: options.registry,
|
|
86
|
+
registryOnly: options.registryOnly,
|
|
87
|
+
fromSource: options.fromSource,
|
|
88
|
+
skipUsage: options.skipUsage,
|
|
89
|
+
skipStorybook: options.skipStorybook,
|
|
90
|
+
verbose: options.verbose,
|
|
91
|
+
});
|
|
92
|
+
if (!result.success) {
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// ============================================================================
|
|
102
|
+
// CONTEXT COMMAND
|
|
103
|
+
// ============================================================================
|
|
104
|
+
program
|
|
105
|
+
.command('context')
|
|
106
|
+
.description('Generate AI-ready context for your design system')
|
|
107
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
108
|
+
.option('-i, --input <path>', `Path to ${BRAND.outFile} (builds if not provided)`)
|
|
109
|
+
.option('-f, --format <format>', 'Output format (markdown/json)', 'markdown')
|
|
110
|
+
.option('--compact', 'Minimal output for token efficiency')
|
|
111
|
+
.option('--code', 'Include code examples')
|
|
112
|
+
.option('--relations', 'Include component relationships')
|
|
113
|
+
.option('--tokens', 'Only output token estimate')
|
|
114
|
+
.action(async (options) => {
|
|
115
|
+
try {
|
|
116
|
+
const result = await context({
|
|
117
|
+
config: options.config,
|
|
118
|
+
input: options.input,
|
|
119
|
+
format: options.format,
|
|
120
|
+
compact: options.compact,
|
|
121
|
+
includeCode: options.code,
|
|
122
|
+
includeRelations: options.relations,
|
|
123
|
+
tokensOnly: options.tokens,
|
|
124
|
+
});
|
|
125
|
+
if (!result.success) {
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// ============================================================================
|
|
135
|
+
// AI COMMAND (for Claude Desktop integration)
|
|
136
|
+
// ============================================================================
|
|
137
|
+
program
|
|
138
|
+
.command('ai')
|
|
139
|
+
.description('Generate context optimized for AI assistants (Claude Desktop)')
|
|
140
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
141
|
+
.option('--live', 'Connect to running dev server for live data')
|
|
142
|
+
.option('-p, --port <port>', 'Dev server port', '6006')
|
|
143
|
+
.action(async (options) => {
|
|
144
|
+
try {
|
|
145
|
+
// If live mode, fetch from dev server
|
|
146
|
+
if (options.live) {
|
|
147
|
+
const baseUrl = `http://localhost:${options.port}`;
|
|
148
|
+
const response = await fetch(`${baseUrl}/${BRAND.nameLower}/context?format=markdown`);
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
throw new Error('Failed to fetch context. Make sure dev server is running.');
|
|
151
|
+
}
|
|
152
|
+
const contextContent = await response.text();
|
|
153
|
+
console.log(contextContent);
|
|
154
|
+
} else {
|
|
155
|
+
// Build and generate context
|
|
156
|
+
const result = await context({
|
|
157
|
+
config: options.config,
|
|
158
|
+
format: 'markdown',
|
|
159
|
+
compact: false,
|
|
160
|
+
includeCode: true,
|
|
161
|
+
includeRelations: true,
|
|
162
|
+
});
|
|
163
|
+
if (!result.success) {
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} catch (error) {
|
|
168
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// ============================================================================
|
|
174
|
+
// LIST COMMAND
|
|
175
|
+
// ============================================================================
|
|
176
|
+
program
|
|
177
|
+
.command('list')
|
|
178
|
+
.description('List all discovered fragment files')
|
|
179
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
180
|
+
.action(async (options) => {
|
|
181
|
+
try {
|
|
182
|
+
await list(options);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// ============================================================================
|
|
190
|
+
// RESET COMMAND
|
|
191
|
+
// ============================================================================
|
|
192
|
+
program
|
|
193
|
+
.command('reset')
|
|
194
|
+
.description('Reset to initial state (delete all generated files)')
|
|
195
|
+
.option('-y, --yes', 'Skip confirmation prompt')
|
|
196
|
+
.option('--dry-run', 'Show what would be deleted without deleting')
|
|
197
|
+
.action(async (options) => {
|
|
198
|
+
try {
|
|
199
|
+
await reset(options);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// ============================================================================
|
|
207
|
+
// LINK COMMAND (parent for subcommands)
|
|
208
|
+
// ============================================================================
|
|
209
|
+
const linkCommand = program
|
|
210
|
+
.command('link')
|
|
211
|
+
.description('Link external resources (Figma designs, Storybook stories) to fragments');
|
|
212
|
+
|
|
213
|
+
// Link Figma subcommand
|
|
214
|
+
linkCommand
|
|
215
|
+
.command('figma')
|
|
216
|
+
.argument('[figma-url]', 'Figma file URL to link components from')
|
|
217
|
+
.description('Interactive wizard to link Figma components to code')
|
|
218
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
219
|
+
.option('--auto', 'Auto-link matching components without prompts')
|
|
220
|
+
.option('--dry-run', 'Show matches without updating files')
|
|
221
|
+
.option('--no-variants', 'Skip linking individual variants to their Figma frames')
|
|
222
|
+
.action(async (figmaUrl, options) => {
|
|
223
|
+
try {
|
|
224
|
+
await linkFigma(figmaUrl, {
|
|
225
|
+
config: options.config,
|
|
226
|
+
auto: options.auto,
|
|
227
|
+
dryRun: options.dryRun,
|
|
228
|
+
variants: options.variants,
|
|
229
|
+
});
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Link Storybook subcommand
|
|
237
|
+
linkCommand
|
|
238
|
+
.command('storybook')
|
|
239
|
+
.description('Bootstrap fragments from existing Storybook stories')
|
|
240
|
+
.option('-c, --config <path>', 'Path to .storybook/main.* config')
|
|
241
|
+
.option('-o, --out <dir>', 'Output directory for fragment files')
|
|
242
|
+
.option('--yes', 'Skip confirmation prompts')
|
|
243
|
+
.option('--dry-run', 'Preview what would be generated without writing files')
|
|
244
|
+
.option('--include <glob>', 'Only process stories matching glob')
|
|
245
|
+
.option('--exclude <glob>', 'Skip stories matching glob')
|
|
246
|
+
.action(async (options) => {
|
|
247
|
+
try {
|
|
248
|
+
await linkStorybook({
|
|
249
|
+
config: options.config,
|
|
250
|
+
out: options.out,
|
|
251
|
+
yes: options.yes,
|
|
252
|
+
dryRun: options.dryRun,
|
|
253
|
+
include: options.include,
|
|
254
|
+
exclude: options.exclude,
|
|
255
|
+
});
|
|
256
|
+
} catch (error) {
|
|
257
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
258
|
+
process.exit(1);
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// ============================================================================
|
|
263
|
+
// DEV COMMAND
|
|
264
|
+
// ============================================================================
|
|
265
|
+
program
|
|
266
|
+
.command('dev')
|
|
267
|
+
.description('Start the development server with live component rendering')
|
|
268
|
+
.option('-p, --port <port>', 'Port to run on', '6006')
|
|
269
|
+
.option('--no-open', 'Do not open browser')
|
|
270
|
+
.option('--skip-setup', 'Skip auto-setup (Storybook import, build, Figma link)')
|
|
271
|
+
.option('--skip-storybook', 'Skip auto-importing from Storybook')
|
|
272
|
+
.option('--skip-figma', 'Skip Figma link check')
|
|
273
|
+
.option('--skip-build', `Skip auto-building ${BRAND.outFile}`)
|
|
274
|
+
.action(async (options) => {
|
|
275
|
+
try {
|
|
276
|
+
await dev({
|
|
277
|
+
port: options.port,
|
|
278
|
+
open: options.open,
|
|
279
|
+
skipSetup: options.skipSetup,
|
|
280
|
+
skipStorybook: options.skipStorybook,
|
|
281
|
+
skipFigma: options.skipFigma,
|
|
282
|
+
skipBuild: options.skipBuild,
|
|
283
|
+
});
|
|
284
|
+
} catch (error) {
|
|
285
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
286
|
+
if (error instanceof Error && error.stack) {
|
|
287
|
+
console.error(pc.dim(error.stack));
|
|
288
|
+
}
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// ============================================================================
|
|
294
|
+
// SCREENSHOT COMMAND
|
|
295
|
+
// ============================================================================
|
|
296
|
+
program
|
|
297
|
+
.command('screenshot')
|
|
298
|
+
.description('Capture screenshots of component variants')
|
|
299
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
300
|
+
.option('--component <name>', 'Capture specific component only')
|
|
301
|
+
.option('--variant <name>', 'Capture specific variant only')
|
|
302
|
+
.option('--theme <theme>', 'Theme to capture (light/dark)', 'light')
|
|
303
|
+
.option('--update', 'Update existing baselines')
|
|
304
|
+
.option('--width <pixels>', 'Viewport width', parseInt)
|
|
305
|
+
.option('--height <pixels>', 'Viewport height', parseInt)
|
|
306
|
+
.option('--ci', 'CI mode - no interactive prompts')
|
|
307
|
+
.action(async (options) => {
|
|
308
|
+
try {
|
|
309
|
+
const { config, configDir } = await loadConfig(options.config);
|
|
310
|
+
const result = await runScreenshotCommand(config, configDir, {
|
|
311
|
+
component: options.component,
|
|
312
|
+
variant: options.variant,
|
|
313
|
+
theme: options.theme,
|
|
314
|
+
update: options.update,
|
|
315
|
+
ci: options.ci,
|
|
316
|
+
width: options.width,
|
|
317
|
+
height: options.height,
|
|
318
|
+
});
|
|
319
|
+
if (!result.success) {
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
} catch (error) {
|
|
323
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
324
|
+
process.exit(1);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// ============================================================================
|
|
329
|
+
// DIFF COMMAND
|
|
330
|
+
// ============================================================================
|
|
331
|
+
program
|
|
332
|
+
.command('diff')
|
|
333
|
+
.argument('[component]', 'Component name to diff (optional)')
|
|
334
|
+
.description('Compare current renders against baselines')
|
|
335
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
336
|
+
.option('--variant <name>', 'Compare specific variant only')
|
|
337
|
+
.option('--theme <theme>', 'Theme to compare (light/dark)', 'light')
|
|
338
|
+
.option('--threshold <percent>', 'Diff threshold percentage', parseFloat)
|
|
339
|
+
.option('--ci', 'CI mode - exit 1 on differences')
|
|
340
|
+
.option('--open', 'Open diff images')
|
|
341
|
+
.action(async (component, options) => {
|
|
342
|
+
try {
|
|
343
|
+
const { config, configDir } = await loadConfig(options.config);
|
|
344
|
+
const result = await runDiffCommand(config, configDir, {
|
|
345
|
+
component,
|
|
346
|
+
variant: options.variant,
|
|
347
|
+
theme: options.theme,
|
|
348
|
+
threshold: options.threshold,
|
|
349
|
+
ci: options.ci,
|
|
350
|
+
open: options.open,
|
|
351
|
+
});
|
|
352
|
+
if (!result.success && options.ci) {
|
|
353
|
+
process.exit(1);
|
|
354
|
+
}
|
|
355
|
+
} catch (error) {
|
|
356
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
357
|
+
process.exit(1);
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// ============================================================================
|
|
362
|
+
// COMPARE COMMAND
|
|
363
|
+
// ============================================================================
|
|
364
|
+
program
|
|
365
|
+
.command('compare')
|
|
366
|
+
.argument('[component]', 'Component name to compare')
|
|
367
|
+
.description('Compare component renders against Figma designs')
|
|
368
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
369
|
+
.option('--variant <name>', 'Compare specific variant')
|
|
370
|
+
.option('--figma <url>', 'Figma frame URL (uses fragment figma link if not provided)')
|
|
371
|
+
.option('--threshold <percent>', 'Diff threshold percentage', parseFloat, 1.0)
|
|
372
|
+
.option('--all', 'Compare all components with Figma links')
|
|
373
|
+
.option('--output <dir>', 'Save diff images to directory')
|
|
374
|
+
.option('-p, --port <port>', 'Dev server port', '6006')
|
|
375
|
+
.action(async (component, options) => {
|
|
376
|
+
try {
|
|
377
|
+
const result = await compare(component, {
|
|
378
|
+
config: options.config,
|
|
379
|
+
variant: options.variant,
|
|
380
|
+
figma: options.figma,
|
|
381
|
+
threshold: options.threshold,
|
|
382
|
+
all: options.all,
|
|
383
|
+
output: options.output,
|
|
384
|
+
port: options.port,
|
|
385
|
+
});
|
|
386
|
+
if (!result.success) {
|
|
387
|
+
process.exit(1);
|
|
388
|
+
}
|
|
389
|
+
} catch (error) {
|
|
390
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
391
|
+
console.log(pc.dim(`\nMake sure the dev server is running: ${BRAND.cliCommand} dev`));
|
|
392
|
+
process.exit(1);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// ============================================================================
|
|
397
|
+
// ANALYZE COMMAND
|
|
398
|
+
// ============================================================================
|
|
399
|
+
program
|
|
400
|
+
.command('analyze')
|
|
401
|
+
.description('Analyze design system and generate report')
|
|
402
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
403
|
+
.option('-f, --format <format>', 'Output format (html/json/console)', 'html')
|
|
404
|
+
.option('-o, --output <path>', 'Output file path')
|
|
405
|
+
.option('--open', 'Open report in browser after generation')
|
|
406
|
+
.option('--ci', 'CI mode - exit with code based on score')
|
|
407
|
+
.option('--min-score <score>', 'Minimum score to pass in CI mode', parseFloat)
|
|
408
|
+
.action(async (options) => {
|
|
409
|
+
try {
|
|
410
|
+
const { config, configDir } = await loadConfig(options.config);
|
|
411
|
+
const result = await runAnalyzeCommand(config, configDir, {
|
|
412
|
+
format: options.format,
|
|
413
|
+
output: options.output,
|
|
414
|
+
open: options.open,
|
|
415
|
+
ci: options.ci,
|
|
416
|
+
minScore: options.minScore,
|
|
417
|
+
});
|
|
418
|
+
if (!result.success) {
|
|
419
|
+
process.exit(1);
|
|
420
|
+
}
|
|
421
|
+
} catch (error) {
|
|
422
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
423
|
+
process.exit(1);
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// ============================================================================
|
|
428
|
+
// VERIFY COMMAND
|
|
429
|
+
// ============================================================================
|
|
430
|
+
program
|
|
431
|
+
.command('verify')
|
|
432
|
+
.argument('[component]', 'Component name to verify (optional, verifies all if omitted)')
|
|
433
|
+
.description('Verify component compliance for CI pipelines')
|
|
434
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
435
|
+
.option('--ci', 'CI mode - output JSON and exit non-zero on failure')
|
|
436
|
+
.option('--min-compliance <percent>', 'Minimum compliance percentage (default: 80)', parseFloat, 80)
|
|
437
|
+
.option('-p, --port <port>', 'Dev server port', '6006')
|
|
438
|
+
.action(async (component, options) => {
|
|
439
|
+
try {
|
|
440
|
+
const summary = await verify(component, {
|
|
441
|
+
config: options.config,
|
|
442
|
+
ci: options.ci,
|
|
443
|
+
minCompliance: options.minCompliance,
|
|
444
|
+
port: options.port,
|
|
445
|
+
});
|
|
446
|
+
if (!summary.passed && options.ci) {
|
|
447
|
+
process.exit(1);
|
|
448
|
+
}
|
|
449
|
+
} catch (error) {
|
|
450
|
+
if (options.ci) {
|
|
451
|
+
console.log(JSON.stringify({ error: error instanceof Error ? error.message : 'Verification failed' }));
|
|
452
|
+
} else {
|
|
453
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
454
|
+
}
|
|
455
|
+
process.exit(1);
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
// ============================================================================
|
|
460
|
+
// AUDIT COMMAND
|
|
461
|
+
// ============================================================================
|
|
462
|
+
program
|
|
463
|
+
.command('audit')
|
|
464
|
+
.description('Scan all fragments and show compliance metrics across the design system')
|
|
465
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
466
|
+
.option('--json', 'Output machine-readable JSON format')
|
|
467
|
+
.option('--sort <field>', 'Sort by field: compliance, name, hardcoded (default: compliance)', 'compliance')
|
|
468
|
+
.option('-p, --port <port>', 'Dev server port', '6006')
|
|
469
|
+
.action(async (options) => {
|
|
470
|
+
try {
|
|
471
|
+
await audit({
|
|
472
|
+
config: options.config,
|
|
473
|
+
json: options.json,
|
|
474
|
+
sort: options.sort,
|
|
475
|
+
port: options.port,
|
|
476
|
+
});
|
|
477
|
+
} catch (error) {
|
|
478
|
+
if (options.json) {
|
|
479
|
+
console.log(JSON.stringify({ error: error instanceof Error ? error.message : 'Audit failed' }));
|
|
480
|
+
} else {
|
|
481
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
482
|
+
}
|
|
483
|
+
process.exit(1);
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// ============================================================================
|
|
488
|
+
// A11Y COMMAND
|
|
489
|
+
// ============================================================================
|
|
490
|
+
program
|
|
491
|
+
.command('a11y')
|
|
492
|
+
.description('Run accessibility checks on all component variants')
|
|
493
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
494
|
+
.option('--json', 'Output results as JSON')
|
|
495
|
+
.option('--ci', 'CI mode (exit code 1 if any critical/serious violations)')
|
|
496
|
+
.option('--component <name>', 'Check specific component only')
|
|
497
|
+
.option('-p, --port <port>', 'Dev server port', '6006')
|
|
498
|
+
.action(async (options) => {
|
|
499
|
+
try {
|
|
500
|
+
await a11y({
|
|
501
|
+
config: options.config,
|
|
502
|
+
json: options.json,
|
|
503
|
+
ci: options.ci,
|
|
504
|
+
component: options.component,
|
|
505
|
+
port: options.port,
|
|
506
|
+
});
|
|
507
|
+
} catch (error) {
|
|
508
|
+
if (options.json) {
|
|
509
|
+
console.log(JSON.stringify({ error: error instanceof Error ? error.message : 'A11y check failed' }));
|
|
510
|
+
} else {
|
|
511
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
512
|
+
}
|
|
513
|
+
process.exit(1);
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
// ============================================================================
|
|
518
|
+
// ENHANCE COMMAND
|
|
519
|
+
// ============================================================================
|
|
520
|
+
program
|
|
521
|
+
.command('enhance')
|
|
522
|
+
.description('AI-powered documentation generation from codebase analysis')
|
|
523
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
524
|
+
.option('--component <name>', 'Enhance specific component (or "all")')
|
|
525
|
+
.option('--yes', 'Skip confirmation prompts')
|
|
526
|
+
.option('--dry-run', 'Only analyze, do not modify files')
|
|
527
|
+
.option('--format <format>', 'Output format: interactive, json, quiet, context', 'interactive')
|
|
528
|
+
.option('--provider <provider>', 'AI provider: anthropic, openai (auto-detected from API key)')
|
|
529
|
+
.option('--api-key <key>', 'API key (or use ANTHROPIC_API_KEY/OPENAI_API_KEY env)')
|
|
530
|
+
.option('--model <model>', 'AI model to use (default: claude-sonnet-4 for anthropic, gpt-4o for openai)')
|
|
531
|
+
.option('--root <dir>', 'Root directory to scan', process.cwd())
|
|
532
|
+
.option('--context-only', 'Output context for IDE AI (Cursor, Copilot) without calling API')
|
|
533
|
+
.action(async (options) => {
|
|
534
|
+
try {
|
|
535
|
+
await enhance({
|
|
536
|
+
config: options.config,
|
|
537
|
+
component: options.component,
|
|
538
|
+
yes: options.yes,
|
|
539
|
+
dryRun: options.dryRun,
|
|
540
|
+
format: options.format,
|
|
541
|
+
provider: options.provider,
|
|
542
|
+
apiKey: options.apiKey,
|
|
543
|
+
model: options.model,
|
|
544
|
+
root: options.root,
|
|
545
|
+
contextOnly: options.contextOnly,
|
|
546
|
+
});
|
|
547
|
+
} catch (error) {
|
|
548
|
+
if (options.format === 'json') {
|
|
549
|
+
console.log(JSON.stringify({ success: false, error: error instanceof Error ? error.message : 'Enhance failed' }));
|
|
550
|
+
} else {
|
|
551
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
552
|
+
}
|
|
553
|
+
process.exit(1);
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// ============================================================================
|
|
558
|
+
// SCAN COMMAND
|
|
559
|
+
// ============================================================================
|
|
560
|
+
program
|
|
561
|
+
.command('scan')
|
|
562
|
+
.description(`Zero-config ${BRAND.outFile} generation from source code`)
|
|
563
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
564
|
+
.option('-o, --output <path>', 'Output file path', BRAND.outFile)
|
|
565
|
+
.option('--patterns <patterns...>', 'Component file patterns to scan')
|
|
566
|
+
.option('--barrel <files...>', 'Barrel export files to parse')
|
|
567
|
+
.option('--usage-dir <dir>', 'Directory to scan for usage patterns')
|
|
568
|
+
.option('--skip-usage', 'Skip usage pattern analysis')
|
|
569
|
+
.option('--skip-storybook', 'Skip Storybook story parsing')
|
|
570
|
+
.option('-v, --verbose', 'Verbose output')
|
|
571
|
+
.action(async (options) => {
|
|
572
|
+
try {
|
|
573
|
+
const result = await scan({
|
|
574
|
+
config: options.config,
|
|
575
|
+
output: options.output,
|
|
576
|
+
componentPatterns: options.patterns,
|
|
577
|
+
barrelFiles: options.barrel,
|
|
578
|
+
usageDir: options.usageDir,
|
|
579
|
+
skipUsage: options.skipUsage,
|
|
580
|
+
skipStorybook: options.skipStorybook,
|
|
581
|
+
verbose: options.verbose,
|
|
582
|
+
});
|
|
583
|
+
if (!result.success) {
|
|
584
|
+
process.exit(1);
|
|
585
|
+
}
|
|
586
|
+
} catch (error) {
|
|
587
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
588
|
+
process.exit(1);
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
// ============================================================================
|
|
593
|
+
// STORYGEN COMMAND
|
|
594
|
+
// ============================================================================
|
|
595
|
+
program
|
|
596
|
+
.command('storygen')
|
|
597
|
+
.description('Generate Storybook stories from fragment definitions')
|
|
598
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
599
|
+
.option('-o, --output <dir>', 'Output directory', '.storybook/generated')
|
|
600
|
+
.option('--watch', 'Watch for segment changes and regenerate')
|
|
601
|
+
.option('--format <format>', 'Story format (csf3)', 'csf3')
|
|
602
|
+
.action(async (options) => {
|
|
603
|
+
try {
|
|
604
|
+
await storygen({
|
|
605
|
+
config: options.config,
|
|
606
|
+
output: options.output,
|
|
607
|
+
watch: options.watch,
|
|
608
|
+
format: options.format,
|
|
609
|
+
});
|
|
610
|
+
} catch (error) {
|
|
611
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
612
|
+
process.exit(1);
|
|
613
|
+
}
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
// ============================================================================
|
|
617
|
+
// METRICS COMMAND
|
|
618
|
+
// ============================================================================
|
|
619
|
+
program
|
|
620
|
+
.command('metrics')
|
|
621
|
+
.argument('[component]', 'Component name (optional, shows system-wide if omitted)')
|
|
622
|
+
.description('View compliance trends over time')
|
|
623
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
624
|
+
.option('--days <number>', 'Number of days to look back', parseInt, 30)
|
|
625
|
+
.option('--json', 'Output JSON format')
|
|
626
|
+
.action(async (component, options) => {
|
|
627
|
+
try {
|
|
628
|
+
await metrics(component, {
|
|
629
|
+
config: options.config,
|
|
630
|
+
days: options.days,
|
|
631
|
+
json: options.json,
|
|
632
|
+
});
|
|
633
|
+
} catch (error) {
|
|
634
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
635
|
+
process.exit(1);
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
// ============================================================================
|
|
640
|
+
// BASELINE COMMAND
|
|
641
|
+
// ============================================================================
|
|
642
|
+
program
|
|
643
|
+
.command('baseline')
|
|
644
|
+
.description('Manage visual regression baselines')
|
|
645
|
+
.argument('<action>', 'Action to perform: update, list, delete')
|
|
646
|
+
.argument('[component]', 'Component name (optional for update/delete)')
|
|
647
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
648
|
+
.option('--variant <name>', 'Specific variant to update')
|
|
649
|
+
.option('--all', 'Update/delete all baselines')
|
|
650
|
+
.option('--theme <theme>', 'Theme for baseline (light/dark)', 'light')
|
|
651
|
+
.option('-p, --port <port>', 'Dev server port', '6006')
|
|
652
|
+
.action(async (action, component, options) => {
|
|
653
|
+
try {
|
|
654
|
+
await baseline(action, component, {
|
|
655
|
+
config: options.config,
|
|
656
|
+
variant: options.variant,
|
|
657
|
+
all: options.all,
|
|
658
|
+
theme: options.theme as 'light' | 'dark' | undefined,
|
|
659
|
+
port: options.port,
|
|
660
|
+
});
|
|
661
|
+
} catch (error) {
|
|
662
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
663
|
+
if (action === 'update') {
|
|
664
|
+
console.log(pc.dim(`\nMake sure the dev server is running: ${BRAND.cliCommand} dev`));
|
|
665
|
+
}
|
|
666
|
+
process.exit(1);
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
// ============================================================================
|
|
671
|
+
// VIEW COMMAND
|
|
672
|
+
// ============================================================================
|
|
673
|
+
program
|
|
674
|
+
.command('view')
|
|
675
|
+
.description(`Generate a static HTML viewer for ${BRAND.outFile}`)
|
|
676
|
+
.option('-i, --input <path>', `Path to ${BRAND.outFile}`, BRAND.outFile)
|
|
677
|
+
.option('-o, --output <path>', 'Output HTML file path', BRAND.viewerHtmlFile)
|
|
678
|
+
.option('--open', 'Open in browser after generation')
|
|
679
|
+
.action(async (options) => {
|
|
680
|
+
try {
|
|
681
|
+
const { generateViewerFromJson } = await import('./static-viewer.js');
|
|
682
|
+
const fs = await import('node:fs/promises');
|
|
683
|
+
const path = await import('node:path');
|
|
684
|
+
|
|
685
|
+
const inputPath = path.resolve(process.cwd(), options.input);
|
|
686
|
+
const outputPath = path.resolve(process.cwd(), options.output);
|
|
687
|
+
|
|
688
|
+
console.log(pc.cyan(`\n${BRAND.name} Viewer Generator\n`));
|
|
689
|
+
|
|
690
|
+
try {
|
|
691
|
+
await fs.access(inputPath);
|
|
692
|
+
} catch {
|
|
693
|
+
console.log(pc.red(`Error: ${options.input} not found.`));
|
|
694
|
+
console.log(pc.dim(`\nRun ${pc.cyan(`${BRAND.cliCommand} build`)} first to generate ${BRAND.outFile}\n`));
|
|
695
|
+
process.exit(1);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
console.log(pc.dim(`Reading: ${options.input}`));
|
|
699
|
+
const html = await generateViewerFromJson(inputPath);
|
|
700
|
+
|
|
701
|
+
await fs.writeFile(outputPath, html);
|
|
702
|
+
console.log(pc.green(`\n✓ Generated: ${options.output}\n`));
|
|
703
|
+
|
|
704
|
+
if (options.open) {
|
|
705
|
+
const { exec } = await import('node:child_process');
|
|
706
|
+
const openCmd = process.platform === 'darwin' ? 'open' :
|
|
707
|
+
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
708
|
+
exec(`${openCmd} "${outputPath}"`);
|
|
709
|
+
}
|
|
710
|
+
} catch (error) {
|
|
711
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
712
|
+
process.exit(1);
|
|
713
|
+
}
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
// ============================================================================
|
|
717
|
+
// ADD COMMAND
|
|
718
|
+
// ============================================================================
|
|
719
|
+
program
|
|
720
|
+
.command('add')
|
|
721
|
+
.argument('[name]', 'Component name (e.g., "Button", "TextField")')
|
|
722
|
+
.description('Scaffold a new component with fragment file')
|
|
723
|
+
.option('-c, --category <category>', 'Component category')
|
|
724
|
+
.option('-d, --dir <directory>', 'Output directory')
|
|
725
|
+
.option('-t, --template <template>', 'Template to use (action, form-input, layout, display)')
|
|
726
|
+
.option('--no-component', 'Only generate fragment file, skip component stub')
|
|
727
|
+
.action(async (name, options) => {
|
|
728
|
+
try {
|
|
729
|
+
await add(name, {
|
|
730
|
+
category: options.category,
|
|
731
|
+
dir: options.dir,
|
|
732
|
+
template: options.template,
|
|
733
|
+
component: options.component,
|
|
734
|
+
});
|
|
735
|
+
} catch (error) {
|
|
736
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
737
|
+
process.exit(1);
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
// ============================================================================
|
|
742
|
+
// INIT COMMAND
|
|
743
|
+
// ============================================================================
|
|
744
|
+
program
|
|
745
|
+
.command('init')
|
|
746
|
+
.description('Initialize fragments in a project (interactive by default)')
|
|
747
|
+
.option('--force', 'Overwrite existing config')
|
|
748
|
+
.option('-y, --yes', 'Non-interactive mode - auto-detect and use defaults')
|
|
749
|
+
.action(async (options) => {
|
|
750
|
+
try {
|
|
751
|
+
const { init } = await import('./commands/init.js');
|
|
752
|
+
const result = await init({
|
|
753
|
+
projectRoot: process.cwd(),
|
|
754
|
+
force: options.force,
|
|
755
|
+
yes: options.yes,
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
if (!result.success) {
|
|
759
|
+
console.error(pc.red('\nInit failed with errors:'));
|
|
760
|
+
for (const error of result.errors) {
|
|
761
|
+
console.error(pc.red(` - ${error}`));
|
|
762
|
+
}
|
|
763
|
+
process.exit(1);
|
|
764
|
+
}
|
|
765
|
+
} catch (error) {
|
|
766
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
767
|
+
process.exit(1);
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
// ============================================================================
|
|
772
|
+
// TOKENS COMMAND
|
|
773
|
+
// ============================================================================
|
|
774
|
+
program
|
|
775
|
+
.command('tokens')
|
|
776
|
+
.description('Discover and list design tokens from CSS/SCSS files')
|
|
777
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
778
|
+
.option('--json', 'Output as JSON')
|
|
779
|
+
.option('--categories', 'Group tokens by category')
|
|
780
|
+
.option('--theme <theme>', 'Filter by theme name')
|
|
781
|
+
.option('--category <category>', 'Filter by category (color, spacing, typography, etc.)')
|
|
782
|
+
.option('--verbose', 'Show all tokens (no truncation)')
|
|
783
|
+
.action(async (options) => {
|
|
784
|
+
try {
|
|
785
|
+
const { tokens } = await import('./commands/tokens.js');
|
|
786
|
+
const result = await tokens({
|
|
787
|
+
config: options.config,
|
|
788
|
+
json: options.json,
|
|
789
|
+
categories: options.categories,
|
|
790
|
+
theme: options.theme,
|
|
791
|
+
category: options.category,
|
|
792
|
+
verbose: options.verbose,
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
if (!result.success) {
|
|
796
|
+
process.exit(1);
|
|
797
|
+
}
|
|
798
|
+
} catch (error) {
|
|
799
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
800
|
+
process.exit(1);
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
// ============================================================================
|
|
805
|
+
// GENERATE COMMAND
|
|
806
|
+
// ============================================================================
|
|
807
|
+
program
|
|
808
|
+
.command('generate')
|
|
809
|
+
.description('Generate fragment files from component source code')
|
|
810
|
+
.argument('[component]', 'Specific component name to generate (optional)')
|
|
811
|
+
.option('--force', 'Overwrite existing fragment files')
|
|
812
|
+
.option('--pattern <glob>', 'Pattern for component files', 'src/components/**/*.tsx')
|
|
813
|
+
.action(async (component, options) => {
|
|
814
|
+
try {
|
|
815
|
+
const { generate } = await import('./commands/generate.js');
|
|
816
|
+
const result = await generate({
|
|
817
|
+
projectRoot: process.cwd(),
|
|
818
|
+
component,
|
|
819
|
+
force: options.force,
|
|
820
|
+
componentPattern: options.pattern,
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
if (!result.success) {
|
|
824
|
+
console.error(pc.red('\nGenerate completed with errors'));
|
|
825
|
+
process.exit(1);
|
|
826
|
+
}
|
|
827
|
+
} catch (error) {
|
|
828
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
829
|
+
process.exit(1);
|
|
830
|
+
}
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
// ============================================================================
|
|
834
|
+
// TEST COMMAND
|
|
835
|
+
// ============================================================================
|
|
836
|
+
program
|
|
837
|
+
.command('test')
|
|
838
|
+
.description('Run interaction tests for fragments with play functions')
|
|
839
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
840
|
+
// Discovery options
|
|
841
|
+
.option('--component <name>', 'Filter by component name')
|
|
842
|
+
.option('--tags <tags>', 'Filter by tags (comma-separated)')
|
|
843
|
+
.option('--grep <pattern>', 'Filter by variant name pattern')
|
|
844
|
+
.option('--exclude <pattern>', 'Exclude tests matching pattern')
|
|
845
|
+
// Execution options
|
|
846
|
+
.option('--parallel <count>', 'Number of parallel browser contexts', parseInt, 4)
|
|
847
|
+
.option('--timeout <ms>', 'Timeout per test in milliseconds', parseInt, 30000)
|
|
848
|
+
.option('--retries <count>', 'Number of retries for failed tests', parseInt, 0)
|
|
849
|
+
.option('--bail', 'Stop on first failure')
|
|
850
|
+
.option('--browser <name>', 'Browser to use (chromium, firefox, webkit)', 'chromium')
|
|
851
|
+
.option('--headed', 'Run in headed mode (show browser)')
|
|
852
|
+
// Feature flags
|
|
853
|
+
.option('--a11y', 'Run accessibility checks with axe-core')
|
|
854
|
+
.option('--visual', 'Capture screenshots for visual regression')
|
|
855
|
+
.option('--update-snapshots', 'Update visual snapshots')
|
|
856
|
+
.option('--watch', 'Watch mode - re-run on file changes')
|
|
857
|
+
// Output options
|
|
858
|
+
.option('--reporters <names>', 'Reporters to use (console, junit, json)', 'console')
|
|
859
|
+
.option('-o, --output <dir>', 'Output directory for results', './test-results')
|
|
860
|
+
// Server options
|
|
861
|
+
.option('--server-url <url>', 'URL of running dev server (skips starting server)')
|
|
862
|
+
.option('-p, --port <port>', 'Port for dev server', parseInt, 6006)
|
|
863
|
+
// CI mode
|
|
864
|
+
.option('--ci', 'CI mode - non-interactive, exit with code 1 on failure')
|
|
865
|
+
// List only
|
|
866
|
+
.option('--list', 'List available tests without running them')
|
|
867
|
+
.action(async (options) => {
|
|
868
|
+
try {
|
|
869
|
+
const { config, configDir } = await loadConfig(options.config);
|
|
870
|
+
|
|
871
|
+
// Import test module
|
|
872
|
+
const { runTestCommand, listTests } = await import('./test/index.js');
|
|
873
|
+
|
|
874
|
+
// List mode
|
|
875
|
+
if (options.list) {
|
|
876
|
+
await listTests(config, configDir, {
|
|
877
|
+
component: options.component,
|
|
878
|
+
tags: options.tags,
|
|
879
|
+
grep: options.grep,
|
|
880
|
+
exclude: options.exclude,
|
|
881
|
+
});
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// Run tests
|
|
886
|
+
const exitCode = await runTestCommand(config, configDir, {
|
|
887
|
+
component: options.component,
|
|
888
|
+
tags: options.tags,
|
|
889
|
+
grep: options.grep,
|
|
890
|
+
exclude: options.exclude,
|
|
891
|
+
parallel: options.parallel,
|
|
892
|
+
timeout: options.timeout,
|
|
893
|
+
retries: options.retries,
|
|
894
|
+
bail: options.bail,
|
|
895
|
+
browser: options.browser as 'chromium' | 'firefox' | 'webkit',
|
|
896
|
+
headless: !options.headed,
|
|
897
|
+
a11y: options.a11y,
|
|
898
|
+
visual: options.visual,
|
|
899
|
+
updateSnapshots: options.updateSnapshots,
|
|
900
|
+
watch: options.watch,
|
|
901
|
+
reporters: options.reporters,
|
|
902
|
+
output: options.output,
|
|
903
|
+
serverUrl: options.serverUrl,
|
|
904
|
+
port: options.port,
|
|
905
|
+
ci: options.ci,
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
process.exit(exitCode);
|
|
909
|
+
} catch (error) {
|
|
910
|
+
console.error(pc.red('Error:'), error instanceof Error ? error.message : error);
|
|
911
|
+
process.exit(1);
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
// Parse command line arguments
|
|
916
|
+
program.parse();
|