@fragments-sdk/cli 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +4783 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-4FDQSGKX.js +786 -0
- package/dist/chunk-4FDQSGKX.js.map +1 -0
- package/dist/chunk-7H2MMGYG.js +369 -0
- package/dist/chunk-7H2MMGYG.js.map +1 -0
- package/dist/chunk-BSCG3IP7.js +619 -0
- package/dist/chunk-BSCG3IP7.js.map +1 -0
- package/dist/chunk-LY2CFFPY.js +898 -0
- package/dist/chunk-LY2CFFPY.js.map +1 -0
- package/dist/chunk-MUZ6CM66.js +6636 -0
- package/dist/chunk-MUZ6CM66.js.map +1 -0
- package/dist/chunk-OAENNG3G.js +1489 -0
- package/dist/chunk-OAENNG3G.js.map +1 -0
- package/dist/chunk-XHNKNI6J.js +235 -0
- package/dist/chunk-XHNKNI6J.js.map +1 -0
- package/dist/core-DWKLGY4N.js +68 -0
- package/dist/core-DWKLGY4N.js.map +1 -0
- package/dist/generate-4LQNJ7SX.js +249 -0
- package/dist/generate-4LQNJ7SX.js.map +1 -0
- package/dist/index.d.ts +775 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/init-EMVI47QG.js +416 -0
- package/dist/init-EMVI47QG.js.map +1 -0
- package/dist/mcp-bin.d.ts +1 -0
- package/dist/mcp-bin.js +1117 -0
- package/dist/mcp-bin.js.map +1 -0
- package/dist/scan-4YPRF7FV.js +12 -0
- package/dist/scan-4YPRF7FV.js.map +1 -0
- package/dist/service-QSZMZJBJ.js +208 -0
- package/dist/service-QSZMZJBJ.js.map +1 -0
- package/dist/static-viewer-MIPGZ4Z7.js +12 -0
- package/dist/static-viewer-MIPGZ4Z7.js.map +1 -0
- package/dist/test-SQ5ZHXWU.js +1067 -0
- package/dist/test-SQ5ZHXWU.js.map +1 -0
- package/dist/tokens-HSGMYK64.js +173 -0
- package/dist/tokens-HSGMYK64.js.map +1 -0
- package/dist/viewer-YRF4SQE4.js +11101 -0
- package/dist/viewer-YRF4SQE4.js.map +1 -0
- package/package.json +107 -0
- package/src/ai.ts +266 -0
- package/src/analyze.ts +265 -0
- package/src/bin.ts +916 -0
- package/src/build.ts +248 -0
- package/src/commands/a11y.ts +302 -0
- package/src/commands/add.ts +313 -0
- package/src/commands/audit.ts +195 -0
- package/src/commands/baseline.ts +221 -0
- package/src/commands/build.ts +144 -0
- package/src/commands/compare.ts +337 -0
- package/src/commands/context.ts +107 -0
- package/src/commands/dev.ts +107 -0
- package/src/commands/enhance.ts +858 -0
- package/src/commands/generate.ts +391 -0
- package/src/commands/init.ts +531 -0
- package/src/commands/link/figma.ts +645 -0
- package/src/commands/link/index.ts +10 -0
- package/src/commands/link/storybook.ts +267 -0
- package/src/commands/list.ts +49 -0
- package/src/commands/metrics.ts +114 -0
- package/src/commands/reset.ts +242 -0
- package/src/commands/scan.ts +537 -0
- package/src/commands/storygen.ts +207 -0
- package/src/commands/tokens.ts +251 -0
- package/src/commands/validate.ts +93 -0
- package/src/commands/verify.ts +215 -0
- package/src/core/composition.test.ts +262 -0
- package/src/core/composition.ts +255 -0
- package/src/core/config.ts +84 -0
- package/src/core/constants.ts +111 -0
- package/src/core/context.ts +380 -0
- package/src/core/defineSegment.ts +137 -0
- package/src/core/discovery.ts +337 -0
- package/src/core/figma.ts +263 -0
- package/src/core/fragment-types.ts +214 -0
- package/src/core/generators/context.ts +389 -0
- package/src/core/generators/index.ts +23 -0
- package/src/core/generators/registry.ts +364 -0
- package/src/core/generators/typescript-extractor.ts +374 -0
- package/src/core/importAnalyzer.ts +217 -0
- package/src/core/index.ts +149 -0
- package/src/core/loader.ts +155 -0
- package/src/core/node.ts +63 -0
- package/src/core/parser.ts +551 -0
- package/src/core/previewLoader.ts +172 -0
- package/src/core/schema/fragment.schema.json +189 -0
- package/src/core/schema/registry.schema.json +137 -0
- package/src/core/schema.ts +182 -0
- package/src/core/storyAdapter.test.ts +571 -0
- package/src/core/storyAdapter.ts +761 -0
- package/src/core/token-types.ts +287 -0
- package/src/core/types.ts +754 -0
- package/src/diff.ts +323 -0
- package/src/index.ts +43 -0
- package/src/mcp/__tests__/projectFields.test.ts +130 -0
- package/src/mcp/bin.ts +36 -0
- package/src/mcp/index.ts +8 -0
- package/src/mcp/server.ts +1310 -0
- package/src/mcp/utils.ts +54 -0
- package/src/mcp-bin.ts +36 -0
- package/src/migrate/__tests__/argTypes/argTypes.test.ts +189 -0
- package/src/migrate/__tests__/args/args.test.ts +452 -0
- package/src/migrate/__tests__/meta/meta.test.ts +198 -0
- package/src/migrate/__tests__/stories/stories.test.ts +278 -0
- package/src/migrate/__tests__/utils/utils.test.ts +371 -0
- package/src/migrate/__tests__/values/values.test.ts +303 -0
- package/src/migrate/bin.ts +108 -0
- package/src/migrate/converter.ts +658 -0
- package/src/migrate/detect.ts +196 -0
- package/src/migrate/index.ts +45 -0
- package/src/migrate/migrate.ts +163 -0
- package/src/migrate/parser.ts +1136 -0
- package/src/migrate/report.ts +624 -0
- package/src/migrate/types.ts +169 -0
- package/src/screenshot.ts +249 -0
- package/src/service/__tests__/ast-utils.test.ts +426 -0
- package/src/service/__tests__/enhance-scanner.test.ts +200 -0
- package/src/service/__tests__/figma/figma.test.ts +652 -0
- package/src/service/__tests__/metrics-store.test.ts +409 -0
- package/src/service/__tests__/patch-generator.test.ts +186 -0
- package/src/service/__tests__/props-extractor.test.ts +365 -0
- package/src/service/__tests__/token-registry.test.ts +267 -0
- package/src/service/analytics.ts +659 -0
- package/src/service/ast-utils.ts +444 -0
- package/src/service/browser-pool.ts +339 -0
- package/src/service/capture.ts +267 -0
- package/src/service/diff.ts +279 -0
- package/src/service/enhance/aggregator.ts +489 -0
- package/src/service/enhance/cache.ts +275 -0
- package/src/service/enhance/codebase-scanner.ts +357 -0
- package/src/service/enhance/context-generator.ts +529 -0
- package/src/service/enhance/doc-extractor.ts +523 -0
- package/src/service/enhance/index.ts +131 -0
- package/src/service/enhance/props-extractor.ts +665 -0
- package/src/service/enhance/scanner.ts +445 -0
- package/src/service/enhance/storybook-parser.ts +552 -0
- package/src/service/enhance/types.ts +346 -0
- package/src/service/enhance/variant-renderer.ts +479 -0
- package/src/service/figma.ts +1008 -0
- package/src/service/index.ts +249 -0
- package/src/service/metrics-store.ts +333 -0
- package/src/service/patch-generator.ts +349 -0
- package/src/service/report.ts +854 -0
- package/src/service/storage.ts +401 -0
- package/src/service/token-fixes.ts +281 -0
- package/src/service/token-parser.ts +504 -0
- package/src/service/token-registry.ts +721 -0
- package/src/service/utils.ts +172 -0
- package/src/setup.ts +241 -0
- package/src/shared/command-wrapper.ts +81 -0
- package/src/shared/dev-server-client.ts +199 -0
- package/src/shared/index.ts +8 -0
- package/src/shared/segment-loader.ts +59 -0
- package/src/shared/types.ts +147 -0
- package/src/static-viewer.ts +715 -0
- package/src/test/discovery.ts +172 -0
- package/src/test/index.ts +281 -0
- package/src/test/reporters/console.ts +194 -0
- package/src/test/reporters/json.ts +190 -0
- package/src/test/reporters/junit.ts +186 -0
- package/src/test/runner.ts +598 -0
- package/src/test/types.ts +245 -0
- package/src/test/watch.ts +200 -0
- package/src/validators.ts +152 -0
- package/src/viewer/__tests__/jsx-parser.test.ts +502 -0
- package/src/viewer/__tests__/render-utils.test.ts +232 -0
- package/src/viewer/__tests__/style-utils.test.ts +404 -0
- package/src/viewer/bin.ts +86 -0
- package/src/viewer/cli/health.ts +256 -0
- package/src/viewer/cli/index.ts +33 -0
- package/src/viewer/cli/scan.ts +124 -0
- package/src/viewer/cli/utils.ts +174 -0
- package/src/viewer/components/AccessibilityPanel.tsx +1404 -0
- package/src/viewer/components/ActionCapture.tsx +172 -0
- package/src/viewer/components/ActionsPanel.tsx +371 -0
- package/src/viewer/components/App.tsx +638 -0
- package/src/viewer/components/BottomPanel.tsx +224 -0
- package/src/viewer/components/CodePanel.tsx +589 -0
- package/src/viewer/components/CommandPalette.tsx +336 -0
- package/src/viewer/components/ComponentGraph.tsx +394 -0
- package/src/viewer/components/ComponentHeader.tsx +85 -0
- package/src/viewer/components/ContractPanel.tsx +234 -0
- package/src/viewer/components/ErrorBoundary.tsx +85 -0
- package/src/viewer/components/FigmaEmbed.tsx +231 -0
- package/src/viewer/components/FragmentEditor.tsx +485 -0
- package/src/viewer/components/HealthDashboard.tsx +452 -0
- package/src/viewer/components/HmrStatusIndicator.tsx +71 -0
- package/src/viewer/components/Icons.tsx +417 -0
- package/src/viewer/components/InteractionsPanel.tsx +720 -0
- package/src/viewer/components/IsolatedPreviewFrame.tsx +321 -0
- package/src/viewer/components/IsolatedRender.tsx +111 -0
- package/src/viewer/components/KeyboardShortcutsHelp.tsx +89 -0
- package/src/viewer/components/LandingPage.tsx +441 -0
- package/src/viewer/components/Layout.tsx +22 -0
- package/src/viewer/components/LeftSidebar.tsx +391 -0
- package/src/viewer/components/MultiViewportPreview.tsx +429 -0
- package/src/viewer/components/PreviewArea.tsx +404 -0
- package/src/viewer/components/PreviewFrameHost.tsx +310 -0
- package/src/viewer/components/PreviewPane.tsx +150 -0
- package/src/viewer/components/PreviewToolbar.tsx +176 -0
- package/src/viewer/components/PropsEditor.tsx +512 -0
- package/src/viewer/components/PropsTable.tsx +98 -0
- package/src/viewer/components/RelationsSection.tsx +57 -0
- package/src/viewer/components/ResizablePanel.tsx +328 -0
- package/src/viewer/components/RightSidebar.tsx +118 -0
- package/src/viewer/components/ScreenshotButton.tsx +90 -0
- package/src/viewer/components/Sidebar.tsx +169 -0
- package/src/viewer/components/SkeletonLoader.tsx +156 -0
- package/src/viewer/components/StoryRenderer.tsx +128 -0
- package/src/viewer/components/ThemeProvider.tsx +96 -0
- package/src/viewer/components/Toast.tsx +67 -0
- package/src/viewer/components/TokenStylePanel.tsx +708 -0
- package/src/viewer/components/UsageSection.tsx +95 -0
- package/src/viewer/components/VariantMatrix.tsx +350 -0
- package/src/viewer/components/VariantRenderer.tsx +131 -0
- package/src/viewer/components/VariantTabs.tsx +84 -0
- package/src/viewer/components/ViewportSelector.tsx +165 -0
- package/src/viewer/components/_future/CreatePage.tsx +836 -0
- package/src/viewer/composition-renderer.ts +381 -0
- package/src/viewer/constants/index.ts +1 -0
- package/src/viewer/constants/ui.ts +185 -0
- package/src/viewer/entry.tsx +299 -0
- package/src/viewer/hooks/index.ts +2 -0
- package/src/viewer/hooks/useA11yCache.ts +383 -0
- package/src/viewer/hooks/useA11yService.ts +498 -0
- package/src/viewer/hooks/useActions.ts +138 -0
- package/src/viewer/hooks/useAppState.ts +124 -0
- package/src/viewer/hooks/useFigmaIntegration.ts +132 -0
- package/src/viewer/hooks/useHmrStatus.ts +109 -0
- package/src/viewer/hooks/useKeyboardShortcuts.ts +222 -0
- package/src/viewer/hooks/usePreviewBridge.ts +347 -0
- package/src/viewer/hooks/useScrollSpy.ts +78 -0
- package/src/viewer/hooks/useUrlState.ts +330 -0
- package/src/viewer/hooks/useViewSettings.ts +125 -0
- package/src/viewer/index.html +28 -0
- package/src/viewer/index.ts +14 -0
- package/src/viewer/intelligence/healthReport.ts +505 -0
- package/src/viewer/intelligence/styleDrift.ts +340 -0
- package/src/viewer/intelligence/usageScanner.ts +309 -0
- package/src/viewer/jsx-parser.ts +485 -0
- package/src/viewer/postcss.config.js +6 -0
- package/src/viewer/preview-frame-entry.tsx +25 -0
- package/src/viewer/preview-frame.html +109 -0
- package/src/viewer/render-template.html +68 -0
- package/src/viewer/render-utils.ts +170 -0
- package/src/viewer/server.ts +276 -0
- package/src/viewer/style-utils.ts +414 -0
- package/src/viewer/styles/globals.css +355 -0
- package/src/viewer/tailwind.config.js +37 -0
- package/src/viewer/types/a11y.ts +197 -0
- package/src/viewer/utils/a11y-fixes.ts +471 -0
- package/src/viewer/utils/actionExport.ts +372 -0
- package/src/viewer/utils/colorSchemes.ts +201 -0
- package/src/viewer/utils/detectRelationships.ts +256 -0
- package/src/viewer/vite-plugin.ts +2143 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render utilities for AI preview endpoint.
|
|
3
|
+
* Generates code to render design system components in isolation.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface RenderRequest {
|
|
7
|
+
/** Component name (e.g., "Button", "Card") */
|
|
8
|
+
component: string;
|
|
9
|
+
/** Props to pass to the component */
|
|
10
|
+
props?: Record<string, unknown>;
|
|
11
|
+
/** Viewport dimensions */
|
|
12
|
+
viewport?: {
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface SegmentInfo {
|
|
19
|
+
name: string;
|
|
20
|
+
path: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Serialize a value to JavaScript code string.
|
|
25
|
+
* Handles strings, numbers, booleans, null, undefined, arrays, and objects.
|
|
26
|
+
*/
|
|
27
|
+
export function serializeValue(value: unknown): string {
|
|
28
|
+
if (value === null) return "null";
|
|
29
|
+
if (value === undefined) return "undefined";
|
|
30
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
31
|
+
if (typeof value === "number") return String(value);
|
|
32
|
+
if (typeof value === "boolean") return String(value);
|
|
33
|
+
if (Array.isArray(value)) {
|
|
34
|
+
return `[${value.map(serializeValue).join(", ")}]`;
|
|
35
|
+
}
|
|
36
|
+
if (typeof value === "object") {
|
|
37
|
+
const entries = Object.entries(value)
|
|
38
|
+
.map(([k, v]) => `${JSON.stringify(k)}: ${serializeValue(v)}`)
|
|
39
|
+
.join(", ");
|
|
40
|
+
return `{${entries}}`;
|
|
41
|
+
}
|
|
42
|
+
// Functions and other types - skip
|
|
43
|
+
return "undefined";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Serialize props object to JSX attribute string.
|
|
48
|
+
* Example: { variant: "primary", disabled: true } -> variant="primary" disabled={true}
|
|
49
|
+
*/
|
|
50
|
+
export function serializePropsToJsx(props: Record<string, unknown>): string {
|
|
51
|
+
return Object.entries(props)
|
|
52
|
+
.filter(([_, v]) => v !== undefined)
|
|
53
|
+
.map(([key, value]) => {
|
|
54
|
+
if (typeof value === "string") {
|
|
55
|
+
return `${key}=${JSON.stringify(value)}`;
|
|
56
|
+
}
|
|
57
|
+
return `${key}={${serializeValue(value)}}`;
|
|
58
|
+
})
|
|
59
|
+
.join(" ");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Find a segment by component name.
|
|
64
|
+
* Returns the segment info if found, null otherwise.
|
|
65
|
+
*/
|
|
66
|
+
export function findSegmentByName(
|
|
67
|
+
componentName: string,
|
|
68
|
+
segments: Array<{ path: string; segment: { meta: { name: string } } }>
|
|
69
|
+
): SegmentInfo | null {
|
|
70
|
+
const match = segments.find(
|
|
71
|
+
(s) => s.segment.meta.name.toLowerCase() === componentName.toLowerCase()
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (!match) return null;
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
name: match.segment.meta.name,
|
|
78
|
+
path: match.path,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get list of available component names from loaded segments.
|
|
84
|
+
*/
|
|
85
|
+
export function getAvailableComponents(
|
|
86
|
+
segments: Array<{ segment: { meta: { name: string } } }>
|
|
87
|
+
): string[] {
|
|
88
|
+
return segments.map((s) => s.segment.meta.name).sort();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Generate the render script that will be injected into the template.
|
|
93
|
+
* This script imports the component and renders it with the given props.
|
|
94
|
+
*/
|
|
95
|
+
export function generateRenderScript(
|
|
96
|
+
segmentPath: string,
|
|
97
|
+
componentName: string,
|
|
98
|
+
props: Record<string, unknown> = {}
|
|
99
|
+
): string {
|
|
100
|
+
const propsJsx = serializePropsToJsx(props);
|
|
101
|
+
const propsString = propsJsx ? ` ${propsJsx}` : "";
|
|
102
|
+
|
|
103
|
+
// Handle children prop specially - render as content between tags
|
|
104
|
+
const hasChildren = "children" in props && props.children !== undefined;
|
|
105
|
+
const childrenContent = hasChildren ? String(props.children) : "";
|
|
106
|
+
const propsWithoutChildren = { ...props };
|
|
107
|
+
delete propsWithoutChildren.children;
|
|
108
|
+
const propsJsxNoChildren = serializePropsToJsx(propsWithoutChildren);
|
|
109
|
+
const propsStringNoChildren = propsJsxNoChildren ? ` ${propsJsxNoChildren}` : "";
|
|
110
|
+
|
|
111
|
+
return `
|
|
112
|
+
import React from "react";
|
|
113
|
+
import { createRoot } from "react-dom/client";
|
|
114
|
+
|
|
115
|
+
// Import the segment to get the component
|
|
116
|
+
async function render() {
|
|
117
|
+
const root = document.getElementById("render-root");
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
// Dynamic import of the segment file
|
|
121
|
+
const segmentModule = await import("${segmentPath}");
|
|
122
|
+
const segment = segmentModule.default;
|
|
123
|
+
|
|
124
|
+
if (!segment || !segment.component) {
|
|
125
|
+
throw new Error("Segment does not export a component");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const Component = segment.component;
|
|
129
|
+
|
|
130
|
+
// Create React root and render
|
|
131
|
+
const reactRoot = createRoot(root);
|
|
132
|
+
${
|
|
133
|
+
hasChildren
|
|
134
|
+
? `reactRoot.render(React.createElement(Component, ${JSON.stringify(propsWithoutChildren)}, ${JSON.stringify(childrenContent)}));`
|
|
135
|
+
: `reactRoot.render(React.createElement(Component, ${JSON.stringify(props)}));`
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Signal that rendering is complete
|
|
139
|
+
// Wait a frame for React to flush
|
|
140
|
+
requestAnimationFrame(() => {
|
|
141
|
+
requestAnimationFrame(() => {
|
|
142
|
+
root.classList.add("ready");
|
|
143
|
+
window.__RENDER_READY__ = true;
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error("Render error:", error);
|
|
148
|
+
root.innerHTML = \`
|
|
149
|
+
<div class="render-error">
|
|
150
|
+
<strong>Render Error</strong>
|
|
151
|
+
<pre>\${error.message}</pre>
|
|
152
|
+
</div>
|
|
153
|
+
\`;
|
|
154
|
+
root.classList.add("ready");
|
|
155
|
+
window.__RENDER_READY__ = true;
|
|
156
|
+
window.__RENDER_ERROR__ = error.message;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
render();
|
|
161
|
+
`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Generate a virtual module ID for a render request.
|
|
166
|
+
* This creates a unique ID that Vite can resolve.
|
|
167
|
+
*/
|
|
168
|
+
export function generateRenderModuleId(componentName: string, requestId: string): string {
|
|
169
|
+
return `virtual:segments-render-${componentName}-${requestId}`;
|
|
170
|
+
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Segments Dev Server
|
|
3
|
+
*
|
|
4
|
+
* Runs a Vite dev server that:
|
|
5
|
+
* 1. Uses the project's existing Vite/build configuration
|
|
6
|
+
* 2. Has access to all project dependencies
|
|
7
|
+
* 3. Can render actual components with proper styling
|
|
8
|
+
* 4. Supports HMR for segment file changes
|
|
9
|
+
*
|
|
10
|
+
* This is the "Storybook-like" experience for Segments.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
createServer,
|
|
15
|
+
mergeConfig,
|
|
16
|
+
loadConfigFromFile,
|
|
17
|
+
type ViteDevServer,
|
|
18
|
+
type InlineConfig,
|
|
19
|
+
} from "vite";
|
|
20
|
+
import react from "@vitejs/plugin-react";
|
|
21
|
+
import tailwindcss from "tailwindcss";
|
|
22
|
+
import autoprefixer from "autoprefixer";
|
|
23
|
+
import { resolve, dirname, join } from "node:path";
|
|
24
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
25
|
+
import { fileURLToPath } from "node:url";
|
|
26
|
+
import { loadConfig, discoverSegmentFiles } from "../core/node.js";
|
|
27
|
+
import { segmentsPlugin } from "./vite-plugin.js";
|
|
28
|
+
|
|
29
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
30
|
+
const viewerRoot = resolve(__dirname, "..");
|
|
31
|
+
const viewerTailwindConfig = resolve(viewerRoot, "tailwind.config.js");
|
|
32
|
+
// The monorepo packages directory (sibling packages)
|
|
33
|
+
const packagesRoot = resolve(viewerRoot, "..");
|
|
34
|
+
|
|
35
|
+
export interface DevServerOptions {
|
|
36
|
+
/** Port to run the server on */
|
|
37
|
+
port?: number;
|
|
38
|
+
/** Path to segments config file */
|
|
39
|
+
configPath?: string;
|
|
40
|
+
/** Open browser on start */
|
|
41
|
+
open?: boolean;
|
|
42
|
+
/** Project root (defaults to cwd) */
|
|
43
|
+
projectRoot?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Create and start the Segments dev server.
|
|
48
|
+
*
|
|
49
|
+
* The server runs WITHIN the project's context, meaning:
|
|
50
|
+
* - All project dependencies are available
|
|
51
|
+
* - SCSS, CSS modules, etc. work automatically
|
|
52
|
+
* - Path aliases from tsconfig.json work
|
|
53
|
+
* - Components render exactly as they would in the app
|
|
54
|
+
*/
|
|
55
|
+
export async function createDevServer(
|
|
56
|
+
options: DevServerOptions = {}
|
|
57
|
+
): Promise<ViteDevServer> {
|
|
58
|
+
const startTime = performance.now();
|
|
59
|
+
const {
|
|
60
|
+
port = 6006,
|
|
61
|
+
configPath,
|
|
62
|
+
open = true,
|
|
63
|
+
projectRoot = process.cwd(),
|
|
64
|
+
} = options;
|
|
65
|
+
|
|
66
|
+
console.log("\n🔧 Loading configuration...");
|
|
67
|
+
|
|
68
|
+
// Load segments config
|
|
69
|
+
const { config, configDir } = await loadConfig(configPath);
|
|
70
|
+
|
|
71
|
+
// Discover segment files
|
|
72
|
+
const segmentFiles = await discoverSegmentFiles(config, configDir);
|
|
73
|
+
console.log(`📦 Found ${segmentFiles.length} fragment file(s)`);
|
|
74
|
+
|
|
75
|
+
// Try to load project's Vite config
|
|
76
|
+
let projectViteConfig: InlineConfig = {};
|
|
77
|
+
const viteConfigPath = findViteConfig(projectRoot);
|
|
78
|
+
|
|
79
|
+
if (viteConfigPath) {
|
|
80
|
+
console.log(`📄 Using project Vite config: ${viteConfigPath}`);
|
|
81
|
+
try {
|
|
82
|
+
const loaded = await loadConfigFromFile(
|
|
83
|
+
{ command: "serve", mode: "development" },
|
|
84
|
+
viteConfigPath,
|
|
85
|
+
projectRoot
|
|
86
|
+
);
|
|
87
|
+
if (loaded) {
|
|
88
|
+
projectViteConfig = loaded.config;
|
|
89
|
+
}
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.warn("⚠️ Could not load project Vite config:", error);
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
console.log("ℹ️ No project Vite config found, using defaults");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Find node_modules (handles monorepo setups)
|
|
98
|
+
const nodeModulesPath = findNodeModules(projectRoot);
|
|
99
|
+
console.log(`📁 Using node_modules: ${nodeModulesPath}`);
|
|
100
|
+
|
|
101
|
+
// Our Segments-specific configuration
|
|
102
|
+
const segmentsConfig: InlineConfig = {
|
|
103
|
+
configFile: false, // Don't load config again
|
|
104
|
+
root: projectRoot, // Run from PROJECT root
|
|
105
|
+
base: "/",
|
|
106
|
+
|
|
107
|
+
server: {
|
|
108
|
+
port,
|
|
109
|
+
open: open ? "/fragments/" : false,
|
|
110
|
+
fs: {
|
|
111
|
+
// Allow serving files from viewer package, project, and node_modules root
|
|
112
|
+
allow: [viewerRoot, projectRoot, configDir, dirname(nodeModulesPath)],
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
plugins: [
|
|
117
|
+
// React support (if not already in project config)
|
|
118
|
+
...(hasReactPlugin(projectViteConfig) ? [] : [react()]),
|
|
119
|
+
|
|
120
|
+
// Segments plugins (array including SVGR)
|
|
121
|
+
...segmentsPlugin({
|
|
122
|
+
segmentFiles,
|
|
123
|
+
config,
|
|
124
|
+
projectRoot,
|
|
125
|
+
}),
|
|
126
|
+
],
|
|
127
|
+
|
|
128
|
+
// CSS configuration for viewer's Tailwind
|
|
129
|
+
css: {
|
|
130
|
+
postcss: {
|
|
131
|
+
plugins: [
|
|
132
|
+
tailwindcss({
|
|
133
|
+
config: viewerTailwindConfig,
|
|
134
|
+
}),
|
|
135
|
+
autoprefixer(),
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
optimizeDeps: {
|
|
141
|
+
// Include common dependencies for faster startup
|
|
142
|
+
include: ["react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime"],
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
// Ensure we can resolve viewer's dependencies
|
|
146
|
+
resolve: {
|
|
147
|
+
// Dedupe ensures all imports of these packages resolve to the same copy
|
|
148
|
+
dedupe: ["react", "react-dom"],
|
|
149
|
+
alias: {
|
|
150
|
+
// Allow importing from viewer package
|
|
151
|
+
"@fragments/viewer": viewerRoot,
|
|
152
|
+
// Ensure ALL react imports resolve to project's node_modules
|
|
153
|
+
// This is critical for viewer files loaded from outside project root
|
|
154
|
+
"react": safeRealpath(join(nodeModulesPath, "react")),
|
|
155
|
+
"react-dom": safeRealpath(join(nodeModulesPath, "react-dom")),
|
|
156
|
+
"react/jsx-runtime": safeRealpath(join(nodeModulesPath, "react/jsx-runtime")),
|
|
157
|
+
"react/jsx-dev-runtime": safeRealpath(join(nodeModulesPath, "react/jsx-dev-runtime")),
|
|
158
|
+
// Resolve @fragments packages - try project's node_modules first, fall back to monorepo
|
|
159
|
+
"@fragments/core": resolveFragmentsPackage("core", nodeModulesPath),
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// Merge project config with our config
|
|
165
|
+
// Project config takes precedence for most things
|
|
166
|
+
const mergedConfig = mergeConfig(projectViteConfig, segmentsConfig);
|
|
167
|
+
|
|
168
|
+
console.log("🚀 Starting dev server...\n");
|
|
169
|
+
|
|
170
|
+
// Create and start the server
|
|
171
|
+
const server = await createServer(mergedConfig);
|
|
172
|
+
await server.listen();
|
|
173
|
+
|
|
174
|
+
// Log startup time
|
|
175
|
+
const startupTime = ((performance.now() - startTime) / 1000).toFixed(2);
|
|
176
|
+
console.log(`⚡ Server ready in ${startupTime}s`);
|
|
177
|
+
|
|
178
|
+
return server;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Resolve symlinks to real paths, with fallback.
|
|
183
|
+
*/
|
|
184
|
+
function safeRealpath(p: string): string {
|
|
185
|
+
try {
|
|
186
|
+
return realpathSync(p);
|
|
187
|
+
} catch {
|
|
188
|
+
return p;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Resolve @fragments packages with fallback to monorepo packages.
|
|
194
|
+
* This handles the case where the viewer is served from the fragments monorepo
|
|
195
|
+
* but the user's project doesn't have @fragments packages installed.
|
|
196
|
+
*/
|
|
197
|
+
function resolveFragmentsPackage(packageName: string, nodeModulesPath: string): string {
|
|
198
|
+
// Try project's node_modules first
|
|
199
|
+
const projectPath = join(nodeModulesPath, `@fragments/${packageName}/dist/index.js`);
|
|
200
|
+
if (existsSync(projectPath)) {
|
|
201
|
+
return safeRealpath(projectPath);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Fall back to monorepo packages (sibling to viewer)
|
|
205
|
+
const monorepoPath = resolve(packagesRoot, packageName, "dist/index.js");
|
|
206
|
+
if (existsSync(monorepoPath)) {
|
|
207
|
+
return safeRealpath(monorepoPath);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Return project path anyway (will error with helpful message)
|
|
211
|
+
return projectPath;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Find node_modules directory by walking up from projectRoot.
|
|
216
|
+
* This handles monorepo setups where node_modules is at the root.
|
|
217
|
+
*/
|
|
218
|
+
function findNodeModules(startDir: string): string {
|
|
219
|
+
let current = startDir;
|
|
220
|
+
while (current !== dirname(current)) {
|
|
221
|
+
const nodeModulesPath = join(current, "node_modules");
|
|
222
|
+
if (existsSync(join(nodeModulesPath, "react"))) {
|
|
223
|
+
return nodeModulesPath;
|
|
224
|
+
}
|
|
225
|
+
current = dirname(current);
|
|
226
|
+
}
|
|
227
|
+
// Fallback to projectRoot/node_modules
|
|
228
|
+
return join(startDir, "node_modules");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Find Vite config file in project.
|
|
233
|
+
*/
|
|
234
|
+
function findViteConfig(projectRoot: string): string | null {
|
|
235
|
+
const configFiles = [
|
|
236
|
+
"vite.config.ts",
|
|
237
|
+
"vite.config.js",
|
|
238
|
+
"vite.config.mts",
|
|
239
|
+
"vite.config.mjs",
|
|
240
|
+
];
|
|
241
|
+
|
|
242
|
+
for (const file of configFiles) {
|
|
243
|
+
const path = join(projectRoot, file);
|
|
244
|
+
if (existsSync(path)) {
|
|
245
|
+
return path;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Check if project config already has React plugin.
|
|
254
|
+
*/
|
|
255
|
+
function hasReactPlugin(config: InlineConfig): boolean {
|
|
256
|
+
if (!config.plugins) return false;
|
|
257
|
+
|
|
258
|
+
const plugins = Array.isArray(config.plugins)
|
|
259
|
+
? config.plugins
|
|
260
|
+
: [config.plugins];
|
|
261
|
+
|
|
262
|
+
return plugins.some((plugin) => {
|
|
263
|
+
if (!plugin) return false;
|
|
264
|
+
if (Array.isArray(plugin)) {
|
|
265
|
+
return plugin.some(
|
|
266
|
+
(p) =>
|
|
267
|
+
p && typeof p === "object" && "name" in p && p.name?.includes("react")
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
return (
|
|
271
|
+
typeof plugin === "object" &&
|
|
272
|
+
"name" in plugin &&
|
|
273
|
+
plugin.name?.includes("react")
|
|
274
|
+
);
|
|
275
|
+
});
|
|
276
|
+
}
|