@fragments-sdk/cli 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/dist/bin.d.ts +1 -0
  2. package/dist/bin.js +502 -84
  3. package/dist/bin.js.map +1 -1
  4. package/dist/{chunk-CJEGT3WD.js → chunk-566BNPQZ.js} +21 -6
  5. package/dist/chunk-566BNPQZ.js.map +1 -0
  6. package/dist/{chunk-WI6SLMSO.js → chunk-CAMXG5HJ.js} +5 -5
  7. package/dist/chunk-D2CDBRNU.js +2 -0
  8. package/dist/{chunk-YMPGYEWK.js → chunk-D5PYOXEI.js} +2 -2
  9. package/dist/{chunk-NGIMCIK2.js → chunk-OQO55NKV.js} +405 -34
  10. package/dist/chunk-OQO55NKV.js.map +1 -0
  11. package/dist/{chunk-TOIE7VXF.js → chunk-PW7QTQA6.js} +2 -2
  12. package/dist/{chunk-AWYCDRPG.js → chunk-WXSR2II7.js} +2 -2
  13. package/dist/chunk-WXSR2II7.js.map +1 -0
  14. package/dist/{chunk-2JIKCJX3.js → chunk-ZDA3PLQ6.js} +17 -14
  15. package/dist/chunk-ZDA3PLQ6.js.map +1 -0
  16. package/dist/core/index.d.ts +1 -2092
  17. package/dist/core/index.js +26 -21
  18. package/dist/{discovery-Z4RDDFVR.js → discovery-NEOY4MPN.js} +3 -3
  19. package/dist/generate-BGKTKO6E.js +459 -0
  20. package/dist/generate-BGKTKO6E.js.map +1 -0
  21. package/dist/index.d.ts +3 -5
  22. package/dist/index.js +7 -8
  23. package/dist/index.js.map +1 -1
  24. package/dist/{init-KSAAS7X3.js → init-Q53R5Q2T.js} +66 -76
  25. package/dist/init-Q53R5Q2T.js.map +1 -0
  26. package/dist/mcp-bin.js +5 -7
  27. package/dist/mcp-bin.js.map +1 -1
  28. package/dist/scan-OQU7M4GH.js +14 -0
  29. package/dist/scan-generate-T5QNUG7N.js +691 -0
  30. package/dist/scan-generate-T5QNUG7N.js.map +1 -0
  31. package/dist/{service-A5GIGGGK.js → service-TQYWY65E.js} +4 -5
  32. package/dist/{static-viewer-NSODM5VX.js → static-viewer-NUBFPKWH.js} +4 -5
  33. package/dist/static-viewer-NUBFPKWH.js.map +1 -0
  34. package/dist/{test-RPWZAYSJ.js → test-2CSOSS3B.js} +4 -5
  35. package/dist/{test-RPWZAYSJ.js.map → test-2CSOSS3B.js.map} +1 -1
  36. package/dist/{tokens-NIXSZRX7.js → tokens-DXEGYTOJ.js} +6 -7
  37. package/dist/{tokens-NIXSZRX7.js.map → tokens-DXEGYTOJ.js.map} +1 -1
  38. package/dist/{viewer-SBTJDMP7.js → viewer-DBEPYM3G.js} +245 -23
  39. package/dist/viewer-DBEPYM3G.js.map +1 -0
  40. package/package.json +2 -1
  41. package/src/bin.ts +33 -1
  42. package/src/build.ts +13 -3
  43. package/src/commands/__tests__/scan-generate.test.ts +308 -0
  44. package/src/commands/build.ts +16 -2
  45. package/src/commands/generate.ts +383 -68
  46. package/src/commands/init.ts +81 -56
  47. package/src/commands/perf.ts +1 -1
  48. package/src/commands/scan-generate.ts +1013 -0
  49. package/src/commands/setup.ts +499 -0
  50. package/src/core/auto-props.ts +1 -1
  51. package/src/core/bundle-measurer.ts +2 -2
  52. package/src/core/config.ts +16 -4
  53. package/src/core/discovery.ts +2 -2
  54. package/src/core/generators/context.ts +1 -1
  55. package/src/core/generators/registry.ts +3 -3
  56. package/src/core/generators/typescript-extractor.ts +11 -1
  57. package/src/core/graph-extractor.ts +1 -1
  58. package/src/core/index.ts +3 -190
  59. package/src/core/loader.ts +2 -2
  60. package/src/core/parser.ts +1 -1
  61. package/src/core/previewLoader.ts +1 -1
  62. package/src/index.ts +2 -2
  63. package/src/migrate/converter.ts +9 -1
  64. package/src/migrate/parser.ts +2 -0
  65. package/src/migrate/types.ts +2 -0
  66. package/src/service/snippet-validation.test.ts +1 -1
  67. package/src/service/snippet-validation.ts +2 -2
  68. package/src/setup.ts +69 -24
  69. package/src/viewer/__tests__/viewer-integration.test.ts +4 -10
  70. package/src/viewer/components/AccessibilityPanel.tsx +305 -312
  71. package/src/viewer/components/ActionsPanel.tsx +31 -29
  72. package/src/viewer/components/AllVariantsPreview.tsx +78 -0
  73. package/src/viewer/components/App.tsx +187 -740
  74. package/src/viewer/components/BottomPanel.tsx +228 -132
  75. package/src/viewer/components/CodePanel.tsx +1 -1
  76. package/src/viewer/components/CommandPalette.tsx +7 -10
  77. package/src/viewer/components/ComponentDocView.tsx +164 -0
  78. package/src/viewer/components/ComponentGraph.tsx +111 -142
  79. package/src/viewer/components/ContractPanel.tsx +6 -6
  80. package/src/viewer/components/EmptyVariantMessage.tsx +54 -0
  81. package/src/viewer/components/FigmaEmbed.tsx +20 -18
  82. package/src/viewer/components/FragmentEditor.tsx +92 -115
  83. package/src/viewer/components/HeaderSearch.tsx +24 -0
  84. package/src/viewer/components/HealthDashboard.tsx +16 -2
  85. package/src/viewer/components/Icons.tsx +9 -0
  86. package/src/viewer/components/InteractionsPanel.tsx +101 -117
  87. package/src/viewer/components/IsolatedPreviewFrame.tsx +1 -0
  88. package/src/viewer/components/LandingPage.tsx +3 -3
  89. package/src/viewer/components/LeftSidebar.tsx +141 -63
  90. package/src/viewer/components/LoadErrorMessage.tsx +102 -0
  91. package/src/viewer/components/MultiViewportPreview.tsx +61 -142
  92. package/src/viewer/components/NoVariantsMessage.tsx +59 -0
  93. package/src/viewer/components/PanelShell.tsx +161 -0
  94. package/src/viewer/components/PerformancePanel.tsx +31 -28
  95. package/src/viewer/components/PreviewArea.tsx +1 -1
  96. package/src/viewer/components/PreviewAside.tsx +168 -0
  97. package/src/viewer/components/PreviewFrameHost.tsx +3 -3
  98. package/src/viewer/components/PropsEditor.tsx +70 -156
  99. package/src/viewer/components/ResizablePanel.tsx +103 -263
  100. package/src/viewer/components/RightSidebar.tsx +3 -9
  101. package/src/viewer/components/SkeletonLoader.tsx +13 -13
  102. package/src/viewer/components/TokenStylePanel.tsx +182 -209
  103. package/src/viewer/components/TopToolbar.tsx +159 -0
  104. package/src/viewer/components/VariantMatrix.tsx +42 -86
  105. package/src/viewer/components/VariantTabs.tsx +3 -3
  106. package/src/viewer/components/ViewerHeader.tsx +69 -0
  107. package/src/viewer/components/WebMCPDevTools.tsx +17 -23
  108. package/src/viewer/components/viewer-utils.ts +16 -0
  109. package/src/viewer/entry.tsx +5 -0
  110. package/src/viewer/hooks/useAppState.ts +27 -4
  111. package/src/viewer/hooks/usePreviewBridge.ts +2 -2
  112. package/src/viewer/preview-frame.html +6 -12
  113. package/src/viewer/server.ts +169 -2
  114. package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss +10 -0
  115. package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss.d.ts +2 -0
  116. package/src/viewer/vendor/shared/src/ComponentDocContent.tsx +274 -0
  117. package/src/viewer/vendor/shared/src/DocsHeaderBar.tsx +6 -18
  118. package/src/viewer/vendor/shared/src/DocsPageShell.tsx +5 -0
  119. package/src/viewer/vendor/shared/src/DocsSidebarNav.tsx +5 -16
  120. package/src/viewer/vendor/shared/src/PropsTable.module.scss +68 -0
  121. package/src/viewer/vendor/shared/src/PropsTable.module.scss.d.ts +2 -0
  122. package/src/viewer/vendor/shared/src/PropsTable.tsx +76 -0
  123. package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss +114 -0
  124. package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss.d.ts +2 -0
  125. package/src/viewer/vendor/shared/src/VariantPreviewCard.tsx +134 -0
  126. package/src/viewer/vendor/shared/src/index.ts +8 -0
  127. package/src/viewer/vendor/shared/src/types.ts +12 -0
  128. package/src/viewer/vite-plugin.ts +109 -4
  129. package/dist/chunk-2JIKCJX3.js.map +0 -1
  130. package/dist/chunk-AWYCDRPG.js.map +0 -1
  131. package/dist/chunk-CJEGT3WD.js.map +0 -1
  132. package/dist/chunk-EKLMXTWU.js +0 -80
  133. package/dist/chunk-EKLMXTWU.js.map +0 -1
  134. package/dist/chunk-GOVI6COW.js +0 -195
  135. package/dist/chunk-GOVI6COW.js.map +0 -1
  136. package/dist/chunk-NGIMCIK2.js.map +0 -1
  137. package/dist/defineFragment-D0UTve-I.d.ts +0 -665
  138. package/dist/generate-35OIMW4Y.js +0 -252
  139. package/dist/generate-35OIMW4Y.js.map +0 -1
  140. package/dist/init-KSAAS7X3.js.map +0 -1
  141. package/dist/scan-65RH3QMM.js +0 -15
  142. package/dist/viewer-SBTJDMP7.js.map +0 -1
  143. package/src/core/__tests__/preview-runtime.test.tsx +0 -111
  144. package/src/core/composition.test.ts +0 -262
  145. package/src/core/composition.ts +0 -318
  146. package/src/core/constants.ts +0 -114
  147. package/src/core/context.ts +0 -2
  148. package/src/core/defineFragment.ts +0 -141
  149. package/src/core/figma.ts +0 -263
  150. package/src/core/fragment-types.ts +0 -214
  151. package/src/core/performance-presets.ts +0 -142
  152. package/src/core/preview-runtime.tsx +0 -144
  153. package/src/core/schema.ts +0 -221
  154. package/src/core/storyAdapter.test.ts +0 -571
  155. package/src/core/storyAdapter.ts +0 -761
  156. package/src/core/storybook-csf.ts +0 -11
  157. package/src/core/token-parser.ts +0 -321
  158. package/src/core/token-types.ts +0 -287
  159. package/src/core/types.ts +0 -762
  160. /package/dist/{chunk-WI6SLMSO.js.map → chunk-CAMXG5HJ.js.map} +0 -0
  161. /package/dist/{discovery-Z4RDDFVR.js.map → chunk-D2CDBRNU.js.map} +0 -0
  162. /package/dist/{chunk-YMPGYEWK.js.map → chunk-D5PYOXEI.js.map} +0 -0
  163. /package/dist/{chunk-TOIE7VXF.js.map → chunk-PW7QTQA6.js.map} +0 -0
  164. /package/dist/{scan-65RH3QMM.js.map → discovery-NEOY4MPN.js.map} +0 -0
  165. /package/dist/{service-A5GIGGGK.js.map → scan-OQU7M4GH.js.map} +0 -0
  166. /package/dist/{static-viewer-NSODM5VX.js.map → service-TQYWY65E.js.map} +0 -0
package/src/core/index.ts CHANGED
@@ -1,192 +1,5 @@
1
1
  /**
2
- * Browser-safe exports for @fragments-sdk/cli/core
3
- * For Node.js-only APIs (config, discovery, loader), import from '@fragments-sdk/cli' directly.
2
+ * @deprecated Import from '@fragments-sdk/core' directly.
3
+ * This re-export exists for backwards compatibility with '@fragments-sdk/cli/core'.
4
4
  */
5
-
6
- // Brand and default constants
7
- export { BRAND, DEFAULTS } from "./constants.js";
8
- export type { Brand, Defaults } from "./constants.js";
9
-
10
- // Types
11
- export type {
12
- FragmentComponent,
13
- FragmentMeta,
14
- FragmentUsage,
15
- PropType,
16
- PropDefinition,
17
- ControlType,
18
- RelationshipType,
19
- ComponentRelation,
20
- VariantLoader,
21
- PlayFunction,
22
- PlayFunctionContext,
23
- FragmentVariant,
24
- FragmentDefinition,
25
- FragmentsConfig,
26
- SnippetPolicyConfig,
27
- RegistryOptions,
28
- CompiledFragment,
29
- CompiledFragmentsFile,
30
- BlockDefinition,
31
- CompiledBlock,
32
- RecipeDefinition, // @deprecated - use BlockDefinition
33
- CompiledRecipe, // @deprecated - use CompiledBlock
34
- // Contract and provenance types
35
- FragmentContract,
36
- FragmentGenerated,
37
- // AI metadata type
38
- AIMetadata,
39
- // Screenshot types
40
- ScreenshotConfig,
41
- ServiceConfig,
42
- Viewport,
43
- Theme,
44
- Screenshot,
45
- ScreenshotMetadata,
46
- DiffResult,
47
- BoundingBox,
48
- BaselineInfo,
49
- Manifest,
50
- VerifyRequest,
51
- VerifyResult,
52
- // Figma property mapping types
53
- FigmaPropMapping,
54
- FigmaStringMapping,
55
- FigmaBooleanMapping,
56
- FigmaEnumMapping,
57
- FigmaInstanceMapping,
58
- FigmaChildrenMapping,
59
- FigmaTextContentMapping,
60
- // Token configuration
61
- TokenConfig,
62
- // Compiled token types
63
- CompiledTokenEntry,
64
- CompiledTokenData,
65
- } from "./types.js";
66
-
67
- // Performance presets
68
- export {
69
- resolvePerformanceConfig,
70
- classifyComplexity,
71
- formatBytes,
72
- budgetBar,
73
- PRESET_NAMES,
74
- } from "./performance-presets.js";
75
- export type {
76
- PerformanceBudgets,
77
- PerformanceConfig,
78
- ComplexityTier,
79
- PerformanceData as PerfData,
80
- PerformanceSummary as PerfSummary,
81
- } from "./performance-presets.js";
82
-
83
- // Token types
84
- export type {
85
- TokenCategory,
86
- DesignToken,
87
- TokenRegistry,
88
- TokenRegistryMeta,
89
- EnhancedStyleDiffItem,
90
- TokenFix,
91
- TokenParseResult,
92
- TokenParseError,
93
- TokenMatchRequest,
94
- TokenMatchResult,
95
- TokenUsageSummary,
96
- } from "./token-types.js";
97
-
98
- // Schema validation (Zod is browser-safe)
99
- export {
100
- fragmentMetaSchema,
101
- fragmentUsageSchema,
102
- propDefinitionSchema,
103
- componentRelationSchema,
104
- fragmentVariantSchema,
105
- fragmentDefinitionSchema,
106
- fragmentsConfigSchema,
107
- figmaPropMappingSchema,
108
- // Contract and provenance schemas
109
- fragmentContractSchema,
110
- fragmentGeneratedSchema,
111
- fragmentBanSchema,
112
- blockDefinitionSchema,
113
- recipeDefinitionSchema, // @deprecated - use blockDefinitionSchema
114
- // AI metadata schema
115
- aiMetadataSchema,
116
- } from "./schema.js";
117
-
118
- // Main API
119
- export { defineFragment, compileFragment, defineBlock, compileBlock, defineRecipe, compileRecipe } from "./defineFragment.js";
120
- export type { InferProps } from "./defineFragment.js";
121
-
122
- // Story adapter (runtime conversion of Storybook modules)
123
- export {
124
- storyModuleToFragment,
125
- setPreviewConfig,
126
- getPreviewConfig,
127
- // Re-export @storybook/csf utilities
128
- toId,
129
- storyNameFromExport,
130
- isExportStory,
131
- } from "./storyAdapter.js";
132
- export type {
133
- StoryModule,
134
- StoryMeta,
135
- Story,
136
- StoryArgType,
137
- StoryContext,
138
- Decorator,
139
- Loader,
140
- PreviewConfig,
141
- CSF2Story,
142
- } from "./storyAdapter.js";
143
-
144
- // Context generation for AI agents
145
- export { generateContext } from "./context.js";
146
- export type { ContextOptions, ContextResult } from "./context.js";
147
-
148
- // Figma property mapping DSL
149
- export { figma, isFigmaPropMapping, resolveFigmaMapping } from "./figma.js";
150
-
151
- // Fragment JSON types (for .fragment.json files)
152
- export type {
153
- Fragment,
154
- FragmentFigma,
155
- FragmentUsage as FragmentJsonUsage,
156
- FragmentDoNotItem,
157
- FragmentPattern,
158
- FragmentAccessibility,
159
- FragmentRelated,
160
- FragmentMeta as FragmentJsonMeta,
161
- FragmentIndex,
162
- FragmentRegistry,
163
- RegistryComponentEntry,
164
- RegistryPropEntry,
165
- FragmentContextOptions,
166
- } from "./fragment-types.js";
167
-
168
- // Token parsing
169
- export { parseTokenFile } from "./token-parser.js";
170
- export type { ParsedToken, TokenParseOutput } from "./token-parser.js";
171
-
172
- // Composition analysis
173
- export { analyzeComposition } from "./composition.js";
174
- export type {
175
- CompositionAnalysis,
176
- CompositionWarning,
177
- CompositionSuggestion,
178
- CompositionGuideline,
179
- } from "./composition.js";
180
-
181
- // Shared preview runtime
182
- export {
183
- executeVariantLoaders,
184
- resolvePreviewRuntimeState,
185
- usePreviewVariantRuntime,
186
- PreviewVariantRuntime,
187
- } from "./preview-runtime.js";
188
- export type {
189
- PreviewVariantLike,
190
- PreviewRuntimeState,
191
- PreviewRuntimeOptions,
192
- } from "./preview-runtime.js";
5
+ export * from '@fragments-sdk/core';
@@ -2,8 +2,8 @@ import { writeFile, unlink } from 'node:fs/promises';
2
2
  import { dirname, basename, join } from 'node:path';
3
3
  import { pathToFileURL } from 'node:url';
4
4
  import { build, type Plugin } from 'esbuild';
5
- import type { FragmentDefinition } from './types.js';
6
- import { BRAND } from './constants.js';
5
+ import { BRAND } from '@fragments-sdk/core';
6
+ import type { FragmentDefinition } from '@fragments-sdk/core';
7
7
 
8
8
  // Inline implementation of defineFragment that will be injected into bundled files
9
9
  // This allows fragment files to work without @fragments/core being installed
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  import ts from "typescript";
11
- import type { FragmentMeta, FragmentUsage, PropDefinition, AIMetadata, FragmentContract } from "./types.js";
11
+ import type { FragmentMeta, FragmentUsage, PropDefinition, AIMetadata, FragmentContract } from '@fragments-sdk/core';
12
12
 
13
13
  /**
14
14
  * Parsed fragment metadata (extracted statically from AST)
@@ -12,7 +12,7 @@
12
12
 
13
13
  import { existsSync } from "node:fs";
14
14
  import { join, resolve } from "node:path";
15
- import type { PreviewConfig } from "./storyAdapter.js";
15
+ import type { PreviewConfig } from '@fragments-sdk/core';
16
16
 
17
17
  /**
18
18
  * Possible file extensions and names for preview config
package/src/index.ts CHANGED
@@ -44,10 +44,10 @@ export type { AnalyzeOptions, AnalyzeResult } from "./analyze.js";
44
44
  export { generateStaticViewer, generateViewerFromJson } from "./static-viewer.js";
45
45
 
46
46
  // Config type (used by generated fragments.config.ts)
47
- export type { FragmentsConfig } from "./core/types.js";
47
+ export type { FragmentsConfig } from "./core/index.js";
48
48
 
49
49
  // Fragment definition API (used by generated .fragment.tsx files)
50
- export { defineFragment, defineBlock } from "./core/defineFragment.js";
50
+ export { defineFragment, defineBlock } from "./core/index.js";
51
51
 
52
52
  // CLI Command metadata (for docs)
53
53
  export { CLI_COMMANDS, CLI_COMMAND_CATEGORIES } from "./cli-commands.js";
@@ -87,6 +87,7 @@ export function convertToFragment(parsed: ParsedStoryFile): ConversionResult {
87
87
  const code = generateFragmentCode({
88
88
  componentName,
89
89
  componentImport: parsed.meta.componentImport,
90
+ isDefaultImport: parsed.meta.isDefaultImport,
90
91
  description: parsed.meta.description,
91
92
  category,
92
93
  tags: parsed.meta.tags,
@@ -484,6 +485,7 @@ interface GeneratedMetadata {
484
485
  interface GenerateOptions {
485
486
  componentName: string;
486
487
  componentImport: string;
488
+ isDefaultImport?: boolean;
487
489
  description?: string;
488
490
  category: string;
489
491
  tags?: string[];
@@ -504,6 +506,7 @@ function generateFragmentCode(options: GenerateOptions): string {
504
506
  const {
505
507
  componentName,
506
508
  componentImport,
509
+ isDefaultImport,
507
510
  description,
508
511
  category,
509
512
  tags,
@@ -550,8 +553,13 @@ ${generated.skippedVariants.map(sv => ` { name: "${escapeString(sv.name)}",
550
553
  }
551
554
 
552
555
  // Import the actual component - this makes the fragment immediately usable
556
+ // Use default import when the source component uses export default
557
+ const componentImportStatement = isDefaultImport
558
+ ? `import ${componentName} from "${componentImport}";`
559
+ : `import { ${componentName} } from "${componentImport}";`;
560
+
553
561
  return `import { defineFragment } from "@fragments-sdk/cli/core";
554
- import { ${componentName} } from "${componentImport}";
562
+ ${componentImportStatement}
555
563
 
556
564
  export default defineFragment({
557
565
  component: ${componentName},
@@ -235,6 +235,7 @@ function parseMeta(
235
235
  );
236
236
  if (importMatch) {
237
237
  result.componentImport = importMatch[1];
238
+ result.isDefaultImport = false;
238
239
  } else {
239
240
  // Try default import
240
241
  const defaultImportMatch = content.match(
@@ -244,6 +245,7 @@ function parseMeta(
244
245
  );
245
246
  if (defaultImportMatch) {
246
247
  result.componentImport = defaultImportMatch[1];
248
+ result.isDefaultImport = true;
247
249
  }
248
250
  }
249
251
  }
@@ -12,6 +12,8 @@ export interface ParsedMeta {
12
12
  componentName: string;
13
13
  /** Component import path */
14
14
  componentImport?: string;
15
+ /** Whether the component uses a default import (export default) */
16
+ isDefaultImport?: boolean;
15
17
  /** Tags from the story */
16
18
  tags?: string[];
17
19
  /** Description from parameters.docs */
@@ -2,7 +2,7 @@ import { afterEach, describe, expect, it } from 'vitest';
2
2
  import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
3
3
  import { resolve } from 'node:path';
4
4
  import { tmpdir } from 'node:os';
5
- import type { FragmentsConfig } from '../core/types.js';
5
+ import type { FragmentsConfig } from '../core/index.js';
6
6
  import { validateSnippetPolicy } from './snippet-validation.js';
7
7
 
8
8
  async function createTempProject(): Promise<string> {
@@ -2,8 +2,8 @@ import ts from 'typescript';
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import { existsSync } from 'node:fs';
4
4
  import { join } from 'node:path';
5
- import type { FragmentsConfig, SnippetPolicyConfig } from '../core/types.js';
6
- import { BRAND } from '../core/constants.js';
5
+ import type { FragmentsConfig, SnippetPolicyConfig } from '../core/index.js';
6
+ import { BRAND } from '../core/index.js';
7
7
  import {
8
8
  discoverBlockFiles,
9
9
  discoverFragmentFiles,
package/src/setup.ts CHANGED
@@ -2,6 +2,7 @@ import pc from 'picocolors';
2
2
  import { BRAND } from './core/index.js';
3
3
  import { loadConfig, discoverFragmentFiles } from './core/node.js';
4
4
  import { buildFragments } from './build.js';
5
+ import { scan } from './commands/scan.js';
5
6
  import {
6
7
  detectStorybookConfig,
7
8
  discoverStoryFiles as discoverStorybookFiles,
@@ -125,48 +126,78 @@ export async function runSetup(options: SetupOptions = {}): Promise<SetupResult>
125
126
  let fragmentFiles = await discoverFragmentFiles(config, configDir);
126
127
 
127
128
  if (fragmentFiles.length === 0 && !options.skipStorybook) {
128
- // No fragment files - check for Storybook
129
+ // No fragment files - check for Storybook stories that the viewer can load directly
129
130
  log(pc.yellow('\n No fragment files found'));
130
131
 
131
132
  const sbConfig = await detectStorybookConfig(configDir);
132
133
  if (sbConfig) {
133
134
  log(pc.dim(` Found Storybook at ${sbConfig.configPath}`));
134
- log(pc.dim(' Converting stories to fragments...\n'));
135
135
 
136
- // Discover and convert stories
137
- const storyFiles = await discoverStorybookFiles(configDir, sbConfig.storyPatterns);
136
+ // Check if config.include already covers story files
137
+ const hasStoryPatterns = config.include.some((p: string) => p.includes('.stories.'));
138
138
 
139
- if (storyFiles.length > 0) {
140
- let converted = 0;
141
- for (const storyFile of storyFiles) {
142
- try {
143
- const parsed = await parseStoryFile(storyFile);
144
- const fragmentResult = convertToFragment(parsed);
139
+ if (hasStoryPatterns) {
140
+ // Stories are in the include config — discover them directly
141
+ // The viewer handles .stories.tsx natively via storyModuleToFragment()
142
+ log(pc.dim(' Stories included in config — viewer will load them directly\n'));
143
+ fragmentFiles = await discoverFragmentFiles(config, configDir);
145
144
 
146
- // Create directory and write file
147
- await fs.mkdir(path.dirname(fragmentResult.outputFile), { recursive: true });
148
- await fs.writeFile(fragmentResult.outputFile, fragmentResult.code);
149
- converted++;
150
- } catch {
151
- // Skip files that can't be converted
152
- }
145
+ if (fragmentFiles.length > 0) {
146
+ log(pc.green(` Found ${fragmentFiles.length} story/fragment file(s)`));
153
147
  }
148
+ } else {
149
+ // Stories not in config — fall back to conversion
150
+ log(pc.dim(' Converting stories to fragments...\n'));
151
+
152
+ const storyFiles = await discoverStorybookFiles(configDir, sbConfig.storyPatterns);
153
+
154
+ if (storyFiles.length > 0) {
155
+ let converted = 0;
156
+ for (const storyFile of storyFiles) {
157
+ try {
158
+ const parsed = await parseStoryFile(storyFile);
159
+ const fragmentResult = convertToFragment(parsed);
160
+
161
+ // Create directory and write file
162
+ await fs.mkdir(path.dirname(fragmentResult.outputFile), { recursive: true });
163
+ await fs.writeFile(fragmentResult.outputFile, fragmentResult.code);
164
+ converted++;
165
+ } catch {
166
+ // Skip files that can't be converted
167
+ }
168
+ }
154
169
 
155
- result.fragmentFilesCreated = converted;
156
- log(pc.green(` Generated ${converted} fragment file(s)`));
170
+ result.fragmentFilesCreated = converted;
171
+ log(pc.green(` Generated ${converted} fragment file(s)`));
157
172
 
158
- // Refresh fragment files list
159
- fragmentFiles = await discoverFragmentFiles(config, configDir);
173
+ // Refresh fragment files list
174
+ fragmentFiles = await discoverFragmentFiles(config, configDir);
175
+ }
160
176
  }
161
177
  } else {
178
+ // No Storybook — auto-scan source code to generate fragments.json
162
179
  log(pc.dim(' No Storybook config found'));
163
- log(pc.dim(` Run ${pc.cyan(`${BRAND.cliCommand} add <ComponentName>`)} to create your first fragment`));
180
+ log(pc.dim(' Auto-scanning source code...\n'));
181
+
182
+ try {
183
+ const scanResult = await scan({
184
+ config: options.configPath,
185
+ verbose: false,
186
+ });
187
+
188
+ if (scanResult.componentCount > 0) {
189
+ result.fragmentsBuilt = scanResult.componentCount;
190
+ log(pc.green(` Scanned ${scanResult.componentCount} component(s) from source`));
191
+ }
192
+ } catch {
193
+ log(pc.dim(` Run ${pc.cyan(`${BRAND.cliCommand} scan`)} to generate documentation from source`));
194
+ }
164
195
  }
165
196
  } else if (fragmentFiles.length > 0) {
166
197
  log(pc.green(` Found ${fragmentFiles.length} fragment file(s)`));
167
198
  }
168
199
 
169
- // Step 2: Build fragments.json if needed
200
+ // Step 2: Build fragments.json if needed (only when fragment files exist)
170
201
  if (fragmentFiles.length > 0 && !options.skipBuild) {
171
202
  const outFile = config.outFile || BRAND.outFile;
172
203
  const { stale, missing } = await isFragmentsJsonStale(configDir, outFile);
@@ -185,7 +216,21 @@ export async function runSetup(options: SetupOptions = {}): Promise<SetupResult>
185
216
  }
186
217
  }
187
218
 
188
- log(pc.green(` Built ${buildResult.fragmentCount} fragment(s)`));
219
+ if (buildResult.fragmentCount > 0) {
220
+ log(pc.green(` Built ${buildResult.fragmentCount} fragment(s)`));
221
+ } else {
222
+ // Build found 0 fragments — fallback to scan
223
+ log(pc.dim(' No compilable fragments found, falling back to source scan...'));
224
+ try {
225
+ const scanResult = await scan({ verbose: false });
226
+ if (scanResult.componentCount > 0) {
227
+ result.fragmentsBuilt = scanResult.componentCount;
228
+ log(pc.green(` Scanned ${scanResult.componentCount} component(s) from source`));
229
+ }
230
+ } catch {
231
+ // scan failed silently
232
+ }
233
+ }
189
234
  } catch (error) {
190
235
  result.errors.push(`Build failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
191
236
  }
@@ -68,17 +68,11 @@ describe("@fragments-sdk/cli/core alias resolution", () => {
68
68
  expect(existsSync(corePath)).toBe(true);
69
69
  });
70
70
 
71
- it("core/index.ts exports storyModuleToFragment", async () => {
71
+ it("core/index.ts re-exports from @fragments-sdk/core", async () => {
72
72
  const corePath = resolve(cliPackageRoot, "src/core/index.ts");
73
73
  const content = await readFile(corePath, "utf-8");
74
- // The virtual module imports { storyModuleToFragment, setPreviewConfig }
75
- expect(content).toContain("storyModuleToFragment");
76
- });
77
-
78
- it("core/index.ts exports setPreviewConfig", async () => {
79
- const corePath = resolve(cliPackageRoot, "src/core/index.ts");
80
- const content = await readFile(corePath, "utf-8");
81
- expect(content).toContain("setPreviewConfig");
74
+ // After core extraction, cli/core is a thin re-export shim
75
+ expect(content).toContain("@fragments-sdk/core");
82
76
  });
83
77
  });
84
78
 
@@ -91,7 +85,7 @@ describe("virtual module @fragments-sdk/cli/core import", () => {
91
85
 
92
86
  // The generated virtual module string should reference @fragments-sdk/cli/core
93
87
  expect(content).toContain(
94
- 'import { storyModuleToFragment, setPreviewConfig } from "@fragments-sdk/cli/core"'
88
+ 'import { storyModuleToFragment, setPreviewConfig, checkStoryExclusion, isForceIncluded, isConfigExcluded } from "@fragments-sdk/cli/core"'
95
89
  );
96
90
  });
97
91