@fragments-sdk/cli 0.10.1 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-client-I6MDWNYA.js +21 -0
- package/dist/bin.js +292 -367
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-PW7QTQA6.js → chunk-4OC7FTJB.js} +2 -2
- package/dist/{chunk-HRFUSSZI.js → chunk-AM4MRTMN.js} +2 -2
- package/dist/{chunk-5G3VZH43.js → chunk-GVDSFQ4E.js} +281 -351
- package/dist/chunk-GVDSFQ4E.js.map +1 -0
- package/dist/chunk-JJ2VRTBU.js +626 -0
- package/dist/chunk-JJ2VRTBU.js.map +1 -0
- package/dist/{chunk-D5PYOXEI.js → chunk-LVWFOLUZ.js} +148 -13
- package/dist/{chunk-D5PYOXEI.js.map → chunk-LVWFOLUZ.js.map} +1 -1
- package/dist/{chunk-WXSR2II7.js → chunk-OQKMEFOS.js} +58 -6
- package/dist/chunk-OQKMEFOS.js.map +1 -0
- package/dist/chunk-SXTKFDCR.js +104 -0
- package/dist/chunk-SXTKFDCR.js.map +1 -0
- package/dist/chunk-T5OMVL7E.js +443 -0
- package/dist/chunk-T5OMVL7E.js.map +1 -0
- package/dist/{chunk-ZM4ZQZWZ.js → chunk-TPWGL2XS.js} +39 -37
- package/dist/chunk-TPWGL2XS.js.map +1 -0
- package/dist/{chunk-OQO55NKV.js → chunk-WFS63PCW.js} +85 -11
- package/dist/chunk-WFS63PCW.js.map +1 -0
- package/dist/core/index.js +9 -1
- package/dist/{discovery-NEOY4MPN.js → discovery-ZJQSXF56.js} +3 -3
- package/dist/{generate-FBHSXR3D.js → generate-RJFS2JWA.js} +4 -4
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/dist/init-ZSX3NRCZ.js +636 -0
- package/dist/init-ZSX3NRCZ.js.map +1 -0
- package/dist/mcp-bin.js +2 -2
- package/dist/{scan-CJF2DOQW.js → scan-3PMCJ4RB.js} +6 -6
- package/dist/scan-generate-SYU4PYZD.js +1115 -0
- package/dist/scan-generate-SYU4PYZD.js.map +1 -0
- package/dist/{service-TQYWY65E.js → service-VMGNJZ42.js} +3 -3
- package/dist/snapshot-XOISO2IS.js +139 -0
- package/dist/snapshot-XOISO2IS.js.map +1 -0
- package/dist/{static-viewer-NUBFPKWH.js → static-viewer-5GXH2MGE.js} +3 -3
- package/dist/static-viewer-5GXH2MGE.js.map +1 -0
- package/dist/{test-Z5LVO724.js → test-SI4NSHQX.js} +4 -4
- package/dist/{tokens-CE46OTMD.js → tokens-T6SIVUT5.js} +5 -5
- package/dist/{viewer-DNMNC5VS.js → viewer-7ZEAFBVN.js} +80 -58
- package/dist/viewer-7ZEAFBVN.js.map +1 -0
- package/package.json +6 -14
- package/src/ai-client.ts +156 -0
- package/src/bin.ts +74 -2
- package/src/build.ts +95 -33
- package/src/commands/__tests__/drift-sync.test.ts +252 -0
- package/src/commands/__tests__/scan-generate.test.ts +497 -45
- package/src/commands/enhance.ts +11 -35
- package/src/commands/init.ts +296 -193
- package/src/commands/scan-generate.ts +740 -139
- package/src/commands/scan.ts +37 -32
- package/src/commands/setup.ts +143 -52
- package/src/commands/snapshot.ts +197 -0
- package/src/commands/sync.ts +357 -0
- package/src/commands/validate.ts +43 -1
- package/src/core/component-extractor.test.ts +282 -0
- package/src/core/component-extractor.ts +1030 -0
- package/src/core/discovery.ts +93 -7
- package/src/service/enhance/props-extractor.ts +235 -13
- package/src/validators.ts +236 -0
- package/src/viewer/__tests__/viewer-integration.test.ts +85 -74
- package/src/viewer/server.ts +37 -22
- package/src/viewer/vite-plugin.ts +25 -9
- package/dist/chunk-5G3VZH43.js.map +0 -1
- package/dist/chunk-OQO55NKV.js.map +0 -1
- package/dist/chunk-WXSR2II7.js.map +0 -1
- package/dist/chunk-ZM4ZQZWZ.js.map +0 -1
- package/dist/init-NDQXUWDU.js +0 -796
- package/dist/init-NDQXUWDU.js.map +0 -1
- package/dist/scan-generate-SJAN5MVI.js +0 -691
- package/dist/scan-generate-SJAN5MVI.js.map +0 -1
- package/dist/viewer-DNMNC5VS.js.map +0 -1
- package/src/ai.ts +0 -266
- package/src/commands/init-framework.ts +0 -414
- package/src/mcp/bin.ts +0 -36
- package/src/migrate/bin.ts +0 -114
- package/src/theme/index.ts +0 -77
- package/src/viewer/__tests__/a11y-fixes.test.ts +0 -358
- package/src/viewer/__tests__/jsx-parser.test.ts +0 -502
- package/src/viewer/__tests__/render-utils.test.ts +0 -232
- package/src/viewer/__tests__/style-utils.test.ts +0 -404
- package/src/viewer/assets/fragments-logo.ts +0 -4
- package/src/viewer/assets/fragments_logo.png +0 -0
- package/src/viewer/bin.ts +0 -86
- package/src/viewer/cli/health.ts +0 -256
- package/src/viewer/cli/index.ts +0 -33
- package/src/viewer/cli/scan.ts +0 -124
- package/src/viewer/cli/utils.ts +0 -174
- package/src/viewer/components/AccessibilityPanel.tsx +0 -1457
- package/src/viewer/components/ActionCapture.tsx +0 -172
- package/src/viewer/components/ActionsPanel.tsx +0 -332
- package/src/viewer/components/AllVariantsPreview.tsx +0 -78
- package/src/viewer/components/App.tsx +0 -582
- package/src/viewer/components/BottomPanel.tsx +0 -288
- package/src/viewer/components/CodePanel.naming.test.tsx +0 -59
- package/src/viewer/components/CodePanel.tsx +0 -118
- package/src/viewer/components/CommandPalette.tsx +0 -392
- package/src/viewer/components/ComponentDocView.tsx +0 -164
- package/src/viewer/components/ComponentGraph.tsx +0 -380
- package/src/viewer/components/ComponentHeader.tsx +0 -88
- package/src/viewer/components/ContractPanel.tsx +0 -241
- package/src/viewer/components/EmptyVariantMessage.tsx +0 -54
- package/src/viewer/components/ErrorBoundary.tsx +0 -97
- package/src/viewer/components/FigmaEmbed.tsx +0 -238
- package/src/viewer/components/FragmentEditor.tsx +0 -525
- package/src/viewer/components/FragmentRenderer.tsx +0 -61
- package/src/viewer/components/HeaderSearch.tsx +0 -24
- package/src/viewer/components/HealthDashboard.tsx +0 -441
- package/src/viewer/components/HmrStatusIndicator.tsx +0 -61
- package/src/viewer/components/Icons.tsx +0 -479
- package/src/viewer/components/InteractionsPanel.tsx +0 -757
- package/src/viewer/components/IsolatedPreviewFrame.tsx +0 -346
- package/src/viewer/components/IsolatedRender.tsx +0 -113
- package/src/viewer/components/KeyboardShortcutsHelp.tsx +0 -53
- package/src/viewer/components/LandingPage.tsx +0 -421
- package/src/viewer/components/Layout.tsx +0 -27
- package/src/viewer/components/LeftSidebar.tsx +0 -472
- package/src/viewer/components/LoadErrorMessage.tsx +0 -102
- package/src/viewer/components/MultiViewportPreview.tsx +0 -522
- package/src/viewer/components/NoVariantsMessage.tsx +0 -59
- package/src/viewer/components/PanelShell.tsx +0 -161
- package/src/viewer/components/PerformancePanel.tsx +0 -304
- package/src/viewer/components/PreviewArea.tsx +0 -472
- package/src/viewer/components/PreviewAside.tsx +0 -168
- package/src/viewer/components/PreviewFrameHost.tsx +0 -303
- package/src/viewer/components/PreviewPane.tsx +0 -149
- package/src/viewer/components/PreviewToolbar.tsx +0 -80
- package/src/viewer/components/PropsEditor.tsx +0 -506
- package/src/viewer/components/PropsTable.tsx +0 -111
- package/src/viewer/components/RelationsSection.tsx +0 -88
- package/src/viewer/components/ResizablePanel.tsx +0 -271
- package/src/viewer/components/RightSidebar.tsx +0 -102
- package/src/viewer/components/RuntimeToolsRegistrar.tsx +0 -17
- package/src/viewer/components/ScreenshotButton.tsx +0 -90
- package/src/viewer/components/Sidebar.tsx +0 -169
- package/src/viewer/components/SkeletonLoader.tsx +0 -161
- package/src/viewer/components/ThemeProvider.tsx +0 -42
- package/src/viewer/components/Toast.tsx +0 -3
- package/src/viewer/components/TokenStylePanel.tsx +0 -699
- package/src/viewer/components/TopToolbar.tsx +0 -159
- package/src/viewer/components/UsageSection.tsx +0 -95
- package/src/viewer/components/VariantMatrix.tsx +0 -388
- package/src/viewer/components/VariantRenderer.tsx +0 -131
- package/src/viewer/components/VariantTabs.tsx +0 -40
- package/src/viewer/components/ViewerHeader.tsx +0 -69
- package/src/viewer/components/ViewerStateSync.tsx +0 -52
- package/src/viewer/components/ViewportSelector.tsx +0 -172
- package/src/viewer/components/WebMCPDevTools.tsx +0 -503
- package/src/viewer/components/WebMCPIntegration.tsx +0 -47
- package/src/viewer/components/WebMCPStatusIndicator.tsx +0 -60
- package/src/viewer/components/_future/CreatePage.tsx +0 -836
- package/src/viewer/components/viewer-utils.ts +0 -16
- package/src/viewer/composition-renderer.ts +0 -381
- package/src/viewer/constants/index.ts +0 -1
- package/src/viewer/constants/ui.ts +0 -166
- package/src/viewer/entry.tsx +0 -335
- package/src/viewer/hooks/index.ts +0 -2
- package/src/viewer/hooks/useA11yCache.ts +0 -383
- package/src/viewer/hooks/useA11yService.ts +0 -364
- package/src/viewer/hooks/useActions.ts +0 -138
- package/src/viewer/hooks/useAppState.ts +0 -147
- package/src/viewer/hooks/useCompiledFragments.ts +0 -42
- package/src/viewer/hooks/useFigmaIntegration.ts +0 -132
- package/src/viewer/hooks/useHmrStatus.ts +0 -109
- package/src/viewer/hooks/useKeyboardShortcuts.ts +0 -270
- package/src/viewer/hooks/usePreviewBridge.ts +0 -347
- package/src/viewer/hooks/useScrollSpy.ts +0 -78
- package/src/viewer/hooks/useUrlState.ts +0 -318
- package/src/viewer/hooks/useViewSettings.ts +0 -111
- package/src/viewer/index.html +0 -28
- package/src/viewer/intelligence/healthReport.ts +0 -505
- package/src/viewer/intelligence/styleDrift.ts +0 -340
- package/src/viewer/intelligence/usageScanner.ts +0 -309
- package/src/viewer/jsx-parser.ts +0 -486
- package/src/viewer/preview-frame-entry.tsx +0 -25
- package/src/viewer/preview-frame.html +0 -125
- package/src/viewer/public/favicon.ico +0 -0
- package/src/viewer/render-template.html +0 -68
- package/src/viewer/styles/globals.css +0 -278
- package/src/viewer/types/a11y.ts +0 -197
- package/src/viewer/utils/a11y-fixes.ts +0 -509
- package/src/viewer/utils/actionExport.ts +0 -372
- package/src/viewer/utils/colorSchemes.ts +0 -201
- package/src/viewer/utils/detectRelationships.ts +0 -256
- package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss +0 -10
- package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss.d.ts +0 -2
- package/src/viewer/vendor/shared/src/ComponentDocContent.tsx +0 -274
- package/src/viewer/vendor/shared/src/DocsHeaderBar.tsx +0 -129
- package/src/viewer/vendor/shared/src/DocsPageAsideHost.tsx +0 -89
- package/src/viewer/vendor/shared/src/DocsPageShell.tsx +0 -124
- package/src/viewer/vendor/shared/src/DocsSearchCommand.tsx +0 -99
- package/src/viewer/vendor/shared/src/DocsSidebarNav.tsx +0 -66
- package/src/viewer/vendor/shared/src/PropsTable.module.scss +0 -68
- package/src/viewer/vendor/shared/src/PropsTable.module.scss.d.ts +0 -2
- package/src/viewer/vendor/shared/src/PropsTable.tsx +0 -76
- package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss +0 -114
- package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss.d.ts +0 -2
- package/src/viewer/vendor/shared/src/VariantPreviewCard.tsx +0 -137
- package/src/viewer/vendor/shared/src/docs-data/index.ts +0 -32
- package/src/viewer/vendor/shared/src/docs-data/mcp-configs.ts +0 -72
- package/src/viewer/vendor/shared/src/docs-data/palettes.ts +0 -75
- package/src/viewer/vendor/shared/src/docs-data/setup-examples.ts +0 -55
- package/src/viewer/vendor/shared/src/docs-layout.scss +0 -28
- package/src/viewer/vendor/shared/src/docs-layout.scss.d.ts +0 -2
- package/src/viewer/vendor/shared/src/index.ts +0 -34
- package/src/viewer/vendor/shared/src/types.ts +0 -53
- package/src/viewer/webmcp/__tests__/analytics.test.ts +0 -108
- package/src/viewer/webmcp/analytics.ts +0 -165
- package/src/viewer/webmcp/index.ts +0 -3
- package/src/viewer/webmcp/posthog-bridge.ts +0 -39
- package/src/viewer/webmcp/runtime-tools.ts +0 -152
- package/src/viewer/webmcp/scan-utils.ts +0 -135
- package/src/viewer/webmcp/use-tool-analytics.ts +0 -69
- package/src/viewer/webmcp/viewer-state.ts +0 -45
- /package/dist/{discovery-NEOY4MPN.js.map → ai-client-I6MDWNYA.js.map} +0 -0
- /package/dist/{chunk-PW7QTQA6.js.map → chunk-4OC7FTJB.js.map} +0 -0
- /package/dist/{chunk-HRFUSSZI.js.map → chunk-AM4MRTMN.js.map} +0 -0
- /package/dist/{scan-CJF2DOQW.js.map → discovery-ZJQSXF56.js.map} +0 -0
- /package/dist/{generate-FBHSXR3D.js.map → generate-RJFS2JWA.js.map} +0 -0
- /package/dist/{service-TQYWY65E.js.map → scan-3PMCJ4RB.js.map} +0 -0
- /package/dist/{static-viewer-NUBFPKWH.js.map → service-VMGNJZ42.js.map} +0 -0
- /package/dist/{test-Z5LVO724.js.map → test-SI4NSHQX.js.map} +0 -0
- /package/dist/{tokens-CE46OTMD.js.map → tokens-T6SIVUT5.js.map} +0 -0
package/src/commands/init.ts
CHANGED
|
@@ -1,37 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* fragments init -
|
|
2
|
+
* fragments init - Zero-config project initialization
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* 3. Components found (no stories) → Auto-generate documentation
|
|
8
|
-
* 4. Fresh project → Guided setup with example component
|
|
4
|
+
* Default: auto-detect everything, zero prompts, instant setup.
|
|
5
|
+
* --configure: interactive mode for theme seeds, snapshots, etc.
|
|
6
|
+
* --scan <path>: scan external component library, generate fragment files.
|
|
9
7
|
*/
|
|
10
8
|
|
|
11
9
|
import { readFile, writeFile, mkdir, access } from "node:fs/promises";
|
|
12
|
-
import { resolve, join, relative
|
|
10
|
+
import { resolve, join, relative } from "node:path";
|
|
13
11
|
import { spawn } from "node:child_process";
|
|
14
12
|
import pc from "picocolors";
|
|
15
13
|
import { BRAND } from "../core/index.js";
|
|
16
14
|
import fg from "fast-glob";
|
|
17
|
-
import { input, confirm, select } from "@inquirer/prompts";
|
|
18
15
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
detectSetupFramework,
|
|
17
|
+
findEntryFile,
|
|
18
|
+
addStylesImport,
|
|
19
|
+
addThemeProvider,
|
|
20
|
+
addTranspilePackages,
|
|
21
|
+
} from "./setup.js";
|
|
23
22
|
|
|
24
23
|
export interface InitOptions {
|
|
25
24
|
/** Project root directory */
|
|
26
25
|
projectRoot?: string;
|
|
27
26
|
/** Force overwrite existing config */
|
|
28
27
|
force?: boolean;
|
|
29
|
-
/** Non-interactive mode - auto-detect and use defaults */
|
|
28
|
+
/** Non-interactive mode - auto-detect and use defaults (legacy, now the default behavior) */
|
|
30
29
|
yes?: boolean;
|
|
31
30
|
/** Explicit framework override */
|
|
32
31
|
framework?: string;
|
|
33
32
|
/** Path to scan for components (enables scan mode) */
|
|
34
33
|
scan?: string;
|
|
34
|
+
/** Enable interactive configuration (theme seeds, snapshots, etc.) */
|
|
35
|
+
configure?: boolean;
|
|
36
|
+
/** Use AI to fill knowledge fields during --scan */
|
|
37
|
+
enrich?: boolean;
|
|
38
|
+
/** Show what --enrich would generate without calling API */
|
|
39
|
+
dryRun?: boolean;
|
|
40
|
+
/** AI provider for enrichment: anthropic or openai */
|
|
41
|
+
provider?: 'anthropic' | 'openai';
|
|
42
|
+
/** API key for AI enrichment */
|
|
43
|
+
apiKey?: string;
|
|
44
|
+
/** Override AI model for enrichment */
|
|
45
|
+
model?: string;
|
|
35
46
|
}
|
|
36
47
|
|
|
37
48
|
export interface InitResult {
|
|
@@ -49,14 +60,13 @@ interface DetectionResult {
|
|
|
49
60
|
hasConfig: boolean;
|
|
50
61
|
configPath: string | null;
|
|
51
62
|
suggestedComponentPath: string;
|
|
63
|
+
scenario: "stories" | "components" | "fresh";
|
|
52
64
|
}
|
|
53
65
|
|
|
54
66
|
/**
|
|
55
67
|
* Detect what exists in the project
|
|
56
68
|
*/
|
|
57
69
|
async function detectProject(projectRoot: string): Promise<DetectionResult> {
|
|
58
|
-
console.log(pc.dim("\nScanning project...\n"));
|
|
59
|
-
|
|
60
70
|
// Check for existing config
|
|
61
71
|
const configPath = join(projectRoot, BRAND.configFile);
|
|
62
72
|
const legacyConfigPath = join(projectRoot, BRAND.legacyConfigFile);
|
|
@@ -127,12 +137,16 @@ async function detectProject(projectRoot: string): Promise<DetectionResult> {
|
|
|
127
137
|
}
|
|
128
138
|
}
|
|
129
139
|
|
|
140
|
+
const scenario: DetectionResult["scenario"] =
|
|
141
|
+
storyFiles.length > 0 ? "stories" : likelyComponents.length > 0 ? "components" : "fresh";
|
|
142
|
+
|
|
130
143
|
return {
|
|
131
144
|
storyFiles,
|
|
132
145
|
componentFiles: likelyComponents,
|
|
133
146
|
hasConfig,
|
|
134
147
|
configPath: foundConfigPath,
|
|
135
148
|
suggestedComponentPath,
|
|
149
|
+
scenario,
|
|
136
150
|
};
|
|
137
151
|
}
|
|
138
152
|
|
|
@@ -143,6 +157,8 @@ function generateConfig(options: {
|
|
|
143
157
|
includePaths: string[];
|
|
144
158
|
componentPaths: string[];
|
|
145
159
|
framework: string;
|
|
160
|
+
themeBlock?: string;
|
|
161
|
+
snapshotsBlock?: string;
|
|
146
162
|
}): string {
|
|
147
163
|
const includeStr = options.includePaths.map((p) => ` '${p}'`).join(",\n");
|
|
148
164
|
const componentStr = options.componentPaths.map((p) => ` '${p}'`).join(",\n");
|
|
@@ -165,7 +181,7 @@ ${componentStr}
|
|
|
165
181
|
|
|
166
182
|
// Framework (react, vue, svelte)
|
|
167
183
|
framework: '${options.framework}',
|
|
168
|
-
};
|
|
184
|
+
${options.themeBlock || ""}${options.snapshotsBlock || ""}};
|
|
169
185
|
|
|
170
186
|
export default config;
|
|
171
187
|
`;
|
|
@@ -326,52 +342,6 @@ export default defineFragment({
|
|
|
326
342
|
`;
|
|
327
343
|
}
|
|
328
344
|
|
|
329
|
-
/**
|
|
330
|
-
* Convert a filename to PascalCase component name
|
|
331
|
-
*/
|
|
332
|
-
function toPascalCase(str: string): string {
|
|
333
|
-
return str
|
|
334
|
-
.replace(/[-_.](\w)/g, (_, c) => c.toUpperCase())
|
|
335
|
-
.replace(/^\w/, (c) => c.toUpperCase());
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Generate a minimal fragment stub for a discovered component
|
|
340
|
-
*/
|
|
341
|
-
function generateFragmentStub(componentName: string, importPath: string): string {
|
|
342
|
-
return `import React from 'react';
|
|
343
|
-
import { defineFragment } from '@fragments-sdk/cli/core';
|
|
344
|
-
import { ${componentName} } from '${importPath}';
|
|
345
|
-
|
|
346
|
-
export default defineFragment({
|
|
347
|
-
component: ${componentName},
|
|
348
|
-
|
|
349
|
-
meta: {
|
|
350
|
-
name: '${componentName}',
|
|
351
|
-
description: '${componentName} component',
|
|
352
|
-
category: 'general',
|
|
353
|
-
status: 'beta',
|
|
354
|
-
},
|
|
355
|
-
|
|
356
|
-
usage: {
|
|
357
|
-
when: ['TODO: describe when to use ${componentName}'],
|
|
358
|
-
whenNot: ['TODO: describe when not to use ${componentName}'],
|
|
359
|
-
},
|
|
360
|
-
|
|
361
|
-
props: {},
|
|
362
|
-
|
|
363
|
-
variants: [
|
|
364
|
-
{
|
|
365
|
-
name: 'Default',
|
|
366
|
-
description: 'Default ${componentName}',
|
|
367
|
-
code: \`<${componentName} />\`,
|
|
368
|
-
render: () => <${componentName} />,
|
|
369
|
-
},
|
|
370
|
-
],
|
|
371
|
-
});
|
|
372
|
-
`;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
345
|
/**
|
|
376
346
|
* Start the dev server
|
|
377
347
|
*/
|
|
@@ -393,7 +363,142 @@ function startDevServer(projectRoot: string): void {
|
|
|
393
363
|
}
|
|
394
364
|
|
|
395
365
|
/**
|
|
396
|
-
*
|
|
366
|
+
* Map init-framework's Framework type to setup's Framework type
|
|
367
|
+
*/
|
|
368
|
+
function mapFrameworkLabel(framework: string): string {
|
|
369
|
+
const labels: Record<string, string> = {
|
|
370
|
+
"nextjs-app": "Next.js (App Router)",
|
|
371
|
+
"nextjs-pages": "Next.js (Pages Router)",
|
|
372
|
+
"vite": "Vite",
|
|
373
|
+
"remix": "Remix",
|
|
374
|
+
"astro": "Astro",
|
|
375
|
+
};
|
|
376
|
+
return labels[framework] || "Unknown";
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// ============================================
|
|
380
|
+
// Interactive configuration (--configure)
|
|
381
|
+
// ============================================
|
|
382
|
+
|
|
383
|
+
interface ConfigureResult {
|
|
384
|
+
componentPath: string;
|
|
385
|
+
createExample: boolean;
|
|
386
|
+
startServer: boolean;
|
|
387
|
+
themeBlock: string;
|
|
388
|
+
snapshotsBlock: string;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
async function runInteractiveConfigure(
|
|
392
|
+
detection: DetectionResult,
|
|
393
|
+
scenario: "stories" | "components" | "fresh"
|
|
394
|
+
): Promise<ConfigureResult> {
|
|
395
|
+
const { input, confirm, select } = await import("@inquirer/prompts");
|
|
396
|
+
|
|
397
|
+
const componentPath = await input({
|
|
398
|
+
message: "Where are your components located?",
|
|
399
|
+
default: detection.suggestedComponentPath,
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
let createExample = scenario === "fresh";
|
|
403
|
+
if (scenario === "fresh") {
|
|
404
|
+
createExample = await confirm({
|
|
405
|
+
message: "Create an example Button component to get started?",
|
|
406
|
+
default: true,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Theme seed configuration
|
|
411
|
+
let themeBlock = "";
|
|
412
|
+
const configureTheme = await confirm({
|
|
413
|
+
message: "Configure theme seeds? (brand color, density, radius)",
|
|
414
|
+
default: false,
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
if (configureTheme) {
|
|
418
|
+
const brand = await input({
|
|
419
|
+
message: "Brand color (hex)",
|
|
420
|
+
default: "#18181b",
|
|
421
|
+
validate: (v) => /^#[0-9a-fA-F]{6}$/.test(v) || "Enter a valid hex color (e.g., #6366f1)",
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
const neutral = await select({
|
|
425
|
+
message: "Neutral palette",
|
|
426
|
+
choices: [
|
|
427
|
+
{ value: "stone", name: "Stone (warm gray)" },
|
|
428
|
+
{ value: "ice", name: "Ice (cool blue-gray)" },
|
|
429
|
+
{ value: "earth", name: "Earth (olive/khaki)" },
|
|
430
|
+
{ value: "sand", name: "Sand (warm beige)" },
|
|
431
|
+
{ value: "fire", name: "Fire (warm red-gray)" },
|
|
432
|
+
],
|
|
433
|
+
default: "stone",
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
const density = await select({
|
|
437
|
+
message: "Spacing density",
|
|
438
|
+
choices: [
|
|
439
|
+
{ value: "compact", name: "Compact (tighter spacing)" },
|
|
440
|
+
{ value: "default", name: "Default" },
|
|
441
|
+
{ value: "relaxed", name: "Relaxed (more breathing room)" },
|
|
442
|
+
],
|
|
443
|
+
default: "default",
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
const radiusStyle = await select({
|
|
447
|
+
message: "Border radius style",
|
|
448
|
+
choices: [
|
|
449
|
+
{ value: "sharp", name: "Sharp (0px)" },
|
|
450
|
+
{ value: "subtle", name: "Subtle (2px)" },
|
|
451
|
+
{ value: "default", name: "Default (6px)" },
|
|
452
|
+
{ value: "rounded", name: "Rounded (10px)" },
|
|
453
|
+
{ value: "pill", name: "Pill (999px)" },
|
|
454
|
+
],
|
|
455
|
+
default: "default",
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
// Build theme config block — only include non-default values
|
|
459
|
+
const themeEntries: string[] = [];
|
|
460
|
+
if (brand !== "#18181b") themeEntries.push(` brand: '${brand}'`);
|
|
461
|
+
if (neutral !== "stone") themeEntries.push(` neutral: '${neutral}'`);
|
|
462
|
+
if (density !== "default") themeEntries.push(` density: '${density}'`);
|
|
463
|
+
if (radiusStyle !== "default") themeEntries.push(` radiusStyle: '${radiusStyle}'`);
|
|
464
|
+
|
|
465
|
+
if (themeEntries.length > 0) {
|
|
466
|
+
themeBlock = `\n // Theme seed values (derives 120+ CSS custom properties)\n theme: {\n${themeEntries.join(",\n")},\n },\n`;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Snapshot toggle
|
|
471
|
+
let snapshotsBlock = "";
|
|
472
|
+
const enableSnapshots = await confirm({
|
|
473
|
+
message: "Enable visual snapshot tests per component variant?",
|
|
474
|
+
default: false,
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
if (enableSnapshots) {
|
|
478
|
+
snapshotsBlock = `\n // Visual snapshot testing\n snapshots: {\n enabled: true,\n },\n`;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Ask about starting the server
|
|
482
|
+
const startServer = await confirm({
|
|
483
|
+
message: "Start the viewer now?",
|
|
484
|
+
default: true,
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
return {
|
|
488
|
+
componentPath,
|
|
489
|
+
createExample,
|
|
490
|
+
startServer,
|
|
491
|
+
themeBlock,
|
|
492
|
+
snapshotsBlock,
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// ============================================
|
|
497
|
+
// Main init function
|
|
498
|
+
// ============================================
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Main init function - zero-config by default, interactive with --configure
|
|
397
502
|
*/
|
|
398
503
|
export async function init(options: InitOptions = {}): Promise<InitResult> {
|
|
399
504
|
const projectRoot = resolve(options.projectRoot || process.cwd());
|
|
@@ -423,6 +528,11 @@ export async function init(options: InitOptions = {}): Promise<InitResult> {
|
|
|
423
528
|
scanPath,
|
|
424
529
|
force: options.force,
|
|
425
530
|
verbose: true,
|
|
531
|
+
enrich: options.enrich,
|
|
532
|
+
dryRun: options.dryRun,
|
|
533
|
+
provider: options.provider,
|
|
534
|
+
apiKey: options.apiKey,
|
|
535
|
+
model: options.model,
|
|
426
536
|
});
|
|
427
537
|
|
|
428
538
|
// Create config pointing at the scanned path
|
|
@@ -444,9 +554,16 @@ export async function init(options: InitOptions = {}): Promise<InitResult> {
|
|
|
444
554
|
// Next steps
|
|
445
555
|
if (scanResult.success) {
|
|
446
556
|
console.log(pc.cyan("Next steps:"));
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
557
|
+
if (options.enrich && !options.dryRun) {
|
|
558
|
+
console.log(` 1. Review AI-enriched fields (usage.when, a11yRules, scenarioTags) in generated files`);
|
|
559
|
+
if (scanResult.generated.some(g => g.todoCount > 0)) {
|
|
560
|
+
console.log(` 2. Search remaining ${pc.bold("TODO:")} markers and fill in human knowledge`);
|
|
561
|
+
}
|
|
562
|
+
} else {
|
|
563
|
+
console.log(` 1. Search generated files for ${pc.bold("TODO:")} markers and fill in human knowledge`);
|
|
564
|
+
}
|
|
565
|
+
console.log(` ${options.enrich ? '3' : '2'}. Run ${pc.bold(`${BRAND.cliCommand} dev`)} to preview your components`);
|
|
566
|
+
console.log(` ${options.enrich ? '4' : '3'}. Run ${pc.bold(`${BRAND.cliCommand} build`)} to compile fragments.json`);
|
|
450
567
|
console.log();
|
|
451
568
|
}
|
|
452
569
|
|
|
@@ -463,146 +580,152 @@ export async function init(options: InitOptions = {}): Promise<InitResult> {
|
|
|
463
580
|
};
|
|
464
581
|
}
|
|
465
582
|
|
|
466
|
-
console.log(pc.cyan(`\n
|
|
583
|
+
console.log(pc.cyan(`\n${BRAND.name} init\n`));
|
|
467
584
|
|
|
468
585
|
// Step 1: Detect what exists
|
|
469
586
|
const detection = await detectProject(projectRoot);
|
|
470
587
|
|
|
471
588
|
// Check for existing config
|
|
472
589
|
if (detection.hasConfig && !options.force) {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
};
|
|
490
|
-
}
|
|
590
|
+
if (options.configure) {
|
|
591
|
+
// In configure mode, warn but continue
|
|
592
|
+
console.log(pc.yellow(` ! Config exists: ${BRAND.configFile} (will overwrite)`));
|
|
593
|
+
} else {
|
|
594
|
+
console.log(pc.green(` ✓ Already initialized`) + pc.dim(` (${BRAND.configFile} exists)`));
|
|
595
|
+
console.log();
|
|
596
|
+
console.log(pc.dim(` Run ${pc.bold(`${BRAND.cliCommand} init --force`)} to reinitialize`));
|
|
597
|
+
console.log(pc.dim(` Run ${pc.bold(`${BRAND.cliCommand} init --configure`)} to customize theme, snapshots, etc.`));
|
|
598
|
+
console.log();
|
|
599
|
+
return {
|
|
600
|
+
success: true,
|
|
601
|
+
scenario: detection.scenario,
|
|
602
|
+
storiesFound: detection.storyFiles.length,
|
|
603
|
+
componentsFound: detection.componentFiles.length,
|
|
604
|
+
errors: [],
|
|
605
|
+
};
|
|
491
606
|
}
|
|
492
607
|
}
|
|
493
608
|
|
|
494
|
-
// Step 2: Determine scenario
|
|
609
|
+
// Step 2: Determine scenario
|
|
495
610
|
let scenario: "stories" | "components" | "fresh";
|
|
496
611
|
|
|
497
612
|
if (detection.storyFiles.length > 0) {
|
|
498
613
|
scenario = "stories";
|
|
499
|
-
console.log(pc.green(
|
|
500
|
-
console.log(pc.dim(` ${detection.storyFiles.slice(0, 3).join("\n ")}`));
|
|
501
|
-
if (detection.storyFiles.length > 3) {
|
|
502
|
-
console.log(pc.dim(` ... and ${detection.storyFiles.length - 3} more`));
|
|
503
|
-
}
|
|
504
|
-
console.log();
|
|
505
|
-
console.log(
|
|
506
|
-
pc.cyan("Great news! ") +
|
|
507
|
-
"Fragments can load your existing stories automatically."
|
|
508
|
-
);
|
|
614
|
+
console.log(pc.green(` ✓ Found ${detection.storyFiles.length} Storybook stories`));
|
|
509
615
|
} else if (detection.componentFiles.length > 0) {
|
|
510
616
|
scenario = "components";
|
|
511
|
-
console.log(pc.green(
|
|
512
|
-
console.log(pc.dim(` ${detection.componentFiles.slice(0, 3).join("\n ")}`));
|
|
513
|
-
if (detection.componentFiles.length > 3) {
|
|
514
|
-
console.log(pc.dim(` ... and ${detection.componentFiles.length - 3} more`));
|
|
515
|
-
}
|
|
516
|
-
console.log();
|
|
517
|
-
console.log(
|
|
518
|
-
pc.cyan("No stories found, but that's fine! ") +
|
|
519
|
-
"Fragments can auto-generate documentation from your TypeScript."
|
|
520
|
-
);
|
|
617
|
+
console.log(pc.green(` ✓ Found ${detection.componentFiles.length} components`) + pc.dim(` in ${detection.suggestedComponentPath}`));
|
|
521
618
|
} else {
|
|
522
619
|
scenario = "fresh";
|
|
523
|
-
console.log(pc.
|
|
524
|
-
console.log();
|
|
525
|
-
console.log(pc.cyan("Let's create your first fragment!"));
|
|
620
|
+
console.log(pc.dim(` · No existing components found`));
|
|
526
621
|
}
|
|
527
622
|
|
|
528
|
-
|
|
623
|
+
// Step 3: Detect framework
|
|
624
|
+
const framework = await detectSetupFramework(projectRoot);
|
|
625
|
+
console.log(pc.green(` ✓ Detected ${mapFrameworkLabel(framework)}`));
|
|
529
626
|
|
|
530
|
-
// Step
|
|
627
|
+
// Step 4: Branch — interactive configure or fast path
|
|
531
628
|
let componentPath = detection.suggestedComponentPath;
|
|
532
|
-
let runScan = scenario === "components" || scenario === "stories";
|
|
533
629
|
let createExample = scenario === "fresh";
|
|
534
630
|
let startServer = false;
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
createExample = await confirm({
|
|
546
|
-
message: "Create an example Button component to get started?",
|
|
547
|
-
default: true,
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
// Ask about starting the server
|
|
552
|
-
startServer = await confirm({
|
|
553
|
-
message: "Start the viewer now?",
|
|
554
|
-
default: true,
|
|
555
|
-
});
|
|
631
|
+
let themeBlock = "";
|
|
632
|
+
let snapshotsBlock = "";
|
|
633
|
+
|
|
634
|
+
if (options.configure) {
|
|
635
|
+
const config = await runInteractiveConfigure(detection, scenario);
|
|
636
|
+
componentPath = config.componentPath;
|
|
637
|
+
createExample = config.createExample;
|
|
638
|
+
startServer = config.startServer;
|
|
639
|
+
themeBlock = config.themeBlock;
|
|
640
|
+
snapshotsBlock = config.snapshotsBlock;
|
|
556
641
|
}
|
|
557
642
|
|
|
558
|
-
// Step
|
|
559
|
-
console.log(pc.dim("\nCreating configuration...\n"));
|
|
560
|
-
|
|
561
|
-
// Build include patterns
|
|
643
|
+
// Step 5: Create configuration file
|
|
562
644
|
const includePaths: string[] = [
|
|
563
645
|
`${componentPath}/**/*.fragment.tsx`,
|
|
564
646
|
];
|
|
565
647
|
|
|
566
|
-
// If Storybook stories detected, also include them for direct rendering
|
|
567
648
|
if (scenario === 'stories') {
|
|
568
649
|
includePaths.push(`${componentPath}/**/*.stories.tsx`);
|
|
569
650
|
includePaths.push(`${componentPath}/**/*.stories.ts`);
|
|
570
651
|
}
|
|
571
652
|
|
|
572
|
-
// Create config file
|
|
573
653
|
const configPath = join(projectRoot, BRAND.configFile);
|
|
574
654
|
const configContent = generateConfig({
|
|
575
655
|
includePaths,
|
|
576
656
|
componentPaths: [`${componentPath}/**/*.tsx`],
|
|
577
657
|
framework: "react",
|
|
658
|
+
themeBlock,
|
|
659
|
+
snapshotsBlock,
|
|
578
660
|
});
|
|
579
661
|
|
|
580
662
|
try {
|
|
581
663
|
await writeFile(configPath, configContent, "utf-8");
|
|
582
|
-
console.log(pc.green(
|
|
664
|
+
console.log(pc.green(` ✓ Created ${BRAND.configFile}`));
|
|
583
665
|
} catch (e) {
|
|
584
666
|
errors.push(`Failed to create config: ${e}`);
|
|
585
667
|
}
|
|
586
668
|
|
|
587
|
-
// Step
|
|
669
|
+
// Step 6: Auto-inject styles + framework config
|
|
670
|
+
const entryFile = await findEntryFile(projectRoot, framework);
|
|
671
|
+
|
|
672
|
+
if (entryFile) {
|
|
673
|
+
try {
|
|
674
|
+
const stylesResult = await addStylesImport(projectRoot, entryFile);
|
|
675
|
+
if (stylesResult.modified) {
|
|
676
|
+
console.log(pc.green(` ✓ Added styles import to ${entryFile}`));
|
|
677
|
+
} else {
|
|
678
|
+
console.log(pc.dim(` · ${stylesResult.message}`));
|
|
679
|
+
}
|
|
680
|
+
} catch (e) {
|
|
681
|
+
errors.push(`Failed to add styles import: ${e instanceof Error ? e.message : e}`);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
try {
|
|
685
|
+
const providerResult = await addThemeProvider(projectRoot, entryFile, framework);
|
|
686
|
+
if (providerResult.modified) {
|
|
687
|
+
console.log(pc.green(` ✓ Added ThemeProvider to ${entryFile}`));
|
|
688
|
+
} else {
|
|
689
|
+
console.log(pc.dim(` · ${providerResult.message}`));
|
|
690
|
+
}
|
|
691
|
+
} catch (e) {
|
|
692
|
+
errors.push(`Failed to add ThemeProvider: ${e instanceof Error ? e.message : e}`);
|
|
693
|
+
}
|
|
694
|
+
} else {
|
|
695
|
+
console.log(pc.yellow(` ! Could not detect entry file — add styles import manually`));
|
|
696
|
+
console.log(pc.dim(` import '@fragments-sdk/ui/styles'`));
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// Next.js: add transpilePackages
|
|
700
|
+
if (framework === 'nextjs-app' || framework === 'nextjs-pages') {
|
|
701
|
+
try {
|
|
702
|
+
const transpileResult = await addTranspilePackages(projectRoot);
|
|
703
|
+
if (transpileResult.modified) {
|
|
704
|
+
console.log(pc.green(` ✓ ${transpileResult.message}`));
|
|
705
|
+
} else {
|
|
706
|
+
console.log(pc.dim(` · ${transpileResult.message}`));
|
|
707
|
+
}
|
|
708
|
+
} catch (e) {
|
|
709
|
+
errors.push(`Failed to update next.config: ${e instanceof Error ? e.message : e}`);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// Step 7: Handle scenario-specific setup
|
|
588
714
|
if (scenario === "fresh" && createExample) {
|
|
589
|
-
// Create example component
|
|
590
715
|
const exampleDir = join(projectRoot, componentPath, "Button");
|
|
591
716
|
|
|
592
717
|
try {
|
|
593
718
|
await mkdir(exampleDir, { recursive: true });
|
|
594
719
|
|
|
595
|
-
// Write Button.tsx
|
|
596
720
|
await writeFile(
|
|
597
721
|
join(exampleDir, "Button.tsx"),
|
|
598
722
|
generateExampleComponent(),
|
|
599
723
|
"utf-8"
|
|
600
724
|
);
|
|
601
725
|
console.log(
|
|
602
|
-
pc.green(
|
|
726
|
+
pc.green(` ✓ Created ${relative(projectRoot, join(exampleDir, "Button.tsx"))}`)
|
|
603
727
|
);
|
|
604
728
|
|
|
605
|
-
// Write Button.fragment.tsx
|
|
606
729
|
await writeFile(
|
|
607
730
|
join(exampleDir, "Button.fragment.tsx"),
|
|
608
731
|
generateExampleFragment(),
|
|
@@ -610,7 +733,7 @@ export async function init(options: InitOptions = {}): Promise<InitResult> {
|
|
|
610
733
|
);
|
|
611
734
|
console.log(
|
|
612
735
|
pc.green(
|
|
613
|
-
|
|
736
|
+
` ✓ Created ${relative(projectRoot, join(exampleDir, "Button.fragment.tsx"))}`
|
|
614
737
|
)
|
|
615
738
|
);
|
|
616
739
|
} catch (e) {
|
|
@@ -618,70 +741,50 @@ export async function init(options: InitOptions = {}): Promise<InitResult> {
|
|
|
618
741
|
}
|
|
619
742
|
}
|
|
620
743
|
|
|
621
|
-
if (
|
|
622
|
-
// Run scan to generate fragments.json from source code
|
|
623
|
-
console.log(pc.dim("\nScanning source code for documentation...\n"));
|
|
744
|
+
if (scenario === "components" || scenario === "stories") {
|
|
624
745
|
try {
|
|
625
746
|
const { scan } = await import("./scan.js");
|
|
626
|
-
await scan({
|
|
747
|
+
const scanResult = await scan({
|
|
627
748
|
config: configPath,
|
|
628
749
|
verbose: false,
|
|
750
|
+
quiet: true,
|
|
629
751
|
});
|
|
630
|
-
|
|
752
|
+
if (scanResult.success) {
|
|
753
|
+
console.log(pc.green(` ✓ Generated fragments.json`) + pc.dim(` (${scanResult.componentCount} components)`));
|
|
754
|
+
} else {
|
|
755
|
+
console.log(pc.dim(` · Auto-documentation will run when you start the dev server`));
|
|
756
|
+
}
|
|
757
|
+
} catch {
|
|
631
758
|
console.log(
|
|
632
|
-
pc.
|
|
759
|
+
pc.dim(` · Auto-documentation will run when you start the dev server`)
|
|
633
760
|
);
|
|
634
761
|
}
|
|
635
762
|
}
|
|
636
763
|
|
|
637
|
-
// Step
|
|
638
|
-
console.log(
|
|
639
|
-
|
|
640
|
-
const frameworkOverride = options.framework as Framework | undefined;
|
|
641
|
-
const frameworkResult = await setupFramework({
|
|
642
|
-
projectRoot,
|
|
643
|
-
framework: frameworkOverride,
|
|
644
|
-
});
|
|
645
|
-
|
|
646
|
-
if (frameworkResult.filesCreated.length > 0) {
|
|
647
|
-
for (const file of frameworkResult.filesCreated) {
|
|
648
|
-
console.log(pc.green(`✓ Created ${file}`));
|
|
649
|
-
}
|
|
650
|
-
}
|
|
764
|
+
// Step 8: Summary
|
|
765
|
+
console.log();
|
|
651
766
|
|
|
652
|
-
if (
|
|
653
|
-
|
|
654
|
-
|
|
767
|
+
if (errors.length > 0) {
|
|
768
|
+
console.log(pc.red(` ${errors.length} error(s) occurred:`));
|
|
769
|
+
for (const error of errors) {
|
|
770
|
+
console.log(pc.red(` - ${error}`));
|
|
655
771
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
const pkgs = frameworkResult.packagesToInstall.join(" ");
|
|
660
|
-
console.log(
|
|
661
|
-
pc.yellow(`\n⚠ Install required dependencies: `) +
|
|
662
|
-
pc.bold(`pnpm add -D ${pkgs}`)
|
|
663
|
-
);
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
for (const warning of frameworkResult.warnings) {
|
|
667
|
-
console.log(pc.yellow(` Note: ${warning}`));
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
// Step 7: Show next steps or start server
|
|
671
|
-
if (errors.length === 0) {
|
|
672
|
-
console.log(pc.green("\n✓ Setup complete!\n"));
|
|
772
|
+
console.log();
|
|
773
|
+
} else {
|
|
774
|
+
console.log(pc.green(` Done!`) + pc.dim(` Setup complete.\n`));
|
|
673
775
|
|
|
674
776
|
if (startServer) {
|
|
675
|
-
console.log(pc.cyan("Starting viewer...\n"));
|
|
676
777
|
startDevServer(projectRoot);
|
|
677
778
|
} else {
|
|
678
|
-
console.log(pc.
|
|
679
|
-
console.log(`
|
|
680
|
-
if (scenario === "fresh") {
|
|
681
|
-
console.log(` 2. Edit ${pc.bold(`${componentPath}/Button/Button.fragment.tsx`)}`);
|
|
682
|
-
}
|
|
683
|
-
console.log(` 3. Run ${pc.bold(`${BRAND.cliCommand} generate`)} to create fragment files for your components`);
|
|
779
|
+
console.log(` ${pc.bold("Get started:")}`);
|
|
780
|
+
console.log(` ${pc.dim("$")} ${BRAND.cliCommand} dev`);
|
|
684
781
|
console.log();
|
|
782
|
+
|
|
783
|
+
if (!options.configure) {
|
|
784
|
+
console.log(` ${pc.bold("Customize:")} theme seeds, snapshots, and more`);
|
|
785
|
+
console.log(` ${pc.dim("$")} ${BRAND.cliCommand} init --configure`);
|
|
786
|
+
console.log();
|
|
787
|
+
}
|
|
685
788
|
}
|
|
686
789
|
}
|
|
687
790
|
|