@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
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { createRequire as __banner_createRequire } from 'module'; const require = __banner_createRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
|
+
discoverFragmentFiles,
|
|
3
4
|
discoverInstalledFragments,
|
|
4
|
-
discoverSegmentFiles,
|
|
5
5
|
findPreviewConfigPath,
|
|
6
6
|
findStorybookDir,
|
|
7
7
|
generatePreviewModule,
|
|
8
8
|
loadConfig
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-3T6QL7IY.js";
|
|
10
10
|
import {
|
|
11
11
|
generateContext
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-OOGTG5FM.js";
|
|
13
13
|
import {
|
|
14
14
|
BRAND
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-GHYYFAQN.js";
|
|
16
16
|
|
|
17
17
|
// src/viewer/server.ts
|
|
18
18
|
import {
|
|
@@ -56,20 +56,20 @@ function serializePropsToJsx(props) {
|
|
|
56
56
|
return `${key}={${serializeValue(value)}}`;
|
|
57
57
|
}).join(" ");
|
|
58
58
|
}
|
|
59
|
-
function
|
|
60
|
-
const match =
|
|
61
|
-
(s) => s.
|
|
59
|
+
function findFragmentByName(componentName, fragments) {
|
|
60
|
+
const match = fragments.find(
|
|
61
|
+
(s) => s.fragment.meta.name.toLowerCase() === componentName.toLowerCase()
|
|
62
62
|
);
|
|
63
63
|
if (!match) return null;
|
|
64
64
|
return {
|
|
65
|
-
name: match.
|
|
65
|
+
name: match.fragment.meta.name,
|
|
66
66
|
path: match.path
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
|
-
function getAvailableComponents(
|
|
70
|
-
return
|
|
69
|
+
function getAvailableComponents(fragments) {
|
|
70
|
+
return fragments.map((s) => s.fragment.meta.name).sort();
|
|
71
71
|
}
|
|
72
|
-
function generateRenderScript(
|
|
72
|
+
function generateRenderScript(fragmentPath, componentName, props = {}) {
|
|
73
73
|
const propsJsx = serializePropsToJsx(props);
|
|
74
74
|
const propsString = propsJsx ? ` ${propsJsx}` : "";
|
|
75
75
|
const hasChildren = "children" in props && props.children !== void 0;
|
|
@@ -82,20 +82,20 @@ function generateRenderScript(segmentPath, componentName, props = {}) {
|
|
|
82
82
|
import React from "react";
|
|
83
83
|
import { createRoot } from "react-dom/client";
|
|
84
84
|
|
|
85
|
-
// Import the
|
|
85
|
+
// Import the fragment to get the component
|
|
86
86
|
async function render() {
|
|
87
87
|
const root = document.getElementById("render-root");
|
|
88
88
|
|
|
89
89
|
try {
|
|
90
|
-
// Dynamic import of the
|
|
91
|
-
const
|
|
92
|
-
const
|
|
90
|
+
// Dynamic import of the fragment file
|
|
91
|
+
const fragmentModule = await import("${fragmentPath}");
|
|
92
|
+
const fragment = fragmentModule.default;
|
|
93
93
|
|
|
94
|
-
if (!
|
|
95
|
-
throw new Error("
|
|
94
|
+
if (!fragment || !fragment.component) {
|
|
95
|
+
throw new Error("Fragment does not export a component");
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
const Component =
|
|
98
|
+
const Component = fragment.component;
|
|
99
99
|
|
|
100
100
|
// Create React root and render
|
|
101
101
|
const reactRoot = createRoot(root);
|
|
@@ -240,7 +240,7 @@ var sharedRenderPool = null;
|
|
|
240
240
|
var browserPoolModule = null;
|
|
241
241
|
async function getSharedRenderPool() {
|
|
242
242
|
if (!browserPoolModule) {
|
|
243
|
-
browserPoolModule = await import("./service-
|
|
243
|
+
browserPoolModule = await import("./service-PVGTYUKX.js");
|
|
244
244
|
}
|
|
245
245
|
if (!sharedRenderPool) {
|
|
246
246
|
sharedRenderPool = new browserPoolModule.BrowserPool({
|
|
@@ -254,10 +254,10 @@ async function getSharedRenderPool() {
|
|
|
254
254
|
}
|
|
255
255
|
return { pool: sharedRenderPool, bufferToBase64Url: browserPoolModule.bufferToBase64Url };
|
|
256
256
|
}
|
|
257
|
-
function
|
|
258
|
-
const {
|
|
259
|
-
const
|
|
260
|
-
const
|
|
257
|
+
function fragmentsPlugin(options) {
|
|
258
|
+
const { fragmentFiles, config, projectRoot } = options;
|
|
259
|
+
const VIRTUAL_FRAGMENTS = `virtual:${BRAND.nameLower}`;
|
|
260
|
+
const VIRTUAL_FRAGMENTS_RESOLVED = `\0virtual:${BRAND.nameLower}`;
|
|
261
261
|
const VIRTUAL_VIEWER_ENTRY = `virtual:${BRAND.nameLower}-viewer-entry`;
|
|
262
262
|
const VIRTUAL_VIEWER_ENTRY_RESOLVED = `\0virtual:${BRAND.nameLower}-viewer-entry`;
|
|
263
263
|
const VIRTUAL_PREVIEW = `virtual:${BRAND.nameLower}-preview`;
|
|
@@ -266,9 +266,9 @@ function segmentsPlugin(options) {
|
|
|
266
266
|
let resolvedConfig = null;
|
|
267
267
|
const storybookDir = findStorybookDir(projectRoot);
|
|
268
268
|
const previewConfigPath = storybookDir ? findPreviewConfigPath(storybookDir) : null;
|
|
269
|
-
const
|
|
269
|
+
const fragmentFileSet = new Set(fragmentFiles.map((f) => f.absolutePath));
|
|
270
270
|
const mainPlugin = {
|
|
271
|
-
name: "
|
|
271
|
+
name: "fragments",
|
|
272
272
|
// Add process.env shim and esbuild config for Storybook compatibility
|
|
273
273
|
config() {
|
|
274
274
|
return {
|
|
@@ -310,13 +310,13 @@ function segmentsPlugin(options) {
|
|
|
310
310
|
);
|
|
311
311
|
return;
|
|
312
312
|
}
|
|
313
|
-
const
|
|
314
|
-
|
|
313
|
+
const loadedFragments = await loadFragmentsForRender(
|
|
314
|
+
fragmentFiles,
|
|
315
315
|
projectRoot
|
|
316
316
|
);
|
|
317
|
-
const
|
|
318
|
-
if (!
|
|
319
|
-
const available = getAvailableComponents(
|
|
317
|
+
const fragmentInfo = findFragmentByName(component, loadedFragments);
|
|
318
|
+
if (!fragmentInfo) {
|
|
319
|
+
const available = getAvailableComponents(loadedFragments);
|
|
320
320
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
321
321
|
res.end(
|
|
322
322
|
JSON.stringify({
|
|
@@ -327,19 +327,19 @@ function segmentsPlugin(options) {
|
|
|
327
327
|
);
|
|
328
328
|
return;
|
|
329
329
|
}
|
|
330
|
-
const
|
|
331
|
-
(f) => f.relativePath ===
|
|
330
|
+
const fragmentFile = fragmentFiles.find(
|
|
331
|
+
(f) => f.relativePath === fragmentInfo.path
|
|
332
332
|
);
|
|
333
|
-
if (!
|
|
333
|
+
if (!fragmentFile) {
|
|
334
334
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
335
335
|
res.end(
|
|
336
|
-
JSON.stringify({ error: "Could not resolve
|
|
336
|
+
JSON.stringify({ error: "Could not resolve fragment file path" })
|
|
337
337
|
);
|
|
338
338
|
return;
|
|
339
339
|
}
|
|
340
340
|
const renderScript = generateRenderScript(
|
|
341
|
-
|
|
342
|
-
|
|
341
|
+
fragmentFile.absolutePath,
|
|
342
|
+
fragmentInfo.name,
|
|
343
343
|
props
|
|
344
344
|
);
|
|
345
345
|
const requestId = Date.now().toString(36) + Math.random().toString(36).slice(2);
|
|
@@ -406,18 +406,18 @@ function segmentsPlugin(options) {
|
|
|
406
406
|
return;
|
|
407
407
|
}
|
|
408
408
|
console.log("[Fragments] Compare request for:", component);
|
|
409
|
-
console.log("[Fragments]
|
|
410
|
-
console.log("[Fragments] First 3
|
|
409
|
+
console.log("[Fragments] fragmentFiles count:", fragmentFiles.length);
|
|
410
|
+
console.log("[Fragments] First 3 fragment files:", fragmentFiles.slice(0, 3).map((f) => f.relativePath));
|
|
411
411
|
console.log("[Fragments] projectRoot:", projectRoot);
|
|
412
|
-
const
|
|
413
|
-
|
|
412
|
+
const loadedFragments = await loadFragmentsForRender(
|
|
413
|
+
fragmentFiles,
|
|
414
414
|
projectRoot
|
|
415
415
|
);
|
|
416
|
-
console.log("[Fragments]
|
|
417
|
-
console.log("[Fragments] First 3 loaded:",
|
|
418
|
-
const
|
|
419
|
-
if (!
|
|
420
|
-
const available = getAvailableComponents(
|
|
416
|
+
console.log("[Fragments] loadedFragments count:", loadedFragments.length);
|
|
417
|
+
console.log("[Fragments] First 3 loaded:", loadedFragments.slice(0, 3).map((s) => s.fragment.meta.name));
|
|
418
|
+
const fragmentInfo = findFragmentByName(component, loadedFragments);
|
|
419
|
+
if (!fragmentInfo) {
|
|
420
|
+
const available = getAvailableComponents(loadedFragments);
|
|
421
421
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
422
422
|
res.end(
|
|
423
423
|
JSON.stringify({
|
|
@@ -428,20 +428,20 @@ function segmentsPlugin(options) {
|
|
|
428
428
|
);
|
|
429
429
|
return;
|
|
430
430
|
}
|
|
431
|
-
const
|
|
431
|
+
const fullFragmentData = await loadFullFragmentForCompare(
|
|
432
432
|
_server,
|
|
433
|
-
|
|
433
|
+
fragmentFiles,
|
|
434
434
|
component,
|
|
435
435
|
variant,
|
|
436
436
|
projectRoot
|
|
437
437
|
);
|
|
438
|
-
const effectiveFigmaUrl = figmaUrl ||
|
|
438
|
+
const effectiveFigmaUrl = figmaUrl || fullFragmentData?.figmaUrl;
|
|
439
439
|
if (!effectiveFigmaUrl) {
|
|
440
440
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
441
441
|
res.end(
|
|
442
442
|
JSON.stringify({
|
|
443
443
|
error: `No Figma URL for component '${component}'`,
|
|
444
|
-
suggestion: "Add 'figma' field to
|
|
444
|
+
suggestion: "Add 'figma' field to fragment definition or provide figmaUrl in request"
|
|
445
445
|
})
|
|
446
446
|
);
|
|
447
447
|
return;
|
|
@@ -456,27 +456,27 @@ function segmentsPlugin(options) {
|
|
|
456
456
|
);
|
|
457
457
|
return;
|
|
458
458
|
}
|
|
459
|
-
const
|
|
460
|
-
(f) => f.relativePath ===
|
|
459
|
+
const fragmentFile = fragmentFiles.find(
|
|
460
|
+
(f) => f.relativePath === fragmentInfo.path
|
|
461
461
|
);
|
|
462
|
-
if (!
|
|
462
|
+
if (!fragmentFile) {
|
|
463
463
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
464
464
|
res.end(
|
|
465
|
-
JSON.stringify({ error: "Could not resolve
|
|
465
|
+
JSON.stringify({ error: "Could not resolve fragment file path" })
|
|
466
466
|
);
|
|
467
467
|
return;
|
|
468
468
|
}
|
|
469
469
|
const address = _server.httpServer?.address();
|
|
470
470
|
const port = typeof address === "object" && address ? address.port : 6006;
|
|
471
471
|
const renderViewport = viewport || { width: 800, height: 600 };
|
|
472
|
-
const { FigmaClient, bufferToBase64Url } = await import("./service-
|
|
472
|
+
const { FigmaClient, bufferToBase64Url } = await import("./service-PVGTYUKX.js");
|
|
473
473
|
const figmaClient = new FigmaClient({
|
|
474
474
|
accessToken: figmaToken
|
|
475
475
|
});
|
|
476
476
|
const { fileKey, nodeId } = figmaClient.parseUrl(effectiveFigmaUrl);
|
|
477
477
|
const renderScript = generateRenderScript(
|
|
478
|
-
|
|
479
|
-
|
|
478
|
+
fragmentFile.absolutePath,
|
|
479
|
+
fragmentInfo.name,
|
|
480
480
|
props
|
|
481
481
|
);
|
|
482
482
|
const requestId = Date.now().toString(36) + Math.random().toString(36).slice(2);
|
|
@@ -560,7 +560,7 @@ function segmentsPlugin(options) {
|
|
|
560
560
|
);
|
|
561
561
|
return;
|
|
562
562
|
}
|
|
563
|
-
const { FigmaClient } = await import("./service-
|
|
563
|
+
const { FigmaClient } = await import("./service-PVGTYUKX.js");
|
|
564
564
|
const figmaClient = new FigmaClient({ accessToken: figmaToken });
|
|
565
565
|
const { fileKey, nodeId } = figmaClient.parseUrl(figmaUrl);
|
|
566
566
|
const figmaDesignProps = await figmaClient.getNodeProperties(
|
|
@@ -601,7 +601,7 @@ function segmentsPlugin(options) {
|
|
|
601
601
|
}));
|
|
602
602
|
return;
|
|
603
603
|
}
|
|
604
|
-
const { getSharedTokenRegistry } = await import("./service-
|
|
604
|
+
const { getSharedTokenRegistry } = await import("./service-PVGTYUKX.js");
|
|
605
605
|
const registry = getSharedTokenRegistry();
|
|
606
606
|
if (!registry.isInitialized()) {
|
|
607
607
|
await registry.initialize(config.tokens, projectRoot);
|
|
@@ -661,7 +661,7 @@ function segmentsPlugin(options) {
|
|
|
661
661
|
}));
|
|
662
662
|
return;
|
|
663
663
|
}
|
|
664
|
-
const { getSharedTokenRegistry } = await import("./service-
|
|
664
|
+
const { getSharedTokenRegistry } = await import("./service-PVGTYUKX.js");
|
|
665
665
|
const registry = getSharedTokenRegistry();
|
|
666
666
|
if (!registry.isInitialized()) {
|
|
667
667
|
await registry.initialize(config.tokens, projectRoot);
|
|
@@ -705,25 +705,25 @@ function segmentsPlugin(options) {
|
|
|
705
705
|
}));
|
|
706
706
|
return;
|
|
707
707
|
}
|
|
708
|
-
const
|
|
709
|
-
const
|
|
710
|
-
if (!
|
|
711
|
-
const available = getAvailableComponents(
|
|
708
|
+
const loadedFragments = await loadFragmentsForRender(fragmentFiles, projectRoot);
|
|
709
|
+
const fragmentInfo = findFragmentByName(component, loadedFragments);
|
|
710
|
+
if (!fragmentInfo) {
|
|
711
|
+
const available = getAvailableComponents(loadedFragments);
|
|
712
712
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
713
713
|
res.end(JSON.stringify({
|
|
714
714
|
error: `Component '${component}' not found. Available: ${available.join(", ")}`
|
|
715
715
|
}));
|
|
716
716
|
return;
|
|
717
717
|
}
|
|
718
|
-
const
|
|
719
|
-
(f) => f.relativePath ===
|
|
718
|
+
const fragmentFile = fragmentFiles.find(
|
|
719
|
+
(f) => f.relativePath === fragmentInfo.path
|
|
720
720
|
);
|
|
721
|
-
if (!
|
|
721
|
+
if (!fragmentFile) {
|
|
722
722
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
723
|
-
res.end(JSON.stringify({ error: "Could not resolve
|
|
723
|
+
res.end(JSON.stringify({ error: "Could not resolve fragment file path" }));
|
|
724
724
|
return;
|
|
725
725
|
}
|
|
726
|
-
const { getSharedTokenRegistry } = await import("./service-
|
|
726
|
+
const { getSharedTokenRegistry } = await import("./service-PVGTYUKX.js");
|
|
727
727
|
const registry = getSharedTokenRegistry();
|
|
728
728
|
if (!registry.isInitialized()) {
|
|
729
729
|
await registry.initialize(config.tokens, projectRoot);
|
|
@@ -732,8 +732,8 @@ function segmentsPlugin(options) {
|
|
|
732
732
|
const port = typeof address === "object" && address ? address.port : 6006;
|
|
733
733
|
const renderViewport = { width: 800, height: 600 };
|
|
734
734
|
const renderScript = generateRenderScript(
|
|
735
|
-
|
|
736
|
-
|
|
735
|
+
fragmentFile.absolutePath,
|
|
736
|
+
fragmentInfo.name,
|
|
737
737
|
{}
|
|
738
738
|
);
|
|
739
739
|
const requestId = Date.now().toString(36) + Math.random().toString(36).slice(2);
|
|
@@ -802,14 +802,14 @@ function segmentsPlugin(options) {
|
|
|
802
802
|
const url = new URL(req.url, "http://localhost");
|
|
803
803
|
const format = url.searchParams.get("format") || "markdown";
|
|
804
804
|
const compact = url.searchParams.get("compact") === "true";
|
|
805
|
-
const
|
|
805
|
+
const compiledFragments = await loadFragmentsForContext(
|
|
806
806
|
_server,
|
|
807
|
-
|
|
807
|
+
fragmentFiles,
|
|
808
808
|
config,
|
|
809
809
|
projectRoot
|
|
810
810
|
);
|
|
811
811
|
const { content, tokenEstimate } = generateContext(
|
|
812
|
-
|
|
812
|
+
compiledFragments,
|
|
813
813
|
{
|
|
814
814
|
format,
|
|
815
815
|
compact,
|
|
@@ -849,7 +849,7 @@ function segmentsPlugin(options) {
|
|
|
849
849
|
}
|
|
850
850
|
const { writeFile, mkdir } = await import("fs/promises");
|
|
851
851
|
const { join: join2 } = await import("path");
|
|
852
|
-
const { BRAND: BRAND2 } = await import("./core-
|
|
852
|
+
const { BRAND: BRAND2 } = await import("./core-UQXZTBFZ.js");
|
|
853
853
|
const fragmentsDir = join2(projectRoot, BRAND2.dataDir, BRAND2.componentsDir);
|
|
854
854
|
await mkdir(fragmentsDir, { recursive: true });
|
|
855
855
|
const fragmentPath = join2(
|
|
@@ -891,10 +891,10 @@ function segmentsPlugin(options) {
|
|
|
891
891
|
}));
|
|
892
892
|
return;
|
|
893
893
|
}
|
|
894
|
-
const
|
|
895
|
-
const
|
|
896
|
-
if (!
|
|
897
|
-
const available = getAvailableComponents(
|
|
894
|
+
const loadedFragments = await loadFragmentsForRender(fragmentFiles, projectRoot);
|
|
895
|
+
const fragmentInfo = findFragmentByName(component, loadedFragments);
|
|
896
|
+
if (!fragmentInfo) {
|
|
897
|
+
const available = getAvailableComponents(loadedFragments);
|
|
898
898
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
899
899
|
res.end(JSON.stringify({
|
|
900
900
|
error: `Component '${component}' not found. Available: ${available.join(", ")}`
|
|
@@ -904,15 +904,15 @@ function segmentsPlugin(options) {
|
|
|
904
904
|
const {
|
|
905
905
|
getSharedTokenRegistry,
|
|
906
906
|
generateTokenPatches
|
|
907
|
-
} = await import("./service-
|
|
907
|
+
} = await import("./service-PVGTYUKX.js");
|
|
908
908
|
const registry = getSharedTokenRegistry();
|
|
909
909
|
if (!registry.isInitialized()) {
|
|
910
910
|
await registry.initialize(config.tokens, projectRoot);
|
|
911
911
|
}
|
|
912
|
-
const
|
|
913
|
-
(f) => f.relativePath ===
|
|
912
|
+
const fragmentFile = fragmentFiles.find(
|
|
913
|
+
(f) => f.relativePath === fragmentInfo.path
|
|
914
914
|
);
|
|
915
|
-
const sourceFile =
|
|
915
|
+
const sourceFile = fragmentFile?.relativePath || `${component}.tsx`;
|
|
916
916
|
const result = generateTokenPatches(
|
|
917
917
|
component,
|
|
918
918
|
[],
|
|
@@ -945,7 +945,7 @@ function segmentsPlugin(options) {
|
|
|
945
945
|
await servePreviewFrameHTML(res, _server);
|
|
946
946
|
return;
|
|
947
947
|
}
|
|
948
|
-
if (req.url === "/
|
|
948
|
+
if (req.url === "/fragments" || req.url === "/fragments/") {
|
|
949
949
|
if (!req.url.endsWith("/")) {
|
|
950
950
|
res.writeHead(302, { Location: "/fragments/" });
|
|
951
951
|
res.end();
|
|
@@ -968,8 +968,8 @@ function segmentsPlugin(options) {
|
|
|
968
968
|
},
|
|
969
969
|
// Resolve virtual modules
|
|
970
970
|
resolveId(id) {
|
|
971
|
-
if (id ===
|
|
972
|
-
return
|
|
971
|
+
if (id === VIRTUAL_FRAGMENTS) {
|
|
972
|
+
return VIRTUAL_FRAGMENTS_RESOLVED;
|
|
973
973
|
}
|
|
974
974
|
if (id === VIRTUAL_VIEWER_ENTRY) {
|
|
975
975
|
return VIRTUAL_VIEWER_ENTRY_RESOLVED;
|
|
@@ -981,8 +981,8 @@ function segmentsPlugin(options) {
|
|
|
981
981
|
},
|
|
982
982
|
// Load virtual modules
|
|
983
983
|
load(id) {
|
|
984
|
-
if (id ===
|
|
985
|
-
return
|
|
984
|
+
if (id === VIRTUAL_FRAGMENTS_RESOLVED) {
|
|
985
|
+
return generateFragmentsModule(fragmentFiles, config, previewConfigPath);
|
|
986
986
|
}
|
|
987
987
|
if (id === VIRTUAL_VIEWER_ENTRY_RESOLVED) {
|
|
988
988
|
return generateViewerEntry();
|
|
@@ -992,16 +992,16 @@ function segmentsPlugin(options) {
|
|
|
992
992
|
}
|
|
993
993
|
return null;
|
|
994
994
|
},
|
|
995
|
-
// Handle HMR for
|
|
995
|
+
// Handle HMR for fragment files
|
|
996
996
|
handleHotUpdate({ file, server: server2 }) {
|
|
997
|
-
if (
|
|
998
|
-
const mod = server2.moduleGraph.getModuleById(
|
|
997
|
+
if (fragmentFileSet.has(file)) {
|
|
998
|
+
const mod = server2.moduleGraph.getModuleById(VIRTUAL_FRAGMENTS_RESOLVED);
|
|
999
999
|
if (mod) {
|
|
1000
1000
|
server2.moduleGraph.invalidateModule(mod);
|
|
1001
1001
|
}
|
|
1002
1002
|
server2.ws.send({
|
|
1003
1003
|
type: "custom",
|
|
1004
|
-
event: "
|
|
1004
|
+
event: "fragments:update",
|
|
1005
1005
|
data: { file }
|
|
1006
1006
|
});
|
|
1007
1007
|
return [];
|
|
@@ -1009,7 +1009,7 @@ function segmentsPlugin(options) {
|
|
|
1009
1009
|
}
|
|
1010
1010
|
};
|
|
1011
1011
|
const jsxTransformPlugin = {
|
|
1012
|
-
name: "
|
|
1012
|
+
name: "fragments-jsx-transform",
|
|
1013
1013
|
enforce: "pre",
|
|
1014
1014
|
async load(id) {
|
|
1015
1015
|
if (!id.endsWith(".js")) return null;
|
|
@@ -1053,7 +1053,7 @@ function segmentsPlugin(options) {
|
|
|
1053
1053
|
},
|
|
1054
1054
|
include: "**/*.svg"
|
|
1055
1055
|
}),
|
|
1056
|
-
// Main
|
|
1056
|
+
// Main fragments plugin
|
|
1057
1057
|
mainPlugin
|
|
1058
1058
|
];
|
|
1059
1059
|
}
|
|
@@ -1061,26 +1061,26 @@ function isStoryFile(filePath) {
|
|
|
1061
1061
|
return /\.stories\.(tsx?|jsx?)$/.test(filePath);
|
|
1062
1062
|
}
|
|
1063
1063
|
function getBaseComponentPath(filePath) {
|
|
1064
|
-
return filePath.replace(/\.(
|
|
1064
|
+
return filePath.replace(/\.(fragment|stories)\.(tsx?|jsx?)$/, "");
|
|
1065
1065
|
}
|
|
1066
|
-
function
|
|
1066
|
+
function generateFragmentsModule(fragmentFiles, config, previewConfigPath) {
|
|
1067
1067
|
const filesByBasePath = /* @__PURE__ */ new Map();
|
|
1068
|
-
for (const file of
|
|
1068
|
+
for (const file of fragmentFiles) {
|
|
1069
1069
|
const basePath = getBaseComponentPath(file.relativePath);
|
|
1070
1070
|
const isStory = isStoryFile(file.relativePath);
|
|
1071
1071
|
const existing = filesByBasePath.get(basePath) || {};
|
|
1072
1072
|
if (isStory) {
|
|
1073
1073
|
existing.storyFile = file;
|
|
1074
1074
|
} else {
|
|
1075
|
-
existing.
|
|
1075
|
+
existing.fragmentFile = file;
|
|
1076
1076
|
}
|
|
1077
1077
|
filesByBasePath.set(basePath, existing);
|
|
1078
1078
|
}
|
|
1079
1079
|
const loaders = Array.from(filesByBasePath.values()).map((files) => {
|
|
1080
|
-
const primaryFile = files.storyFile || files.
|
|
1080
|
+
const primaryFile = files.storyFile || files.fragmentFile;
|
|
1081
1081
|
if (!primaryFile) return null;
|
|
1082
1082
|
const isStory = !!files.storyFile;
|
|
1083
|
-
const metadataPath = files.storyFile && files.
|
|
1083
|
+
const metadataPath = files.storyFile && files.fragmentFile ? files.fragmentFile.absolutePath : null;
|
|
1084
1084
|
return ` {
|
|
1085
1085
|
path: "${primaryFile.relativePath}",
|
|
1086
1086
|
isStory: ${isStory},
|
|
@@ -1088,9 +1088,9 @@ function generateSegmentsModule(segmentFiles, config, previewConfigPath) {
|
|
|
1088
1088
|
metadataLoader: ${metadataPath ? `() => import("${metadataPath}")` : "null"}
|
|
1089
1089
|
}`;
|
|
1090
1090
|
}).filter(Boolean).join(",\n");
|
|
1091
|
-
const previewImport = previewConfigPath ? `import * as previewConfig from "virtual:
|
|
1091
|
+
const previewImport = previewConfigPath ? `import * as previewConfig from "virtual:fragments-preview";` : "";
|
|
1092
1092
|
const previewSetup = previewConfigPath ? `
|
|
1093
|
-
// Set global preview config before loading
|
|
1093
|
+
// Set global preview config before loading fragments
|
|
1094
1094
|
setPreviewConfig({
|
|
1095
1095
|
decorators: previewConfig.decorators,
|
|
1096
1096
|
parameters: previewConfig.parameters,
|
|
@@ -1101,88 +1101,88 @@ setPreviewConfig({
|
|
|
1101
1101
|
});
|
|
1102
1102
|
` : "";
|
|
1103
1103
|
return `
|
|
1104
|
-
import {
|
|
1104
|
+
import { storyModuleToFragment, setPreviewConfig } from "@fragments/core";
|
|
1105
1105
|
${previewImport}
|
|
1106
1106
|
${previewSetup}
|
|
1107
|
-
// Lazy
|
|
1108
|
-
const
|
|
1107
|
+
// Lazy fragment loaders (supports both .fragment.tsx and .stories.tsx)
|
|
1108
|
+
const fragmentLoaders = [
|
|
1109
1109
|
${loaders}
|
|
1110
1110
|
];
|
|
1111
1111
|
|
|
1112
|
-
// Cache for loaded
|
|
1113
|
-
const
|
|
1112
|
+
// Cache for loaded fragments
|
|
1113
|
+
const loadedFragments = new Map();
|
|
1114
1114
|
|
|
1115
1115
|
/**
|
|
1116
|
-
* Merge metadata from a
|
|
1116
|
+
* Merge metadata from a fragment file into a story-based fragment.
|
|
1117
1117
|
* This preserves Figma URLs and other AI-agent focused data.
|
|
1118
1118
|
*/
|
|
1119
|
-
function mergeMetadata(
|
|
1120
|
-
if (!metadataModule?.default) return
|
|
1119
|
+
function mergeMetadata(fragment, metadataModule) {
|
|
1120
|
+
if (!metadataModule?.default) return fragment;
|
|
1121
1121
|
|
|
1122
1122
|
const metadata = metadataModule.default;
|
|
1123
1123
|
|
|
1124
1124
|
// Merge meta-level Figma URL
|
|
1125
|
-
if (metadata.meta?.figma && !
|
|
1126
|
-
|
|
1125
|
+
if (metadata.meta?.figma && !fragment.meta.figma) {
|
|
1126
|
+
fragment.meta.figma = metadata.meta.figma;
|
|
1127
1127
|
}
|
|
1128
1128
|
|
|
1129
1129
|
// Merge description if not present
|
|
1130
|
-
if (metadata.meta?.description && !
|
|
1131
|
-
|
|
1130
|
+
if (metadata.meta?.description && !fragment.meta.description) {
|
|
1131
|
+
fragment.meta.description = metadata.meta.description;
|
|
1132
1132
|
}
|
|
1133
1133
|
|
|
1134
1134
|
// Merge variant-level Figma URLs
|
|
1135
|
-
if (metadata.variants &&
|
|
1135
|
+
if (metadata.variants && fragment.variants) {
|
|
1136
1136
|
for (const metaVariant of metadata.variants) {
|
|
1137
|
-
const
|
|
1138
|
-
if (
|
|
1139
|
-
|
|
1137
|
+
const fragmentVariant = fragment.variants.find(v => v.name === metaVariant.name);
|
|
1138
|
+
if (fragmentVariant && metaVariant.figma && !fragmentVariant.figma) {
|
|
1139
|
+
fragmentVariant.figma = metaVariant.figma;
|
|
1140
1140
|
}
|
|
1141
1141
|
}
|
|
1142
1142
|
}
|
|
1143
1143
|
|
|
1144
|
-
return
|
|
1144
|
+
return fragment;
|
|
1145
1145
|
}
|
|
1146
1146
|
|
|
1147
|
-
// Load all
|
|
1148
|
-
// Gracefully handles individual failures - one bad story won't break all
|
|
1149
|
-
export async function
|
|
1147
|
+
// Load all fragments (for initial render)
|
|
1148
|
+
// Gracefully handles individual failures - one bad story won't break all fragments
|
|
1149
|
+
export async function loadAllFragments() {
|
|
1150
1150
|
const results = await Promise.all(
|
|
1151
|
-
|
|
1151
|
+
fragmentLoaders.map(async (loader) => {
|
|
1152
1152
|
try {
|
|
1153
|
-
if (
|
|
1154
|
-
const cached =
|
|
1155
|
-
return cached ? { path: loader.path,
|
|
1153
|
+
if (loadedFragments.has(loader.path)) {
|
|
1154
|
+
const cached = loadedFragments.get(loader.path);
|
|
1155
|
+
return cached ? { path: loader.path, fragment: cached } : null;
|
|
1156
1156
|
}
|
|
1157
1157
|
|
|
1158
1158
|
const module = await loader.loader();
|
|
1159
1159
|
|
|
1160
|
-
// Convert story modules to
|
|
1161
|
-
let
|
|
1160
|
+
// Convert story modules to fragments at runtime
|
|
1161
|
+
let fragment;
|
|
1162
1162
|
if (loader.isStory) {
|
|
1163
|
-
|
|
1164
|
-
//
|
|
1165
|
-
if (!
|
|
1166
|
-
|
|
1163
|
+
fragment = storyModuleToFragment(module, loader.path);
|
|
1164
|
+
// storyModuleToFragment returns null for stories without a component
|
|
1165
|
+
if (!fragment) {
|
|
1166
|
+
loadedFragments.set(loader.path, null);
|
|
1167
1167
|
return null;
|
|
1168
1168
|
}
|
|
1169
1169
|
} else {
|
|
1170
|
-
|
|
1170
|
+
fragment = module.default;
|
|
1171
1171
|
}
|
|
1172
1172
|
|
|
1173
|
-
// Merge metadata from corresponding
|
|
1173
|
+
// Merge metadata from corresponding fragment file if available
|
|
1174
1174
|
if (loader.metadataLoader) {
|
|
1175
1175
|
try {
|
|
1176
1176
|
const metadataModule = await loader.metadataLoader();
|
|
1177
|
-
|
|
1177
|
+
fragment = mergeMetadata(fragment, metadataModule);
|
|
1178
1178
|
} catch (metaError) {
|
|
1179
1179
|
// Metadata loading is optional - don't fail if it errors
|
|
1180
1180
|
console.warn("[Fragments] Could not load metadata for " + loader.path + ":", metaError.message);
|
|
1181
1181
|
}
|
|
1182
1182
|
}
|
|
1183
1183
|
|
|
1184
|
-
|
|
1185
|
-
return { path: loader.path,
|
|
1184
|
+
loadedFragments.set(loader.path, fragment);
|
|
1185
|
+
return { path: loader.path, fragment };
|
|
1186
1186
|
} catch (error) {
|
|
1187
1187
|
console.warn("[Fragments] Failed to load " + loader.path + ":", error.message);
|
|
1188
1188
|
return null;
|
|
@@ -1193,84 +1193,84 @@ export async function loadAllSegments() {
|
|
|
1193
1193
|
return results.filter(r => r !== null);
|
|
1194
1194
|
}
|
|
1195
1195
|
|
|
1196
|
-
// Load a single
|
|
1197
|
-
export async function
|
|
1198
|
-
const loader =
|
|
1196
|
+
// Load a single fragment by path
|
|
1197
|
+
export async function loadFragment(path) {
|
|
1198
|
+
const loader = fragmentLoaders.find(l => l.path === path);
|
|
1199
1199
|
if (!loader) return null;
|
|
1200
1200
|
|
|
1201
|
-
if (
|
|
1202
|
-
return
|
|
1201
|
+
if (loadedFragments.has(path)) {
|
|
1202
|
+
return loadedFragments.get(path);
|
|
1203
1203
|
}
|
|
1204
1204
|
|
|
1205
1205
|
const module = await loader.loader();
|
|
1206
1206
|
|
|
1207
|
-
// Convert story modules to
|
|
1208
|
-
let
|
|
1207
|
+
// Convert story modules to fragments at runtime
|
|
1208
|
+
let fragment;
|
|
1209
1209
|
if (loader.isStory) {
|
|
1210
|
-
|
|
1210
|
+
fragment = storyModuleToFragment(module, path);
|
|
1211
1211
|
} else {
|
|
1212
|
-
|
|
1212
|
+
fragment = module.default;
|
|
1213
1213
|
}
|
|
1214
1214
|
|
|
1215
|
-
// Merge metadata from corresponding
|
|
1216
|
-
if (loader.metadataLoader &&
|
|
1215
|
+
// Merge metadata from corresponding fragment file if available
|
|
1216
|
+
if (loader.metadataLoader && fragment) {
|
|
1217
1217
|
try {
|
|
1218
1218
|
const metadataModule = await loader.metadataLoader();
|
|
1219
|
-
|
|
1219
|
+
fragment = mergeMetadata(fragment, metadataModule);
|
|
1220
1220
|
} catch (metaError) {
|
|
1221
1221
|
console.warn("[Fragments] Could not load metadata for " + path + ":", metaError.message);
|
|
1222
1222
|
}
|
|
1223
1223
|
}
|
|
1224
1224
|
|
|
1225
|
-
|
|
1226
|
-
return
|
|
1225
|
+
loadedFragments.set(path, fragment);
|
|
1226
|
+
return fragment;
|
|
1227
1227
|
}
|
|
1228
1228
|
|
|
1229
|
-
// For backwards compatibility, load all
|
|
1229
|
+
// For backwards compatibility, load all fragments synchronously on import
|
|
1230
1230
|
// This is still lazy per-file but awaited at module load
|
|
1231
|
-
let
|
|
1232
|
-
const
|
|
1231
|
+
let fragments = [];
|
|
1232
|
+
const fragmentsPromise = loadAllFragments().then(s => { fragments = s; return s; });
|
|
1233
1233
|
|
|
1234
|
-
export {
|
|
1234
|
+
export { fragments, fragmentsPromise };
|
|
1235
1235
|
export const config = ${JSON.stringify(config)};
|
|
1236
1236
|
|
|
1237
1237
|
// HMR support
|
|
1238
1238
|
if (import.meta.hot) {
|
|
1239
1239
|
import.meta.hot.accept();
|
|
1240
1240
|
|
|
1241
|
-
import.meta.hot.on("
|
|
1241
|
+
import.meta.hot.on("fragments:update", (data) => {
|
|
1242
1242
|
console.log("[Fragments] File updated:", data.file);
|
|
1243
|
-
// Clear cache for the updated file (handles both .
|
|
1244
|
-
for (const [path, _] of
|
|
1245
|
-
const basePath = path.replace(/\\.(
|
|
1243
|
+
// Clear cache for the updated file (handles both .fragment and .stories)
|
|
1244
|
+
for (const [path, _] of loadedFragments) {
|
|
1245
|
+
const basePath = path.replace(/\\.(fragment|stories)\\.tsx?$/, '');
|
|
1246
1246
|
if (data.file.includes(basePath)) {
|
|
1247
|
-
|
|
1247
|
+
loadedFragments.delete(path);
|
|
1248
1248
|
}
|
|
1249
1249
|
}
|
|
1250
1250
|
// Trigger re-render in viewer
|
|
1251
|
-
window.dispatchEvent(new CustomEvent("
|
|
1251
|
+
window.dispatchEvent(new CustomEvent("fragments:update"));
|
|
1252
1252
|
});
|
|
1253
1253
|
}
|
|
1254
1254
|
`;
|
|
1255
1255
|
}
|
|
1256
1256
|
function generateViewerEntry() {
|
|
1257
1257
|
return `
|
|
1258
|
-
import {
|
|
1258
|
+
import { fragments, config } from "virtual:fragments";
|
|
1259
1259
|
|
|
1260
1260
|
// Re-export for viewer
|
|
1261
|
-
export {
|
|
1261
|
+
export { fragments, config };
|
|
1262
1262
|
|
|
1263
1263
|
// Initialize viewer
|
|
1264
|
-
console.log("[Fragments] Loaded",
|
|
1264
|
+
console.log("[Fragments] Loaded", fragments.length, "fragment(s)");
|
|
1265
1265
|
`;
|
|
1266
1266
|
}
|
|
1267
|
-
async function
|
|
1267
|
+
async function loadFragmentsForContext(_server, _fragmentFiles, _config, configDir) {
|
|
1268
1268
|
const { join: join2 } = await import("path");
|
|
1269
|
-
const
|
|
1269
|
+
const fragmentsJsonPath = join2(configDir || process.cwd(), BRAND.outFile);
|
|
1270
1270
|
try {
|
|
1271
|
-
const content = await readFile(
|
|
1271
|
+
const content = await readFile(fragmentsJsonPath, "utf-8");
|
|
1272
1272
|
const data = JSON.parse(content);
|
|
1273
|
-
return Object.values(data.
|
|
1273
|
+
return Object.values(data.fragments || {});
|
|
1274
1274
|
} catch (error) {
|
|
1275
1275
|
console.warn(
|
|
1276
1276
|
`[${BRAND.name}] Failed to load ${BRAND.outFile} for context:`,
|
|
@@ -1292,7 +1292,7 @@ async function serveViewerHTML(res, server) {
|
|
|
1292
1292
|
} catch (error) {
|
|
1293
1293
|
console.error("[Fragments] Error serving viewer:", error);
|
|
1294
1294
|
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
1295
|
-
res.end("Error loading
|
|
1295
|
+
res.end("Error loading Fragments viewer");
|
|
1296
1296
|
}
|
|
1297
1297
|
}
|
|
1298
1298
|
async function servePreviewFrameHTML(res, server) {
|
|
@@ -1326,33 +1326,33 @@ async function parseJsonBody(req) {
|
|
|
1326
1326
|
req.on("error", reject);
|
|
1327
1327
|
});
|
|
1328
1328
|
}
|
|
1329
|
-
async function
|
|
1329
|
+
async function loadFragmentsForRender(fragmentFiles, configDir) {
|
|
1330
1330
|
const { join: join2 } = await import("path");
|
|
1331
|
-
const
|
|
1331
|
+
const fragmentsJsonPath = join2(configDir, BRAND.outFile);
|
|
1332
1332
|
try {
|
|
1333
|
-
const content = await readFile(
|
|
1333
|
+
const content = await readFile(fragmentsJsonPath, "utf-8");
|
|
1334
1334
|
const data = JSON.parse(content);
|
|
1335
|
-
const
|
|
1336
|
-
if (
|
|
1337
|
-
return
|
|
1338
|
-
path:
|
|
1339
|
-
|
|
1335
|
+
const fragmentEntries = Object.values(data.fragments || {});
|
|
1336
|
+
if (fragmentEntries.length > 0) {
|
|
1337
|
+
return fragmentEntries.map((fragment) => ({
|
|
1338
|
+
path: fragment.filePath,
|
|
1339
|
+
fragment: { meta: { name: fragment.meta.name } }
|
|
1340
1340
|
}));
|
|
1341
1341
|
}
|
|
1342
1342
|
} catch {
|
|
1343
1343
|
}
|
|
1344
|
-
return
|
|
1344
|
+
return fragmentFiles.map((f) => {
|
|
1345
1345
|
let name;
|
|
1346
1346
|
if (isStoryFile(f.relativePath)) {
|
|
1347
1347
|
const match = f.relativePath.match(/\/([^/]+)\.stories\./);
|
|
1348
1348
|
name = match ? match[1] : f.relativePath;
|
|
1349
1349
|
} else {
|
|
1350
|
-
const match = f.relativePath.match(/\/([^/]+)\.
|
|
1350
|
+
const match = f.relativePath.match(/\/([^/]+)\.fragment\./);
|
|
1351
1351
|
name = match ? match[1] : f.relativePath;
|
|
1352
1352
|
}
|
|
1353
1353
|
return {
|
|
1354
1354
|
path: f.relativePath,
|
|
1355
|
-
|
|
1355
|
+
fragment: { meta: { name } }
|
|
1356
1356
|
};
|
|
1357
1357
|
});
|
|
1358
1358
|
}
|
|
@@ -1538,24 +1538,24 @@ async function captureRenderWithStyles(url, viewport, extractStyles) {
|
|
|
1538
1538
|
pool.release(ctx);
|
|
1539
1539
|
}
|
|
1540
1540
|
}
|
|
1541
|
-
async function
|
|
1541
|
+
async function loadFullFragmentForCompare(_server, _fragmentFiles, componentName, variantName, configDir) {
|
|
1542
1542
|
const { join: join2 } = await import("path");
|
|
1543
|
-
const
|
|
1543
|
+
const fragmentsJsonPath = join2(configDir || process.cwd(), BRAND.outFile);
|
|
1544
1544
|
try {
|
|
1545
|
-
const content = await readFile(
|
|
1545
|
+
const content = await readFile(fragmentsJsonPath, "utf-8");
|
|
1546
1546
|
const data = JSON.parse(content);
|
|
1547
|
-
const
|
|
1548
|
-
if (!
|
|
1547
|
+
const fragment = data.fragments[componentName];
|
|
1548
|
+
if (!fragment) {
|
|
1549
1549
|
return null;
|
|
1550
1550
|
}
|
|
1551
|
-
if (variantName &&
|
|
1552
|
-
const variant =
|
|
1551
|
+
if (variantName && fragment.variants) {
|
|
1552
|
+
const variant = fragment.variants.find((v) => v.name === variantName);
|
|
1553
1553
|
if (variant?.figma) {
|
|
1554
1554
|
return { figmaUrl: variant.figma };
|
|
1555
1555
|
}
|
|
1556
1556
|
}
|
|
1557
|
-
if (
|
|
1558
|
-
return { figmaUrl:
|
|
1557
|
+
if (fragment.meta.figma) {
|
|
1558
|
+
return { figmaUrl: fragment.meta.figma };
|
|
1559
1559
|
}
|
|
1560
1560
|
return null;
|
|
1561
1561
|
} catch {
|
|
@@ -1566,7 +1566,7 @@ async function loadFullSegmentForCompare(_server, _segmentFiles, componentName,
|
|
|
1566
1566
|
}
|
|
1567
1567
|
}
|
|
1568
1568
|
async function compareImages(image1Base64, image2Base64, threshold) {
|
|
1569
|
-
const { DiffEngine, base64UrlToBuffer, bufferToBase64Url } = await import("./service-
|
|
1569
|
+
const { DiffEngine, base64UrlToBuffer, bufferToBase64Url } = await import("./service-PVGTYUKX.js");
|
|
1570
1570
|
const { PNG } = await import("pngjs");
|
|
1571
1571
|
const buffer1 = base64UrlToBuffer(image1Base64);
|
|
1572
1572
|
const buffer2 = base64UrlToBuffer(image2Base64);
|
|
@@ -1677,10 +1677,10 @@ async function createDevServer(options = {}) {
|
|
|
1677
1677
|
} = options;
|
|
1678
1678
|
console.log("\n\u{1F527} Loading configuration...");
|
|
1679
1679
|
const { config, configDir } = await loadConfig(configPath);
|
|
1680
|
-
const
|
|
1680
|
+
const fragmentFiles = await discoverFragmentFiles(config, configDir);
|
|
1681
1681
|
const installedFiles = await discoverInstalledFragments(projectRoot);
|
|
1682
|
-
const
|
|
1683
|
-
console.log(`\u{1F4E6} Found ${
|
|
1682
|
+
const allFragmentFiles = [...fragmentFiles, ...installedFiles];
|
|
1683
|
+
console.log(`\u{1F4E6} Found ${fragmentFiles.length} local + ${installedFiles.length} installed fragment file(s)`);
|
|
1684
1684
|
let projectViteConfig = {};
|
|
1685
1685
|
const viteConfigPath = findViteConfig(projectRoot);
|
|
1686
1686
|
if (viteConfigPath) {
|
|
@@ -1711,7 +1711,7 @@ async function createDevServer(options = {}) {
|
|
|
1711
1711
|
return resolve2(projectRoot, "node_modules", pkgName);
|
|
1712
1712
|
})
|
|
1713
1713
|
)];
|
|
1714
|
-
const
|
|
1714
|
+
const fragmentsConfig = {
|
|
1715
1715
|
configFile: false,
|
|
1716
1716
|
// Don't load config again
|
|
1717
1717
|
root: projectRoot,
|
|
@@ -1728,9 +1728,9 @@ async function createDevServer(options = {}) {
|
|
|
1728
1728
|
plugins: [
|
|
1729
1729
|
// React support (if not already in project config)
|
|
1730
1730
|
...hasReactPlugin(projectViteConfig) ? [] : [react()],
|
|
1731
|
-
//
|
|
1732
|
-
...
|
|
1733
|
-
|
|
1731
|
+
// Fragments plugins (array including SVGR)
|
|
1732
|
+
...fragmentsPlugin({
|
|
1733
|
+
fragmentFiles: allFragmentFiles,
|
|
1734
1734
|
config,
|
|
1735
1735
|
projectRoot
|
|
1736
1736
|
})
|
|
@@ -1761,7 +1761,7 @@ async function createDevServer(options = {}) {
|
|
|
1761
1761
|
}
|
|
1762
1762
|
}
|
|
1763
1763
|
};
|
|
1764
|
-
const mergedConfig = mergeConfig(projectViteConfig,
|
|
1764
|
+
const mergedConfig = mergeConfig(projectViteConfig, fragmentsConfig);
|
|
1765
1765
|
console.log("\u{1F680} Starting dev server...\n");
|
|
1766
1766
|
const server = await createServer(mergedConfig);
|
|
1767
1767
|
await server.listen();
|
|
@@ -1817,6 +1817,6 @@ function hasReactPlugin(config) {
|
|
|
1817
1817
|
}
|
|
1818
1818
|
export {
|
|
1819
1819
|
createDevServer,
|
|
1820
|
-
|
|
1820
|
+
fragmentsPlugin
|
|
1821
1821
|
};
|
|
1822
|
-
//# sourceMappingURL=viewer-
|
|
1822
|
+
//# sourceMappingURL=viewer-K42REJU2.js.map
|