@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
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fragments link storybook - Bootstrap segments from Storybook stories
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { writeFile, mkdir } from 'node:fs/promises';
|
|
6
|
+
import { join, dirname, relative } from 'node:path';
|
|
7
|
+
import pc from 'picocolors';
|
|
8
|
+
import { BRAND } from '../../core/index.js';
|
|
9
|
+
import {
|
|
10
|
+
detectStorybookConfig,
|
|
11
|
+
discoverStoryFiles as discoverStorybookFiles,
|
|
12
|
+
parseStoryFile,
|
|
13
|
+
convertToSegment,
|
|
14
|
+
} from '../../migrate/index.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Options for link storybook command
|
|
18
|
+
*/
|
|
19
|
+
export interface LinkStorybookOptions {
|
|
20
|
+
/** Path to .storybook/main.* config */
|
|
21
|
+
config?: string;
|
|
22
|
+
/** Output directory for segment files */
|
|
23
|
+
out?: string;
|
|
24
|
+
/** Skip confirmation prompts */
|
|
25
|
+
yes?: boolean;
|
|
26
|
+
/** Preview what would be generated without writing files */
|
|
27
|
+
dryRun?: boolean;
|
|
28
|
+
/** Only process stories matching glob */
|
|
29
|
+
include?: string;
|
|
30
|
+
/** Skip stories matching glob */
|
|
31
|
+
exclude?: string;
|
|
32
|
+
/** Show verbose output */
|
|
33
|
+
verbose?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Result of link storybook command
|
|
38
|
+
*/
|
|
39
|
+
export interface LinkStorybookResult {
|
|
40
|
+
success: boolean;
|
|
41
|
+
generated: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Preview item for stories
|
|
46
|
+
*/
|
|
47
|
+
interface PreviewItem {
|
|
48
|
+
componentName: string;
|
|
49
|
+
sourceFile: string;
|
|
50
|
+
outputFile: string;
|
|
51
|
+
variantCount: number;
|
|
52
|
+
propCount: number;
|
|
53
|
+
confidence: number;
|
|
54
|
+
warnings: string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Run the link storybook command
|
|
59
|
+
*/
|
|
60
|
+
export async function linkStorybook(
|
|
61
|
+
options: LinkStorybookOptions = {}
|
|
62
|
+
): Promise<LinkStorybookResult> {
|
|
63
|
+
const { out, yes = false, dryRun = false, verbose = false } = options;
|
|
64
|
+
|
|
65
|
+
console.log(pc.cyan(`\n${BRAND.name} Storybook Link\n`));
|
|
66
|
+
|
|
67
|
+
// Detect Storybook config
|
|
68
|
+
const projectRoot = process.cwd();
|
|
69
|
+
console.log(pc.dim('Detecting Storybook configuration...'));
|
|
70
|
+
|
|
71
|
+
const sbConfig = await detectStorybookConfig(projectRoot);
|
|
72
|
+
if (!sbConfig) {
|
|
73
|
+
console.log(pc.yellow('\nNo Storybook configuration found.'));
|
|
74
|
+
console.log(pc.dim('Looking for: .storybook/main.ts, .storybook/main.js, etc.'));
|
|
75
|
+
console.log(pc.dim('\nUse --config to specify a custom path.'));
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
console.log(pc.green(`ā Found: ${sbConfig.configPath}`));
|
|
80
|
+
if (sbConfig.framework) {
|
|
81
|
+
console.log(pc.dim(` Framework: ${sbConfig.framework}`));
|
|
82
|
+
}
|
|
83
|
+
if (sbConfig.errors?.length) {
|
|
84
|
+
for (const err of sbConfig.errors) {
|
|
85
|
+
console.log(pc.yellow(` Warning: ${err}`));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Discover story files
|
|
90
|
+
console.log(pc.dim('\nDiscovering story files...'));
|
|
91
|
+
const storyFiles = await discoverStorybookFiles(projectRoot, sbConfig.storyPatterns);
|
|
92
|
+
|
|
93
|
+
if (storyFiles.length === 0) {
|
|
94
|
+
console.log(pc.yellow('\nNo story files found.'));
|
|
95
|
+
console.log(pc.dim(`Patterns: ${sbConfig.storyPatterns.join(', ')}`));
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
console.log(pc.green(`ā Found ${storyFiles.length} story file(s)\n`));
|
|
100
|
+
|
|
101
|
+
// Parse and convert each story
|
|
102
|
+
const previews: PreviewItem[] = [];
|
|
103
|
+
let parseErrors = 0;
|
|
104
|
+
const total = storyFiles.length;
|
|
105
|
+
|
|
106
|
+
console.log(pc.dim(`Analyzing ${total} stories...\n`));
|
|
107
|
+
|
|
108
|
+
for (let i = 0; i < storyFiles.length; i++) {
|
|
109
|
+
const storyFile = storyFiles[i];
|
|
110
|
+
const relativePath = relative(projectRoot, storyFile);
|
|
111
|
+
|
|
112
|
+
// Show progress
|
|
113
|
+
process.stdout.write(`\r ${pc.dim(`[${i + 1}/${total}]`)} ${relativePath.slice(0, 60).padEnd(60)}`);
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
const parsed = await parseStoryFile(storyFile);
|
|
117
|
+
const result = convertToSegment(parsed);
|
|
118
|
+
|
|
119
|
+
// Determine output path
|
|
120
|
+
const outputFile = out
|
|
121
|
+
? join(out, relativePath.replace(/\.stories\.(tsx?|jsx?)$/, BRAND.fileExtension))
|
|
122
|
+
: result.outputFile;
|
|
123
|
+
|
|
124
|
+
previews.push({
|
|
125
|
+
componentName: result.componentName,
|
|
126
|
+
sourceFile: relativePath,
|
|
127
|
+
outputFile: relative(projectRoot, outputFile),
|
|
128
|
+
variantCount: result.variantCount,
|
|
129
|
+
propCount: result.propCount,
|
|
130
|
+
confidence: result.confidence,
|
|
131
|
+
warnings: result.warnings,
|
|
132
|
+
});
|
|
133
|
+
} catch (error) {
|
|
134
|
+
parseErrors++;
|
|
135
|
+
if (verbose) {
|
|
136
|
+
process.stdout.write('\n');
|
|
137
|
+
console.log(pc.red(`ā ${relativePath}: ${error instanceof Error ? error.message : 'Parse error'}`));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Clear progress line
|
|
143
|
+
process.stdout.write('\r' + ' '.repeat(80) + '\r');
|
|
144
|
+
|
|
145
|
+
// Display preview table
|
|
146
|
+
console.log(pc.bold('Preview:\n'));
|
|
147
|
+
console.log(pc.dim('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
148
|
+
console.log(pc.dim('ā') + ' Component'.padEnd(20) + pc.dim('ā') + ' Stories'.padEnd(10) + pc.dim('ā') + ' Props'.padEnd(8) + pc.dim('ā') + ' Confidence'.padEnd(13) + pc.dim('ā'));
|
|
149
|
+
console.log(pc.dim('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤'));
|
|
150
|
+
|
|
151
|
+
for (const item of previews) {
|
|
152
|
+
const confColor = item.confidence >= 0.8 ? pc.green : item.confidence >= 0.5 ? pc.yellow : pc.red;
|
|
153
|
+
const confLabel = item.confidence >= 0.8 ? 'high' : item.confidence >= 0.5 ? 'medium' : 'low';
|
|
154
|
+
console.log(
|
|
155
|
+
pc.dim('ā') +
|
|
156
|
+
` ${item.componentName}`.padEnd(20).slice(0, 20) +
|
|
157
|
+
pc.dim('ā') +
|
|
158
|
+
` ${item.variantCount}`.padEnd(10) +
|
|
159
|
+
pc.dim('ā') +
|
|
160
|
+
` ${item.propCount}`.padEnd(8) +
|
|
161
|
+
pc.dim('ā') +
|
|
162
|
+
` ${confColor(confLabel)}`.padEnd(13 + (confColor === pc.green ? 10 : confColor === pc.yellow ? 11 : 9)) +
|
|
163
|
+
pc.dim('ā')
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
console.log(pc.dim('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
168
|
+
|
|
169
|
+
if (parseErrors > 0) {
|
|
170
|
+
console.log(pc.yellow(`\n${parseErrors} file(s) could not be parsed (use --verbose for details)`));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (previews.length === 0) {
|
|
174
|
+
console.log(pc.yellow('\nNo stories could be parsed successfully.'));
|
|
175
|
+
return { success: true, generated: 0 };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Dry run stops here
|
|
179
|
+
if (dryRun) {
|
|
180
|
+
console.log(pc.dim(`\n${previews.length} segment file(s) would be created`));
|
|
181
|
+
console.log(pc.yellow('\n[Dry run - no files were written]'));
|
|
182
|
+
return { success: true, generated: 0 };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Interactive selection or auto mode (--yes)
|
|
186
|
+
let selectedPreviews = previews;
|
|
187
|
+
|
|
188
|
+
if (yes) {
|
|
189
|
+
console.log(pc.dim(`\n${previews.length} segment file(s) will be created`));
|
|
190
|
+
} else {
|
|
191
|
+
const { checkbox } = await import('@inquirer/prompts');
|
|
192
|
+
|
|
193
|
+
console.log(pc.dim('\nUse ā/ā to navigate, Space to toggle, Enter to confirm\n'));
|
|
194
|
+
|
|
195
|
+
const choices = previews.map((item) => {
|
|
196
|
+
const confColor = item.confidence >= 0.8 ? pc.green : item.confidence >= 0.5 ? pc.yellow : pc.red;
|
|
197
|
+
const confLabel = item.confidence >= 0.8 ? 'high' : item.confidence >= 0.5 ? 'medium' : 'low';
|
|
198
|
+
return {
|
|
199
|
+
name: `${pc.bold(item.componentName)} ${pc.dim(`(${item.variantCount} stories, ${confLabel} confidence)`)}`,
|
|
200
|
+
value: item,
|
|
201
|
+
checked: true,
|
|
202
|
+
};
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
selectedPreviews = await checkbox({
|
|
207
|
+
message: 'Select stories to convert:',
|
|
208
|
+
choices,
|
|
209
|
+
pageSize: 15,
|
|
210
|
+
});
|
|
211
|
+
} catch {
|
|
212
|
+
// User cancelled (Ctrl+C)
|
|
213
|
+
console.log(pc.dim('\nNo changes made.'));
|
|
214
|
+
return { success: true, generated: 0 };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (selectedPreviews.length === 0) {
|
|
218
|
+
console.log(pc.dim('\nNo stories selected. No changes made.'));
|
|
219
|
+
return { success: true, generated: 0 };
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Generate files
|
|
224
|
+
const genTotal = selectedPreviews.length;
|
|
225
|
+
console.log(pc.dim(`\nGenerating ${genTotal} segment file(s)...\n`));
|
|
226
|
+
|
|
227
|
+
let generated = 0;
|
|
228
|
+
let genErrors = 0;
|
|
229
|
+
for (let i = 0; i < selectedPreviews.length; i++) {
|
|
230
|
+
const preview = selectedPreviews[i];
|
|
231
|
+
const storyFile = join(projectRoot, preview.sourceFile);
|
|
232
|
+
try {
|
|
233
|
+
const parsed = await parseStoryFile(storyFile);
|
|
234
|
+
const result = convertToSegment(parsed);
|
|
235
|
+
|
|
236
|
+
// Determine output path
|
|
237
|
+
const outputFile = out
|
|
238
|
+
? join(projectRoot, out, preview.sourceFile.replace(/\.stories\.(tsx?|jsx?)$/, BRAND.fileExtension))
|
|
239
|
+
: result.outputFile;
|
|
240
|
+
|
|
241
|
+
// Create directory if needed
|
|
242
|
+
await mkdir(dirname(outputFile), { recursive: true });
|
|
243
|
+
|
|
244
|
+
// Write file
|
|
245
|
+
await writeFile(outputFile, result.code);
|
|
246
|
+
generated++;
|
|
247
|
+
|
|
248
|
+
console.log(`${pc.dim(`[${i + 1}/${genTotal}]`)} ${pc.green('ā')} ${result.componentName}`);
|
|
249
|
+
} catch (error) {
|
|
250
|
+
genErrors++;
|
|
251
|
+
console.log(`${pc.dim(`[${i + 1}/${genTotal}]`)} ${pc.red('ā')} ${preview.componentName}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
console.log(pc.green(`\nā Generated ${generated} segment file(s)\n`));
|
|
256
|
+
|
|
257
|
+
// Next steps
|
|
258
|
+
console.log(pc.dim('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
259
|
+
console.log(pc.bold('\nNext steps:'));
|
|
260
|
+
console.log(` 1. Review generated ${BRAND.fileExtension} files`);
|
|
261
|
+
console.log(` 2. Fill in usage.when and usage.whenNot fields`);
|
|
262
|
+
console.log(` 3. Run ${pc.cyan(`${BRAND.cliCommand} build`)} to compile`);
|
|
263
|
+
console.log(` 4. Run ${pc.cyan(`${BRAND.cliCommand} dev`)} to view your design system`);
|
|
264
|
+
console.log();
|
|
265
|
+
|
|
266
|
+
return { success: true, generated };
|
|
267
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fragments list - List all discovered fragment files
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import pc from 'picocolors';
|
|
6
|
+
import { BRAND } from '../core/index.js';
|
|
7
|
+
import { loadConfig, discoverSegmentFiles } from '../core/node.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Options for list command
|
|
11
|
+
*/
|
|
12
|
+
export interface ListOptions {
|
|
13
|
+
/** Path to config file */
|
|
14
|
+
config?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Result of list command
|
|
19
|
+
*/
|
|
20
|
+
export interface ListResult {
|
|
21
|
+
success: boolean;
|
|
22
|
+
files: Array<{
|
|
23
|
+
absolutePath: string;
|
|
24
|
+
relativePath: string;
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Run the list command
|
|
30
|
+
*/
|
|
31
|
+
export async function list(options: ListOptions = {}): Promise<ListResult> {
|
|
32
|
+
const { config, configDir } = await loadConfig(options.config);
|
|
33
|
+
const files = await discoverSegmentFiles(config, configDir);
|
|
34
|
+
|
|
35
|
+
console.log(pc.cyan(`\n${BRAND.name} - Discovered Fragments\n`));
|
|
36
|
+
|
|
37
|
+
if (files.length === 0) {
|
|
38
|
+
console.log(pc.yellow('No fragment files found.\n'));
|
|
39
|
+
console.log(pc.dim(`Looking for: ${config.include.join(', ')}`));
|
|
40
|
+
return { success: true, files: [] };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
for (const file of files) {
|
|
44
|
+
console.log(` ${pc.dim('ā¢')} ${file.relativePath}`);
|
|
45
|
+
}
|
|
46
|
+
console.log(pc.dim(`\n ${files.length} fragment(s) found\n`));
|
|
47
|
+
|
|
48
|
+
return { success: true, files };
|
|
49
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fragments metrics - View compliance trends over time
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import pc from 'picocolors';
|
|
6
|
+
import { BRAND } from '../core/index.js';
|
|
7
|
+
import { loadConfig } from '../core/node.js';
|
|
8
|
+
import { createMetricsStore } from '../service/index.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Options for metrics command
|
|
12
|
+
*/
|
|
13
|
+
export interface MetricsOptions {
|
|
14
|
+
/** Path to config file */
|
|
15
|
+
config?: string;
|
|
16
|
+
/** Number of days to look back */
|
|
17
|
+
days?: number;
|
|
18
|
+
/** Output JSON format */
|
|
19
|
+
json?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Trend data point
|
|
24
|
+
*/
|
|
25
|
+
export interface TrendDataPoint {
|
|
26
|
+
date: string;
|
|
27
|
+
compliance: number;
|
|
28
|
+
violations: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Trend result
|
|
33
|
+
*/
|
|
34
|
+
export interface TrendResult {
|
|
35
|
+
dataPoints: TrendDataPoint[];
|
|
36
|
+
averageCompliance: number;
|
|
37
|
+
trend: 'improving' | 'declining' | 'stable';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Result of metrics command
|
|
42
|
+
*/
|
|
43
|
+
export interface MetricsResult {
|
|
44
|
+
success: boolean;
|
|
45
|
+
trend?: TrendResult;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Run the metrics command
|
|
50
|
+
*/
|
|
51
|
+
export async function metrics(
|
|
52
|
+
component: string | undefined,
|
|
53
|
+
options: MetricsOptions = {}
|
|
54
|
+
): Promise<MetricsResult> {
|
|
55
|
+
const { config: configPath, days = 30, json = false } = options;
|
|
56
|
+
|
|
57
|
+
const { configDir } = await loadConfig(configPath);
|
|
58
|
+
const store = createMetricsStore(configDir);
|
|
59
|
+
|
|
60
|
+
console.log(pc.cyan(`\n${BRAND.name} Compliance Metrics\n`));
|
|
61
|
+
|
|
62
|
+
const trend = await store.getTrend(component || 'all', {
|
|
63
|
+
days,
|
|
64
|
+
groupBy: days > 14 ? 'week' : 'day',
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (json) {
|
|
68
|
+
console.log(JSON.stringify(trend, null, 2));
|
|
69
|
+
return { success: true, trend };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Header
|
|
73
|
+
const title = component ? `Component: ${component}` : 'System-wide';
|
|
74
|
+
console.log(pc.bold(title));
|
|
75
|
+
console.log(pc.dim(`Last ${days} days\n`));
|
|
76
|
+
|
|
77
|
+
// Check if we have data
|
|
78
|
+
if (trend.dataPoints.length === 0) {
|
|
79
|
+
console.log(pc.yellow('No metrics data found.\n'));
|
|
80
|
+
console.log(pc.dim('Metrics are recorded automatically when running verification commands.'));
|
|
81
|
+
console.log(pc.dim(`Try running: ${pc.cyan(`${BRAND.cliCommand} verify --ci`)}\n`));
|
|
82
|
+
return { success: true, trend };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Sparkline
|
|
86
|
+
const sparkline = store.generateSparkline(trend.dataPoints);
|
|
87
|
+
console.log(pc.bold('Trend: ') + sparkline);
|
|
88
|
+
console.log();
|
|
89
|
+
|
|
90
|
+
// Stats
|
|
91
|
+
const trendColor = trend.trend === 'improving' ? pc.green :
|
|
92
|
+
trend.trend === 'declining' ? pc.red : pc.dim;
|
|
93
|
+
const trendIcon = trend.trend === 'improving' ? 'ā' :
|
|
94
|
+
trend.trend === 'declining' ? 'ā' : 'ā';
|
|
95
|
+
|
|
96
|
+
console.log(` Average compliance: ${pc.bold(`${trend.averageCompliance}%`)}`);
|
|
97
|
+
console.log(` Direction: ${trendColor(`${trendIcon} ${trend.trend}`)}`);
|
|
98
|
+
console.log(` Data points: ${trend.dataPoints.length}`);
|
|
99
|
+
console.log();
|
|
100
|
+
|
|
101
|
+
// Recent data points
|
|
102
|
+
const recent = trend.dataPoints.slice(-5);
|
|
103
|
+
if (recent.length > 0) {
|
|
104
|
+
console.log(pc.dim('Recent data:'));
|
|
105
|
+
for (const point of recent) {
|
|
106
|
+
const complianceColor = point.compliance >= 90 ? pc.green :
|
|
107
|
+
point.compliance >= 70 ? pc.yellow : pc.red;
|
|
108
|
+
console.log(` ${pc.dim(point.date)} ${complianceColor(`${point.compliance}%`)} ${pc.dim(`(${point.violations} violations)`)}`);
|
|
109
|
+
}
|
|
110
|
+
console.log();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return { success: true, trend };
|
|
114
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fragments reset - Reset to initial state (delete all generated files)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { stat, rm, unlink, readdir } from 'node:fs/promises';
|
|
6
|
+
import { join, relative } from 'node:path';
|
|
7
|
+
import pc from 'picocolors';
|
|
8
|
+
import fg from 'fast-glob';
|
|
9
|
+
import { BRAND } from '../core/index.js';
|
|
10
|
+
import { loadConfig } from '../core/node.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Options for reset command
|
|
14
|
+
*/
|
|
15
|
+
export interface ResetOptions {
|
|
16
|
+
/** Skip confirmation prompt */
|
|
17
|
+
yes?: boolean;
|
|
18
|
+
/** Show what would be deleted without deleting */
|
|
19
|
+
dryRun?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Result of reset command
|
|
24
|
+
*/
|
|
25
|
+
export interface ResetResult {
|
|
26
|
+
success: boolean;
|
|
27
|
+
deletedFiles: number;
|
|
28
|
+
deletedDirs: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Run the reset command
|
|
33
|
+
*/
|
|
34
|
+
export async function reset(options: ResetOptions = {}): Promise<ResetResult> {
|
|
35
|
+
const { yes = false, dryRun = false } = options;
|
|
36
|
+
|
|
37
|
+
console.log(pc.cyan(`\n${BRAND.name} Reset\n`));
|
|
38
|
+
|
|
39
|
+
const projectRoot = process.cwd();
|
|
40
|
+
|
|
41
|
+
// Items to delete
|
|
42
|
+
const filesToDelete: string[] = [];
|
|
43
|
+
const dirsToDelete: string[] = [];
|
|
44
|
+
|
|
45
|
+
// Check data directory (.segments/)
|
|
46
|
+
const dataDir = join(projectRoot, BRAND.dataDir);
|
|
47
|
+
try {
|
|
48
|
+
const dataDirStat = await stat(dataDir);
|
|
49
|
+
if (dataDirStat.isDirectory()) {
|
|
50
|
+
dirsToDelete.push(dataDir);
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
53
|
+
// Directory doesn't exist
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Check for segments.json (default output file)
|
|
57
|
+
const defaultOutFile = join(projectRoot, 'segments.json');
|
|
58
|
+
try {
|
|
59
|
+
const fileStat = await stat(defaultOutFile);
|
|
60
|
+
if (fileStat.isFile()) {
|
|
61
|
+
filesToDelete.push(defaultOutFile);
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
// File doesn't exist
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Try to load config for custom outFile and include patterns
|
|
68
|
+
let segmentPatterns = [`**/*${BRAND.fileExtension}`];
|
|
69
|
+
try {
|
|
70
|
+
const { config } = await loadConfig();
|
|
71
|
+
if (config.outFile && config.outFile !== 'segments.json') {
|
|
72
|
+
const customOutFile = join(projectRoot, config.outFile);
|
|
73
|
+
try {
|
|
74
|
+
const fileStat = await stat(customOutFile);
|
|
75
|
+
if (fileStat.isFile()) {
|
|
76
|
+
filesToDelete.push(customOutFile);
|
|
77
|
+
}
|
|
78
|
+
} catch {
|
|
79
|
+
// Custom outfile doesn't exist
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Use config include patterns if available
|
|
83
|
+
if (config.include && config.include.length > 0) {
|
|
84
|
+
segmentPatterns = config.include;
|
|
85
|
+
}
|
|
86
|
+
} catch {
|
|
87
|
+
// No config file, use defaults
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Find all segment files (*.segment.tsx)
|
|
91
|
+
console.log(pc.dim('Scanning for generated files...\n'));
|
|
92
|
+
for (const pattern of segmentPatterns) {
|
|
93
|
+
const matches = await fg(pattern, {
|
|
94
|
+
cwd: projectRoot,
|
|
95
|
+
ignore: ['**/node_modules/**'],
|
|
96
|
+
absolute: true,
|
|
97
|
+
});
|
|
98
|
+
for (const match of matches) {
|
|
99
|
+
if (!filesToDelete.includes(match)) {
|
|
100
|
+
filesToDelete.push(match);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Find all Documentation.mdx files (generated by link storybook)
|
|
106
|
+
const mdxFiles = await fg('**/Documentation.mdx', {
|
|
107
|
+
cwd: projectRoot,
|
|
108
|
+
ignore: ['**/node_modules/**'],
|
|
109
|
+
absolute: true,
|
|
110
|
+
});
|
|
111
|
+
for (const mdxFile of mdxFiles) {
|
|
112
|
+
if (!filesToDelete.includes(mdxFile)) {
|
|
113
|
+
filesToDelete.push(mdxFile);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Check if nothing to delete
|
|
118
|
+
if (filesToDelete.length === 0 && dirsToDelete.length === 0) {
|
|
119
|
+
console.log(pc.yellow('Nothing to reset. No generated files found.\n'));
|
|
120
|
+
return { success: true, deletedFiles: 0, deletedDirs: 0 };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Show summary
|
|
124
|
+
console.log(pc.dim('The following will be deleted:\n'));
|
|
125
|
+
|
|
126
|
+
// Show directories
|
|
127
|
+
for (const dir of dirsToDelete) {
|
|
128
|
+
const relativePath = relative(projectRoot, dir);
|
|
129
|
+
console.log(` š ${relativePath}/`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Group files by type for cleaner output
|
|
133
|
+
const segmentFiles = filesToDelete.filter((f) => f.endsWith(BRAND.fileExtension));
|
|
134
|
+
const mdxFilesFound = filesToDelete.filter((f) => f.endsWith('.mdx'));
|
|
135
|
+
const otherFiles = filesToDelete.filter(
|
|
136
|
+
(f) => !f.endsWith(BRAND.fileExtension) && !f.endsWith('.mdx')
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
if (segmentFiles.length > 0) {
|
|
140
|
+
console.log(` š ${segmentFiles.length} segment file(s) (*${BRAND.fileExtension})`);
|
|
141
|
+
if (segmentFiles.length <= 5) {
|
|
142
|
+
for (const f of segmentFiles) {
|
|
143
|
+
console.log(pc.dim(` ${relative(projectRoot, f)}`));
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
for (const f of segmentFiles.slice(0, 3)) {
|
|
147
|
+
console.log(pc.dim(` ${relative(projectRoot, f)}`));
|
|
148
|
+
}
|
|
149
|
+
console.log(pc.dim(` ... and ${segmentFiles.length - 3} more`));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (mdxFilesFound.length > 0) {
|
|
154
|
+
console.log(` š ${mdxFilesFound.length} documentation file(s) (*.mdx)`);
|
|
155
|
+
if (mdxFilesFound.length <= 5) {
|
|
156
|
+
for (const f of mdxFilesFound) {
|
|
157
|
+
console.log(pc.dim(` ${relative(projectRoot, f)}`));
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
for (const f of mdxFilesFound.slice(0, 3)) {
|
|
161
|
+
console.log(pc.dim(` ${relative(projectRoot, f)}`));
|
|
162
|
+
}
|
|
163
|
+
console.log(pc.dim(` ... and ${mdxFilesFound.length - 3} more`));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
for (const f of otherFiles) {
|
|
168
|
+
console.log(` š ${relative(projectRoot, f)}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const totalCount = filesToDelete.length + dirsToDelete.length;
|
|
172
|
+
console.log(pc.dim(`\n Total: ${totalCount} item(s)\n`));
|
|
173
|
+
|
|
174
|
+
// Dry run exits here
|
|
175
|
+
if (dryRun) {
|
|
176
|
+
console.log(pc.yellow('[Dry run - no files were deleted]\n'));
|
|
177
|
+
return { success: true, deletedFiles: 0, deletedDirs: 0 };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Confirm unless --yes
|
|
181
|
+
let proceed = yes;
|
|
182
|
+
if (!proceed) {
|
|
183
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
184
|
+
try {
|
|
185
|
+
proceed = await confirm({
|
|
186
|
+
message: `Delete ${totalCount} item(s)?`,
|
|
187
|
+
default: false,
|
|
188
|
+
});
|
|
189
|
+
} catch {
|
|
190
|
+
// User cancelled (Ctrl+C)
|
|
191
|
+
proceed = false;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (!proceed) {
|
|
196
|
+
console.log(pc.dim('\nNo changes made.\n'));
|
|
197
|
+
return { success: true, deletedFiles: 0, deletedDirs: 0 };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Delete directories first
|
|
201
|
+
console.log();
|
|
202
|
+
let deletedDirs = 0;
|
|
203
|
+
for (const dir of dirsToDelete) {
|
|
204
|
+
try {
|
|
205
|
+
const relativePath = relative(projectRoot, dir);
|
|
206
|
+
await rm(dir, { recursive: true, force: true });
|
|
207
|
+
console.log(` ${pc.green('ā')} Deleted ${relativePath}/`);
|
|
208
|
+
deletedDirs++;
|
|
209
|
+
} catch {
|
|
210
|
+
console.log(` ${pc.red('ā')} Failed: ${relative(projectRoot, dir)}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Delete files
|
|
215
|
+
let deletedCount = 0;
|
|
216
|
+
let failedCount = 0;
|
|
217
|
+
for (const file of filesToDelete) {
|
|
218
|
+
try {
|
|
219
|
+
await unlink(file);
|
|
220
|
+
deletedCount++;
|
|
221
|
+
} catch {
|
|
222
|
+
failedCount++;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (deletedCount > 0) {
|
|
227
|
+
console.log(` ${pc.green('ā')} Deleted ${deletedCount} file(s)`);
|
|
228
|
+
}
|
|
229
|
+
if (failedCount > 0) {
|
|
230
|
+
console.log(` ${pc.red('ā')} Failed to delete ${failedCount} file(s)`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
console.log(pc.green(`\nā Reset complete\n`));
|
|
234
|
+
console.log(pc.dim(`Config file retained: ${BRAND.configFile}`));
|
|
235
|
+
console.log(pc.dim(`Run ${pc.cyan(`${BRAND.cliCommand} init`)} to start fresh\n`));
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
success: true,
|
|
239
|
+
deletedFiles: deletedCount,
|
|
240
|
+
deletedDirs,
|
|
241
|
+
};
|
|
242
|
+
}
|