@fragments-sdk/cli 0.10.1 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-client-I6MDWNYA.js +21 -0
- package/dist/bin.js +292 -367
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-PW7QTQA6.js → chunk-4OC7FTJB.js} +2 -2
- package/dist/{chunk-HRFUSSZI.js → chunk-AM4MRTMN.js} +2 -2
- package/dist/{chunk-5G3VZH43.js → chunk-GVDSFQ4E.js} +281 -351
- package/dist/chunk-GVDSFQ4E.js.map +1 -0
- package/dist/chunk-JJ2VRTBU.js +626 -0
- package/dist/chunk-JJ2VRTBU.js.map +1 -0
- package/dist/{chunk-D5PYOXEI.js → chunk-LVWFOLUZ.js} +148 -13
- package/dist/{chunk-D5PYOXEI.js.map → chunk-LVWFOLUZ.js.map} +1 -1
- package/dist/{chunk-WXSR2II7.js → chunk-OQKMEFOS.js} +58 -6
- package/dist/chunk-OQKMEFOS.js.map +1 -0
- package/dist/chunk-SXTKFDCR.js +104 -0
- package/dist/chunk-SXTKFDCR.js.map +1 -0
- package/dist/chunk-T5OMVL7E.js +443 -0
- package/dist/chunk-T5OMVL7E.js.map +1 -0
- package/dist/{chunk-ZM4ZQZWZ.js → chunk-TPWGL2XS.js} +39 -37
- package/dist/chunk-TPWGL2XS.js.map +1 -0
- package/dist/{chunk-OQO55NKV.js → chunk-WFS63PCW.js} +85 -11
- package/dist/chunk-WFS63PCW.js.map +1 -0
- package/dist/core/index.js +9 -1
- package/dist/{discovery-NEOY4MPN.js → discovery-ZJQSXF56.js} +3 -3
- package/dist/{generate-FBHSXR3D.js → generate-RJFS2JWA.js} +4 -4
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/dist/init-ZSX3NRCZ.js +636 -0
- package/dist/init-ZSX3NRCZ.js.map +1 -0
- package/dist/mcp-bin.js +2 -2
- package/dist/{scan-CJF2DOQW.js → scan-3PMCJ4RB.js} +6 -6
- package/dist/scan-generate-SYU4PYZD.js +1115 -0
- package/dist/scan-generate-SYU4PYZD.js.map +1 -0
- package/dist/{service-TQYWY65E.js → service-VMGNJZ42.js} +3 -3
- package/dist/snapshot-XOISO2IS.js +139 -0
- package/dist/snapshot-XOISO2IS.js.map +1 -0
- package/dist/{static-viewer-NUBFPKWH.js → static-viewer-5GXH2MGE.js} +3 -3
- package/dist/static-viewer-5GXH2MGE.js.map +1 -0
- package/dist/{test-Z5LVO724.js → test-SI4NSHQX.js} +4 -4
- package/dist/{tokens-CE46OTMD.js → tokens-T6SIVUT5.js} +5 -5
- package/dist/{viewer-DNMNC5VS.js → viewer-7ZEAFBVN.js} +80 -58
- package/dist/viewer-7ZEAFBVN.js.map +1 -0
- package/package.json +6 -14
- package/src/ai-client.ts +156 -0
- package/src/bin.ts +74 -2
- package/src/build.ts +95 -33
- package/src/commands/__tests__/drift-sync.test.ts +252 -0
- package/src/commands/__tests__/scan-generate.test.ts +497 -45
- package/src/commands/enhance.ts +11 -35
- package/src/commands/init.ts +296 -193
- package/src/commands/scan-generate.ts +740 -139
- package/src/commands/scan.ts +37 -32
- package/src/commands/setup.ts +143 -52
- package/src/commands/snapshot.ts +197 -0
- package/src/commands/sync.ts +357 -0
- package/src/commands/validate.ts +43 -1
- package/src/core/component-extractor.test.ts +282 -0
- package/src/core/component-extractor.ts +1030 -0
- package/src/core/discovery.ts +93 -7
- package/src/service/enhance/props-extractor.ts +235 -13
- package/src/validators.ts +236 -0
- package/src/viewer/__tests__/viewer-integration.test.ts +85 -74
- package/src/viewer/server.ts +37 -22
- package/src/viewer/vite-plugin.ts +25 -9
- package/dist/chunk-5G3VZH43.js.map +0 -1
- package/dist/chunk-OQO55NKV.js.map +0 -1
- package/dist/chunk-WXSR2II7.js.map +0 -1
- package/dist/chunk-ZM4ZQZWZ.js.map +0 -1
- package/dist/init-NDQXUWDU.js +0 -796
- package/dist/init-NDQXUWDU.js.map +0 -1
- package/dist/scan-generate-SJAN5MVI.js +0 -691
- package/dist/scan-generate-SJAN5MVI.js.map +0 -1
- package/dist/viewer-DNMNC5VS.js.map +0 -1
- package/src/ai.ts +0 -266
- package/src/commands/init-framework.ts +0 -414
- package/src/mcp/bin.ts +0 -36
- package/src/migrate/bin.ts +0 -114
- package/src/theme/index.ts +0 -77
- package/src/viewer/__tests__/a11y-fixes.test.ts +0 -358
- package/src/viewer/__tests__/jsx-parser.test.ts +0 -502
- package/src/viewer/__tests__/render-utils.test.ts +0 -232
- package/src/viewer/__tests__/style-utils.test.ts +0 -404
- package/src/viewer/assets/fragments-logo.ts +0 -4
- package/src/viewer/assets/fragments_logo.png +0 -0
- package/src/viewer/bin.ts +0 -86
- package/src/viewer/cli/health.ts +0 -256
- package/src/viewer/cli/index.ts +0 -33
- package/src/viewer/cli/scan.ts +0 -124
- package/src/viewer/cli/utils.ts +0 -174
- package/src/viewer/components/AccessibilityPanel.tsx +0 -1457
- package/src/viewer/components/ActionCapture.tsx +0 -172
- package/src/viewer/components/ActionsPanel.tsx +0 -332
- package/src/viewer/components/AllVariantsPreview.tsx +0 -78
- package/src/viewer/components/App.tsx +0 -582
- package/src/viewer/components/BottomPanel.tsx +0 -288
- package/src/viewer/components/CodePanel.naming.test.tsx +0 -59
- package/src/viewer/components/CodePanel.tsx +0 -118
- package/src/viewer/components/CommandPalette.tsx +0 -392
- package/src/viewer/components/ComponentDocView.tsx +0 -164
- package/src/viewer/components/ComponentGraph.tsx +0 -380
- package/src/viewer/components/ComponentHeader.tsx +0 -88
- package/src/viewer/components/ContractPanel.tsx +0 -241
- package/src/viewer/components/EmptyVariantMessage.tsx +0 -54
- package/src/viewer/components/ErrorBoundary.tsx +0 -97
- package/src/viewer/components/FigmaEmbed.tsx +0 -238
- package/src/viewer/components/FragmentEditor.tsx +0 -525
- package/src/viewer/components/FragmentRenderer.tsx +0 -61
- package/src/viewer/components/HeaderSearch.tsx +0 -24
- package/src/viewer/components/HealthDashboard.tsx +0 -441
- package/src/viewer/components/HmrStatusIndicator.tsx +0 -61
- package/src/viewer/components/Icons.tsx +0 -479
- package/src/viewer/components/InteractionsPanel.tsx +0 -757
- package/src/viewer/components/IsolatedPreviewFrame.tsx +0 -346
- package/src/viewer/components/IsolatedRender.tsx +0 -113
- package/src/viewer/components/KeyboardShortcutsHelp.tsx +0 -53
- package/src/viewer/components/LandingPage.tsx +0 -421
- package/src/viewer/components/Layout.tsx +0 -27
- package/src/viewer/components/LeftSidebar.tsx +0 -472
- package/src/viewer/components/LoadErrorMessage.tsx +0 -102
- package/src/viewer/components/MultiViewportPreview.tsx +0 -522
- package/src/viewer/components/NoVariantsMessage.tsx +0 -59
- package/src/viewer/components/PanelShell.tsx +0 -161
- package/src/viewer/components/PerformancePanel.tsx +0 -304
- package/src/viewer/components/PreviewArea.tsx +0 -472
- package/src/viewer/components/PreviewAside.tsx +0 -168
- package/src/viewer/components/PreviewFrameHost.tsx +0 -303
- package/src/viewer/components/PreviewPane.tsx +0 -149
- package/src/viewer/components/PreviewToolbar.tsx +0 -80
- package/src/viewer/components/PropsEditor.tsx +0 -506
- package/src/viewer/components/PropsTable.tsx +0 -111
- package/src/viewer/components/RelationsSection.tsx +0 -88
- package/src/viewer/components/ResizablePanel.tsx +0 -271
- package/src/viewer/components/RightSidebar.tsx +0 -102
- package/src/viewer/components/RuntimeToolsRegistrar.tsx +0 -17
- package/src/viewer/components/ScreenshotButton.tsx +0 -90
- package/src/viewer/components/Sidebar.tsx +0 -169
- package/src/viewer/components/SkeletonLoader.tsx +0 -161
- package/src/viewer/components/ThemeProvider.tsx +0 -42
- package/src/viewer/components/Toast.tsx +0 -3
- package/src/viewer/components/TokenStylePanel.tsx +0 -699
- package/src/viewer/components/TopToolbar.tsx +0 -159
- package/src/viewer/components/UsageSection.tsx +0 -95
- package/src/viewer/components/VariantMatrix.tsx +0 -388
- package/src/viewer/components/VariantRenderer.tsx +0 -131
- package/src/viewer/components/VariantTabs.tsx +0 -40
- package/src/viewer/components/ViewerHeader.tsx +0 -69
- package/src/viewer/components/ViewerStateSync.tsx +0 -52
- package/src/viewer/components/ViewportSelector.tsx +0 -172
- package/src/viewer/components/WebMCPDevTools.tsx +0 -503
- package/src/viewer/components/WebMCPIntegration.tsx +0 -47
- package/src/viewer/components/WebMCPStatusIndicator.tsx +0 -60
- package/src/viewer/components/_future/CreatePage.tsx +0 -836
- package/src/viewer/components/viewer-utils.ts +0 -16
- package/src/viewer/composition-renderer.ts +0 -381
- package/src/viewer/constants/index.ts +0 -1
- package/src/viewer/constants/ui.ts +0 -166
- package/src/viewer/entry.tsx +0 -335
- package/src/viewer/hooks/index.ts +0 -2
- package/src/viewer/hooks/useA11yCache.ts +0 -383
- package/src/viewer/hooks/useA11yService.ts +0 -364
- package/src/viewer/hooks/useActions.ts +0 -138
- package/src/viewer/hooks/useAppState.ts +0 -147
- package/src/viewer/hooks/useCompiledFragments.ts +0 -42
- package/src/viewer/hooks/useFigmaIntegration.ts +0 -132
- package/src/viewer/hooks/useHmrStatus.ts +0 -109
- package/src/viewer/hooks/useKeyboardShortcuts.ts +0 -270
- package/src/viewer/hooks/usePreviewBridge.ts +0 -347
- package/src/viewer/hooks/useScrollSpy.ts +0 -78
- package/src/viewer/hooks/useUrlState.ts +0 -318
- package/src/viewer/hooks/useViewSettings.ts +0 -111
- package/src/viewer/index.html +0 -28
- package/src/viewer/intelligence/healthReport.ts +0 -505
- package/src/viewer/intelligence/styleDrift.ts +0 -340
- package/src/viewer/intelligence/usageScanner.ts +0 -309
- package/src/viewer/jsx-parser.ts +0 -486
- package/src/viewer/preview-frame-entry.tsx +0 -25
- package/src/viewer/preview-frame.html +0 -125
- package/src/viewer/public/favicon.ico +0 -0
- package/src/viewer/render-template.html +0 -68
- package/src/viewer/styles/globals.css +0 -278
- package/src/viewer/types/a11y.ts +0 -197
- package/src/viewer/utils/a11y-fixes.ts +0 -509
- package/src/viewer/utils/actionExport.ts +0 -372
- package/src/viewer/utils/colorSchemes.ts +0 -201
- package/src/viewer/utils/detectRelationships.ts +0 -256
- package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss +0 -10
- package/src/viewer/vendor/shared/src/ComponentDocContent.module.scss.d.ts +0 -2
- package/src/viewer/vendor/shared/src/ComponentDocContent.tsx +0 -274
- package/src/viewer/vendor/shared/src/DocsHeaderBar.tsx +0 -129
- package/src/viewer/vendor/shared/src/DocsPageAsideHost.tsx +0 -89
- package/src/viewer/vendor/shared/src/DocsPageShell.tsx +0 -124
- package/src/viewer/vendor/shared/src/DocsSearchCommand.tsx +0 -99
- package/src/viewer/vendor/shared/src/DocsSidebarNav.tsx +0 -66
- package/src/viewer/vendor/shared/src/PropsTable.module.scss +0 -68
- package/src/viewer/vendor/shared/src/PropsTable.module.scss.d.ts +0 -2
- package/src/viewer/vendor/shared/src/PropsTable.tsx +0 -76
- package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss +0 -114
- package/src/viewer/vendor/shared/src/VariantPreviewCard.module.scss.d.ts +0 -2
- package/src/viewer/vendor/shared/src/VariantPreviewCard.tsx +0 -137
- package/src/viewer/vendor/shared/src/docs-data/index.ts +0 -32
- package/src/viewer/vendor/shared/src/docs-data/mcp-configs.ts +0 -72
- package/src/viewer/vendor/shared/src/docs-data/palettes.ts +0 -75
- package/src/viewer/vendor/shared/src/docs-data/setup-examples.ts +0 -55
- package/src/viewer/vendor/shared/src/docs-layout.scss +0 -28
- package/src/viewer/vendor/shared/src/docs-layout.scss.d.ts +0 -2
- package/src/viewer/vendor/shared/src/index.ts +0 -34
- package/src/viewer/vendor/shared/src/types.ts +0 -53
- package/src/viewer/webmcp/__tests__/analytics.test.ts +0 -108
- package/src/viewer/webmcp/analytics.ts +0 -165
- package/src/viewer/webmcp/index.ts +0 -3
- package/src/viewer/webmcp/posthog-bridge.ts +0 -39
- package/src/viewer/webmcp/runtime-tools.ts +0 -152
- package/src/viewer/webmcp/scan-utils.ts +0 -135
- package/src/viewer/webmcp/use-tool-analytics.ts +0 -69
- package/src/viewer/webmcp/viewer-state.ts +0 -45
- /package/dist/{discovery-NEOY4MPN.js.map → ai-client-I6MDWNYA.js.map} +0 -0
- /package/dist/{chunk-PW7QTQA6.js.map → chunk-4OC7FTJB.js.map} +0 -0
- /package/dist/{chunk-HRFUSSZI.js.map → chunk-AM4MRTMN.js.map} +0 -0
- /package/dist/{scan-CJF2DOQW.js.map → discovery-ZJQSXF56.js.map} +0 -0
- /package/dist/{generate-FBHSXR3D.js.map → generate-RJFS2JWA.js.map} +0 -0
- /package/dist/{service-TQYWY65E.js.map → scan-3PMCJ4RB.js.map} +0 -0
- /package/dist/{static-viewer-NUBFPKWH.js.map → service-VMGNJZ42.js.map} +0 -0
- /package/dist/{test-Z5LVO724.js.map → test-SI4NSHQX.js.map} +0 -0
- /package/dist/{tokens-CE46OTMD.js.map → tokens-T6SIVUT5.js.map} +0 -0
package/dist/bin.js
CHANGED
|
@@ -1,28 +1,42 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire as __banner_createRequire } from 'module'; const require = __banner_createRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
createAIClient,
|
|
5
|
+
detectProvider,
|
|
6
|
+
getApiKey
|
|
7
|
+
} from "./chunk-SXTKFDCR.js";
|
|
8
|
+
import {
|
|
9
|
+
setup
|
|
10
|
+
} from "./chunk-T5OMVL7E.js";
|
|
3
11
|
import {
|
|
4
12
|
buildFragments,
|
|
5
13
|
buildFragmentsDir,
|
|
6
14
|
measureBundleSizes,
|
|
15
|
+
resolveComponentSourcePath,
|
|
7
16
|
runAnalyzeCommand,
|
|
8
17
|
runDiffCommand,
|
|
9
18
|
runScreenshotCommand,
|
|
10
19
|
toPerformanceData,
|
|
11
20
|
validateAll,
|
|
12
21
|
validateCoverage,
|
|
22
|
+
validateDrift,
|
|
13
23
|
validateSchema,
|
|
14
24
|
validateSnippets
|
|
15
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-GVDSFQ4E.js";
|
|
26
|
+
import {
|
|
27
|
+
createComponentExtractor
|
|
28
|
+
} from "./chunk-JJ2VRTBU.js";
|
|
16
29
|
import {
|
|
17
30
|
scan
|
|
18
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-TPWGL2XS.js";
|
|
19
32
|
import {
|
|
20
33
|
loadConfig,
|
|
21
|
-
loadFragmentFile
|
|
22
|
-
|
|
34
|
+
loadFragmentFile,
|
|
35
|
+
parseFragmentFile
|
|
36
|
+
} from "./chunk-AM4MRTMN.js";
|
|
23
37
|
import {
|
|
24
38
|
discoverFragmentFiles
|
|
25
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-OQKMEFOS.js";
|
|
26
40
|
import {
|
|
27
41
|
FigmaClient,
|
|
28
42
|
StorageManager,
|
|
@@ -38,7 +52,7 @@ import {
|
|
|
38
52
|
renderAllComponentVariants,
|
|
39
53
|
scanCodebase,
|
|
40
54
|
shutdownSharedPool
|
|
41
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-LVWFOLUZ.js";
|
|
42
56
|
import "./chunk-D2CDBRNU.js";
|
|
43
57
|
import {
|
|
44
58
|
BRAND,
|
|
@@ -46,7 +60,7 @@ import {
|
|
|
46
60
|
formatBytes,
|
|
47
61
|
generateContext,
|
|
48
62
|
resolvePerformanceConfig
|
|
49
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-WFS63PCW.js";
|
|
50
64
|
import "./chunk-Z7EY4VHE.js";
|
|
51
65
|
|
|
52
66
|
// src/bin.ts
|
|
@@ -54,7 +68,7 @@ import { Command } from "commander";
|
|
|
54
68
|
import pc24 from "picocolors";
|
|
55
69
|
import { readFileSync } from "fs";
|
|
56
70
|
import { fileURLToPath } from "url";
|
|
57
|
-
import { dirname as
|
|
71
|
+
import { dirname as dirname4, join as join11 } from "path";
|
|
58
72
|
|
|
59
73
|
// src/commands/validate.ts
|
|
60
74
|
import pc from "picocolors";
|
|
@@ -78,6 +92,11 @@ ${BRAND.name} Validator
|
|
|
78
92
|
componentStart: options.componentStart,
|
|
79
93
|
componentLimit
|
|
80
94
|
});
|
|
95
|
+
} else if (options.drift) {
|
|
96
|
+
console.log(pc.dim("Running drift detection...\n"));
|
|
97
|
+
const driftResult = await validateDrift(config, configDir, { tsconfig: options.tsconfig });
|
|
98
|
+
result = driftResult;
|
|
99
|
+
printDriftReport(driftResult);
|
|
81
100
|
} else {
|
|
82
101
|
console.log(pc.dim("Running all validations...\n"));
|
|
83
102
|
result = await validateAll(config, configDir, {
|
|
@@ -118,6 +137,30 @@ ${BRAND.name} Validator
|
|
|
118
137
|
}
|
|
119
138
|
return result;
|
|
120
139
|
}
|
|
140
|
+
function printDriftReport(result) {
|
|
141
|
+
if (result.reports.length === 0) {
|
|
142
|
+
console.log(pc.green("No drift detected \u2014 fragments are in sync with source.\n"));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
console.log(pc.bold(`Drift detected in ${result.reports.length} component(s):
|
|
146
|
+
`));
|
|
147
|
+
for (const report of result.reports) {
|
|
148
|
+
console.log(` ${pc.bold(report.component)} ${pc.dim(`(${report.file})`)}`);
|
|
149
|
+
for (const drift of report.drifts) {
|
|
150
|
+
const icon = drift.kind === "removed" ? pc.red("\u2212") : drift.kind === "added" ? pc.green("+") : pc.yellow("~");
|
|
151
|
+
const label = drift.kind.replace("_", " ");
|
|
152
|
+
console.log(` ${icon} ${drift.prop}: ${label}`);
|
|
153
|
+
if (drift.kind !== "added" && drift.kind !== "removed") {
|
|
154
|
+
console.log(pc.dim(` fragment: ${drift.fragment}`));
|
|
155
|
+
console.log(pc.dim(` source: ${drift.source}`));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (report.compositionDrift) {
|
|
159
|
+
console.log(` ${pc.yellow("~")} composition: ${report.compositionDrift}`);
|
|
160
|
+
}
|
|
161
|
+
console.log();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
121
164
|
|
|
122
165
|
// src/commands/build.ts
|
|
123
166
|
import pc2 from "picocolors";
|
|
@@ -1858,7 +1901,7 @@ ${BRAND.name} Dev Server
|
|
|
1858
1901
|
}
|
|
1859
1902
|
}
|
|
1860
1903
|
}
|
|
1861
|
-
const { createDevServer } = await import("./viewer-
|
|
1904
|
+
const { createDevServer } = await import("./viewer-7ZEAFBVN.js");
|
|
1862
1905
|
console.log(pc7.dim("\nStarting dev server..."));
|
|
1863
1906
|
const parsedPort = typeof port === "string" ? parseInt(port, 10) : port;
|
|
1864
1907
|
try {
|
|
@@ -4453,8 +4496,8 @@ async function enhance(options = {}) {
|
|
|
4453
4496
|
yes = false,
|
|
4454
4497
|
dryRun = false,
|
|
4455
4498
|
format = "interactive",
|
|
4456
|
-
provider =
|
|
4457
|
-
apiKey =
|
|
4499
|
+
provider = detectProvider2(options),
|
|
4500
|
+
apiKey = getApiKey2(provider, options.apiKey),
|
|
4458
4501
|
model = options.model || DEFAULT_MODELS[provider],
|
|
4459
4502
|
root = process.cwd(),
|
|
4460
4503
|
contextOnly = false,
|
|
@@ -4817,33 +4860,12 @@ For each component, provide your response in JSON format:
|
|
|
4817
4860
|
context: fullContext
|
|
4818
4861
|
};
|
|
4819
4862
|
}
|
|
4820
|
-
function
|
|
4863
|
+
function detectProvider2(options) {
|
|
4821
4864
|
if (options.contextOnly) return "none";
|
|
4822
|
-
|
|
4823
|
-
if (options.apiKey) {
|
|
4824
|
-
if (options.apiKey.startsWith("sk-ant-")) return "anthropic";
|
|
4825
|
-
if (options.apiKey.startsWith("sk-")) return "openai";
|
|
4826
|
-
}
|
|
4827
|
-
if (process.env.ANTHROPIC_API_KEY) return "anthropic";
|
|
4828
|
-
if (process.env.OPENAI_API_KEY) return "openai";
|
|
4829
|
-
return "none";
|
|
4865
|
+
return detectProvider({ provider: options.provider, apiKey: options.apiKey });
|
|
4830
4866
|
}
|
|
4831
|
-
function
|
|
4832
|
-
|
|
4833
|
-
if (provider === "anthropic") return process.env.ANTHROPIC_API_KEY;
|
|
4834
|
-
if (provider === "openai") return process.env.OPENAI_API_KEY;
|
|
4835
|
-
return void 0;
|
|
4836
|
-
}
|
|
4837
|
-
async function createAIClient(provider, apiKey) {
|
|
4838
|
-
if (provider === "anthropic") {
|
|
4839
|
-
const Anthropic = (await import("@anthropic-ai/sdk")).default;
|
|
4840
|
-
return new Anthropic({ apiKey });
|
|
4841
|
-
}
|
|
4842
|
-
if (provider === "openai") {
|
|
4843
|
-
const OpenAI = (await import("openai")).default;
|
|
4844
|
-
return new OpenAI({ apiKey });
|
|
4845
|
-
}
|
|
4846
|
-
throw new Error(`Unknown provider: ${provider}`);
|
|
4867
|
+
function getApiKey2(provider, explicitKey) {
|
|
4868
|
+
return getApiKey(provider, explicitKey);
|
|
4847
4869
|
}
|
|
4848
4870
|
async function generateEnhancement(client, provider, model, context2) {
|
|
4849
4871
|
const systemPrompt = generateSystemPrompt();
|
|
@@ -5785,365 +5807,228 @@ ${BRAND.name} Doctor
|
|
|
5785
5807
|
return result;
|
|
5786
5808
|
}
|
|
5787
5809
|
|
|
5788
|
-
// src/commands/
|
|
5789
|
-
import { readFile as readFile9, writeFile as writeFile9, access as access4, mkdir as mkdir7 } from "fs/promises";
|
|
5790
|
-
import { join as join11, resolve as resolve9, dirname as dirname4 } from "path";
|
|
5810
|
+
// src/commands/sync.ts
|
|
5791
5811
|
import pc23 from "picocolors";
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
async function detectFramework(root) {
|
|
5801
|
-
if (await fileExists(join11(root, "app/layout.tsx")) || await fileExists(join11(root, "src/app/layout.tsx"))) {
|
|
5802
|
-
return "nextjs-app";
|
|
5803
|
-
}
|
|
5804
|
-
if (await fileExists(join11(root, "pages/_app.tsx")) || await fileExists(join11(root, "pages/_app.ts"))) {
|
|
5805
|
-
return "nextjs-pages";
|
|
5806
|
-
}
|
|
5807
|
-
if (await fileExists(join11(root, "next.config.ts")) || await fileExists(join11(root, "next.config.js")) || await fileExists(join11(root, "next.config.mjs"))) {
|
|
5808
|
-
return "nextjs-app";
|
|
5809
|
-
}
|
|
5810
|
-
if (await fileExists(join11(root, "vite.config.ts")) || await fileExists(join11(root, "vite.config.js"))) {
|
|
5811
|
-
return "vite";
|
|
5812
|
-
}
|
|
5813
|
-
return "unknown";
|
|
5814
|
-
}
|
|
5815
|
-
async function findEntryFile(root, framework) {
|
|
5816
|
-
const candidates = [];
|
|
5817
|
-
switch (framework) {
|
|
5818
|
-
case "nextjs-app":
|
|
5819
|
-
candidates.push(
|
|
5820
|
-
"src/app/layout.tsx",
|
|
5821
|
-
"app/layout.tsx",
|
|
5822
|
-
"src/app/layout.ts",
|
|
5823
|
-
"app/layout.ts"
|
|
5824
|
-
);
|
|
5825
|
-
break;
|
|
5826
|
-
case "nextjs-pages":
|
|
5827
|
-
candidates.push("pages/_app.tsx", "pages/_app.ts");
|
|
5828
|
-
break;
|
|
5829
|
-
case "vite":
|
|
5830
|
-
candidates.push(
|
|
5831
|
-
"src/main.tsx",
|
|
5832
|
-
"src/main.ts",
|
|
5833
|
-
"src/index.tsx",
|
|
5834
|
-
"src/index.ts"
|
|
5835
|
-
);
|
|
5836
|
-
break;
|
|
5837
|
-
default:
|
|
5838
|
-
candidates.push(
|
|
5839
|
-
"src/main.tsx",
|
|
5840
|
-
"src/main.ts",
|
|
5841
|
-
"src/index.tsx",
|
|
5842
|
-
"src/index.ts",
|
|
5843
|
-
"src/App.tsx",
|
|
5844
|
-
"src/App.ts"
|
|
5845
|
-
);
|
|
5846
|
-
}
|
|
5847
|
-
for (const candidate of candidates) {
|
|
5848
|
-
if (await fileExists(join11(root, candidate))) {
|
|
5849
|
-
return candidate;
|
|
5850
|
-
}
|
|
5812
|
+
import { readFile as readFile9, writeFile as writeFile9 } from "fs/promises";
|
|
5813
|
+
async function sync(options = {}) {
|
|
5814
|
+
const { config, configDir } = await loadConfig(options.config);
|
|
5815
|
+
console.log(pc23.cyan(`
|
|
5816
|
+
${BRAND.name} Sync
|
|
5817
|
+
`));
|
|
5818
|
+
if (options.dryRun) {
|
|
5819
|
+
console.log(pc23.dim("Dry run \u2014 no files will be modified.\n"));
|
|
5851
5820
|
}
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5821
|
+
const result = await runSync(config, configDir, options);
|
|
5822
|
+
if (result.updated.length > 0) {
|
|
5823
|
+
const verb = options.dryRun ? "Would update" : "Updated";
|
|
5824
|
+
console.log(pc23.bold(`${verb} ${result.updated.length} fragment(s):
|
|
5825
|
+
`));
|
|
5826
|
+
for (const comp of result.updated) {
|
|
5827
|
+
console.log(` ${pc23.green("\u2713")} ${pc23.bold(comp.name)} ${pc23.dim(`(${comp.file})`)}`);
|
|
5828
|
+
for (const change of comp.changes) {
|
|
5829
|
+
console.log(` ${pc23.dim("\u2022")} ${change}`);
|
|
5830
|
+
}
|
|
5859
5831
|
}
|
|
5832
|
+
console.log();
|
|
5860
5833
|
}
|
|
5861
|
-
|
|
5862
|
-
}
|
|
5863
|
-
|
|
5864
|
-
const fullPath = join11(root, entryFile);
|
|
5865
|
-
const content = await readFile9(fullPath, "utf-8");
|
|
5866
|
-
if (content.includes("@fragments-sdk/ui/styles")) {
|
|
5867
|
-
return { modified: false, message: `Styles already imported in ${entryFile}` };
|
|
5868
|
-
}
|
|
5869
|
-
const stylesImport = "import '@fragments-sdk/ui/styles';";
|
|
5870
|
-
let newContent;
|
|
5871
|
-
if (content.startsWith("'use client'") || content.startsWith('"use client"')) {
|
|
5872
|
-
const directiveEnd = content.indexOf("\n") + 1;
|
|
5873
|
-
newContent = content.slice(0, directiveEnd) + stylesImport + "\n" + content.slice(directiveEnd);
|
|
5874
|
-
} else {
|
|
5875
|
-
newContent = stylesImport + "\n" + content;
|
|
5834
|
+
if (result.skipped.length > 0) {
|
|
5835
|
+
console.log(pc23.dim(`Skipped ${result.skipped.length}: ${result.skipped.map((s) => s.name).join(", ")}
|
|
5836
|
+
`));
|
|
5876
5837
|
}
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
const fullPath = join11(root, entryFile);
|
|
5882
|
-
const content = await readFile9(fullPath, "utf-8");
|
|
5883
|
-
if (content.includes("ThemeProvider")) {
|
|
5884
|
-
return { modified: false, message: `ThemeProvider already present in ${entryFile}` };
|
|
5885
|
-
}
|
|
5886
|
-
if (framework === "nextjs-app") {
|
|
5887
|
-
const providerImport = "import { ThemeProvider, TooltipProvider, ToastProvider } from '@fragments-sdk/ui';";
|
|
5888
|
-
let newContent = content;
|
|
5889
|
-
const importLines = content.split("\n");
|
|
5890
|
-
let lastImportIdx = -1;
|
|
5891
|
-
for (let i = 0; i < importLines.length; i++) {
|
|
5892
|
-
if (importLines[i].startsWith("import ") || importLines[i].startsWith("import '") || importLines[i].startsWith('import "')) {
|
|
5893
|
-
lastImportIdx = i;
|
|
5894
|
-
}
|
|
5895
|
-
}
|
|
5896
|
-
if (lastImportIdx >= 0) {
|
|
5897
|
-
importLines.splice(lastImportIdx + 1, 0, providerImport);
|
|
5898
|
-
newContent = importLines.join("\n");
|
|
5899
|
-
} else {
|
|
5900
|
-
newContent = providerImport + "\n" + content;
|
|
5901
|
-
}
|
|
5902
|
-
if (!content.includes("suppressHydrationWarning")) {
|
|
5903
|
-
await writeFile9(fullPath, newContent, "utf-8");
|
|
5904
|
-
return {
|
|
5905
|
-
modified: true,
|
|
5906
|
-
message: `Added provider imports to ${entryFile}. Wrap your {children} with:
|
|
5907
|
-
<ThemeProvider defaultMode="system"><TooltipProvider><ToastProvider>{children}</ToastProvider></TooltipProvider></ThemeProvider>
|
|
5908
|
-
Add suppressHydrationWarning to your <html> tag`
|
|
5909
|
-
};
|
|
5838
|
+
if (result.errors.length > 0) {
|
|
5839
|
+
console.log(pc23.red(pc23.bold("Errors:")));
|
|
5840
|
+
for (const err of result.errors) {
|
|
5841
|
+
console.log(` ${pc23.red("\u2717")} ${pc23.bold(err.file)}: ${err.message}`);
|
|
5910
5842
|
}
|
|
5911
|
-
|
|
5912
|
-
return { modified: true, message: `Added provider imports to ${entryFile}. Wrap {children} with ThemeProvider.` };
|
|
5913
|
-
}
|
|
5914
|
-
return { modified: false, message: "Manual ThemeProvider setup needed \u2014 see https://usefragments.com/getting-started#provider-setup" };
|
|
5915
|
-
}
|
|
5916
|
-
async function addTranspilePackages(root) {
|
|
5917
|
-
const configFile = await findNextConfig(root);
|
|
5918
|
-
if (!configFile) {
|
|
5919
|
-
return { modified: false, message: "No next.config found" };
|
|
5843
|
+
console.log();
|
|
5920
5844
|
}
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
if (content.includes("transpilePackages") && content.includes("@fragments-sdk/ui")) {
|
|
5924
|
-
return { modified: false, message: `transpilePackages already configured in ${configFile}` };
|
|
5845
|
+
if (result.updated.length === 0 && result.errors.length === 0) {
|
|
5846
|
+
console.log(pc23.green("All fragments are in sync \u2014 nothing to update.\n"));
|
|
5925
5847
|
}
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5848
|
+
return result;
|
|
5849
|
+
}
|
|
5850
|
+
async function runSync(config, configDir, options) {
|
|
5851
|
+
const fragmentFiles = await discoverFragmentFiles(config, configDir);
|
|
5852
|
+
const updated = [];
|
|
5853
|
+
const skipped = [];
|
|
5854
|
+
const errors = [];
|
|
5855
|
+
if (fragmentFiles.length === 0) {
|
|
5856
|
+
return { success: true, updated, skipped, errors };
|
|
5931
5857
|
}
|
|
5932
|
-
const
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5858
|
+
const extractor = createComponentExtractor(options.tsconfig);
|
|
5859
|
+
try {
|
|
5860
|
+
for (const file of fragmentFiles) {
|
|
5861
|
+
try {
|
|
5862
|
+
const fragment = await loadFragmentFile(file.absolutePath);
|
|
5863
|
+
if (!fragment?.meta?.name) continue;
|
|
5864
|
+
if (options.component && fragment.meta.name !== options.component) continue;
|
|
5865
|
+
const fileContent = await readFile9(file.absolutePath, "utf-8");
|
|
5866
|
+
const parsed = parseFragmentFile(fileContent, file.absolutePath);
|
|
5867
|
+
if (!parsed.componentImport) {
|
|
5868
|
+
skipped.push({ name: fragment.meta.name, reason: "No component import found" });
|
|
5869
|
+
continue;
|
|
5870
|
+
}
|
|
5871
|
+
const sourcePath = resolveComponentSourcePath(file.absolutePath, parsed.componentImport);
|
|
5872
|
+
if (!sourcePath) {
|
|
5873
|
+
skipped.push({ name: fragment.meta.name, reason: "Cannot resolve component source" });
|
|
5874
|
+
continue;
|
|
5875
|
+
}
|
|
5876
|
+
const meta = extractor.extract(sourcePath, fragment.meta.name);
|
|
5877
|
+
if (!meta) {
|
|
5878
|
+
skipped.push({ name: fragment.meta.name, reason: "Extraction returned null" });
|
|
5879
|
+
continue;
|
|
5880
|
+
}
|
|
5881
|
+
const patch = computePatch(fileContent, fragment, meta);
|
|
5882
|
+
if (patch.changes.length === 0) {
|
|
5883
|
+
skipped.push({ name: fragment.meta.name, reason: "Already in sync" });
|
|
5884
|
+
continue;
|
|
5885
|
+
}
|
|
5886
|
+
if (!options.dryRun) {
|
|
5887
|
+
await writeFile9(file.absolutePath, patch.updatedContent, "utf-8");
|
|
5888
|
+
}
|
|
5889
|
+
updated.push({
|
|
5890
|
+
name: fragment.meta.name,
|
|
5891
|
+
file: file.relativePath,
|
|
5892
|
+
changes: patch.changes
|
|
5893
|
+
});
|
|
5894
|
+
} catch (err) {
|
|
5895
|
+
errors.push({
|
|
5896
|
+
file: file.relativePath,
|
|
5897
|
+
message: err instanceof Error ? err.message : String(err)
|
|
5898
|
+
});
|
|
5899
|
+
}
|
|
5948
5900
|
}
|
|
5901
|
+
} finally {
|
|
5902
|
+
extractor.dispose();
|
|
5949
5903
|
}
|
|
5950
5904
|
return {
|
|
5951
|
-
|
|
5952
|
-
|
|
5905
|
+
success: errors.length === 0,
|
|
5906
|
+
updated,
|
|
5907
|
+
skipped,
|
|
5908
|
+
errors
|
|
5953
5909
|
};
|
|
5954
5910
|
}
|
|
5955
|
-
|
|
5956
|
-
const
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
}
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
const
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
if (hasFragments) {
|
|
6000
|
-
return { modified: false, message: `MCP server already configured in ${configPath}` };
|
|
5911
|
+
function computePatch(fileContent, fragment, meta) {
|
|
5912
|
+
const changes = [];
|
|
5913
|
+
let content = fileContent;
|
|
5914
|
+
const localSourceProps = Object.fromEntries(
|
|
5915
|
+
Object.entries(meta.props).filter(([_, p]) => p.source === "local")
|
|
5916
|
+
);
|
|
5917
|
+
const addedProps = [];
|
|
5918
|
+
for (const name of Object.keys(localSourceProps)) {
|
|
5919
|
+
if (!(name in fragment.props)) {
|
|
5920
|
+
addedProps.push(name);
|
|
5921
|
+
}
|
|
5922
|
+
}
|
|
5923
|
+
const removedProps = [];
|
|
5924
|
+
for (const name of Object.keys(fragment.props)) {
|
|
5925
|
+
if (!(name in localSourceProps)) {
|
|
5926
|
+
removedProps.push(name);
|
|
5927
|
+
}
|
|
5928
|
+
}
|
|
5929
|
+
if (addedProps.length > 0) {
|
|
5930
|
+
const propsBlockEnd = findPropsBlockEnd(content);
|
|
5931
|
+
if (propsBlockEnd !== -1) {
|
|
5932
|
+
const newEntries = addedProps.map((name) => {
|
|
5933
|
+
const prop = localSourceProps[name];
|
|
5934
|
+
return formatPropEntry(name, prop);
|
|
5935
|
+
}).join("\n");
|
|
5936
|
+
content = content.slice(0, propsBlockEnd) + newEntries + "\n " + content.slice(propsBlockEnd);
|
|
5937
|
+
changes.push(`Added props: ${addedProps.join(", ")}`);
|
|
5938
|
+
}
|
|
5939
|
+
}
|
|
5940
|
+
if (removedProps.length > 0) {
|
|
5941
|
+
for (const name of removedProps) {
|
|
5942
|
+
const propRegex = new RegExp(`([ \\t]*)${escapeRegex(name)}:\\s*\\{`, "g");
|
|
5943
|
+
const match = propRegex.exec(content);
|
|
5944
|
+
if (match) {
|
|
5945
|
+
const indent = match[1];
|
|
5946
|
+
const startIdx = match.index;
|
|
5947
|
+
const endIdx = findMatchingBrace2(content, match.index + match[0].length - 1);
|
|
5948
|
+
if (endIdx !== -1) {
|
|
5949
|
+
let lineEnd = endIdx + 1;
|
|
5950
|
+
if (content[lineEnd] === ",") lineEnd++;
|
|
5951
|
+
if (content[lineEnd] === "\n") lineEnd++;
|
|
5952
|
+
const removedBlock = content.slice(startIdx, lineEnd);
|
|
5953
|
+
const commented = removedBlock.split("\n").map((line) => line ? `${indent}// [drift:removed] ${line.trimStart()}` : "").join("\n");
|
|
5954
|
+
content = content.slice(0, startIdx) + commented + content.slice(lineEnd);
|
|
6001
5955
|
}
|
|
6002
|
-
servers.fragments = {
|
|
6003
|
-
command: "npx",
|
|
6004
|
-
args: ["@fragments-sdk/mcp"]
|
|
6005
|
-
};
|
|
6006
|
-
config.mcpServers = servers;
|
|
6007
|
-
await writeFile9(fullPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
6008
|
-
return { modified: true, message: `Added Fragments MCP server to ${configPath}` };
|
|
6009
|
-
} catch {
|
|
6010
|
-
return { modified: false, message: `Could not parse ${configPath}` };
|
|
6011
5956
|
}
|
|
6012
5957
|
}
|
|
5958
|
+
changes.push(`Removed props: ${removedProps.join(", ")}`);
|
|
6013
5959
|
}
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
mcpServers: {
|
|
6019
|
-
fragments: {
|
|
6020
|
-
command: "npx",
|
|
6021
|
-
args: ["@fragments-sdk/mcp"]
|
|
6022
|
-
}
|
|
6023
|
-
}
|
|
6024
|
-
};
|
|
6025
|
-
await writeFile9(cursorMcpPath, JSON.stringify(mcpConfig, null, 2) + "\n", "utf-8");
|
|
6026
|
-
return { modified: true, message: "Created .cursor/mcp.json with Fragments MCP server" };
|
|
5960
|
+
if (meta.composition && !fragment.ai?.compositionPattern) {
|
|
5961
|
+
changes.push(`Composition: "${meta.composition.pattern}" pattern detected`);
|
|
5962
|
+
}
|
|
5963
|
+
return { updatedContent: content, changes };
|
|
6027
5964
|
}
|
|
6028
|
-
function
|
|
6029
|
-
const
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
$fui-density: "default",
|
|
6034
|
-
$fui-radius-style: "rounded"
|
|
6035
|
-
);
|
|
6036
|
-
`;
|
|
5965
|
+
function findPropsBlockEnd(content) {
|
|
5966
|
+
const propsStart = content.search(/\bprops:\s*\{/);
|
|
5967
|
+
if (propsStart === -1) return -1;
|
|
5968
|
+
const braceStart = content.indexOf("{", propsStart);
|
|
5969
|
+
return findMatchingBrace2(content, braceStart);
|
|
6037
5970
|
}
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
console.log(` ${pc23.dim("Framework:")} ${frameworkLabel}`);
|
|
6048
|
-
const entryFile = await findEntryFile(root, framework);
|
|
6049
|
-
if (entryFile) {
|
|
6050
|
-
console.log(` ${pc23.dim("Entry:")} ${entryFile}`);
|
|
6051
|
-
} else {
|
|
6052
|
-
console.log(` ${pc23.yellow("!")} Could not detect entry file`);
|
|
6053
|
-
}
|
|
6054
|
-
console.log();
|
|
6055
|
-
if (entryFile) {
|
|
6056
|
-
try {
|
|
6057
|
-
const result = await addStylesImport(root, entryFile);
|
|
6058
|
-
const icon = result.modified ? pc23.green("+") : pc23.dim("\xB7");
|
|
6059
|
-
console.log(` ${icon} ${result.message}`);
|
|
6060
|
-
if (result.modified) actions.push(result.message);
|
|
6061
|
-
} catch (error) {
|
|
6062
|
-
const msg = `Failed to add styles import: ${error instanceof Error ? error.message : error}`;
|
|
6063
|
-
console.log(` ${pc23.red("\u2717")} ${msg}`);
|
|
6064
|
-
errors.push(msg);
|
|
5971
|
+
function findMatchingBrace2(content, start) {
|
|
5972
|
+
let depth = 0;
|
|
5973
|
+
let inString = null;
|
|
5974
|
+
let escaped = false;
|
|
5975
|
+
for (let i = start; i < content.length; i++) {
|
|
5976
|
+
const ch = content[i];
|
|
5977
|
+
if (escaped) {
|
|
5978
|
+
escaped = false;
|
|
5979
|
+
continue;
|
|
6065
5980
|
}
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
const result = await addThemeProvider(root, entryFile, framework);
|
|
6070
|
-
const icon = result.modified ? pc23.green("+") : pc23.dim("\xB7");
|
|
6071
|
-
console.log(` ${icon} ${result.message}`);
|
|
6072
|
-
if (result.modified) actions.push(result.message);
|
|
6073
|
-
} catch (error) {
|
|
6074
|
-
const msg = `Failed to add ThemeProvider: ${error instanceof Error ? error.message : error}`;
|
|
6075
|
-
console.log(` ${pc23.red("\u2717")} ${msg}`);
|
|
6076
|
-
errors.push(msg);
|
|
5981
|
+
if (ch === "\\") {
|
|
5982
|
+
escaped = true;
|
|
5983
|
+
continue;
|
|
6077
5984
|
}
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
const result = await addTranspilePackages(root);
|
|
6082
|
-
const icon = result.modified ? pc23.green("+") : pc23.dim("\xB7");
|
|
6083
|
-
console.log(` ${icon} ${result.message}`);
|
|
6084
|
-
if (result.modified) actions.push(result.message);
|
|
6085
|
-
} catch (error) {
|
|
6086
|
-
const msg = `Failed to update next.config: ${error instanceof Error ? error.message : error}`;
|
|
6087
|
-
console.log(` ${pc23.red("\u2717")} ${msg}`);
|
|
6088
|
-
errors.push(msg);
|
|
5985
|
+
if (inString) {
|
|
5986
|
+
if (ch === inString) inString = null;
|
|
5987
|
+
continue;
|
|
6089
5988
|
}
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
const result = await createScssSeeds(root, options.brand);
|
|
6094
|
-
const icon = result.modified ? pc23.green("+") : pc23.dim("\xB7");
|
|
6095
|
-
console.log(` ${icon} ${result.message}`);
|
|
6096
|
-
if (result.modified) actions.push(result.message);
|
|
6097
|
-
} catch (error) {
|
|
6098
|
-
const msg = `Failed to create SCSS seeds: ${error instanceof Error ? error.message : error}`;
|
|
6099
|
-
console.log(` ${pc23.red("\u2717")} ${msg}`);
|
|
6100
|
-
errors.push(msg);
|
|
5989
|
+
if (ch === "'" || ch === '"' || ch === "`") {
|
|
5990
|
+
inString = ch;
|
|
5991
|
+
continue;
|
|
6101
5992
|
}
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
const icon = result.modified ? pc23.green("+") : pc23.dim("\xB7");
|
|
6107
|
-
console.log(` ${icon} ${result.message}`);
|
|
6108
|
-
if (result.modified) actions.push(result.message);
|
|
6109
|
-
} catch (error) {
|
|
6110
|
-
const msg = `Failed to configure MCP: ${error instanceof Error ? error.message : error}`;
|
|
6111
|
-
console.log(` ${pc23.red("\u2717")} ${msg}`);
|
|
6112
|
-
errors.push(msg);
|
|
5993
|
+
if (ch === "{") depth++;
|
|
5994
|
+
else if (ch === "}") {
|
|
5995
|
+
depth--;
|
|
5996
|
+
if (depth === 0) return i;
|
|
6113
5997
|
}
|
|
6114
5998
|
}
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
5999
|
+
return -1;
|
|
6000
|
+
}
|
|
6001
|
+
function formatPropEntry(name, prop) {
|
|
6002
|
+
const lines = [];
|
|
6003
|
+
lines.push(` ${name}: {`);
|
|
6004
|
+
lines.push(` type: '${prop.typeKind}',`);
|
|
6005
|
+
if (prop.description) {
|
|
6006
|
+
lines.push(` description: ${JSON.stringify(prop.description)},`);
|
|
6120
6007
|
} else {
|
|
6121
|
-
|
|
6008
|
+
lines.push(` description: '', // TODO: add description`);
|
|
6122
6009
|
}
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
if (!options.scss && !options.brand) {
|
|
6126
|
-
console.log(pc23.dim(" \u2022 Run with --scss to add build-time theme seeds"));
|
|
6010
|
+
if (prop.required) {
|
|
6011
|
+
lines.push(` required: true,`);
|
|
6127
6012
|
}
|
|
6128
|
-
if (
|
|
6129
|
-
|
|
6013
|
+
if (prop.values && prop.values.length > 0) {
|
|
6014
|
+
lines.push(` values: [${prop.values.map((v) => `'${v}'`).join(", ")}],`);
|
|
6130
6015
|
}
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
};
|
|
6016
|
+
if (prop.default !== void 0) {
|
|
6017
|
+
lines.push(` default: '${prop.default}',`);
|
|
6018
|
+
}
|
|
6019
|
+
lines.push(` },`);
|
|
6020
|
+
return lines.join("\n");
|
|
6021
|
+
}
|
|
6022
|
+
function escapeRegex(str) {
|
|
6023
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6139
6024
|
}
|
|
6140
6025
|
|
|
6141
6026
|
// src/bin.ts
|
|
6142
|
-
var __dirname =
|
|
6143
|
-
var pkg = JSON.parse(readFileSync(
|
|
6027
|
+
var __dirname = dirname4(fileURLToPath(import.meta.url));
|
|
6028
|
+
var pkg = JSON.parse(readFileSync(join11(__dirname, "../package.json"), "utf-8"));
|
|
6144
6029
|
var program = new Command();
|
|
6145
6030
|
program.name(BRAND.cliCommand).description(`${BRAND.name} - Design system documentation and compliance tool`).version(pkg.version);
|
|
6146
|
-
program.command("validate").description("Validate fragment files").option("-c, --config <path>", "Path to config file").option("--schema", "Validate fragment schema only").option("--coverage", "Validate coverage only").option("--snippets", "Validate snippet/render policy only").option("--snippet-mode <mode>", "Override snippet policy mode (warn|error)").option("--component-start <name>", "Start component name for alphabetical snippet batch validation").option("--component-limit <n>", "Component count for alphabetical snippet batch validation", (value) => Number.parseInt(value, 10)).action(async (options) => {
|
|
6031
|
+
program.command("validate").description("Validate fragment files").option("-c, --config <path>", "Path to config file").option("--schema", "Validate fragment schema only").option("--coverage", "Validate coverage only").option("--snippets", "Validate snippet/render policy only").option("--drift", "Detect metadata drift between source and fragments").option("--tsconfig <path>", "Path to tsconfig.json (for drift detection)").option("--snippet-mode <mode>", "Override snippet policy mode (warn|error)").option("--component-start <name>", "Start component name for alphabetical snippet batch validation").option("--component-limit <n>", "Component count for alphabetical snippet batch validation", (value) => Number.parseInt(value, 10)).action(async (options) => {
|
|
6147
6032
|
try {
|
|
6148
6033
|
const result = await validate(options);
|
|
6149
6034
|
if (!result.valid) {
|
|
@@ -6154,6 +6039,22 @@ program.command("validate").description("Validate fragment files").option("-c, -
|
|
|
6154
6039
|
process.exit(1);
|
|
6155
6040
|
}
|
|
6156
6041
|
});
|
|
6042
|
+
program.command("sync").description("Auto-update fragment files from component source").option("-c, --config <path>", "Path to config file").option("--tsconfig <path>", "Path to tsconfig.json").option("--dry-run", "Preview changes without writing").option("--component <name>", "Sync specific component only").action(async (options) => {
|
|
6043
|
+
try {
|
|
6044
|
+
const result = await sync({
|
|
6045
|
+
config: options.config,
|
|
6046
|
+
tsconfig: options.tsconfig,
|
|
6047
|
+
dryRun: options.dryRun,
|
|
6048
|
+
component: options.component
|
|
6049
|
+
});
|
|
6050
|
+
if (!result.success) {
|
|
6051
|
+
process.exit(1);
|
|
6052
|
+
}
|
|
6053
|
+
} catch (error) {
|
|
6054
|
+
console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
|
|
6055
|
+
process.exit(1);
|
|
6056
|
+
}
|
|
6057
|
+
});
|
|
6157
6058
|
program.command("build").description(`Build compiled ${BRAND.outFile} and ${BRAND.dataDir}/ directory`).option("-c, --config <path>", "Path to config file").option("-o, --output <path>", "Output file path").option("--registry", `Also generate ${BRAND.dataDir}/${BRAND.registryFile} and ${BRAND.contextFile}`).option("--registry-only", `Only generate ${BRAND.dataDir}/ directory (skip ${BRAND.outFile})`).option("--from-source", "Build from source code (zero-config, no fragment files needed)").option("--skip-usage", "Skip usage analysis when building from source").option("--skip-storybook", "Skip Storybook parsing when building from source").option("-v, --verbose", "Verbose output").action(async (options) => {
|
|
6158
6059
|
try {
|
|
6159
6060
|
const result = await build({
|
|
@@ -6508,7 +6409,7 @@ Make sure the dev server is running: ${BRAND.cliCommand} dev`));
|
|
|
6508
6409
|
});
|
|
6509
6410
|
program.command("view").description(`Generate a static HTML viewer for ${BRAND.outFile}`).option("-i, --input <path>", `Path to ${BRAND.outFile}`, BRAND.outFile).option("-o, --output <path>", "Output HTML file path", BRAND.viewerHtmlFile).option("--open", "Open in browser after generation").action(async (options) => {
|
|
6510
6411
|
try {
|
|
6511
|
-
const { generateViewerFromJson } = await import("./static-viewer-
|
|
6412
|
+
const { generateViewerFromJson } = await import("./static-viewer-5GXH2MGE.js");
|
|
6512
6413
|
const fs2 = await import("fs/promises");
|
|
6513
6414
|
const path = await import("path");
|
|
6514
6415
|
const inputPath = path.resolve(process.cwd(), options.input);
|
|
@@ -6571,14 +6472,20 @@ program.command("setup").description("Configure @fragments-sdk/ui in a consumer
|
|
|
6571
6472
|
process.exit(1);
|
|
6572
6473
|
}
|
|
6573
6474
|
});
|
|
6574
|
-
program.command("init").description("Initialize fragments in a project (
|
|
6475
|
+
program.command("init").description("Initialize fragments in a project (zero-config by default)").option("--force", "Overwrite existing config").option("-y, --yes", "Non-interactive mode (now the default)").option("--configure", "Interactive mode for theme seeds, snapshots, etc.").option("--scan <path>", "Scan a TypeScript component directory and generate fragment files").option("--enrich", "Use AI to fill knowledge fields during --scan (requires API key)").option("--dry-run", "Show what --enrich would generate without calling API").option("--provider <provider>", "AI provider for enrichment: anthropic or openai").option("--api-key <key>", "API key for AI enrichment").option("--model <model>", "Override AI model for enrichment").action(async (options) => {
|
|
6575
6476
|
try {
|
|
6576
|
-
const { init } = await import("./init-
|
|
6477
|
+
const { init } = await import("./init-ZSX3NRCZ.js");
|
|
6577
6478
|
const result = await init({
|
|
6578
6479
|
projectRoot: process.cwd(),
|
|
6579
6480
|
force: options.force,
|
|
6580
6481
|
yes: options.scan ? true : options.yes,
|
|
6581
|
-
scan: options.scan
|
|
6482
|
+
scan: options.scan,
|
|
6483
|
+
configure: options.configure,
|
|
6484
|
+
enrich: options.enrich,
|
|
6485
|
+
dryRun: options.dryRun,
|
|
6486
|
+
provider: options.provider,
|
|
6487
|
+
apiKey: options.apiKey,
|
|
6488
|
+
model: options.model
|
|
6582
6489
|
});
|
|
6583
6490
|
if (!result.success) {
|
|
6584
6491
|
console.error(pc24.red("\nInit failed with errors:"));
|
|
@@ -6592,9 +6499,27 @@ program.command("init").description("Initialize fragments in a project (interact
|
|
|
6592
6499
|
process.exit(1);
|
|
6593
6500
|
}
|
|
6594
6501
|
});
|
|
6502
|
+
program.command("snapshot").description("Run visual snapshot tests per component variant").option("-p, --port <port>", "Port of running dev server (skips starting one)").option("--update", "Update existing snapshots instead of comparing").option("--component <name>", "Filter to a specific component").option("--spec <path>", "Path to snapshot spec file").option("--ci", "CI mode - exit 1 on mismatch").action(async (options) => {
|
|
6503
|
+
try {
|
|
6504
|
+
const { snapshot } = await import("./snapshot-XOISO2IS.js");
|
|
6505
|
+
const result = await snapshot({
|
|
6506
|
+
port: options.port,
|
|
6507
|
+
update: options.update,
|
|
6508
|
+
component: options.component,
|
|
6509
|
+
spec: options.spec,
|
|
6510
|
+
ci: options.ci
|
|
6511
|
+
});
|
|
6512
|
+
if (!result.success) {
|
|
6513
|
+
process.exit(1);
|
|
6514
|
+
}
|
|
6515
|
+
} catch (error) {
|
|
6516
|
+
console.error(pc24.red("Error:"), error instanceof Error ? error.message : error);
|
|
6517
|
+
process.exit(1);
|
|
6518
|
+
}
|
|
6519
|
+
});
|
|
6595
6520
|
program.command("tokens").description("Discover and list design tokens from CSS/SCSS files").option("-c, --config <path>", "Path to config file").option("--json", "Output as JSON").option("--categories", "Group tokens by category").option("--theme <theme>", "Filter by theme name").option("--category <category>", "Filter by category (color, spacing, typography, etc.)").option("--verbose", "Show all tokens (no truncation)").action(async (options) => {
|
|
6596
6521
|
try {
|
|
6597
|
-
const { tokens } = await import("./tokens-
|
|
6522
|
+
const { tokens } = await import("./tokens-T6SIVUT5.js");
|
|
6598
6523
|
const result = await tokens({
|
|
6599
6524
|
config: options.config,
|
|
6600
6525
|
json: options.json,
|
|
@@ -6613,7 +6538,7 @@ program.command("tokens").description("Discover and list design tokens from CSS/
|
|
|
6613
6538
|
});
|
|
6614
6539
|
program.command("generate").description("Generate fragment files from component source code").argument("[component]", "Specific component name to generate (optional)").option("--force", "Overwrite existing fragment files").option("--pattern <glob>", "Pattern for component files", "src/components/**/*.tsx").action(async (component, options) => {
|
|
6615
6540
|
try {
|
|
6616
|
-
const { generate } = await import("./generate-
|
|
6541
|
+
const { generate } = await import("./generate-RJFS2JWA.js");
|
|
6617
6542
|
const result = await generate({
|
|
6618
6543
|
projectRoot: process.cwd(),
|
|
6619
6544
|
component,
|
|
@@ -6658,7 +6583,7 @@ program.command("perf").description("Profile component bundle sizes and performa
|
|
|
6658
6583
|
program.command("test").description("Run interaction tests for fragments with play functions").option("-c, --config <path>", "Path to config file").option("--component <name>", "Filter by component name").option("--tags <tags>", "Filter by tags (comma-separated)").option("--grep <pattern>", "Filter by variant name pattern").option("--exclude <pattern>", "Exclude tests matching pattern").option("--parallel <count>", "Number of parallel browser contexts", parseInt, 4).option("--timeout <ms>", "Timeout per test in milliseconds", parseInt, 3e4).option("--retries <count>", "Number of retries for failed tests", parseInt, 0).option("--bail", "Stop on first failure").option("--browser <name>", "Browser to use (chromium, firefox, webkit)", "chromium").option("--headed", "Run in headed mode (show browser)").option("--a11y", "Run accessibility checks with axe-core").option("--visual", "Capture screenshots for visual regression").option("--update-snapshots", "Update visual snapshots").option("--watch", "Watch mode - re-run on file changes").option("--reporters <names>", "Reporters to use (console, junit, json)", "console").option("-o, --output <dir>", "Output directory for results", "./test-results").option("--server-url <url>", "URL of running dev server (skips starting server)").option("-p, --port <port>", "Port for dev server", parseInt, 6006).option("--ci", "CI mode - non-interactive, exit with code 1 on failure").option("--list", "List available tests without running them").action(async (options) => {
|
|
6659
6584
|
try {
|
|
6660
6585
|
const { config, configDir } = await loadConfig(options.config);
|
|
6661
|
-
const { runTestCommand, listTests } = await import("./test-
|
|
6586
|
+
const { runTestCommand, listTests } = await import("./test-SI4NSHQX.js");
|
|
6662
6587
|
if (options.list) {
|
|
6663
6588
|
await listTests(config, configDir, {
|
|
6664
6589
|
component: options.component,
|