@fragments-sdk/cli 0.7.0 → 0.7.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 +77 -14
- package/dist/bin.js +247 -247
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-CVXKXVOY.js → chunk-3T6QL7IY.js} +47 -29
- package/dist/chunk-3T6QL7IY.js.map +1 -0
- package/dist/{chunk-7OPWMLOE.js → chunk-7KUSBMI4.js} +114 -112
- package/dist/chunk-7KUSBMI4.js.map +1 -0
- package/dist/{chunk-XHUDJNN3.js → chunk-DH4ETVSM.js} +18 -18
- package/dist/chunk-DH4ETVSM.js.map +1 -0
- package/dist/{chunk-RVRTRESS.js → chunk-DQHWLAUV.js} +29 -29
- package/dist/chunk-DQHWLAUV.js.map +1 -0
- package/dist/{chunk-6JBGU74P.js → chunk-GHYYFAQN.js} +23 -23
- package/dist/chunk-GHYYFAQN.js.map +1 -0
- package/dist/{chunk-NWQ4CJOQ.js → chunk-GKX2HPZ6.js} +40 -40
- package/dist/chunk-GKX2HPZ6.js.map +1 -0
- package/dist/{chunk-TJ34N7C7.js → chunk-OOGTG5FM.js} +34 -33
- package/dist/chunk-OOGTG5FM.js.map +1 -0
- package/dist/{core-W2HYIQW6.js → core-UQXZTBFZ.js} +24 -26
- package/dist/{generate-LMTISDIJ.js → generate-GP6ZLAQB.js} +5 -5
- package/dist/generate-GP6ZLAQB.js.map +1 -0
- package/dist/index.d.ts +23 -27
- package/dist/index.js +10 -10
- package/dist/{init-7CHRKQ7P.js → init-W72WBSU2.js} +5 -5
- package/dist/{init-7CHRKQ7P.js.map → init-W72WBSU2.js.map} +1 -1
- package/dist/mcp-bin.js +73 -73
- package/dist/mcp-bin.js.map +1 -1
- package/dist/scan-V54HWRDY.js +12 -0
- package/dist/{service-T2L7VLTE.js → service-PVGTYUKX.js} +6 -6
- package/dist/{static-viewer-GBR7YNF3.js → static-viewer-KILKIVN7.js} +4 -4
- package/dist/{test-OJRXNDO2.js → test-3YRYQRGV.js} +19 -19
- package/dist/test-3YRYQRGV.js.map +1 -0
- package/dist/{tokens-3BWDESVM.js → tokens-IXSQHPQK.js} +5 -5
- package/dist/{viewer-SUFOISZM.js → viewer-K42REJU2.js} +199 -199
- package/dist/viewer-K42REJU2.js.map +1 -0
- package/package.json +13 -2
- package/src/ai.ts +5 -5
- package/src/analyze.ts +11 -11
- package/src/bin.ts +1 -1
- package/src/build.ts +37 -35
- package/src/commands/a11y.ts +6 -6
- package/src/commands/add.ts +11 -11
- package/src/commands/audit.ts +4 -4
- package/src/commands/baseline.ts +3 -3
- package/src/commands/build.ts +8 -8
- package/src/commands/compare.ts +20 -20
- package/src/commands/context.ts +16 -16
- package/src/commands/enhance.ts +36 -36
- package/src/commands/generate.ts +1 -1
- package/src/commands/graph.ts +5 -5
- package/src/commands/init.ts +1 -1
- package/src/commands/link/figma.ts +82 -82
- package/src/commands/link/index.ts +3 -3
- package/src/commands/link/storybook.ts +9 -9
- package/src/commands/list.ts +2 -2
- package/src/commands/reset.ts +15 -15
- package/src/commands/scan.ts +27 -27
- package/src/commands/storygen.ts +24 -24
- package/src/commands/validate.ts +2 -2
- package/src/commands/verify.ts +8 -8
- package/src/core/auto-props.ts +4 -4
- package/src/core/composition.test.ts +36 -36
- package/src/core/composition.ts +19 -19
- package/src/core/config.ts +6 -6
- package/src/core/{defineSegment.ts → defineFragment.ts} +16 -22
- package/src/core/discovery.ts +6 -6
- package/src/core/figma.ts +2 -2
- package/src/core/graph-extractor.test.ts +77 -77
- package/src/core/graph-extractor.ts +32 -32
- package/src/core/importAnalyzer.ts +1 -1
- package/src/core/index.ts +22 -23
- package/src/core/loader.ts +21 -24
- package/src/core/node.ts +5 -5
- package/src/core/parser.ts +71 -31
- package/src/core/previewLoader.ts +1 -1
- package/src/core/schema.ts +16 -16
- package/src/core/storyAdapter.test.ts +87 -87
- package/src/core/storyAdapter.ts +16 -16
- package/src/core/token-parser.ts +9 -1
- package/src/core/types.ts +21 -26
- package/src/diff.ts +22 -22
- package/src/index.ts +2 -2
- package/src/mcp/server.ts +80 -80
- package/src/migrate/__tests__/utils/utils.test.ts +3 -3
- package/src/migrate/bin.ts +4 -4
- package/src/migrate/converter.ts +16 -16
- package/src/migrate/index.ts +3 -3
- package/src/migrate/migrate.ts +3 -3
- package/src/migrate/parser.ts +8 -8
- package/src/migrate/report.ts +2 -2
- package/src/migrate/types.ts +4 -4
- package/src/screenshot.ts +22 -22
- package/src/service/__tests__/props-extractor.test.ts +15 -15
- package/src/service/analytics.ts +39 -39
- package/src/service/enhance/codebase-scanner.ts +1 -1
- package/src/service/enhance/index.ts +1 -1
- package/src/service/enhance/props-extractor.ts +2 -2
- package/src/service/enhance/types.ts +2 -2
- package/src/service/index.ts +2 -2
- package/src/service/metrics-store.ts +1 -1
- package/src/service/patch-generator.ts +1 -1
- package/src/setup.ts +52 -52
- package/src/shared/dev-server-client.ts +7 -7
- package/src/shared/fragment-loader.ts +59 -0
- package/src/shared/index.ts +1 -1
- package/src/shared/types.ts +4 -4
- package/src/static-viewer.ts +35 -35
- package/src/test/discovery.ts +6 -6
- package/src/test/index.ts +5 -5
- package/src/test/reporters/console.ts +1 -1
- package/src/test/reporters/junit.ts +1 -1
- package/src/test/runner.ts +7 -7
- package/src/test/types.ts +3 -3
- package/src/test/watch.ts +9 -9
- package/src/validators.ts +26 -26
- package/src/viewer/__tests__/render-utils.test.ts +28 -28
- package/src/viewer/__tests__/viewer-integration.test.ts +4 -4
- package/src/viewer/cli/health.ts +26 -26
- package/src/viewer/components/App.tsx +79 -79
- package/src/viewer/components/BottomPanel.tsx +17 -17
- package/src/viewer/components/CodePanel.tsx +3 -3
- package/src/viewer/components/CommandPalette.tsx +11 -11
- package/src/viewer/components/ComponentGraph.tsx +28 -28
- package/src/viewer/components/ComponentHeader.tsx +2 -2
- package/src/viewer/components/ContractPanel.tsx +6 -6
- package/src/viewer/components/FigmaEmbed.tsx +9 -9
- package/src/viewer/components/HealthDashboard.tsx +17 -17
- package/src/viewer/components/InteractionsPanel.tsx +2 -2
- package/src/viewer/components/IsolatedPreviewFrame.tsx +6 -6
- package/src/viewer/components/IsolatedRender.tsx +10 -10
- package/src/viewer/components/LeftSidebar.tsx +28 -28
- package/src/viewer/components/MultiViewportPreview.tsx +14 -14
- package/src/viewer/components/PreviewArea.tsx +11 -11
- package/src/viewer/components/PreviewFrameHost.tsx +51 -51
- package/src/viewer/components/RightSidebar.tsx +9 -9
- package/src/viewer/components/Sidebar.tsx +17 -17
- package/src/viewer/components/StoryRenderer.tsx +2 -2
- package/src/viewer/components/TokenStylePanel.tsx +1 -1
- package/src/viewer/components/UsageSection.tsx +2 -2
- package/src/viewer/components/VariantMatrix.tsx +11 -11
- package/src/viewer/components/VariantRenderer.tsx +3 -3
- package/src/viewer/components/VariantTabs.tsx +2 -2
- package/src/viewer/components/_future/CreatePage.tsx +6 -6
- package/src/viewer/composition-renderer.ts +11 -11
- package/src/viewer/entry.tsx +40 -40
- package/src/viewer/hooks/useFigmaIntegration.ts +1 -1
- package/src/viewer/hooks/usePreviewBridge.ts +5 -5
- package/src/viewer/hooks/useUrlState.ts +6 -6
- package/src/viewer/index.ts +2 -2
- package/src/viewer/intelligence/healthReport.ts +17 -17
- package/src/viewer/intelligence/styleDrift.ts +1 -1
- package/src/viewer/intelligence/usageScanner.ts +1 -1
- package/src/viewer/render-template.html +1 -1
- package/src/viewer/render-utils.ts +21 -21
- package/src/viewer/server.ts +18 -18
- package/src/viewer/utils/detectRelationships.ts +22 -22
- package/src/viewer/vite-plugin.ts +213 -213
- package/dist/chunk-6JBGU74P.js.map +0 -1
- package/dist/chunk-7OPWMLOE.js.map +0 -1
- package/dist/chunk-CVXKXVOY.js.map +0 -1
- package/dist/chunk-NWQ4CJOQ.js.map +0 -1
- package/dist/chunk-RVRTRESS.js.map +0 -1
- package/dist/chunk-TJ34N7C7.js.map +0 -1
- package/dist/chunk-XHUDJNN3.js.map +0 -1
- package/dist/generate-LMTISDIJ.js.map +0 -1
- package/dist/scan-WY23TJCP.js +0 -12
- package/dist/test-OJRXNDO2.js.map +0 -1
- package/dist/viewer-SUFOISZM.js.map +0 -1
- package/src/shared/segment-loader.ts +0 -59
- /package/dist/{core-W2HYIQW6.js.map → core-UQXZTBFZ.js.map} +0 -0
- /package/dist/{scan-WY23TJCP.js.map → scan-V54HWRDY.js.map} +0 -0
- /package/dist/{service-T2L7VLTE.js.map → service-PVGTYUKX.js.map} +0 -0
- /package/dist/{static-viewer-GBR7YNF3.js.map → static-viewer-KILKIVN7.js.map} +0 -0
- /package/dist/{tokens-3BWDESVM.js.map → tokens-IXSQHPQK.js.map} +0 -0
package/src/core/index.ts
CHANGED
|
@@ -9,9 +9,9 @@ export type { Brand, Defaults } from "./constants.js";
|
|
|
9
9
|
|
|
10
10
|
// Types
|
|
11
11
|
export type {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
FragmentComponent,
|
|
13
|
+
FragmentMeta,
|
|
14
|
+
FragmentUsage,
|
|
15
15
|
PropType,
|
|
16
16
|
PropDefinition,
|
|
17
17
|
ControlType,
|
|
@@ -20,20 +20,19 @@ export type {
|
|
|
20
20
|
VariantLoader,
|
|
21
21
|
PlayFunction,
|
|
22
22
|
PlayFunctionContext,
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
FragmentVariant,
|
|
24
|
+
FragmentDefinition,
|
|
25
25
|
FragmentsConfig,
|
|
26
|
-
SegmentsConfig, // @deprecated - use FragmentsConfig
|
|
27
26
|
RegistryOptions,
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
CompiledFragment,
|
|
28
|
+
CompiledFragmentsFile,
|
|
30
29
|
BlockDefinition,
|
|
31
30
|
CompiledBlock,
|
|
32
31
|
RecipeDefinition, // @deprecated - use BlockDefinition
|
|
33
32
|
CompiledRecipe, // @deprecated - use CompiledBlock
|
|
34
33
|
// Contract and provenance types
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
FragmentContract,
|
|
35
|
+
FragmentGenerated,
|
|
37
36
|
// AI metadata type
|
|
38
37
|
AIMetadata,
|
|
39
38
|
// Screenshot types
|
|
@@ -81,18 +80,18 @@ export type {
|
|
|
81
80
|
|
|
82
81
|
// Schema validation (Zod is browser-safe)
|
|
83
82
|
export {
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
fragmentMetaSchema,
|
|
84
|
+
fragmentUsageSchema,
|
|
86
85
|
propDefinitionSchema,
|
|
87
86
|
componentRelationSchema,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
fragmentVariantSchema,
|
|
88
|
+
fragmentDefinitionSchema,
|
|
89
|
+
fragmentsConfigSchema,
|
|
91
90
|
figmaPropMappingSchema,
|
|
92
91
|
// Contract and provenance schemas
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
92
|
+
fragmentContractSchema,
|
|
93
|
+
fragmentGeneratedSchema,
|
|
94
|
+
fragmentBanSchema,
|
|
96
95
|
blockDefinitionSchema,
|
|
97
96
|
recipeDefinitionSchema, // @deprecated - use blockDefinitionSchema
|
|
98
97
|
// AI metadata schema
|
|
@@ -100,12 +99,12 @@ export {
|
|
|
100
99
|
} from "./schema.js";
|
|
101
100
|
|
|
102
101
|
// Main API
|
|
103
|
-
export {
|
|
104
|
-
export type { InferProps } from "./
|
|
102
|
+
export { defineFragment, compileFragment, defineBlock, compileBlock, defineRecipe, compileRecipe } from "./defineFragment.js";
|
|
103
|
+
export type { InferProps } from "./defineFragment.js";
|
|
105
104
|
|
|
106
105
|
// Story adapter (runtime conversion of Storybook modules)
|
|
107
106
|
export {
|
|
108
|
-
|
|
107
|
+
storyModuleToFragment,
|
|
109
108
|
setPreviewConfig,
|
|
110
109
|
getPreviewConfig,
|
|
111
110
|
// Re-export @storybook/csf utilities
|
|
@@ -136,12 +135,12 @@ export { figma, isFigmaPropMapping, resolveFigmaMapping } from "./figma.js";
|
|
|
136
135
|
export type {
|
|
137
136
|
Fragment,
|
|
138
137
|
FragmentFigma,
|
|
139
|
-
FragmentUsage,
|
|
138
|
+
FragmentUsage as FragmentJsonUsage,
|
|
140
139
|
FragmentDoNotItem,
|
|
141
140
|
FragmentPattern,
|
|
142
141
|
FragmentAccessibility,
|
|
143
142
|
FragmentRelated,
|
|
144
|
-
FragmentMeta,
|
|
143
|
+
FragmentMeta as FragmentJsonMeta,
|
|
145
144
|
FragmentIndex,
|
|
146
145
|
FragmentRegistry,
|
|
147
146
|
RegistryComponentEntry,
|
package/src/core/loader.ts
CHANGED
|
@@ -2,15 +2,12 @@ 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 {
|
|
5
|
+
import type { FragmentDefinition } from './types.js';
|
|
6
6
|
import { BRAND } from './constants.js';
|
|
7
7
|
|
|
8
|
-
// Inline implementation of
|
|
9
|
-
// This allows
|
|
10
|
-
const
|
|
11
|
-
export function defineSegment(def) {
|
|
12
|
-
return def;
|
|
13
|
-
}
|
|
8
|
+
// Inline implementation of defineFragment that will be injected into bundled files
|
|
9
|
+
// This allows fragment files to work without @fragments/core being installed
|
|
10
|
+
const DEFINE_FRAGMENT_SHIM = `
|
|
14
11
|
export function defineFragment(def) {
|
|
15
12
|
return def;
|
|
16
13
|
}
|
|
@@ -24,9 +21,9 @@ export function defineRecipe(def) {
|
|
|
24
21
|
|
|
25
22
|
/**
|
|
26
23
|
* Creates an esbuild plugin that replaces @fragments/core imports with an inline shim.
|
|
27
|
-
* This allows
|
|
24
|
+
* This allows fragment files to be loaded without @fragments/core being installed in the target project.
|
|
28
25
|
*/
|
|
29
|
-
function
|
|
26
|
+
function createFragmentsCoreShimPlugin(): Plugin {
|
|
30
27
|
return {
|
|
31
28
|
name: BRAND.vitePluginNamespace,
|
|
32
29
|
setup(build) {
|
|
@@ -41,7 +38,7 @@ function createSegmentsCoreShimPlugin(): Plugin {
|
|
|
41
38
|
// Return the shim content
|
|
42
39
|
build.onLoad({ filter: /.*/, namespace: BRAND.vitePluginNamespace }, () => {
|
|
43
40
|
return {
|
|
44
|
-
contents:
|
|
41
|
+
contents: DEFINE_FRAGMENT_SHIM,
|
|
45
42
|
loader: 'js',
|
|
46
43
|
};
|
|
47
44
|
});
|
|
@@ -50,12 +47,12 @@ function createSegmentsCoreShimPlugin(): Plugin {
|
|
|
50
47
|
}
|
|
51
48
|
|
|
52
49
|
/**
|
|
53
|
-
* Load a
|
|
50
|
+
* Load a fragment file, handling TSX/TS transformation automatically.
|
|
54
51
|
* Uses esbuild to bundle and transform TypeScript/JSX before importing.
|
|
55
52
|
*/
|
|
56
|
-
export async function
|
|
53
|
+
export async function loadFragmentFile(
|
|
57
54
|
absolutePath: string
|
|
58
|
-
): Promise<
|
|
55
|
+
): Promise<FragmentDefinition | null> {
|
|
59
56
|
const ext = absolutePath.split('.').pop()?.toLowerCase();
|
|
60
57
|
const needsTransform = ext === 'tsx' || ext === 'ts' || ext === 'jsx';
|
|
61
58
|
|
|
@@ -70,10 +67,10 @@ export async function loadSegmentFile(
|
|
|
70
67
|
const sourceDir = dirname(absolutePath);
|
|
71
68
|
const baseName = basename(absolutePath, `.${ext}`);
|
|
72
69
|
// Use .cjs extension to ensure CommonJS evaluation
|
|
73
|
-
const tempFile = join(sourceDir, `.${baseName}.
|
|
70
|
+
const tempFile = join(sourceDir, `.${baseName}.fragments-temp-${Date.now()}.cjs`);
|
|
74
71
|
|
|
75
72
|
try {
|
|
76
|
-
// Use esbuild to bundle the
|
|
73
|
+
// Use esbuild to bundle the fragment file
|
|
77
74
|
// We inject a shim for @fragments/core so it doesn't need to be installed
|
|
78
75
|
// Using CommonJS format to avoid ESM/CJS interop issues with node_modules
|
|
79
76
|
await build({
|
|
@@ -84,8 +81,8 @@ export async function loadSegmentFile(
|
|
|
84
81
|
target: 'es2022',
|
|
85
82
|
jsx: 'automatic',
|
|
86
83
|
platform: 'node',
|
|
87
|
-
plugins: [
|
|
88
|
-
// Externalize all node_modules - we only need
|
|
84
|
+
plugins: [createFragmentsCoreShimPlugin()],
|
|
85
|
+
// Externalize all node_modules - we only need fragment metadata, not component code
|
|
89
86
|
packages: 'external',
|
|
90
87
|
// Also explicitly list patterns for nested imports
|
|
91
88
|
external: [
|
|
@@ -129,19 +126,19 @@ export async function loadSegmentFile(
|
|
|
129
126
|
}
|
|
130
127
|
|
|
131
128
|
/**
|
|
132
|
-
* Load multiple
|
|
129
|
+
* Load multiple fragment files in parallel with error handling.
|
|
133
130
|
*/
|
|
134
|
-
export async function
|
|
131
|
+
export async function loadFragmentFiles(
|
|
135
132
|
absolutePaths: string[]
|
|
136
|
-
): Promise<Map<string,
|
|
137
|
-
const results = new Map<string,
|
|
133
|
+
): Promise<Map<string, FragmentDefinition | Error>> {
|
|
134
|
+
const results = new Map<string, FragmentDefinition | Error>();
|
|
138
135
|
|
|
139
136
|
await Promise.all(
|
|
140
137
|
absolutePaths.map(async (path) => {
|
|
141
138
|
try {
|
|
142
|
-
const
|
|
143
|
-
if (
|
|
144
|
-
results.set(path,
|
|
139
|
+
const fragment = await loadFragmentFile(path);
|
|
140
|
+
if (fragment) {
|
|
141
|
+
results.set(path, fragment);
|
|
145
142
|
} else {
|
|
146
143
|
results.set(path, new Error('No default export found'));
|
|
147
144
|
}
|
package/src/core/node.ts
CHANGED
|
@@ -8,13 +8,13 @@ export { loadConfig, findConfigFile } from './config.js';
|
|
|
8
8
|
|
|
9
9
|
// Discovery
|
|
10
10
|
export {
|
|
11
|
-
|
|
11
|
+
discoverFragmentFiles,
|
|
12
12
|
discoverBlockFiles,
|
|
13
13
|
discoverRecipeFiles, // @deprecated - use discoverBlockFiles
|
|
14
14
|
discoverComponentFiles,
|
|
15
15
|
discoverInstalledFragments,
|
|
16
16
|
extractComponentName,
|
|
17
|
-
// New: Component discovery from source files (no
|
|
17
|
+
// New: Component discovery from source files (no fragment files needed)
|
|
18
18
|
discoverComponentsFromSource,
|
|
19
19
|
discoverComponentsFromBarrel,
|
|
20
20
|
discoverAllComponents,
|
|
@@ -23,11 +23,11 @@ export {
|
|
|
23
23
|
export type { DiscoveredFile, DiscoveredComponent } from './discovery.js';
|
|
24
24
|
|
|
25
25
|
// Loader (TSX/TS support)
|
|
26
|
-
export {
|
|
26
|
+
export { loadFragmentFile, loadFragmentFiles } from './loader.js';
|
|
27
27
|
|
|
28
28
|
// AST Parser (no execution, static analysis only)
|
|
29
|
-
export {
|
|
30
|
-
export type {
|
|
29
|
+
export { parseFragmentFile } from './parser.js';
|
|
30
|
+
export type { ParsedFragmentMetadata } from './parser.js';
|
|
31
31
|
|
|
32
32
|
// Preview loader (Storybook .storybook/preview.tsx support)
|
|
33
33
|
export {
|
package/src/core/parser.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AST-Based
|
|
2
|
+
* AST-Based Fragment Parser
|
|
3
3
|
*
|
|
4
|
-
* Extracts
|
|
5
|
-
* This allows `
|
|
4
|
+
* Extracts fragment metadata from .fragment.tsx files WITHOUT executing them.
|
|
5
|
+
* This allows `fragments build` to work without any project dependencies.
|
|
6
6
|
*
|
|
7
7
|
* Uses TypeScript's compiler API to parse and analyze the AST.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import ts from "typescript";
|
|
11
|
-
import type {
|
|
11
|
+
import type { FragmentMeta, FragmentUsage, PropDefinition, AIMetadata, FragmentContract } from "./types.js";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Parsed
|
|
14
|
+
* Parsed fragment metadata (extracted statically from AST)
|
|
15
15
|
*/
|
|
16
|
-
export interface
|
|
16
|
+
export interface ParsedFragmentMetadata {
|
|
17
17
|
/** Component import path (e.g., "./Button" or "@/components/Button") */
|
|
18
18
|
componentImport: string | null;
|
|
19
19
|
|
|
@@ -21,10 +21,10 @@ export interface ParsedSegmentMetadata {
|
|
|
21
21
|
componentName: string | null;
|
|
22
22
|
|
|
23
23
|
/** Component metadata */
|
|
24
|
-
meta: Partial<
|
|
24
|
+
meta: Partial<FragmentMeta>;
|
|
25
25
|
|
|
26
26
|
/** Usage guidelines */
|
|
27
|
-
usage: Partial<
|
|
27
|
+
usage: Partial<FragmentUsage>;
|
|
28
28
|
|
|
29
29
|
/** Props documentation */
|
|
30
30
|
props: Record<string, Partial<PropDefinition>>;
|
|
@@ -48,22 +48,25 @@ export interface ParsedSegmentMetadata {
|
|
|
48
48
|
/** AI-specific metadata for playground context generation */
|
|
49
49
|
ai?: AIMetadata;
|
|
50
50
|
|
|
51
|
+
/** Agent-optimized contract metadata */
|
|
52
|
+
contract?: FragmentContract;
|
|
53
|
+
|
|
51
54
|
/** Parse warnings */
|
|
52
55
|
warnings: string[];
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
/**
|
|
56
|
-
* Parse a
|
|
59
|
+
* Parse a fragment file and extract metadata without executing it.
|
|
57
60
|
*/
|
|
58
|
-
export function
|
|
61
|
+
export function parseFragmentFile(
|
|
59
62
|
fileContent: string,
|
|
60
63
|
filePath?: string
|
|
61
|
-
):
|
|
64
|
+
): ParsedFragmentMetadata {
|
|
62
65
|
const warnings: string[] = [];
|
|
63
66
|
|
|
64
67
|
// Parse the file
|
|
65
68
|
const sourceFile = ts.createSourceFile(
|
|
66
|
-
filePath ?? "
|
|
69
|
+
filePath ?? "fragment.tsx",
|
|
67
70
|
fileContent,
|
|
68
71
|
ts.ScriptTarget.Latest,
|
|
69
72
|
true,
|
|
@@ -73,11 +76,11 @@ export function parseSegmentFile(
|
|
|
73
76
|
// Find imports
|
|
74
77
|
const imports = extractImports(sourceFile);
|
|
75
78
|
|
|
76
|
-
// Find the
|
|
77
|
-
const
|
|
79
|
+
// Find the defineFragment call
|
|
80
|
+
const defineFragmentCall = findDefineFragmentCall(sourceFile);
|
|
78
81
|
|
|
79
|
-
if (!
|
|
80
|
-
warnings.push("No
|
|
82
|
+
if (!defineFragmentCall) {
|
|
83
|
+
warnings.push("No defineFragment() call found");
|
|
81
84
|
return {
|
|
82
85
|
componentImport: null,
|
|
83
86
|
componentName: null,
|
|
@@ -91,9 +94,9 @@ export function parseSegmentFile(
|
|
|
91
94
|
}
|
|
92
95
|
|
|
93
96
|
// Extract the argument (object literal)
|
|
94
|
-
const arg =
|
|
97
|
+
const arg = defineFragmentCall.arguments[0];
|
|
95
98
|
if (!arg || !ts.isObjectLiteralExpression(arg)) {
|
|
96
|
-
warnings.push("
|
|
99
|
+
warnings.push("defineFragment() argument is not an object literal");
|
|
97
100
|
return {
|
|
98
101
|
componentImport: null,
|
|
99
102
|
componentName: null,
|
|
@@ -135,6 +138,9 @@ export function parseSegmentFile(
|
|
|
135
138
|
// Extract AI metadata
|
|
136
139
|
const ai = extractAIMetadata(arg, warnings);
|
|
137
140
|
|
|
141
|
+
// Extract contract metadata
|
|
142
|
+
const contract = extractContractMetadata(arg);
|
|
143
|
+
|
|
138
144
|
return {
|
|
139
145
|
componentImport,
|
|
140
146
|
componentName,
|
|
@@ -144,6 +150,7 @@ export function parseSegmentFile(
|
|
|
144
150
|
variants,
|
|
145
151
|
relations,
|
|
146
152
|
ai,
|
|
153
|
+
contract,
|
|
147
154
|
warnings,
|
|
148
155
|
};
|
|
149
156
|
}
|
|
@@ -185,9 +192,9 @@ function extractImports(sourceFile: ts.SourceFile): Map<string, string> {
|
|
|
185
192
|
}
|
|
186
193
|
|
|
187
194
|
/**
|
|
188
|
-
* Find the
|
|
195
|
+
* Find the defineFragment() call in the source file.
|
|
189
196
|
*/
|
|
190
|
-
function
|
|
197
|
+
function findDefineFragmentCall(
|
|
191
198
|
sourceFile: ts.SourceFile
|
|
192
199
|
): ts.CallExpression | null {
|
|
193
200
|
let result: ts.CallExpression | null = null;
|
|
@@ -195,7 +202,7 @@ function findDefineSegmentCall(
|
|
|
195
202
|
function visit(node: ts.Node): void {
|
|
196
203
|
if (ts.isCallExpression(node)) {
|
|
197
204
|
const expression = node.expression;
|
|
198
|
-
if (ts.isIdentifier(expression) && expression.text === "
|
|
205
|
+
if (ts.isIdentifier(expression) && expression.text === "defineFragment") {
|
|
199
206
|
result = node;
|
|
200
207
|
return;
|
|
201
208
|
}
|
|
@@ -226,19 +233,19 @@ function findProperty(
|
|
|
226
233
|
}
|
|
227
234
|
|
|
228
235
|
/**
|
|
229
|
-
* Extract meta object from
|
|
236
|
+
* Extract meta object from defineFragment call.
|
|
230
237
|
*/
|
|
231
238
|
function extractMeta(
|
|
232
239
|
arg: ts.ObjectLiteralExpression,
|
|
233
240
|
warnings: string[]
|
|
234
|
-
): Partial<
|
|
241
|
+
): Partial<FragmentMeta> {
|
|
235
242
|
const metaProp = findProperty(arg, "meta");
|
|
236
243
|
if (!metaProp || !ts.isObjectLiteralExpression(metaProp)) {
|
|
237
244
|
warnings.push("No meta object found");
|
|
238
245
|
return {};
|
|
239
246
|
}
|
|
240
247
|
|
|
241
|
-
const meta: Partial<
|
|
248
|
+
const meta: Partial<FragmentMeta> = {};
|
|
242
249
|
|
|
243
250
|
// Extract string properties
|
|
244
251
|
const name = extractStringProperty(metaProp, "name");
|
|
@@ -250,7 +257,7 @@ function extractMeta(
|
|
|
250
257
|
const category = extractStringProperty(metaProp, "category");
|
|
251
258
|
if (category) meta.category = category;
|
|
252
259
|
|
|
253
|
-
const status = extractStringProperty(metaProp, "status") as
|
|
260
|
+
const status = extractStringProperty(metaProp, "status") as FragmentMeta["status"];
|
|
254
261
|
if (status) meta.status = status;
|
|
255
262
|
|
|
256
263
|
const since = extractStringProperty(metaProp, "since");
|
|
@@ -268,12 +275,12 @@ function extractMeta(
|
|
|
268
275
|
}
|
|
269
276
|
|
|
270
277
|
/**
|
|
271
|
-
* Extract usage object from
|
|
278
|
+
* Extract usage object from defineFragment call.
|
|
272
279
|
*/
|
|
273
280
|
function extractUsage(
|
|
274
281
|
arg: ts.ObjectLiteralExpression,
|
|
275
282
|
warnings: string[]
|
|
276
|
-
): Partial<
|
|
283
|
+
): Partial<FragmentUsage> {
|
|
277
284
|
const usageProp = findProperty(arg, "usage");
|
|
278
285
|
if (!usageProp || !ts.isObjectLiteralExpression(usageProp)) {
|
|
279
286
|
return { when: [], whenNot: [] };
|
|
@@ -288,7 +295,7 @@ function extractUsage(
|
|
|
288
295
|
}
|
|
289
296
|
|
|
290
297
|
/**
|
|
291
|
-
* Extract props object from
|
|
298
|
+
* Extract props object from defineFragment call.
|
|
292
299
|
*/
|
|
293
300
|
function extractProps(
|
|
294
301
|
arg: ts.ObjectLiteralExpression,
|
|
@@ -348,7 +355,7 @@ function extractPropDefinition(
|
|
|
348
355
|
}
|
|
349
356
|
|
|
350
357
|
/**
|
|
351
|
-
* Extract variants array from
|
|
358
|
+
* Extract variants array from defineFragment call.
|
|
352
359
|
*/
|
|
353
360
|
function extractVariants(
|
|
354
361
|
arg: ts.ObjectLiteralExpression,
|
|
@@ -483,7 +490,7 @@ function extractRenderCode(
|
|
|
483
490
|
}
|
|
484
491
|
|
|
485
492
|
/**
|
|
486
|
-
* Extract relations array from
|
|
493
|
+
* Extract relations array from defineFragment call.
|
|
487
494
|
*/
|
|
488
495
|
function extractRelations(
|
|
489
496
|
arg: ts.ObjectLiteralExpression,
|
|
@@ -516,7 +523,7 @@ function extractRelations(
|
|
|
516
523
|
}
|
|
517
524
|
|
|
518
525
|
/**
|
|
519
|
-
* Extract AI metadata from
|
|
526
|
+
* Extract AI metadata from defineFragment call.
|
|
520
527
|
*/
|
|
521
528
|
function extractAIMetadata(
|
|
522
529
|
arg: ts.ObjectLiteralExpression,
|
|
@@ -561,6 +568,39 @@ function extractAIMetadata(
|
|
|
561
568
|
return undefined;
|
|
562
569
|
}
|
|
563
570
|
|
|
571
|
+
/**
|
|
572
|
+
* Extract contract metadata from defineFragment call.
|
|
573
|
+
*/
|
|
574
|
+
function extractContractMetadata(
|
|
575
|
+
arg: ts.ObjectLiteralExpression,
|
|
576
|
+
): FragmentContract | undefined {
|
|
577
|
+
const contractProp = findProperty(arg, "contract");
|
|
578
|
+
if (!contractProp || !ts.isObjectLiteralExpression(contractProp)) {
|
|
579
|
+
return undefined;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const contract: FragmentContract = {};
|
|
583
|
+
|
|
584
|
+
// Extract propsSummary array
|
|
585
|
+
const propsSummary = extractStringArray(contractProp, "propsSummary");
|
|
586
|
+
if (propsSummary.length > 0) {
|
|
587
|
+
contract.propsSummary = propsSummary;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Extract a11yRules array
|
|
591
|
+
const a11yRules = extractStringArray(contractProp, "a11yRules");
|
|
592
|
+
if (a11yRules.length > 0) {
|
|
593
|
+
contract.a11yRules = a11yRules;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Only return if we have any fields
|
|
597
|
+
if (Object.keys(contract).length > 0) {
|
|
598
|
+
return contract;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
return undefined;
|
|
602
|
+
}
|
|
603
|
+
|
|
564
604
|
/**
|
|
565
605
|
* Extract a string property from an object literal.
|
|
566
606
|
*/
|
|
@@ -91,7 +91,7 @@ export async function loadPreviewConfig(
|
|
|
91
91
|
return config;
|
|
92
92
|
} catch (error) {
|
|
93
93
|
console.warn(
|
|
94
|
-
`[
|
|
94
|
+
`[Fragments] Failed to load preview config from ${previewPath}:`,
|
|
95
95
|
error instanceof Error ? error.message : error
|
|
96
96
|
);
|
|
97
97
|
return {};
|
package/src/core/schema.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Zod schemas for runtime validation of
|
|
4
|
+
* Zod schemas for runtime validation of fragment definitions
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
// Figma property mapping schemas
|
|
@@ -46,7 +46,7 @@ export const figmaPropMappingSchema = z.discriminatedUnion('__type', [
|
|
|
46
46
|
figmaTextContentMappingSchema,
|
|
47
47
|
]);
|
|
48
48
|
|
|
49
|
-
export const
|
|
49
|
+
export const fragmentMetaSchema = z.object({
|
|
50
50
|
name: z.string().min(1),
|
|
51
51
|
description: z.string().min(1),
|
|
52
52
|
category: z.string().min(1),
|
|
@@ -57,7 +57,7 @@ export const segmentMetaSchema = z.object({
|
|
|
57
57
|
figmaProps: z.record(figmaPropMappingSchema).optional(),
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
export const
|
|
60
|
+
export const fragmentUsageSchema = z.object({
|
|
61
61
|
when: z.array(z.string()).min(1),
|
|
62
62
|
whenNot: z.array(z.string()).min(1),
|
|
63
63
|
guidelines: z.array(z.string()).optional(),
|
|
@@ -104,7 +104,7 @@ export const componentRelationSchema = z.object({
|
|
|
104
104
|
note: z.string().min(1),
|
|
105
105
|
});
|
|
106
106
|
|
|
107
|
-
export const
|
|
107
|
+
export const fragmentVariantSchema = z.object({
|
|
108
108
|
name: z.string().min(1),
|
|
109
109
|
description: z.string().min(1),
|
|
110
110
|
render: z.function().returns(z.unknown()),
|
|
@@ -115,7 +115,7 @@ export const segmentVariantSchema = z.object({
|
|
|
115
115
|
/**
|
|
116
116
|
* Schema for banned patterns in codebase
|
|
117
117
|
*/
|
|
118
|
-
export const
|
|
118
|
+
export const fragmentBanSchema = z.object({
|
|
119
119
|
pattern: z.string().min(1),
|
|
120
120
|
message: z.string().min(1),
|
|
121
121
|
});
|
|
@@ -123,17 +123,17 @@ export const segmentBanSchema = z.object({
|
|
|
123
123
|
/**
|
|
124
124
|
* Schema for agent-optimized contract metadata
|
|
125
125
|
*/
|
|
126
|
-
export const
|
|
126
|
+
export const fragmentContractSchema = z.object({
|
|
127
127
|
propsSummary: z.array(z.string()).optional(),
|
|
128
128
|
a11yRules: z.array(z.string()).optional(),
|
|
129
|
-
bans: z.array(
|
|
129
|
+
bans: z.array(fragmentBanSchema).optional(),
|
|
130
130
|
scenarioTags: z.array(z.string()).optional(),
|
|
131
131
|
});
|
|
132
132
|
|
|
133
133
|
/**
|
|
134
|
-
* Schema for provenance tracking of generated
|
|
134
|
+
* Schema for provenance tracking of generated fragments
|
|
135
135
|
*/
|
|
136
|
-
export const
|
|
136
|
+
export const fragmentGeneratedSchema = z.object({
|
|
137
137
|
source: z.enum(['storybook', 'manual', 'ai']),
|
|
138
138
|
sourceFile: z.string().optional(),
|
|
139
139
|
confidence: z.number().min(0).max(1).optional(),
|
|
@@ -162,23 +162,23 @@ export const blockDefinitionSchema = z.object({
|
|
|
162
162
|
tags: z.array(z.string()).optional(),
|
|
163
163
|
});
|
|
164
164
|
|
|
165
|
-
export const
|
|
165
|
+
export const fragmentDefinitionSchema = z.object({
|
|
166
166
|
component: z.any(), // Allow any component type (function, class, forwardRef, etc.)
|
|
167
|
-
meta:
|
|
168
|
-
usage:
|
|
167
|
+
meta: fragmentMetaSchema,
|
|
168
|
+
usage: fragmentUsageSchema,
|
|
169
169
|
props: z.record(propDefinitionSchema),
|
|
170
170
|
relations: z.array(componentRelationSchema).optional(),
|
|
171
|
-
variants: z.array(
|
|
172
|
-
contract:
|
|
171
|
+
variants: z.array(fragmentVariantSchema), // Allow empty variants array
|
|
172
|
+
contract: fragmentContractSchema.optional(),
|
|
173
173
|
ai: aiMetadataSchema.optional(),
|
|
174
|
-
_generated:
|
|
174
|
+
_generated: fragmentGeneratedSchema.optional(),
|
|
175
175
|
});
|
|
176
176
|
|
|
177
177
|
/**
|
|
178
178
|
* Config schema - validates required fields, passes through optional config objects.
|
|
179
179
|
* Type definitions are in types.ts - schema just ensures basic structure.
|
|
180
180
|
*/
|
|
181
|
-
export const
|
|
181
|
+
export const fragmentsConfigSchema = z.object({
|
|
182
182
|
include: z.array(z.string()).min(1),
|
|
183
183
|
exclude: z.array(z.string()).optional(),
|
|
184
184
|
components: z.array(z.string()).optional(),
|