@fragments-sdk/cli 0.15.0 → 0.15.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/dist/{ai-client-I6MDWNYA.js → ai-client-LSLQGOMM.js} +1 -2
- package/dist/bin.js +565 -548
- package/dist/bin.js.map +1 -1
- package/dist/chunk-5JF26E55.js +1255 -0
- package/dist/chunk-5JF26E55.js.map +1 -0
- package/dist/{chunk-XJQ5BIWI.js → chunk-6SQPP47U.js} +30 -314
- package/dist/chunk-6SQPP47U.js.map +1 -0
- package/dist/{chunk-65WSVDV5.js → chunk-HQ6A6DTV.js} +1386 -1097
- package/dist/chunk-HQ6A6DTV.js.map +1 -0
- package/dist/chunk-MHIBEEW4.js +511 -0
- package/dist/chunk-MHIBEEW4.js.map +1 -0
- package/dist/{chunk-CZD3AD4Q.js → chunk-ONUP6Z4W.js} +17 -6
- package/dist/chunk-ONUP6Z4W.js.map +1 -0
- package/dist/{codebase-scanner-VOTPXRYW.js → codebase-scanner-MQHUZC2G.js} +1 -2
- package/dist/{converter-JLINP7CJ.js → converter-7XM3Y6NJ.js} +1 -2
- package/dist/{converter-JLINP7CJ.js.map → converter-7XM3Y6NJ.js.map} +1 -1
- package/dist/core/index.js +0 -1
- package/dist/create-JVAU3YKN.js +852 -0
- package/dist/create-JVAU3YKN.js.map +1 -0
- package/dist/doctor-BDPMYYE6.js +385 -0
- package/dist/doctor-BDPMYYE6.js.map +1 -0
- package/dist/{generate-A4FP5426.js → generate-PVOLUAAC.js} +3 -4
- package/dist/{generate-A4FP5426.js.map → generate-PVOLUAAC.js.map} +1 -1
- package/dist/{govern-scan-UCBZR6D6.js → govern-scan-OYFZYOQW.js} +142 -9
- package/dist/govern-scan-OYFZYOQW.js.map +1 -0
- package/dist/index.d.ts +2 -22
- package/dist/index.js +8 -7
- package/dist/index.js.map +1 -1
- package/dist/{init-HGSM35XA.js → init-SSGUSP7Z.js} +3 -4
- package/dist/{init-HGSM35XA.js.map → init-SSGUSP7Z.js.map} +1 -1
- package/dist/{init-cloud-MQ6GRJAZ.js → init-cloud-3DNKPWFB.js} +29 -4
- package/dist/{init-cloud-MQ6GRJAZ.js.map → init-cloud-3DNKPWFB.js.map} +1 -1
- package/dist/mcp-bin.js +1 -2
- package/dist/mcp-bin.js.map +1 -1
- package/dist/node-37AUE74M.js +65 -0
- package/dist/push-contracts-WY32TFP6.js +84 -0
- package/dist/push-contracts-WY32TFP6.js.map +1 -0
- package/dist/{scan-VNNKACG2.js → scan-PKSYSTRR.js} +5 -5
- package/dist/{scan-generate-TWRHNU5M.js → scan-generate-VY27PIOX.js} +8 -9
- package/dist/scan-generate-VY27PIOX.js.map +1 -0
- package/dist/{scanner-7LAZYPWZ.js → scanner-4KZNOXAK.js} +1 -2
- package/dist/{service-FHQU7YS7.js → service-QJGWUIVL.js} +16 -9
- package/dist/{snapshot-KQEQ6XHL.js → snapshot-WIJMEIFT.js} +1 -2
- package/dist/{snapshot-KQEQ6XHL.js.map → snapshot-WIJMEIFT.js.map} +1 -1
- package/dist/{static-viewer-63PG6FWY.js → static-viewer-7QIBQZRC.js} +1 -2
- package/dist/{test-UQYUCZIS.js → test-64Z5BKBA.js} +2 -3
- package/dist/{test-UQYUCZIS.js.map → test-64Z5BKBA.js.map} +1 -1
- package/dist/token-normalizer-TEPOVBPV.js +312 -0
- package/dist/token-normalizer-TEPOVBPV.js.map +1 -0
- package/dist/token-parser-32KOIOFN.js +22 -0
- package/dist/token-parser-32KOIOFN.js.map +1 -0
- package/dist/{tokens-6GYKDV6U.js → tokens-NZWFQIAB.js} +7 -7
- package/dist/{tokens-generate-VTZV5EEW.js → tokens-generate-5JQSJ27E.js} +1 -2
- package/dist/{tokens-generate-VTZV5EEW.js.map → tokens-generate-5JQSJ27E.js.map} +1 -1
- package/dist/tokens-push-HY3KO36V.js +148 -0
- package/dist/tokens-push-HY3KO36V.js.map +1 -0
- package/package.json +18 -16
- package/src/bin.ts +94 -1
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/label.contract.json +1 -1
- package/src/commands/__fixtures__/shadcn-label-wrapper/src/components/ui/primitive.contract.json +1 -1
- package/src/commands/__tests__/build-freshness.test.ts +231 -0
- package/src/commands/__tests__/create.test.ts +71 -0
- package/src/commands/__tests__/drift-sync.test.ts +1 -1
- package/src/commands/__tests__/govern.test.ts +258 -0
- package/src/commands/__tests__/init.test.ts +9 -1
- package/src/commands/__tests__/scan-generate.test.ts +1 -1
- package/src/commands/build.ts +54 -1
- package/src/commands/context.ts +1 -1
- package/src/commands/create.ts +590 -0
- package/src/commands/doctor.ts +3 -2
- package/src/commands/govern-scan.ts +187 -8
- package/src/commands/govern.ts +65 -2
- package/src/commands/init-cloud.ts +32 -4
- package/src/commands/push-contracts.ts +112 -0
- package/src/commands/scan-generate.ts +1 -1
- package/src/commands/scan.ts +13 -0
- package/src/commands/sync.ts +2 -2
- package/src/commands/tokens-push.ts +199 -0
- package/src/core/__tests__/token-resolver.test.ts +1 -1
- package/src/core/component-extractor.test.ts +1 -1
- package/src/core/drift-verifier.ts +1 -1
- package/src/core/extractor-adapter.ts +1 -1
- package/src/index.ts +3 -3
- package/src/migrate/fragment-to-contract.ts +2 -2
- package/src/service/index.ts +8 -0
- package/src/service/tailwind-v4-parser.ts +314 -0
- package/src/service/token-parser.ts +56 -0
- package/src/setup.ts +10 -39
- package/src/theme/__tests__/component-contrast.test.ts +2 -2
- package/src/theme/__tests__/serializer.test.ts +1 -1
- package/src/theme/generator.ts +30 -1
- package/src/theme/schema.ts +8 -0
- package/src/theme/serializer.ts +13 -9
- package/src/theme/types.ts +8 -0
- package/src/validators.ts +1 -2
- package/dist/chunk-65WSVDV5.js.map +0 -1
- package/dist/chunk-7WHVW72L.js +0 -2664
- package/dist/chunk-7WHVW72L.js.map +0 -1
- package/dist/chunk-CZD3AD4Q.js.map +0 -1
- package/dist/chunk-MN3TJ3D5.js +0 -695
- package/dist/chunk-MN3TJ3D5.js.map +0 -1
- package/dist/chunk-XJQ5BIWI.js.map +0 -1
- package/dist/chunk-Z7EY4VHE.js +0 -50
- package/dist/govern-scan-UCBZR6D6.js.map +0 -1
- package/dist/sass.node-4XJK6YBF.js +0 -130708
- package/dist/sass.node-4XJK6YBF.js.map +0 -1
- package/dist/scan-generate-TWRHNU5M.js.map +0 -1
- package/src/build.ts +0 -736
- package/src/core/auto-props.ts +0 -464
- package/src/core/component-extractor.ts +0 -1121
- package/src/core/token-resolver.ts +0 -155
- package/src/viewer/preview-adapter.ts +0 -116
- /package/dist/{ai-client-I6MDWNYA.js.map → ai-client-LSLQGOMM.js.map} +0 -0
- /package/dist/{chunk-Z7EY4VHE.js.map → codebase-scanner-MQHUZC2G.js.map} +0 -0
- /package/dist/{codebase-scanner-VOTPXRYW.js.map → node-37AUE74M.js.map} +0 -0
- /package/dist/{scan-VNNKACG2.js.map → scan-PKSYSTRR.js.map} +0 -0
- /package/dist/{scanner-7LAZYPWZ.js.map → scanner-4KZNOXAK.js.map} +0 -0
- /package/dist/{service-FHQU7YS7.js.map → service-QJGWUIVL.js.map} +0 -0
- /package/dist/{static-viewer-63PG6FWY.js.map → static-viewer-7QIBQZRC.js.map} +0 -0
- /package/dist/{tokens-6GYKDV6U.js.map → tokens-NZWFQIAB.js.map} +0 -0
package/dist/bin.js
CHANGED
|
@@ -1,43 +1,26 @@
|
|
|
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
|
+
setup
|
|
5
|
+
} from "./chunk-BJE3425I.js";
|
|
3
6
|
import {
|
|
4
7
|
scan
|
|
5
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ONUP6Z4W.js";
|
|
6
9
|
import {
|
|
7
10
|
createAIClient,
|
|
8
11
|
detectProvider,
|
|
9
12
|
getApiKey
|
|
10
13
|
} from "./chunk-SXTKFDCR.js";
|
|
11
14
|
import {
|
|
12
|
-
setup
|
|
13
|
-
} from "./chunk-BJE3425I.js";
|
|
14
|
-
import {
|
|
15
|
-
buildFragments,
|
|
16
|
-
buildFragmentsDir,
|
|
17
|
-
measureBundleSizes,
|
|
18
|
-
resolveComponentSourcePath,
|
|
19
15
|
runAnalyzeCommand,
|
|
20
16
|
runDiffCommand,
|
|
21
17
|
runScreenshotCommand,
|
|
22
|
-
toPerformanceData,
|
|
23
18
|
validateAll,
|
|
24
19
|
validateCoverage,
|
|
25
20
|
validateDrift,
|
|
26
21
|
validateSchema,
|
|
27
22
|
validateSnippets
|
|
28
|
-
} from "./chunk-
|
|
29
|
-
import {
|
|
30
|
-
createComponentExtractor
|
|
31
|
-
} from "./chunk-MN3TJ3D5.js";
|
|
32
|
-
import {
|
|
33
|
-
discoverFragmentFiles,
|
|
34
|
-
loadConfig,
|
|
35
|
-
loadFragmentFile,
|
|
36
|
-
parseFragmentFile
|
|
37
|
-
} from "./chunk-65WSVDV5.js";
|
|
38
|
-
import {
|
|
39
|
-
projectFields
|
|
40
|
-
} from "./chunk-T47OLCSF.js";
|
|
23
|
+
} from "./chunk-5JF26E55.js";
|
|
41
24
|
import {
|
|
42
25
|
FigmaClient,
|
|
43
26
|
StorageManager,
|
|
@@ -51,28 +34,38 @@ import {
|
|
|
51
34
|
parseAllStories,
|
|
52
35
|
renderAllComponentVariants,
|
|
53
36
|
shutdownSharedPool
|
|
54
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-6SQPP47U.js";
|
|
38
|
+
import {
|
|
39
|
+
projectFields
|
|
40
|
+
} from "./chunk-T47OLCSF.js";
|
|
55
41
|
import "./chunk-D2CDBRNU.js";
|
|
42
|
+
import {
|
|
43
|
+
discoverFragmentFiles,
|
|
44
|
+
loadConfig,
|
|
45
|
+
loadFragmentFile,
|
|
46
|
+
parseFragmentFile
|
|
47
|
+
} from "./chunk-HQ6A6DTV.js";
|
|
56
48
|
import {
|
|
57
49
|
BRAND,
|
|
58
50
|
budgetBar,
|
|
51
|
+
classifyComplexity,
|
|
59
52
|
formatBytes,
|
|
60
53
|
generateContext,
|
|
61
54
|
resolvePerformanceConfig
|
|
62
55
|
} from "./chunk-32LIWN2P.js";
|
|
56
|
+
import "./chunk-MHIBEEW4.js";
|
|
63
57
|
import {
|
|
64
58
|
getScanStats,
|
|
65
59
|
scanCodebase
|
|
66
60
|
} from "./chunk-QCN35LJU.js";
|
|
67
61
|
import "./chunk-7DZC4YEV.js";
|
|
68
|
-
import "./chunk-Z7EY4VHE.js";
|
|
69
62
|
|
|
70
63
|
// src/bin.ts
|
|
71
64
|
import { Command } from "commander";
|
|
72
|
-
import
|
|
65
|
+
import pc25 from "picocolors";
|
|
73
66
|
import { readFileSync } from "fs";
|
|
74
67
|
import { fileURLToPath } from "url";
|
|
75
|
-
import { dirname as
|
|
68
|
+
import { dirname as dirname6, join as join11 } from "path";
|
|
76
69
|
|
|
77
70
|
// src/commands/validate.ts
|
|
78
71
|
import pc from "picocolors";
|
|
@@ -168,6 +161,7 @@ function printDriftReport(result) {
|
|
|
168
161
|
|
|
169
162
|
// src/commands/build.ts
|
|
170
163
|
import pc2 from "picocolors";
|
|
164
|
+
import { buildFragments, buildFragmentsDir, getFragmentsJsonStatus } from "@fragments-sdk/compiler";
|
|
171
165
|
async function build(options = {}) {
|
|
172
166
|
if (options.fromSource) {
|
|
173
167
|
console.log(pc2.cyan(`
|
|
@@ -202,6 +196,50 @@ ${BRAND.name} Build
|
|
|
202
196
|
let registryPath;
|
|
203
197
|
let contextPath;
|
|
204
198
|
if (!options.registryOnly) {
|
|
199
|
+
const status = await getFragmentsJsonStatus(config, configDir, {
|
|
200
|
+
output: config.outFile,
|
|
201
|
+
configPath: options.config
|
|
202
|
+
});
|
|
203
|
+
if (options.check) {
|
|
204
|
+
if (status.missing || status.stale) {
|
|
205
|
+
const message = status.missing ? `${BRAND.outFile} is missing` : `${BRAND.outFile} is stale${status.reason ? ` (${status.reason})` : ""}`;
|
|
206
|
+
console.log(pc2.red(`\u2717 ${message}
|
|
207
|
+
`));
|
|
208
|
+
errors.push({
|
|
209
|
+
file: status.outputPath,
|
|
210
|
+
error: message
|
|
211
|
+
});
|
|
212
|
+
return {
|
|
213
|
+
success: false,
|
|
214
|
+
outputPath: status.outputPath,
|
|
215
|
+
errors
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
console.log(pc2.green(`\u2713 ${BRAND.outFile} is up to date`));
|
|
219
|
+
console.log(pc2.dim(` Output: ${status.outputPath}
|
|
220
|
+
`));
|
|
221
|
+
if (!options.registry) {
|
|
222
|
+
return {
|
|
223
|
+
success: true,
|
|
224
|
+
fragmentCount: Object.keys(status.data?.fragments ?? {}).length,
|
|
225
|
+
outputPath: status.outputPath,
|
|
226
|
+
errors
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (options.ifNeeded && !status.missing && !status.stale) {
|
|
231
|
+
console.log(pc2.green(`\u2713 ${BRAND.outFile} is up to date`));
|
|
232
|
+
console.log(pc2.dim(` Output: ${status.outputPath}
|
|
233
|
+
`));
|
|
234
|
+
if (!options.registry) {
|
|
235
|
+
return {
|
|
236
|
+
success: true,
|
|
237
|
+
fragmentCount: Object.keys(status.data?.fragments ?? {}).length,
|
|
238
|
+
outputPath: status.outputPath,
|
|
239
|
+
errors
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
}
|
|
205
243
|
console.log(pc2.dim("Compiling fragments...\n"));
|
|
206
244
|
const result = await buildFragments(config, configDir);
|
|
207
245
|
if (result.errors.length > 0) {
|
|
@@ -277,6 +315,7 @@ ${BRAND.name} Build
|
|
|
277
315
|
import { readFile } from "fs/promises";
|
|
278
316
|
import { resolve } from "path";
|
|
279
317
|
import pc3 from "picocolors";
|
|
318
|
+
import { buildFragments as buildFragments2 } from "@fragments-sdk/compiler";
|
|
280
319
|
async function context(options = {}) {
|
|
281
320
|
const {
|
|
282
321
|
format = "markdown",
|
|
@@ -293,7 +332,7 @@ async function context(options = {}) {
|
|
|
293
332
|
fragments = Object.values(data.fragments);
|
|
294
333
|
} else {
|
|
295
334
|
const { config, configDir } = await loadConfig(options.config);
|
|
296
|
-
const result = await
|
|
335
|
+
const result = await buildFragments2(config, configDir);
|
|
297
336
|
if (result.errors.length > 0 && result.fragmentCount === 0) {
|
|
298
337
|
console.error(pc3.red("Error: No fragments found. Run `fragments build` first or fix errors."));
|
|
299
338
|
return { success: false, tokenEstimate: 0 };
|
|
@@ -5488,13 +5527,259 @@ ${BRAND.name} Components (${fragments.length})
|
|
|
5488
5527
|
// src/commands/perf.ts
|
|
5489
5528
|
import pc21 from "picocolors";
|
|
5490
5529
|
import { readFile as readFile10, writeFile as writeFile8 } from "fs/promises";
|
|
5491
|
-
import { resolve as
|
|
5530
|
+
import { resolve as resolve11 } from "path";
|
|
5531
|
+
|
|
5532
|
+
// src/core/bundle-measurer.ts
|
|
5533
|
+
import { build as build2 } from "esbuild";
|
|
5534
|
+
import { gzipSync } from "zlib";
|
|
5535
|
+
import { resolve as resolve10, dirname as dirname4, join as join10, basename } from "path";
|
|
5536
|
+
import { existsSync as existsSync3 } from "fs";
|
|
5537
|
+
function resolveEntryPoint(fragmentFilePath, configDir) {
|
|
5538
|
+
const absPath = resolve10(configDir, fragmentFilePath);
|
|
5539
|
+
const dir = dirname4(absPath);
|
|
5540
|
+
const candidates = ["index.tsx", "index.ts", "index.jsx", "index.js"];
|
|
5541
|
+
for (const candidate of candidates) {
|
|
5542
|
+
const path = join10(dir, candidate);
|
|
5543
|
+
if (existsSync3(path)) return path;
|
|
5544
|
+
}
|
|
5545
|
+
return null;
|
|
5546
|
+
}
|
|
5547
|
+
function labelForPath(filePath) {
|
|
5548
|
+
const lastNmIdx = filePath.lastIndexOf("node_modules/");
|
|
5549
|
+
if (lastNmIdx >= 0) {
|
|
5550
|
+
const afterNm = filePath.slice(lastNmIdx + "node_modules/".length);
|
|
5551
|
+
if (afterNm.startsWith("@")) {
|
|
5552
|
+
const parts = afterNm.split("/");
|
|
5553
|
+
return parts.slice(0, 2).join("/");
|
|
5554
|
+
}
|
|
5555
|
+
return afterNm.split("/")[0];
|
|
5556
|
+
}
|
|
5557
|
+
const componentsIdx = filePath.indexOf("components/");
|
|
5558
|
+
if (componentsIdx >= 0) {
|
|
5559
|
+
const afterComponents = filePath.slice(componentsIdx + "components/".length);
|
|
5560
|
+
const componentName = afterComponents.split("/")[0];
|
|
5561
|
+
return componentName;
|
|
5562
|
+
}
|
|
5563
|
+
const srcIdx = filePath.indexOf("src/");
|
|
5564
|
+
if (srcIdx >= 0) return filePath.slice(srcIdx);
|
|
5565
|
+
return filePath;
|
|
5566
|
+
}
|
|
5567
|
+
function groupImportsByDirectDep(metafile, entryPoint) {
|
|
5568
|
+
const inputs = metafile.inputs;
|
|
5569
|
+
const outputKey = Object.keys(metafile.outputs)[0];
|
|
5570
|
+
const outputMeta = outputKey ? metafile.outputs[outputKey] : void 0;
|
|
5571
|
+
if (!outputMeta?.inputs) return [];
|
|
5572
|
+
const bytesMap = /* @__PURE__ */ new Map();
|
|
5573
|
+
for (const [path, info] of Object.entries(outputMeta.inputs)) {
|
|
5574
|
+
if (info.bytesInOutput > 0) {
|
|
5575
|
+
bytesMap.set(path, info.bytesInOutput);
|
|
5576
|
+
}
|
|
5577
|
+
}
|
|
5578
|
+
let entryKey;
|
|
5579
|
+
for (const key of Object.keys(inputs)) {
|
|
5580
|
+
if (key === entryPoint || entryPoint.endsWith(key) || key.endsWith(basename(entryPoint))) {
|
|
5581
|
+
const entryDir = dirname4(entryPoint);
|
|
5582
|
+
if (key.includes(basename(entryDir))) {
|
|
5583
|
+
entryKey = key;
|
|
5584
|
+
break;
|
|
5585
|
+
}
|
|
5586
|
+
}
|
|
5587
|
+
}
|
|
5588
|
+
if (!entryKey) {
|
|
5589
|
+
const entryBasename = basename(dirname4(entryPoint));
|
|
5590
|
+
for (const key of Object.keys(inputs)) {
|
|
5591
|
+
if (key.includes(`/${entryBasename}/index.`)) {
|
|
5592
|
+
entryKey = key;
|
|
5593
|
+
break;
|
|
5594
|
+
}
|
|
5595
|
+
}
|
|
5596
|
+
}
|
|
5597
|
+
if (!entryKey || !inputs[entryKey]) {
|
|
5598
|
+
return groupByPackage(bytesMap);
|
|
5599
|
+
}
|
|
5600
|
+
const directImports = inputs[entryKey].imports.map((imp) => imp.path).filter((p) => inputs[p]);
|
|
5601
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
5602
|
+
claimed.add(entryKey);
|
|
5603
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
5604
|
+
for (const directPath of directImports) {
|
|
5605
|
+
if (claimed.has(directPath)) continue;
|
|
5606
|
+
const queue = [directPath];
|
|
5607
|
+
const reachable = /* @__PURE__ */ new Set();
|
|
5608
|
+
while (queue.length > 0) {
|
|
5609
|
+
const current = queue.pop();
|
|
5610
|
+
if (reachable.has(current) || claimed.has(current)) continue;
|
|
5611
|
+
reachable.add(current);
|
|
5612
|
+
claimed.add(current);
|
|
5613
|
+
const entry = inputs[current];
|
|
5614
|
+
if (entry?.imports) {
|
|
5615
|
+
for (const imp of entry.imports) {
|
|
5616
|
+
if (inputs[imp.path] && !claimed.has(imp.path)) {
|
|
5617
|
+
queue.push(imp.path);
|
|
5618
|
+
}
|
|
5619
|
+
}
|
|
5620
|
+
}
|
|
5621
|
+
}
|
|
5622
|
+
let totalBytes = 0;
|
|
5623
|
+
for (const path of reachable) {
|
|
5624
|
+
totalBytes += bytesMap.get(path) ?? 0;
|
|
5625
|
+
}
|
|
5626
|
+
if (totalBytes > 0) {
|
|
5627
|
+
const label = labelForPath(directPath);
|
|
5628
|
+
const existing = groupMap.get(label);
|
|
5629
|
+
if (existing) {
|
|
5630
|
+
existing.bytes += totalBytes;
|
|
5631
|
+
} else {
|
|
5632
|
+
const entry = { path: label, bytes: totalBytes };
|
|
5633
|
+
groupMap.set(label, entry);
|
|
5634
|
+
}
|
|
5635
|
+
}
|
|
5636
|
+
}
|
|
5637
|
+
const entryLabel = labelForPath(entryKey);
|
|
5638
|
+
const selfLabel = entryLabel + " (self)";
|
|
5639
|
+
let selfBytes = bytesMap.get(entryKey) ?? 0;
|
|
5640
|
+
const siblingGroup = groupMap.get(entryLabel);
|
|
5641
|
+
if (siblingGroup) {
|
|
5642
|
+
selfBytes += siblingGroup.bytes;
|
|
5643
|
+
groupMap.delete(entryLabel);
|
|
5644
|
+
}
|
|
5645
|
+
if (selfBytes > 0) {
|
|
5646
|
+
groupMap.set(selfLabel, { path: selfLabel, bytes: selfBytes });
|
|
5647
|
+
}
|
|
5648
|
+
let unclaimedBytes = 0;
|
|
5649
|
+
for (const [path, bytes] of bytesMap) {
|
|
5650
|
+
if (!claimed.has(path)) unclaimedBytes += bytes;
|
|
5651
|
+
}
|
|
5652
|
+
if (unclaimedBytes > 0) {
|
|
5653
|
+
groupMap.set("(other)", { path: "(other)", bytes: unclaimedBytes });
|
|
5654
|
+
}
|
|
5655
|
+
return [...groupMap.values()].sort((a, b) => b.bytes - a.bytes);
|
|
5656
|
+
}
|
|
5657
|
+
function groupByPackage(bytesMap) {
|
|
5658
|
+
const groups = /* @__PURE__ */ new Map();
|
|
5659
|
+
for (const [path, bytes] of bytesMap) {
|
|
5660
|
+
const label = labelForPath(path);
|
|
5661
|
+
const key = label.includes("/") && !label.startsWith("components/") ? label.split("/").slice(0, label.startsWith("@") ? 2 : 1).join("/") : label;
|
|
5662
|
+
groups.set(key, (groups.get(key) ?? 0) + bytes);
|
|
5663
|
+
}
|
|
5664
|
+
return [...groups.entries()].map(([path, bytes]) => ({ path, bytes })).sort((a, b) => b.bytes - a.bytes);
|
|
5665
|
+
}
|
|
5666
|
+
async function measureSingleComponent(entryPoint, name) {
|
|
5667
|
+
const result = await build2({
|
|
5668
|
+
entryPoints: [entryPoint],
|
|
5669
|
+
bundle: true,
|
|
5670
|
+
write: false,
|
|
5671
|
+
minify: true,
|
|
5672
|
+
metafile: true,
|
|
5673
|
+
format: "esm",
|
|
5674
|
+
target: "es2020",
|
|
5675
|
+
platform: "browser",
|
|
5676
|
+
treeShaking: true,
|
|
5677
|
+
external: [
|
|
5678
|
+
"react",
|
|
5679
|
+
"react-dom",
|
|
5680
|
+
"react/jsx-runtime",
|
|
5681
|
+
"react/jsx-dev-runtime",
|
|
5682
|
+
// Optional peer deps — excluded from measurement
|
|
5683
|
+
"recharts",
|
|
5684
|
+
"shiki",
|
|
5685
|
+
"react-day-picker",
|
|
5686
|
+
"@tanstack/react-table",
|
|
5687
|
+
"date-fns",
|
|
5688
|
+
"@base-ui-components/*",
|
|
5689
|
+
"@base-ui/react/*"
|
|
5690
|
+
],
|
|
5691
|
+
loader: {
|
|
5692
|
+
".scss": "empty",
|
|
5693
|
+
".css": "empty",
|
|
5694
|
+
".svg": "empty",
|
|
5695
|
+
".png": "empty",
|
|
5696
|
+
".jpg": "empty",
|
|
5697
|
+
".gif": "empty",
|
|
5698
|
+
".woff": "empty",
|
|
5699
|
+
".woff2": "empty",
|
|
5700
|
+
".ttf": "empty",
|
|
5701
|
+
".eot": "empty"
|
|
5702
|
+
},
|
|
5703
|
+
logLevel: "silent"
|
|
5704
|
+
});
|
|
5705
|
+
const output = result.outputFiles[0];
|
|
5706
|
+
const rawBytes = output.contents.byteLength;
|
|
5707
|
+
const gzipBytes = gzipSync(output.contents).byteLength;
|
|
5708
|
+
const imports = result.metafile ? groupImportsByDirectDep(result.metafile, entryPoint) : void 0;
|
|
5709
|
+
return { name, rawBytes, gzipBytes, imports };
|
|
5710
|
+
}
|
|
5711
|
+
async function measureBundleSizes(fragments, configDir, options = {}) {
|
|
5712
|
+
const concurrency = options.concurrency ?? 4;
|
|
5713
|
+
const measurements = /* @__PURE__ */ new Map();
|
|
5714
|
+
const errors = [];
|
|
5715
|
+
const start = Date.now();
|
|
5716
|
+
const entries = [];
|
|
5717
|
+
for (const [name, fragment] of Object.entries(fragments)) {
|
|
5718
|
+
const entryPoint = resolveEntryPoint(fragment.filePath, configDir);
|
|
5719
|
+
if (entryPoint) {
|
|
5720
|
+
entries.push({ name, entryPoint });
|
|
5721
|
+
} else {
|
|
5722
|
+
errors.push({
|
|
5723
|
+
name,
|
|
5724
|
+
error: `Could not resolve entry point from ${fragment.filePath}`
|
|
5725
|
+
});
|
|
5726
|
+
}
|
|
5727
|
+
}
|
|
5728
|
+
let completed = 0;
|
|
5729
|
+
for (let i = 0; i < entries.length; i += concurrency) {
|
|
5730
|
+
const batch = entries.slice(i, i + concurrency);
|
|
5731
|
+
const results = await Promise.allSettled(
|
|
5732
|
+
batch.map(
|
|
5733
|
+
({ name, entryPoint }) => measureSingleComponent(entryPoint, name)
|
|
5734
|
+
)
|
|
5735
|
+
);
|
|
5736
|
+
for (let j = 0; j < results.length; j++) {
|
|
5737
|
+
const result = results[j];
|
|
5738
|
+
const { name } = batch[j];
|
|
5739
|
+
completed++;
|
|
5740
|
+
if (result.status === "fulfilled") {
|
|
5741
|
+
measurements.set(name, result.value);
|
|
5742
|
+
} else {
|
|
5743
|
+
errors.push({
|
|
5744
|
+
name,
|
|
5745
|
+
error: result.reason instanceof Error ? result.reason.message : String(result.reason)
|
|
5746
|
+
});
|
|
5747
|
+
}
|
|
5748
|
+
options.onProgress?.(completed, entries.length, name);
|
|
5749
|
+
}
|
|
5750
|
+
}
|
|
5751
|
+
return {
|
|
5752
|
+
measurements,
|
|
5753
|
+
errors,
|
|
5754
|
+
elapsed: Date.now() - start
|
|
5755
|
+
};
|
|
5756
|
+
}
|
|
5757
|
+
function toPerformanceData(measurement, config, contractBudget) {
|
|
5758
|
+
const budget = contractBudget ?? config.budgets.bundleSize;
|
|
5759
|
+
const budgetPercent = Math.round(measurement.gzipBytes / budget * 100);
|
|
5760
|
+
const imports = measurement.imports?.slice(0, 10).map((imp) => ({
|
|
5761
|
+
path: imp.path,
|
|
5762
|
+
bytes: imp.bytes,
|
|
5763
|
+
percent: Math.round(imp.bytes / measurement.rawBytes * 100)
|
|
5764
|
+
}));
|
|
5765
|
+
return {
|
|
5766
|
+
bundleSize: measurement.gzipBytes,
|
|
5767
|
+
rawSize: measurement.rawBytes,
|
|
5768
|
+
complexity: classifyComplexity(measurement.gzipBytes),
|
|
5769
|
+
budgetPercent,
|
|
5770
|
+
overBudget: budgetPercent > 100,
|
|
5771
|
+
measuredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5772
|
+
...imports && imports.length > 0 ? { imports } : {}
|
|
5773
|
+
};
|
|
5774
|
+
}
|
|
5775
|
+
|
|
5776
|
+
// src/commands/perf.ts
|
|
5492
5777
|
async function perf(options) {
|
|
5493
5778
|
const { config: configPath, json, component, concurrency, detail } = options;
|
|
5494
5779
|
const shouldWrite = options.write !== false;
|
|
5495
5780
|
const { config, configDir } = await loadConfig(configPath);
|
|
5496
5781
|
const perfConfig = resolvePerformanceConfig(config.performance ?? "standard");
|
|
5497
|
-
const outFile =
|
|
5782
|
+
const outFile = resolve11(configDir, config.outFile ?? BRAND.outFile);
|
|
5498
5783
|
let data;
|
|
5499
5784
|
try {
|
|
5500
5785
|
data = JSON.parse(await readFile10(outFile, "utf-8"));
|
|
@@ -5636,418 +5921,45 @@ ${pc21.bold("Summary")}`);
|
|
|
5636
5921
|
};
|
|
5637
5922
|
}
|
|
5638
5923
|
|
|
5639
|
-
// src/commands/doctor.ts
|
|
5640
|
-
import { readFile as readFile11, access as access3 } from "fs/promises";
|
|
5641
|
-
import { join as join10, resolve as resolve11 } from "path";
|
|
5642
|
-
import pc22 from "picocolors";
|
|
5643
|
-
var VALID_NEUTRALS = ["stone", "ice", "earth", "sand", "fire"];
|
|
5644
|
-
var VALID_DENSITIES = ["compact", "default", "relaxed"];
|
|
5645
|
-
var VALID_RADII = ["sharp", "subtle", "default", "rounded", "pill"];
|
|
5646
|
-
async function checkPackageInstalled(root) {
|
|
5647
|
-
try {
|
|
5648
|
-
const pkgPath = join10(root, "package.json");
|
|
5649
|
-
const content = await readFile11(pkgPath, "utf-8");
|
|
5650
|
-
const pkg2 = JSON.parse(content);
|
|
5651
|
-
const allDeps = { ...pkg2.dependencies, ...pkg2.devDependencies };
|
|
5652
|
-
if (allDeps["@fragments-sdk/ui"]) {
|
|
5653
|
-
return {
|
|
5654
|
-
name: "Package installed",
|
|
5655
|
-
status: "pass",
|
|
5656
|
-
message: `@fragments-sdk/ui ${allDeps["@fragments-sdk/ui"]} found in dependencies`
|
|
5657
|
-
};
|
|
5658
|
-
}
|
|
5659
|
-
return {
|
|
5660
|
-
name: "Package installed",
|
|
5661
|
-
status: "fail",
|
|
5662
|
-
message: "@fragments-sdk/ui not found in package.json",
|
|
5663
|
-
fix: "npm install @fragments-sdk/ui"
|
|
5664
|
-
};
|
|
5665
|
-
} catch {
|
|
5666
|
-
return {
|
|
5667
|
-
name: "Package installed",
|
|
5668
|
-
status: "fail",
|
|
5669
|
-
message: "No package.json found"
|
|
5670
|
-
};
|
|
5671
|
-
}
|
|
5672
|
-
}
|
|
5673
|
-
async function checkStylesImport(root) {
|
|
5674
|
-
const entryPatterns = [
|
|
5675
|
-
"src/main.tsx",
|
|
5676
|
-
"src/main.ts",
|
|
5677
|
-
"src/index.tsx",
|
|
5678
|
-
"src/index.ts",
|
|
5679
|
-
"src/App.tsx",
|
|
5680
|
-
"src/App.ts",
|
|
5681
|
-
"app/layout.tsx",
|
|
5682
|
-
"app/layout.ts",
|
|
5683
|
-
"src/app/layout.tsx",
|
|
5684
|
-
"src/app/layout.ts",
|
|
5685
|
-
"app/root.tsx",
|
|
5686
|
-
"pages/_app.tsx",
|
|
5687
|
-
"pages/_app.ts"
|
|
5688
|
-
];
|
|
5689
|
-
for (const pattern of entryPatterns) {
|
|
5690
|
-
try {
|
|
5691
|
-
const content = await readFile11(join10(root, pattern), "utf-8");
|
|
5692
|
-
if (content.includes("@fragments-sdk/ui/styles")) {
|
|
5693
|
-
return {
|
|
5694
|
-
name: "Styles import",
|
|
5695
|
-
status: "pass",
|
|
5696
|
-
message: `Found styles import in ${pattern}`
|
|
5697
|
-
};
|
|
5698
|
-
}
|
|
5699
|
-
if (content.includes("@fragments-sdk/ui/globals")) {
|
|
5700
|
-
return {
|
|
5701
|
-
name: "Styles import",
|
|
5702
|
-
status: "warn",
|
|
5703
|
-
message: `${pattern} uses deprecated '@fragments-sdk/ui/globals'. Use '@fragments-sdk/ui/styles' instead`,
|
|
5704
|
-
fix: `Replace '@fragments-sdk/ui/globals' with '@fragments-sdk/ui/styles' in ${pattern}`
|
|
5705
|
-
};
|
|
5706
|
-
}
|
|
5707
|
-
} catch {
|
|
5708
|
-
}
|
|
5709
|
-
}
|
|
5710
|
-
const scssPatterns = [
|
|
5711
|
-
"src/styles/globals.scss",
|
|
5712
|
-
"src/globals.scss",
|
|
5713
|
-
"styles/globals.scss",
|
|
5714
|
-
"app/globals.scss",
|
|
5715
|
-
"src/app/globals.scss",
|
|
5716
|
-
"app/styles/globals.scss"
|
|
5717
|
-
];
|
|
5718
|
-
for (const pattern of scssPatterns) {
|
|
5719
|
-
try {
|
|
5720
|
-
const content = await readFile11(join10(root, pattern), "utf-8");
|
|
5721
|
-
if (content.includes("@fragments-sdk/ui/styles")) {
|
|
5722
|
-
return {
|
|
5723
|
-
name: "Styles import",
|
|
5724
|
-
status: "pass",
|
|
5725
|
-
message: `Found SCSS @use import in ${pattern}`
|
|
5726
|
-
};
|
|
5727
|
-
}
|
|
5728
|
-
} catch {
|
|
5729
|
-
}
|
|
5730
|
-
}
|
|
5731
|
-
return {
|
|
5732
|
-
name: "Styles import",
|
|
5733
|
-
status: "fail",
|
|
5734
|
-
message: "No @fragments-sdk/ui/styles import found in entry files",
|
|
5735
|
-
fix: "Add `import '@fragments-sdk/ui/styles'` to your app's entry file"
|
|
5736
|
-
};
|
|
5737
|
-
}
|
|
5738
|
-
async function checkThemeProvider(root) {
|
|
5739
|
-
const providerPatterns = [
|
|
5740
|
-
"src/main.tsx",
|
|
5741
|
-
"src/App.tsx",
|
|
5742
|
-
"src/providers.tsx",
|
|
5743
|
-
"app/layout.tsx",
|
|
5744
|
-
"app/providers.tsx",
|
|
5745
|
-
"src/app/layout.tsx",
|
|
5746
|
-
"src/app/providers.tsx",
|
|
5747
|
-
"app/root.tsx",
|
|
5748
|
-
"pages/_app.tsx"
|
|
5749
|
-
];
|
|
5750
|
-
for (const pattern of providerPatterns) {
|
|
5751
|
-
try {
|
|
5752
|
-
const content = await readFile11(join10(root, pattern), "utf-8");
|
|
5753
|
-
if (content.includes("ThemeProvider")) {
|
|
5754
|
-
if (content.includes("defaultTheme=") || content.includes("defaultTheme =")) {
|
|
5755
|
-
return {
|
|
5756
|
-
name: "ThemeProvider",
|
|
5757
|
-
status: "warn",
|
|
5758
|
-
message: `${pattern} uses deprecated 'defaultTheme' prop. Use 'defaultMode' instead`,
|
|
5759
|
-
fix: `Replace 'defaultTheme' with 'defaultMode' in ${pattern}`
|
|
5760
|
-
};
|
|
5761
|
-
}
|
|
5762
|
-
return {
|
|
5763
|
-
name: "ThemeProvider",
|
|
5764
|
-
status: "pass",
|
|
5765
|
-
message: `ThemeProvider found in ${pattern}`
|
|
5766
|
-
};
|
|
5767
|
-
}
|
|
5768
|
-
} catch {
|
|
5769
|
-
}
|
|
5770
|
-
}
|
|
5771
|
-
return {
|
|
5772
|
-
name: "ThemeProvider",
|
|
5773
|
-
status: "warn",
|
|
5774
|
-
message: "ThemeProvider not found in common entry files (optional but recommended)",
|
|
5775
|
-
fix: 'Wrap your app with <ThemeProvider defaultMode="system">'
|
|
5776
|
-
};
|
|
5777
|
-
}
|
|
5778
|
-
async function checkScssSeeds(root) {
|
|
5779
|
-
const checks = [];
|
|
5780
|
-
const scssPatterns = [
|
|
5781
|
-
"src/styles/globals.scss",
|
|
5782
|
-
"src/globals.scss",
|
|
5783
|
-
"styles/globals.scss",
|
|
5784
|
-
"app/globals.scss",
|
|
5785
|
-
"src/app/globals.scss",
|
|
5786
|
-
"app/styles/globals.scss"
|
|
5787
|
-
];
|
|
5788
|
-
for (const pattern of scssPatterns) {
|
|
5789
|
-
try {
|
|
5790
|
-
const content = await readFile11(join10(root, pattern), "utf-8");
|
|
5791
|
-
if (!content.includes("@fragments-sdk/ui/styles")) continue;
|
|
5792
|
-
const standalonePattern = /^\$fui-\w+:\s*.+;$/m;
|
|
5793
|
-
if (standalonePattern.test(content) && !content.includes("@use")) {
|
|
5794
|
-
checks.push({
|
|
5795
|
-
name: "SCSS syntax",
|
|
5796
|
-
status: "fail",
|
|
5797
|
-
message: `${pattern} uses standalone $fui- variables. Must use @use...with() syntax`,
|
|
5798
|
-
fix: "@use '@fragments-sdk/ui/styles' with ($fui-brand: #0066ff);"
|
|
5799
|
-
});
|
|
5800
|
-
}
|
|
5801
|
-
const neutralMatch = content.match(/\$fui-neutral:\s*"([^"]+)"/);
|
|
5802
|
-
if (neutralMatch && !VALID_NEUTRALS.includes(neutralMatch[1])) {
|
|
5803
|
-
checks.push({
|
|
5804
|
-
name: "SCSS seed: neutral",
|
|
5805
|
-
status: "fail",
|
|
5806
|
-
message: `Invalid $fui-neutral: "${neutralMatch[1]}" in ${pattern}`,
|
|
5807
|
-
fix: `Valid neutrals: ${VALID_NEUTRALS.join(", ")}`
|
|
5808
|
-
});
|
|
5809
|
-
}
|
|
5810
|
-
const densityMatch = content.match(/\$fui-density:\s*"([^"]+)"/);
|
|
5811
|
-
if (densityMatch && !VALID_DENSITIES.includes(densityMatch[1])) {
|
|
5812
|
-
checks.push({
|
|
5813
|
-
name: "SCSS seed: density",
|
|
5814
|
-
status: "fail",
|
|
5815
|
-
message: `Invalid $fui-density: "${densityMatch[1]}" in ${pattern}`,
|
|
5816
|
-
fix: `Valid densities: ${VALID_DENSITIES.join(", ")}`
|
|
5817
|
-
});
|
|
5818
|
-
}
|
|
5819
|
-
const radiusMatch = content.match(/\$fui-radius-style:\s*"([^"]+)"/);
|
|
5820
|
-
if (radiusMatch && !VALID_RADII.includes(radiusMatch[1])) {
|
|
5821
|
-
checks.push({
|
|
5822
|
-
name: "SCSS seed: radius-style",
|
|
5823
|
-
status: "fail",
|
|
5824
|
-
message: `Invalid $fui-radius-style: "${radiusMatch[1]}" in ${pattern}`,
|
|
5825
|
-
fix: `Valid radius styles: ${VALID_RADII.join(", ")}`
|
|
5826
|
-
});
|
|
5827
|
-
}
|
|
5828
|
-
const brandMatch = content.match(/\$fui-brand:\s*(#[0-9a-fA-F]+)/);
|
|
5829
|
-
if (brandMatch) {
|
|
5830
|
-
const hex = brandMatch[1];
|
|
5831
|
-
if (!/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(hex)) {
|
|
5832
|
-
checks.push({
|
|
5833
|
-
name: "SCSS seed: brand",
|
|
5834
|
-
status: "fail",
|
|
5835
|
-
message: `Invalid $fui-brand color: "${hex}" in ${pattern}`,
|
|
5836
|
-
fix: "Must be a valid hex color (e.g., #0066ff)"
|
|
5837
|
-
});
|
|
5838
|
-
}
|
|
5839
|
-
}
|
|
5840
|
-
if (checks.length === 0) {
|
|
5841
|
-
checks.push({
|
|
5842
|
-
name: "SCSS seeds",
|
|
5843
|
-
status: "pass",
|
|
5844
|
-
message: `Seed values in ${pattern} are valid`
|
|
5845
|
-
});
|
|
5846
|
-
}
|
|
5847
|
-
return checks;
|
|
5848
|
-
} catch {
|
|
5849
|
-
}
|
|
5850
|
-
}
|
|
5851
|
-
checks.push({
|
|
5852
|
-
name: "SCSS seeds",
|
|
5853
|
-
status: "pass",
|
|
5854
|
-
message: "No custom SCSS seeds configured (using defaults)"
|
|
5855
|
-
});
|
|
5856
|
-
return checks;
|
|
5857
|
-
}
|
|
5858
|
-
async function checkPeerDeps(root) {
|
|
5859
|
-
const checks = [];
|
|
5860
|
-
try {
|
|
5861
|
-
const pkgPath = join10(root, "package.json");
|
|
5862
|
-
const content = await readFile11(pkgPath, "utf-8");
|
|
5863
|
-
const pkg2 = JSON.parse(content);
|
|
5864
|
-
const allDeps = { ...pkg2.dependencies, ...pkg2.devDependencies };
|
|
5865
|
-
if (!allDeps["react"]) {
|
|
5866
|
-
checks.push({
|
|
5867
|
-
name: "Peer dep: react",
|
|
5868
|
-
status: "fail",
|
|
5869
|
-
message: "react not found in dependencies (required)",
|
|
5870
|
-
fix: "npm install react react-dom"
|
|
5871
|
-
});
|
|
5872
|
-
} else {
|
|
5873
|
-
checks.push({
|
|
5874
|
-
name: "Peer dep: react",
|
|
5875
|
-
status: "pass",
|
|
5876
|
-
message: `react ${allDeps["react"]} installed`
|
|
5877
|
-
});
|
|
5878
|
-
}
|
|
5879
|
-
if (!allDeps["sass"]) {
|
|
5880
|
-
checks.push({
|
|
5881
|
-
name: "Peer dep: sass",
|
|
5882
|
-
status: "warn",
|
|
5883
|
-
message: "sass not installed (needed for custom SCSS theming)",
|
|
5884
|
-
fix: "npm install -D sass"
|
|
5885
|
-
});
|
|
5886
|
-
}
|
|
5887
|
-
const optionalPeers = [
|
|
5888
|
-
{ pkg: "recharts", components: "Chart" },
|
|
5889
|
-
{ pkg: "shiki", components: "CodeBlock" },
|
|
5890
|
-
{ pkg: "react-day-picker", components: "DatePicker" },
|
|
5891
|
-
{ pkg: "@tanstack/react-table", components: "DataTable" }
|
|
5892
|
-
];
|
|
5893
|
-
for (const peer of optionalPeers) {
|
|
5894
|
-
if (allDeps[peer.pkg]) {
|
|
5895
|
-
checks.push({
|
|
5896
|
-
name: `Optional dep: ${peer.pkg}`,
|
|
5897
|
-
status: "pass",
|
|
5898
|
-
message: `${peer.pkg} installed (enables ${peer.components})`
|
|
5899
|
-
});
|
|
5900
|
-
}
|
|
5901
|
-
}
|
|
5902
|
-
} catch {
|
|
5903
|
-
checks.push({
|
|
5904
|
-
name: "Peer dependencies",
|
|
5905
|
-
status: "fail",
|
|
5906
|
-
message: "Could not read package.json"
|
|
5907
|
-
});
|
|
5908
|
-
}
|
|
5909
|
-
return checks;
|
|
5910
|
-
}
|
|
5911
|
-
async function checkMcpConfig(root) {
|
|
5912
|
-
const mcpConfigPaths = [
|
|
5913
|
-
".mcp.json",
|
|
5914
|
-
".cursor/mcp.json",
|
|
5915
|
-
".vscode/mcp.json"
|
|
5916
|
-
];
|
|
5917
|
-
for (const configPath of mcpConfigPaths) {
|
|
5918
|
-
try {
|
|
5919
|
-
const fullPath = join10(root, configPath);
|
|
5920
|
-
const content = await readFile11(fullPath, "utf-8");
|
|
5921
|
-
const config = JSON.parse(content);
|
|
5922
|
-
const servers = config.mcpServers || config.servers || {};
|
|
5923
|
-
const hasFragments = Object.values(servers).some((server) => {
|
|
5924
|
-
const s = server;
|
|
5925
|
-
return s.args?.some((arg) => arg.includes("@fragments-sdk/mcp")) || s.command?.includes("fragments");
|
|
5926
|
-
});
|
|
5927
|
-
if (hasFragments) {
|
|
5928
|
-
return {
|
|
5929
|
-
name: "MCP configuration",
|
|
5930
|
-
status: "pass",
|
|
5931
|
-
message: `Fragments MCP server configured in ${configPath}`
|
|
5932
|
-
};
|
|
5933
|
-
}
|
|
5934
|
-
} catch {
|
|
5935
|
-
}
|
|
5936
|
-
}
|
|
5937
|
-
return {
|
|
5938
|
-
name: "MCP configuration",
|
|
5939
|
-
status: "warn",
|
|
5940
|
-
message: "No Fragments MCP server configuration found (optional)",
|
|
5941
|
-
fix: "Run `fragments init` or add @fragments-sdk/mcp to your MCP config"
|
|
5942
|
-
};
|
|
5943
|
-
}
|
|
5944
|
-
async function checkTypeScript(root) {
|
|
5945
|
-
try {
|
|
5946
|
-
const tsconfigPath = join10(root, "tsconfig.json");
|
|
5947
|
-
await access3(tsconfigPath);
|
|
5948
|
-
return {
|
|
5949
|
-
name: "TypeScript",
|
|
5950
|
-
status: "pass",
|
|
5951
|
-
message: "tsconfig.json found"
|
|
5952
|
-
};
|
|
5953
|
-
} catch {
|
|
5954
|
-
return {
|
|
5955
|
-
name: "TypeScript",
|
|
5956
|
-
status: "warn",
|
|
5957
|
-
message: "No tsconfig.json found (TypeScript recommended but not required)"
|
|
5958
|
-
};
|
|
5959
|
-
}
|
|
5960
|
-
}
|
|
5961
|
-
async function doctor(options = {}) {
|
|
5962
|
-
const root = resolve11(options.root ?? process.cwd());
|
|
5963
|
-
const checks = [];
|
|
5964
|
-
if (!options.json) {
|
|
5965
|
-
console.log(pc22.cyan(`
|
|
5966
|
-
${BRAND.name} Doctor
|
|
5967
|
-
`));
|
|
5968
|
-
console.log(pc22.dim(`Checking project at ${root}
|
|
5969
|
-
`));
|
|
5970
|
-
}
|
|
5971
|
-
checks.push(await checkPackageInstalled(root));
|
|
5972
|
-
checks.push(await checkStylesImport(root));
|
|
5973
|
-
checks.push(await checkThemeProvider(root));
|
|
5974
|
-
checks.push(...await checkScssSeeds(root));
|
|
5975
|
-
checks.push(...await checkPeerDeps(root));
|
|
5976
|
-
checks.push(await checkMcpConfig(root));
|
|
5977
|
-
checks.push(await checkTypeScript(root));
|
|
5978
|
-
const passed = checks.filter((c) => c.status === "pass").length;
|
|
5979
|
-
const warned = checks.filter((c) => c.status === "warn").length;
|
|
5980
|
-
const failed = checks.filter((c) => c.status === "fail").length;
|
|
5981
|
-
const result = {
|
|
5982
|
-
success: failed === 0,
|
|
5983
|
-
checks,
|
|
5984
|
-
passed,
|
|
5985
|
-
warned,
|
|
5986
|
-
failed
|
|
5987
|
-
};
|
|
5988
|
-
if (options.json) {
|
|
5989
|
-
console.log(JSON.stringify(result, null, 2));
|
|
5990
|
-
} else {
|
|
5991
|
-
for (const check of checks) {
|
|
5992
|
-
const icon = check.status === "pass" ? pc22.green("\u2713") : check.status === "warn" ? pc22.yellow("!") : pc22.red("\u2717");
|
|
5993
|
-
const msg = check.status === "pass" ? check.message : check.status === "warn" ? pc22.yellow(check.message) : pc22.red(check.message);
|
|
5994
|
-
console.log(` ${icon} ${pc22.bold(check.name)}: ${msg}`);
|
|
5995
|
-
if (check.fix && check.status !== "pass") {
|
|
5996
|
-
console.log(pc22.dim(` \u2192 ${check.fix}`));
|
|
5997
|
-
}
|
|
5998
|
-
}
|
|
5999
|
-
console.log();
|
|
6000
|
-
if (failed === 0 && warned === 0) {
|
|
6001
|
-
console.log(pc22.green(`\u2713 All ${passed} checks passed \u2014 your setup looks great!`));
|
|
6002
|
-
} else if (failed === 0) {
|
|
6003
|
-
console.log(pc22.green(`\u2713 ${passed} passed`) + pc22.yellow(`, ${warned} warning(s)`));
|
|
6004
|
-
} else {
|
|
6005
|
-
console.log(
|
|
6006
|
-
pc22.red(`\u2717 ${failed} failed`) + (warned > 0 ? pc22.yellow(`, ${warned} warning(s)`) : "") + pc22.dim(`, ${passed} passed`)
|
|
6007
|
-
);
|
|
6008
|
-
}
|
|
6009
|
-
console.log();
|
|
6010
|
-
}
|
|
6011
|
-
return result;
|
|
6012
|
-
}
|
|
6013
|
-
|
|
6014
5924
|
// src/commands/sync.ts
|
|
6015
|
-
import
|
|
6016
|
-
import { readFile as
|
|
5925
|
+
import pc22 from "picocolors";
|
|
5926
|
+
import { readFile as readFile11, writeFile as writeFile9 } from "fs/promises";
|
|
5927
|
+
import { resolveComponentSourcePath } from "@fragments-sdk/extract";
|
|
5928
|
+
import { createComponentExtractor } from "@fragments-sdk/extract";
|
|
6017
5929
|
async function sync(options = {}) {
|
|
6018
5930
|
const { config, configDir } = await loadConfig(options.config);
|
|
6019
|
-
console.log(
|
|
5931
|
+
console.log(pc22.cyan(`
|
|
6020
5932
|
${BRAND.name} Sync
|
|
6021
5933
|
`));
|
|
6022
5934
|
if (options.dryRun) {
|
|
6023
|
-
console.log(
|
|
5935
|
+
console.log(pc22.dim("Dry run \u2014 no files will be modified.\n"));
|
|
6024
5936
|
}
|
|
6025
5937
|
const result = await runSync(config, configDir, options);
|
|
6026
5938
|
if (result.updated.length > 0) {
|
|
6027
5939
|
const verb = options.dryRun ? "Would update" : "Updated";
|
|
6028
|
-
console.log(
|
|
5940
|
+
console.log(pc22.bold(`${verb} ${result.updated.length} fragment(s):
|
|
6029
5941
|
`));
|
|
6030
5942
|
for (const comp of result.updated) {
|
|
6031
|
-
console.log(` ${
|
|
5943
|
+
console.log(` ${pc22.green("\u2713")} ${pc22.bold(comp.name)} ${pc22.dim(`(${comp.file})`)}`);
|
|
6032
5944
|
for (const change of comp.changes) {
|
|
6033
|
-
console.log(` ${
|
|
5945
|
+
console.log(` ${pc22.dim("\u2022")} ${change}`);
|
|
6034
5946
|
}
|
|
6035
5947
|
}
|
|
6036
5948
|
console.log();
|
|
6037
5949
|
}
|
|
6038
5950
|
if (result.skipped.length > 0) {
|
|
6039
|
-
console.log(
|
|
5951
|
+
console.log(pc22.dim(`Skipped ${result.skipped.length}: ${result.skipped.map((s) => s.name).join(", ")}
|
|
6040
5952
|
`));
|
|
6041
5953
|
}
|
|
6042
5954
|
if (result.errors.length > 0) {
|
|
6043
|
-
console.log(
|
|
5955
|
+
console.log(pc22.red(pc22.bold("Errors:")));
|
|
6044
5956
|
for (const err of result.errors) {
|
|
6045
|
-
console.log(` ${
|
|
5957
|
+
console.log(` ${pc22.red("\u2717")} ${pc22.bold(err.file)}: ${err.message}`);
|
|
6046
5958
|
}
|
|
6047
5959
|
console.log();
|
|
6048
5960
|
}
|
|
6049
5961
|
if (result.updated.length === 0 && result.errors.length === 0) {
|
|
6050
|
-
console.log(
|
|
5962
|
+
console.log(pc22.green("All fragments are in sync \u2014 nothing to update.\n"));
|
|
6051
5963
|
}
|
|
6052
5964
|
return result;
|
|
6053
5965
|
}
|
|
@@ -6066,7 +5978,7 @@ async function runSync(config, configDir, options) {
|
|
|
6066
5978
|
const fragment = await loadFragmentFile(file.absolutePath);
|
|
6067
5979
|
if (!fragment?.meta?.name) continue;
|
|
6068
5980
|
if (options.component && fragment.meta.name !== options.component) continue;
|
|
6069
|
-
const fileContent = await
|
|
5981
|
+
const fileContent = await readFile11(file.absolutePath, "utf-8");
|
|
6070
5982
|
const parsed = parseFragmentFile(fileContent, file.absolutePath);
|
|
6071
5983
|
if (!parsed.componentImport) {
|
|
6072
5984
|
skipped.push({ name: fragment.meta.name, reason: "No component import found" });
|
|
@@ -6228,9 +6140,9 @@ function escapeRegex(str) {
|
|
|
6228
6140
|
}
|
|
6229
6141
|
|
|
6230
6142
|
// src/commands/govern.ts
|
|
6231
|
-
import
|
|
6143
|
+
import pc23 from "picocolors";
|
|
6232
6144
|
async function governCheck(options = {}) {
|
|
6233
|
-
console.log(
|
|
6145
|
+
console.log(pc23.cyan(`
|
|
6234
6146
|
${BRAND.name} Governance Check
|
|
6235
6147
|
`));
|
|
6236
6148
|
const { cliCheck, formatVerdict } = await import("@fragments-sdk/govern");
|
|
@@ -6241,42 +6153,89 @@ ${BRAND.name} Governance Check
|
|
|
6241
6153
|
console.log();
|
|
6242
6154
|
}
|
|
6243
6155
|
if (result.verdict.passed) {
|
|
6244
|
-
console.log(
|
|
6156
|
+
console.log(pc23.green(`\u2713 Governance check passed (score: ${result.verdict.score}/100)
|
|
6245
6157
|
`));
|
|
6246
6158
|
} else {
|
|
6247
|
-
console.log(
|
|
6159
|
+
console.log(pc23.red(`\u2717 Governance check failed (score: ${result.verdict.score}/100)
|
|
6248
6160
|
`));
|
|
6249
6161
|
}
|
|
6250
6162
|
return { exitCode: result.exitCode };
|
|
6251
6163
|
}
|
|
6252
6164
|
async function governInit(options = {}) {
|
|
6253
6165
|
const { writeFile: writeFile11 } = await import("fs/promises");
|
|
6166
|
+
const { existsSync: existsSync4 } = await import("fs");
|
|
6254
6167
|
const { resolve: resolve14 } = await import("path");
|
|
6255
6168
|
const { generateConfigTemplate } = await import("@fragments-sdk/govern");
|
|
6169
|
+
const { findTailwindConfig } = await import("./token-normalizer-TEPOVBPV.js");
|
|
6170
|
+
const cwd = process.cwd();
|
|
6171
|
+
const detected = {
|
|
6172
|
+
tokenIncludes: [],
|
|
6173
|
+
projectType: "unknown"
|
|
6174
|
+
};
|
|
6175
|
+
const tailwindPath = findTailwindConfig(cwd);
|
|
6176
|
+
if (tailwindPath) {
|
|
6177
|
+
const { relative: relative9 } = await import("path");
|
|
6178
|
+
detected.tokenIncludes.push(relative9(cwd, tailwindPath));
|
|
6179
|
+
detected.projectType = "tailwind";
|
|
6180
|
+
console.log(pc23.dim(` Detected Tailwind config: ${detected.tokenIncludes[0]}`));
|
|
6181
|
+
}
|
|
6182
|
+
if (detected.tokenIncludes.length === 0) {
|
|
6183
|
+
const candidates = [
|
|
6184
|
+
"src/styles/tokens.scss",
|
|
6185
|
+
"src/styles/variables.scss",
|
|
6186
|
+
"src/styles/theme.scss",
|
|
6187
|
+
"src/tokens.css",
|
|
6188
|
+
"src/styles/tokens.css",
|
|
6189
|
+
"src/styles/variables.css",
|
|
6190
|
+
"styles/tokens.scss",
|
|
6191
|
+
"styles/variables.scss",
|
|
6192
|
+
"tokens.json",
|
|
6193
|
+
"src/tokens.json"
|
|
6194
|
+
];
|
|
6195
|
+
for (const candidate of candidates) {
|
|
6196
|
+
if (existsSync4(resolve14(cwd, candidate))) {
|
|
6197
|
+
detected.tokenIncludes.push(candidate);
|
|
6198
|
+
detected.projectType = candidate.endsWith(".scss") ? "scss" : candidate.endsWith(".json") ? "dtcg" : "css";
|
|
6199
|
+
console.log(pc23.dim(` Detected token source: ${candidate}`));
|
|
6200
|
+
}
|
|
6201
|
+
}
|
|
6202
|
+
}
|
|
6203
|
+
if (detected.tokenIncludes.length === 0) {
|
|
6204
|
+
console.log(pc23.dim(" No token sources auto-detected. You can add them manually."));
|
|
6205
|
+
}
|
|
6256
6206
|
const outputPath = resolve14(options.output ?? "fragments.config.ts");
|
|
6257
|
-
const template = generateConfigTemplate();
|
|
6207
|
+
const template = generateConfigTemplate({ tokenIncludes: detected.tokenIncludes });
|
|
6258
6208
|
await writeFile11(outputPath, template, "utf-8");
|
|
6259
|
-
console.log(
|
|
6209
|
+
console.log(pc23.green(`
|
|
6210
|
+
\u2713 Created ${outputPath}
|
|
6260
6211
|
`));
|
|
6212
|
+
if (detected.tokenIncludes.length > 0) {
|
|
6213
|
+
console.log(
|
|
6214
|
+
pc23.dim(` Token sources configured: ${detected.tokenIncludes.join(", ")}`)
|
|
6215
|
+
);
|
|
6216
|
+
}
|
|
6217
|
+
console.log(
|
|
6218
|
+
pc23.dim(" Run ") + pc23.cyan("fragments govern scan") + pc23.dim(" to check your project.\n")
|
|
6219
|
+
);
|
|
6261
6220
|
}
|
|
6262
6221
|
async function governConnect() {
|
|
6263
|
-
const { readFile:
|
|
6264
|
-
const { existsSync:
|
|
6222
|
+
const { readFile: readFile13, writeFile: writeFile11, appendFile } = await import("fs/promises");
|
|
6223
|
+
const { existsSync: existsSync4 } = await import("fs");
|
|
6265
6224
|
const { resolve: resolve14 } = await import("path");
|
|
6266
6225
|
const { platform } = await import("os");
|
|
6267
6226
|
const { exec } = await import("child_process");
|
|
6268
6227
|
const { password, confirm } = await import("@inquirer/prompts");
|
|
6269
6228
|
const cloudUrl = process.env.FRAGMENTS_URL ?? "https://app.usefragments.com";
|
|
6270
|
-
console.log(
|
|
6229
|
+
console.log(pc23.cyan(`
|
|
6271
6230
|
${BRAND.name} \u2014 Connect to Cloud
|
|
6272
6231
|
`));
|
|
6273
6232
|
console.log(
|
|
6274
|
-
|
|
6233
|
+
pc23.dim(" This will connect your project to the Fragments dashboard\n") + pc23.dim(" for centralized audit tracking and team visibility.\n")
|
|
6275
6234
|
);
|
|
6276
|
-
console.log(
|
|
6235
|
+
console.log(pc23.bold(" Step 1 of 3: Get your API key\n"));
|
|
6277
6236
|
const dashboardUrl = `${cloudUrl}/api-keys`;
|
|
6278
|
-
console.log(
|
|
6279
|
-
console.log(
|
|
6237
|
+
console.log(pc23.dim(` \u2192 Opening the dashboard in your browser...`));
|
|
6238
|
+
console.log(pc23.dim(` Copy your API key from Settings \u2192 API Keys
|
|
6280
6239
|
`));
|
|
6281
6240
|
const os = platform();
|
|
6282
6241
|
const openCmd = os === "darwin" ? `open "${dashboardUrl}"` : os === "win32" ? `start "" "${dashboardUrl}"` : `xdg-open "${dashboardUrl}"`;
|
|
@@ -6289,42 +6248,42 @@ async function governConnect() {
|
|
|
6289
6248
|
mask: "*"
|
|
6290
6249
|
});
|
|
6291
6250
|
if (!apiKey.trim()) {
|
|
6292
|
-
console.log(
|
|
6251
|
+
console.log(pc23.yellow("\n API key cannot be empty. Please try again.\n"));
|
|
6293
6252
|
continue;
|
|
6294
6253
|
}
|
|
6295
|
-
console.log(
|
|
6254
|
+
console.log(pc23.dim("\n Verifying..."));
|
|
6296
6255
|
try {
|
|
6297
6256
|
const response = await fetch(`${cloudUrl}/api/verify`, {
|
|
6298
6257
|
headers: { Authorization: `Bearer ${apiKey.trim()}` }
|
|
6299
6258
|
});
|
|
6300
6259
|
if (!response.ok) {
|
|
6301
|
-
console.log(
|
|
6260
|
+
console.log(pc23.red(`
|
|
6302
6261
|
\u2717 Invalid API key (HTTP ${response.status}). Please try again.
|
|
6303
6262
|
`));
|
|
6304
6263
|
continue;
|
|
6305
6264
|
}
|
|
6306
6265
|
const data = await response.json();
|
|
6307
6266
|
if (!data.valid) {
|
|
6308
|
-
console.log(
|
|
6267
|
+
console.log(pc23.red("\n \u2717 API key not recognized. Please try again.\n"));
|
|
6309
6268
|
continue;
|
|
6310
6269
|
}
|
|
6311
6270
|
orgName = data.orgName ?? "your organization";
|
|
6312
|
-
console.log(
|
|
6271
|
+
console.log(pc23.green(`
|
|
6313
6272
|
\u2713 Connected to "${orgName}" (verified)
|
|
6314
6273
|
`));
|
|
6315
6274
|
break;
|
|
6316
6275
|
} catch (error) {
|
|
6317
6276
|
console.log(
|
|
6318
|
-
|
|
6277
|
+
pc23.red("\n \u2717 Could not reach the dashboard.")
|
|
6319
6278
|
);
|
|
6320
6279
|
console.log(
|
|
6321
|
-
|
|
6280
|
+
pc23.dim(` ${error instanceof Error ? error.message : "Network error"}
|
|
6322
6281
|
`)
|
|
6323
6282
|
);
|
|
6324
6283
|
continue;
|
|
6325
6284
|
}
|
|
6326
6285
|
}
|
|
6327
|
-
console.log(
|
|
6286
|
+
console.log(pc23.bold(" Step 2 of 3: Save configuration\n"));
|
|
6328
6287
|
const saveToEnv = await confirm({
|
|
6329
6288
|
message: "Save API key to .env file?",
|
|
6330
6289
|
default: true
|
|
@@ -6332,77 +6291,77 @@ async function governConnect() {
|
|
|
6332
6291
|
if (saveToEnv) {
|
|
6333
6292
|
const envPath = resolve14(".env");
|
|
6334
6293
|
const envEntry = `FRAGMENTS_API_KEY=${apiKey.trim()}`;
|
|
6335
|
-
if (
|
|
6336
|
-
const envContent = await
|
|
6294
|
+
if (existsSync4(envPath)) {
|
|
6295
|
+
const envContent = await readFile13(envPath, "utf-8");
|
|
6337
6296
|
if (envContent.includes("FRAGMENTS_API_KEY=")) {
|
|
6338
6297
|
const updated = envContent.replace(
|
|
6339
6298
|
/^FRAGMENTS_API_KEY=.*$/m,
|
|
6340
6299
|
envEntry
|
|
6341
6300
|
);
|
|
6342
6301
|
await writeFile11(envPath, updated, "utf-8");
|
|
6343
|
-
console.log(
|
|
6302
|
+
console.log(pc23.green(" \u2713 Updated FRAGMENTS_API_KEY in .env"));
|
|
6344
6303
|
} else {
|
|
6345
6304
|
await appendFile(envPath, `
|
|
6346
6305
|
${envEntry}
|
|
6347
6306
|
`, "utf-8");
|
|
6348
|
-
console.log(
|
|
6307
|
+
console.log(pc23.green(" \u2713 Added FRAGMENTS_API_KEY to .env"));
|
|
6349
6308
|
}
|
|
6350
6309
|
} else {
|
|
6351
6310
|
await writeFile11(envPath, `${envEntry}
|
|
6352
6311
|
`, "utf-8");
|
|
6353
|
-
console.log(
|
|
6312
|
+
console.log(pc23.green(" \u2713 Created .env with FRAGMENTS_API_KEY"));
|
|
6354
6313
|
}
|
|
6355
6314
|
if (cloudUrl !== "https://app.usefragments.com") {
|
|
6356
|
-
const envContent = await
|
|
6315
|
+
const envContent = await readFile13(envPath, "utf-8");
|
|
6357
6316
|
if (!envContent.includes("FRAGMENTS_URL=")) {
|
|
6358
6317
|
await appendFile(envPath, `FRAGMENTS_URL=${cloudUrl}
|
|
6359
6318
|
`, "utf-8");
|
|
6360
|
-
console.log(
|
|
6319
|
+
console.log(pc23.green(` \u2713 Added FRAGMENTS_URL to .env`));
|
|
6361
6320
|
}
|
|
6362
6321
|
}
|
|
6363
6322
|
const gitignorePath = resolve14(".gitignore");
|
|
6364
|
-
if (
|
|
6365
|
-
const gitignore = await
|
|
6323
|
+
if (existsSync4(gitignorePath)) {
|
|
6324
|
+
const gitignore = await readFile13(gitignorePath, "utf-8");
|
|
6366
6325
|
if (!gitignore.split("\n").some((line) => line.trim() === ".env")) {
|
|
6367
6326
|
await appendFile(gitignorePath, "\n.env\n", "utf-8");
|
|
6368
|
-
console.log(
|
|
6327
|
+
console.log(pc23.green(" \u2713 Added .env to .gitignore"));
|
|
6369
6328
|
}
|
|
6370
6329
|
} else {
|
|
6371
6330
|
await writeFile11(gitignorePath, ".env\n", "utf-8");
|
|
6372
|
-
console.log(
|
|
6331
|
+
console.log(pc23.green(" \u2713 Created .gitignore with .env entry"));
|
|
6373
6332
|
}
|
|
6374
6333
|
}
|
|
6375
|
-
console.log(
|
|
6334
|
+
console.log(pc23.bold("\n Step 3 of 3: Config check\n"));
|
|
6376
6335
|
const { findGovernConfig } = await import("@fragments-sdk/govern");
|
|
6377
6336
|
const configPath = findGovernConfig();
|
|
6378
6337
|
if (configPath) {
|
|
6379
|
-
console.log(
|
|
6338
|
+
console.log(pc23.green(` \u2713 Found govern config: ${configPath}`));
|
|
6380
6339
|
} else {
|
|
6381
6340
|
console.log(
|
|
6382
|
-
|
|
6341
|
+
pc23.yellow(" No govern config found \u2014 run `fragments govern init` to create one")
|
|
6383
6342
|
);
|
|
6384
6343
|
}
|
|
6385
|
-
console.log(
|
|
6386
|
-
console.log(
|
|
6387
|
-
console.log(
|
|
6344
|
+
console.log(pc23.dim("\n \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n"));
|
|
6345
|
+
console.log(pc23.green(" \u2713 All set!") + " Run `fragments govern check` to send your first audit.\n");
|
|
6346
|
+
console.log(pc23.dim(` Dashboard: ${cloudUrl}/overview
|
|
6388
6347
|
`));
|
|
6389
6348
|
}
|
|
6390
6349
|
async function governReport() {
|
|
6391
|
-
const { readFile:
|
|
6392
|
-
console.log(
|
|
6350
|
+
const { readFile: readFile13 } = await import("fs/promises");
|
|
6351
|
+
console.log(pc23.cyan(`
|
|
6393
6352
|
${BRAND.name} Governance Report
|
|
6394
6353
|
`));
|
|
6395
6354
|
let raw;
|
|
6396
6355
|
try {
|
|
6397
|
-
raw = await
|
|
6356
|
+
raw = await readFile13(".govern-audit.jsonl", "utf-8");
|
|
6398
6357
|
} catch {
|
|
6399
|
-
console.log(
|
|
6400
|
-
console.log(
|
|
6358
|
+
console.log(pc23.yellow("No audit log found (.govern-audit.jsonl)\n"));
|
|
6359
|
+
console.log(pc23.dim("Run `fragments govern check` to generate audit data.\n"));
|
|
6401
6360
|
return;
|
|
6402
6361
|
}
|
|
6403
6362
|
const lines = raw.trim().split("\n").filter(Boolean);
|
|
6404
6363
|
if (lines.length === 0) {
|
|
6405
|
-
console.log(
|
|
6364
|
+
console.log(pc23.yellow("Audit log is empty.\n"));
|
|
6406
6365
|
return;
|
|
6407
6366
|
}
|
|
6408
6367
|
let totalScore = 0;
|
|
@@ -6427,13 +6386,15 @@ ${BRAND.name} Governance Report
|
|
|
6427
6386
|
}
|
|
6428
6387
|
|
|
6429
6388
|
// src/commands/migrate-contract.ts
|
|
6430
|
-
import
|
|
6389
|
+
import pc24 from "picocolors";
|
|
6431
6390
|
import fg4 from "fast-glob";
|
|
6432
6391
|
import { resolve as resolve13 } from "path";
|
|
6433
6392
|
|
|
6434
6393
|
// src/migrate/fragment-to-contract.ts
|
|
6435
|
-
import { readFile as
|
|
6436
|
-
import { resolve as resolve12, dirname as
|
|
6394
|
+
import { readFile as readFile12, writeFile as writeFile10 } from "fs/promises";
|
|
6395
|
+
import { resolve as resolve12, dirname as dirname5, relative as relative8 } from "path";
|
|
6396
|
+
import { createComponentExtractor as createComponentExtractor2 } from "@fragments-sdk/extract";
|
|
6397
|
+
import { resolveComponentSourcePath as resolveComponentSourcePath2 } from "@fragments-sdk/extract";
|
|
6437
6398
|
function compilePropsSummaryFromExtracted(props) {
|
|
6438
6399
|
return Object.entries(props).filter(([, p]) => p.source === "local").map(([name, prop]) => {
|
|
6439
6400
|
let summary = name + ": ";
|
|
@@ -6471,17 +6432,17 @@ function compilePropsSummaryFromDocs(props) {
|
|
|
6471
6432
|
async function migrateFragmentToContract(fragmentPath, configDir, options) {
|
|
6472
6433
|
const warnings = [];
|
|
6473
6434
|
const absFragmentPath = resolve12(fragmentPath);
|
|
6474
|
-
const content = await
|
|
6435
|
+
const content = await readFile12(absFragmentPath, "utf-8");
|
|
6475
6436
|
const parsed = parseFragmentFile(content, fragmentPath);
|
|
6476
6437
|
warnings.push(...parsed.warnings);
|
|
6477
6438
|
if (!parsed.meta.name) {
|
|
6478
6439
|
throw new Error(`Fragment file ${fragmentPath} has no meta.name`);
|
|
6479
6440
|
}
|
|
6480
6441
|
let extractedMeta = null;
|
|
6481
|
-
const extractor =
|
|
6442
|
+
const extractor = createComponentExtractor2(options?.tsconfigPath);
|
|
6482
6443
|
try {
|
|
6483
6444
|
const componentExportName = parsed.componentName ?? parsed.meta.name;
|
|
6484
|
-
const componentSourcePath =
|
|
6445
|
+
const componentSourcePath = resolveComponentSourcePath2(
|
|
6485
6446
|
absFragmentPath,
|
|
6486
6447
|
parsed.componentImport
|
|
6487
6448
|
);
|
|
@@ -6497,7 +6458,7 @@ async function migrateFragmentToContract(fragmentPath, configDir, options) {
|
|
|
6497
6458
|
}
|
|
6498
6459
|
let sourcePath;
|
|
6499
6460
|
if (parsed.componentImport) {
|
|
6500
|
-
const absSource =
|
|
6461
|
+
const absSource = resolveComponentSourcePath2(absFragmentPath, parsed.componentImport);
|
|
6501
6462
|
if (absSource) {
|
|
6502
6463
|
sourcePath = relative8(configDir, absSource);
|
|
6503
6464
|
} else {
|
|
@@ -6505,7 +6466,7 @@ async function migrateFragmentToContract(fragmentPath, configDir, options) {
|
|
|
6505
6466
|
warnings.push(`Could not resolve component source path: ${parsed.componentImport}`);
|
|
6506
6467
|
}
|
|
6507
6468
|
} else {
|
|
6508
|
-
const fragDir =
|
|
6469
|
+
const fragDir = dirname5(absFragmentPath);
|
|
6509
6470
|
sourcePath = relative8(configDir, resolve12(fragDir, "index.tsx"));
|
|
6510
6471
|
warnings.push("No component import found; assuming ./index.tsx");
|
|
6511
6472
|
}
|
|
@@ -6609,16 +6570,16 @@ async function migrateFragmentToContract(fragmentPath, configDir, options) {
|
|
|
6609
6570
|
async function migrateContract(options) {
|
|
6610
6571
|
const { config, configDir } = await loadConfig(options.config);
|
|
6611
6572
|
const pattern = options.glob ?? "src/**/*.fragment.tsx";
|
|
6612
|
-
console.log(
|
|
6573
|
+
console.log(pc24.blue(`Migrating fragment files matching: ${pattern}`));
|
|
6613
6574
|
if (options.dryRun) {
|
|
6614
|
-
console.log(
|
|
6575
|
+
console.log(pc24.yellow("(dry run \u2014 no files will be written)"));
|
|
6615
6576
|
}
|
|
6616
6577
|
const files = await fg4(pattern, { cwd: configDir, absolute: true });
|
|
6617
6578
|
if (files.length === 0) {
|
|
6618
|
-
console.log(
|
|
6579
|
+
console.log(pc24.yellow("No fragment files found matching pattern."));
|
|
6619
6580
|
return { migrated: 0, failed: 0, warnings: 0 };
|
|
6620
6581
|
}
|
|
6621
|
-
console.log(
|
|
6582
|
+
console.log(pc24.dim(`Found ${files.length} fragment file(s)`));
|
|
6622
6583
|
let migrated = 0;
|
|
6623
6584
|
let failed = 0;
|
|
6624
6585
|
let totalWarnings = 0;
|
|
@@ -6631,31 +6592,31 @@ async function migrateContract(options) {
|
|
|
6631
6592
|
});
|
|
6632
6593
|
migrated++;
|
|
6633
6594
|
const verb = options.dryRun ? "Would migrate" : "Migrated";
|
|
6634
|
-
console.log(
|
|
6595
|
+
console.log(pc24.green(` \u2713 ${verb}: ${result.contractPath}`));
|
|
6635
6596
|
if (result.warnings.length > 0) {
|
|
6636
6597
|
totalWarnings += result.warnings.length;
|
|
6637
6598
|
for (const w of result.warnings) {
|
|
6638
|
-
console.log(
|
|
6599
|
+
console.log(pc24.yellow(` \u26A0 ${w}`));
|
|
6639
6600
|
}
|
|
6640
6601
|
}
|
|
6641
6602
|
} catch (error) {
|
|
6642
6603
|
failed++;
|
|
6643
|
-
console.log(
|
|
6644
|
-
console.log(
|
|
6604
|
+
console.log(pc24.red(` \u2717 Failed: ${file}`));
|
|
6605
|
+
console.log(pc24.dim(` ${error instanceof Error ? error.message : String(error)}`));
|
|
6645
6606
|
}
|
|
6646
6607
|
}
|
|
6647
6608
|
console.log("");
|
|
6648
|
-
console.log(
|
|
6649
|
-
console.log(` ${
|
|
6609
|
+
console.log(pc24.bold("Migration summary:"));
|
|
6610
|
+
console.log(` ${pc24.green(`${migrated} migrated`)} ${pc24.red(`${failed} failed`)} ${pc24.yellow(`${totalWarnings} warnings`)}`);
|
|
6650
6611
|
if (!options.dryRun && migrated > 0) {
|
|
6651
6612
|
console.log("");
|
|
6652
|
-
console.log(
|
|
6613
|
+
console.log(pc24.dim("Run `fragments build` to verify migrated contracts compile correctly."));
|
|
6653
6614
|
}
|
|
6654
6615
|
return { migrated, failed, warnings: totalWarnings };
|
|
6655
6616
|
}
|
|
6656
6617
|
|
|
6657
6618
|
// src/bin.ts
|
|
6658
|
-
var __dirname =
|
|
6619
|
+
var __dirname = dirname6(fileURLToPath(import.meta.url));
|
|
6659
6620
|
var pkg = JSON.parse(readFileSync(join11(__dirname, "../package.json"), "utf-8"));
|
|
6660
6621
|
var EXPERIMENTAL = process.env.FRAGMENTS_EXPERIMENTAL === "1";
|
|
6661
6622
|
var program = new Command();
|
|
@@ -6667,7 +6628,7 @@ program.command("validate").description("Validate fragment files").option("-c, -
|
|
|
6667
6628
|
process.exit(1);
|
|
6668
6629
|
}
|
|
6669
6630
|
} catch (error) {
|
|
6670
|
-
console.error(
|
|
6631
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6671
6632
|
process.exit(1);
|
|
6672
6633
|
}
|
|
6673
6634
|
});
|
|
@@ -6683,11 +6644,11 @@ program.command("sync").description("Auto-update fragment files from component s
|
|
|
6683
6644
|
process.exit(1);
|
|
6684
6645
|
}
|
|
6685
6646
|
} catch (error) {
|
|
6686
|
-
console.error(
|
|
6647
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6687
6648
|
process.exit(1);
|
|
6688
6649
|
}
|
|
6689
6650
|
});
|
|
6690
|
-
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) => {
|
|
6651
|
+
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("--if-needed", `Skip rebuilding when ${BRAND.outFile} is already fresh`).option("--check", `Check whether ${BRAND.outFile} is fresh and exit non-zero if it is stale`).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) => {
|
|
6691
6652
|
try {
|
|
6692
6653
|
const result = await build({
|
|
6693
6654
|
config: options.config,
|
|
@@ -6695,6 +6656,8 @@ program.command("build").description(`Build compiled ${BRAND.outFile} and ${BRAN
|
|
|
6695
6656
|
registry: options.registry,
|
|
6696
6657
|
registryOnly: options.registryOnly,
|
|
6697
6658
|
fromSource: options.fromSource,
|
|
6659
|
+
ifNeeded: options.ifNeeded,
|
|
6660
|
+
check: options.check,
|
|
6698
6661
|
skipUsage: options.skipUsage,
|
|
6699
6662
|
skipStorybook: options.skipStorybook,
|
|
6700
6663
|
verbose: options.verbose
|
|
@@ -6703,7 +6666,7 @@ program.command("build").description(`Build compiled ${BRAND.outFile} and ${BRAN
|
|
|
6703
6666
|
process.exit(1);
|
|
6704
6667
|
}
|
|
6705
6668
|
} catch (error) {
|
|
6706
|
-
console.error(
|
|
6669
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6707
6670
|
process.exit(1);
|
|
6708
6671
|
}
|
|
6709
6672
|
});
|
|
@@ -6722,7 +6685,7 @@ program.command("context").description("Generate AI-ready context for your desig
|
|
|
6722
6685
|
process.exit(1);
|
|
6723
6686
|
}
|
|
6724
6687
|
} catch (error) {
|
|
6725
|
-
console.error(
|
|
6688
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6726
6689
|
process.exit(1);
|
|
6727
6690
|
}
|
|
6728
6691
|
});
|
|
@@ -6749,7 +6712,7 @@ program.command("ai").description("Generate context optimized for AI assistants
|
|
|
6749
6712
|
}
|
|
6750
6713
|
}
|
|
6751
6714
|
} catch (error) {
|
|
6752
|
-
console.error(
|
|
6715
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6753
6716
|
process.exit(1);
|
|
6754
6717
|
}
|
|
6755
6718
|
});
|
|
@@ -6757,7 +6720,7 @@ program.command("list").description("List all discovered fragment files").option
|
|
|
6757
6720
|
try {
|
|
6758
6721
|
await list(options);
|
|
6759
6722
|
} catch (error) {
|
|
6760
|
-
console.error(
|
|
6723
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6761
6724
|
process.exit(1);
|
|
6762
6725
|
}
|
|
6763
6726
|
});
|
|
@@ -6765,7 +6728,7 @@ program.command("reset").description("Reset to initial state (delete all generat
|
|
|
6765
6728
|
try {
|
|
6766
6729
|
await reset(options);
|
|
6767
6730
|
} catch (error) {
|
|
6768
|
-
console.error(
|
|
6731
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6769
6732
|
process.exit(1);
|
|
6770
6733
|
}
|
|
6771
6734
|
});
|
|
@@ -6779,7 +6742,7 @@ linkCommand.command("figma").argument("[figma-url]", "Figma file URL to link com
|
|
|
6779
6742
|
variants: options.variants
|
|
6780
6743
|
});
|
|
6781
6744
|
} catch (error) {
|
|
6782
|
-
console.error(
|
|
6745
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6783
6746
|
process.exit(1);
|
|
6784
6747
|
}
|
|
6785
6748
|
});
|
|
@@ -6794,7 +6757,7 @@ linkCommand.command("storybook").description("Bootstrap fragments from existing
|
|
|
6794
6757
|
exclude: options.exclude
|
|
6795
6758
|
});
|
|
6796
6759
|
} catch (error) {
|
|
6797
|
-
console.error(
|
|
6760
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6798
6761
|
process.exit(1);
|
|
6799
6762
|
}
|
|
6800
6763
|
});
|
|
@@ -6814,7 +6777,7 @@ program.command("screenshot").description("Capture screenshots of component vari
|
|
|
6814
6777
|
process.exit(1);
|
|
6815
6778
|
}
|
|
6816
6779
|
} catch (error) {
|
|
6817
|
-
console.error(
|
|
6780
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6818
6781
|
process.exit(1);
|
|
6819
6782
|
}
|
|
6820
6783
|
});
|
|
@@ -6833,7 +6796,7 @@ program.command("diff").argument("[component]", "Component name to diff (optiona
|
|
|
6833
6796
|
process.exit(1);
|
|
6834
6797
|
}
|
|
6835
6798
|
} catch (error) {
|
|
6836
|
-
console.error(
|
|
6799
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6837
6800
|
process.exit(1);
|
|
6838
6801
|
}
|
|
6839
6802
|
});
|
|
@@ -6852,8 +6815,8 @@ program.command("compare").argument("[component]", "Component name to compare").
|
|
|
6852
6815
|
process.exit(1);
|
|
6853
6816
|
}
|
|
6854
6817
|
} catch (error) {
|
|
6855
|
-
console.error(
|
|
6856
|
-
console.log(
|
|
6818
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6819
|
+
console.log(pc25.dim(`
|
|
6857
6820
|
Make sure a dev server is running on the expected port.`));
|
|
6858
6821
|
process.exit(1);
|
|
6859
6822
|
}
|
|
@@ -6872,7 +6835,7 @@ program.command("analyze").description("Analyze design system and generate repor
|
|
|
6872
6835
|
process.exit(1);
|
|
6873
6836
|
}
|
|
6874
6837
|
} catch (error) {
|
|
6875
|
-
console.error(
|
|
6838
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6876
6839
|
process.exit(1);
|
|
6877
6840
|
}
|
|
6878
6841
|
});
|
|
@@ -6891,7 +6854,7 @@ program.command("verify").argument("[component]", "Component name to verify (opt
|
|
|
6891
6854
|
if (options.ci) {
|
|
6892
6855
|
console.log(JSON.stringify({ error: error instanceof Error ? error.message : "Verification failed" }));
|
|
6893
6856
|
} else {
|
|
6894
|
-
console.error(
|
|
6857
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6895
6858
|
}
|
|
6896
6859
|
process.exit(1);
|
|
6897
6860
|
}
|
|
@@ -6908,7 +6871,7 @@ program.command("audit").description("Scan all fragments and show compliance met
|
|
|
6908
6871
|
if (options.json) {
|
|
6909
6872
|
console.log(JSON.stringify({ error: error instanceof Error ? error.message : "Audit failed" }));
|
|
6910
6873
|
} else {
|
|
6911
|
-
console.error(
|
|
6874
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6912
6875
|
}
|
|
6913
6876
|
process.exit(1);
|
|
6914
6877
|
}
|
|
@@ -6930,7 +6893,7 @@ program.command("a11y").description("Run accessibility checks on all component v
|
|
|
6930
6893
|
if (options.json) {
|
|
6931
6894
|
console.log(JSON.stringify({ error: error instanceof Error ? error.message : "A11y check failed" }));
|
|
6932
6895
|
} else {
|
|
6933
|
-
console.error(
|
|
6896
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6934
6897
|
}
|
|
6935
6898
|
process.exit(1);
|
|
6936
6899
|
}
|
|
@@ -6953,7 +6916,7 @@ program.command("enhance").description("AI-powered documentation generation from
|
|
|
6953
6916
|
if (options.format === "json") {
|
|
6954
6917
|
console.log(JSON.stringify({ success: false, error: error instanceof Error ? error.message : "Enhance failed" }));
|
|
6955
6918
|
} else {
|
|
6956
|
-
console.error(
|
|
6919
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6957
6920
|
}
|
|
6958
6921
|
process.exit(1);
|
|
6959
6922
|
}
|
|
@@ -6974,7 +6937,7 @@ program.command("scan").description(`Zero-config ${BRAND.outFile} generation fro
|
|
|
6974
6937
|
process.exit(1);
|
|
6975
6938
|
}
|
|
6976
6939
|
} catch (error) {
|
|
6977
|
-
console.error(
|
|
6940
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6978
6941
|
process.exit(1);
|
|
6979
6942
|
}
|
|
6980
6943
|
});
|
|
@@ -6990,7 +6953,7 @@ program.command("migrate-contract").description("Migrate .fragment.tsx files to
|
|
|
6990
6953
|
process.exit(1);
|
|
6991
6954
|
}
|
|
6992
6955
|
} catch (error) {
|
|
6993
|
-
console.error(
|
|
6956
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
6994
6957
|
process.exit(1);
|
|
6995
6958
|
}
|
|
6996
6959
|
});
|
|
@@ -7003,7 +6966,7 @@ program.command("storygen").description("Generate Storybook stories from fragmen
|
|
|
7003
6966
|
format: options.format
|
|
7004
6967
|
});
|
|
7005
6968
|
} catch (error) {
|
|
7006
|
-
console.error(
|
|
6969
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7007
6970
|
process.exit(1);
|
|
7008
6971
|
}
|
|
7009
6972
|
});
|
|
@@ -7015,7 +6978,7 @@ program.command("metrics").argument("[component]", "Component name (optional, sh
|
|
|
7015
6978
|
json: options.json
|
|
7016
6979
|
});
|
|
7017
6980
|
} catch (error) {
|
|
7018
|
-
console.error(
|
|
6981
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7019
6982
|
process.exit(1);
|
|
7020
6983
|
}
|
|
7021
6984
|
});
|
|
@@ -7029,9 +6992,9 @@ program.command("baseline").description("Manage visual regression baselines").ar
|
|
|
7029
6992
|
port: options.port
|
|
7030
6993
|
});
|
|
7031
6994
|
} catch (error) {
|
|
7032
|
-
console.error(
|
|
6995
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7033
6996
|
if (action === "update") {
|
|
7034
|
-
console.log(
|
|
6997
|
+
console.log(pc25.dim(`
|
|
7035
6998
|
Make sure a dev server is running on the expected port.`));
|
|
7036
6999
|
}
|
|
7037
7000
|
process.exit(1);
|
|
@@ -7039,27 +7002,27 @@ Make sure a dev server is running on the expected port.`));
|
|
|
7039
7002
|
});
|
|
7040
7003
|
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) => {
|
|
7041
7004
|
try {
|
|
7042
|
-
const { generateViewerFromJson } = await import("./static-viewer-
|
|
7005
|
+
const { generateViewerFromJson } = await import("./static-viewer-7QIBQZRC.js");
|
|
7043
7006
|
const fs2 = await import("fs/promises");
|
|
7044
7007
|
const path = await import("path");
|
|
7045
7008
|
const inputPath = path.resolve(process.cwd(), options.input);
|
|
7046
7009
|
const outputPath = path.resolve(process.cwd(), options.output);
|
|
7047
|
-
console.log(
|
|
7010
|
+
console.log(pc25.cyan(`
|
|
7048
7011
|
${BRAND.name} Viewer Generator
|
|
7049
7012
|
`));
|
|
7050
7013
|
try {
|
|
7051
7014
|
await fs2.access(inputPath);
|
|
7052
7015
|
} catch {
|
|
7053
|
-
console.log(
|
|
7054
|
-
console.log(
|
|
7055
|
-
Run ${
|
|
7016
|
+
console.log(pc25.red(`Error: ${options.input} not found.`));
|
|
7017
|
+
console.log(pc25.dim(`
|
|
7018
|
+
Run ${pc25.cyan(`${BRAND.cliCommand} build`)} first to generate ${BRAND.outFile}
|
|
7056
7019
|
`));
|
|
7057
7020
|
process.exit(1);
|
|
7058
7021
|
}
|
|
7059
|
-
console.log(
|
|
7022
|
+
console.log(pc25.dim(`Reading: ${options.input}`));
|
|
7060
7023
|
const html = await generateViewerFromJson(inputPath);
|
|
7061
7024
|
await fs2.writeFile(outputPath, html);
|
|
7062
|
-
console.log(
|
|
7025
|
+
console.log(pc25.green(`
|
|
7063
7026
|
\u2713 Generated: ${options.output}
|
|
7064
7027
|
`));
|
|
7065
7028
|
if (options.open) {
|
|
@@ -7068,7 +7031,7 @@ Run ${pc26.cyan(`${BRAND.cliCommand} build`)} first to generate ${BRAND.outFile}
|
|
|
7068
7031
|
exec(`${openCmd} "${outputPath}"`);
|
|
7069
7032
|
}
|
|
7070
7033
|
} catch (error) {
|
|
7071
|
-
console.error(
|
|
7034
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7072
7035
|
process.exit(1);
|
|
7073
7036
|
}
|
|
7074
7037
|
});
|
|
@@ -7081,7 +7044,31 @@ program.command("add").argument("[name]", 'Component name (e.g., "Button", "Text
|
|
|
7081
7044
|
component: options.component
|
|
7082
7045
|
});
|
|
7083
7046
|
} catch (error) {
|
|
7084
|
-
console.error(
|
|
7047
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7048
|
+
process.exit(1);
|
|
7049
|
+
}
|
|
7050
|
+
});
|
|
7051
|
+
program.command("create").argument("[name]", "Project name").description("Create a new project with Fragments UI and your custom theme").option("-t, --template <template>", "Framework template (nextjs, vite)", "nextjs").option("--pm <manager>", "Package manager (npm, pnpm, yarn, bun)").option("--theme <encoded>", "Encoded theme string").option("--preset <id>", "Theme preset ID from usefragments.com/create").option("--brand <color>", "Brand color hex (e.g., #6366f1)").option("--scss", "Use SCSS output (installs sass)").option("--mcp", "Configure MCP server for AI tooling").option("-y, --yes", "Skip interactive prompts").option("--no-git", "Skip git initialization").action(async (name, options) => {
|
|
7052
|
+
try {
|
|
7053
|
+
const { create } = await import("./create-JVAU3YKN.js");
|
|
7054
|
+
const result = await create({
|
|
7055
|
+
name,
|
|
7056
|
+
template: options.template,
|
|
7057
|
+
packageManager: options.pm,
|
|
7058
|
+
theme: options.theme,
|
|
7059
|
+
preset: options.preset,
|
|
7060
|
+
brand: options.brand,
|
|
7061
|
+
scss: options.scss,
|
|
7062
|
+
mcp: options.mcp,
|
|
7063
|
+
yes: options.yes,
|
|
7064
|
+
noGit: !options.git
|
|
7065
|
+
});
|
|
7066
|
+
if (!result.success) {
|
|
7067
|
+
if (result.error) console.error(pc25.red(`Error: ${result.error}`));
|
|
7068
|
+
process.exit(1);
|
|
7069
|
+
}
|
|
7070
|
+
} catch (error) {
|
|
7071
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7085
7072
|
process.exit(1);
|
|
7086
7073
|
}
|
|
7087
7074
|
});
|
|
@@ -7098,7 +7085,7 @@ program.command("setup").description("Configure @fragments-sdk/ui in a consumer
|
|
|
7098
7085
|
process.exit(1);
|
|
7099
7086
|
}
|
|
7100
7087
|
} catch (error) {
|
|
7101
|
-
console.error(
|
|
7088
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7102
7089
|
process.exit(1);
|
|
7103
7090
|
}
|
|
7104
7091
|
});
|
|
@@ -7110,13 +7097,13 @@ initCmd.action(async (options) => {
|
|
|
7110
7097
|
try {
|
|
7111
7098
|
if (options.cloud) {
|
|
7112
7099
|
if (!EXPERIMENTAL) {
|
|
7113
|
-
console.log(
|
|
7100
|
+
console.log(pc25.yellow(`
|
|
7114
7101
|
Fragments Cloud is not yet publicly available.`));
|
|
7115
|
-
console.log(
|
|
7102
|
+
console.log(pc25.dim(` Set FRAGMENTS_EXPERIMENTAL=1 to enable preview features.
|
|
7116
7103
|
`));
|
|
7117
7104
|
process.exit(1);
|
|
7118
7105
|
}
|
|
7119
|
-
const { initCloud } = await import("./init-cloud-
|
|
7106
|
+
const { initCloud } = await import("./init-cloud-3DNKPWFB.js");
|
|
7120
7107
|
await initCloud({
|
|
7121
7108
|
url: options.cloudUrl,
|
|
7122
7109
|
port: options.port ? Number(options.port) : void 0,
|
|
@@ -7125,7 +7112,7 @@ initCmd.action(async (options) => {
|
|
|
7125
7112
|
});
|
|
7126
7113
|
return;
|
|
7127
7114
|
}
|
|
7128
|
-
const { init } = await import("./init-
|
|
7115
|
+
const { init } = await import("./init-SSGUSP7Z.js");
|
|
7129
7116
|
const result = await init({
|
|
7130
7117
|
projectRoot: process.cwd(),
|
|
7131
7118
|
force: options.force,
|
|
@@ -7141,20 +7128,20 @@ initCmd.action(async (options) => {
|
|
|
7141
7128
|
govern: options.govern
|
|
7142
7129
|
});
|
|
7143
7130
|
if (!result.success) {
|
|
7144
|
-
console.error(
|
|
7131
|
+
console.error(pc25.red("\nInit failed with errors:"));
|
|
7145
7132
|
for (const error of result.errors) {
|
|
7146
|
-
console.error(
|
|
7133
|
+
console.error(pc25.red(` - ${error}`));
|
|
7147
7134
|
}
|
|
7148
7135
|
process.exit(1);
|
|
7149
7136
|
}
|
|
7150
7137
|
} catch (error) {
|
|
7151
|
-
console.error(
|
|
7138
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7152
7139
|
process.exit(1);
|
|
7153
7140
|
}
|
|
7154
7141
|
});
|
|
7155
7142
|
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) => {
|
|
7156
7143
|
try {
|
|
7157
|
-
const { snapshot } = await import("./snapshot-
|
|
7144
|
+
const { snapshot } = await import("./snapshot-WIJMEIFT.js");
|
|
7158
7145
|
const result = await snapshot({
|
|
7159
7146
|
port: options.port,
|
|
7160
7147
|
update: options.update,
|
|
@@ -7166,14 +7153,14 @@ program.command("snapshot").description("Run visual snapshot tests per component
|
|
|
7166
7153
|
process.exit(1);
|
|
7167
7154
|
}
|
|
7168
7155
|
} catch (error) {
|
|
7169
|
-
console.error(
|
|
7156
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7170
7157
|
process.exit(1);
|
|
7171
7158
|
}
|
|
7172
7159
|
});
|
|
7173
7160
|
var tokensCmd = program.command("tokens").description("Design token discovery, listing, and generation");
|
|
7174
7161
|
tokensCmd.command("list", { isDefault: true }).description("Discover and list design tokens from CSS/SCSS/DTCG 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) => {
|
|
7175
7162
|
try {
|
|
7176
|
-
const { tokens } = await import("./tokens-
|
|
7163
|
+
const { tokens } = await import("./tokens-NZWFQIAB.js");
|
|
7177
7164
|
const result = await tokens({
|
|
7178
7165
|
config: options.config,
|
|
7179
7166
|
json: options.json,
|
|
@@ -7186,13 +7173,13 @@ tokensCmd.command("list", { isDefault: true }).description("Discover and list de
|
|
|
7186
7173
|
process.exit(1);
|
|
7187
7174
|
}
|
|
7188
7175
|
} catch (error) {
|
|
7189
|
-
console.error(
|
|
7176
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7190
7177
|
process.exit(1);
|
|
7191
7178
|
}
|
|
7192
7179
|
});
|
|
7193
7180
|
tokensCmd.command("generate").description("Generate CSS, SCSS, Tailwind, or Figma output from a DTCG .tokens.json file").requiredOption("--from <path>", "Path to DTCG .tokens.json source file").requiredOption("--format <formats>", "Output formats (comma-separated: css, scss, tailwind, figma)").option("--out <dir>", "Output directory (default: same directory as source)").option("--prefix <prefix>", "Token name prefix").option("--selector <selector>", "CSS selector for custom properties (default: :root)").option("--verbose", "Verbose output").action(async (options) => {
|
|
7194
7181
|
try {
|
|
7195
|
-
const { tokensGenerate } = await import("./tokens-generate-
|
|
7182
|
+
const { tokensGenerate } = await import("./tokens-generate-5JQSJ27E.js");
|
|
7196
7183
|
await tokensGenerate({
|
|
7197
7184
|
from: options.from,
|
|
7198
7185
|
format: options.format,
|
|
@@ -7202,13 +7189,27 @@ tokensCmd.command("generate").description("Generate CSS, SCSS, Tailwind, or Figm
|
|
|
7202
7189
|
verbose: options.verbose
|
|
7203
7190
|
});
|
|
7204
7191
|
} catch (error) {
|
|
7205
|
-
console.error(
|
|
7192
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7193
|
+
process.exit(1);
|
|
7194
|
+
}
|
|
7195
|
+
});
|
|
7196
|
+
tokensCmd.command("push").description("Push code tokens to Fragments Cloud for drift comparison").option("-c, --config <path>", "Path to fragments config file").option("--tailwind-v4 <path>", "Path to Tailwind v4 CSS file with @theme block").option("--dry-run", "Parse and display tokens without pushing").option("--verbose", "Show detailed output").action(async (options) => {
|
|
7197
|
+
try {
|
|
7198
|
+
const { tokensPush } = await import("./tokens-push-HY3KO36V.js");
|
|
7199
|
+
await tokensPush({
|
|
7200
|
+
config: options.config,
|
|
7201
|
+
tailwindV4: options.tailwindV4,
|
|
7202
|
+
dryRun: options.dryRun,
|
|
7203
|
+
verbose: options.verbose
|
|
7204
|
+
});
|
|
7205
|
+
} catch (error) {
|
|
7206
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7206
7207
|
process.exit(1);
|
|
7207
7208
|
}
|
|
7208
7209
|
});
|
|
7209
7210
|
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) => {
|
|
7210
7211
|
try {
|
|
7211
|
-
const { generate } = await import("./generate-
|
|
7212
|
+
const { generate } = await import("./generate-PVOLUAAC.js");
|
|
7212
7213
|
const result = await generate({
|
|
7213
7214
|
projectRoot: process.cwd(),
|
|
7214
7215
|
component,
|
|
@@ -7216,11 +7217,11 @@ program.command("generate").description("Generate fragment files from component
|
|
|
7216
7217
|
componentPattern: options.pattern
|
|
7217
7218
|
});
|
|
7218
7219
|
if (!result.success) {
|
|
7219
|
-
console.error(
|
|
7220
|
+
console.error(pc25.red("\nGenerate completed with errors"));
|
|
7220
7221
|
process.exit(1);
|
|
7221
7222
|
}
|
|
7222
7223
|
} catch (error) {
|
|
7223
|
-
console.error(
|
|
7224
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7224
7225
|
process.exit(1);
|
|
7225
7226
|
}
|
|
7226
7227
|
});
|
|
@@ -7228,7 +7229,7 @@ program.command("graph").description("Query the component relationship graph").a
|
|
|
7228
7229
|
try {
|
|
7229
7230
|
await graph(component, options);
|
|
7230
7231
|
} catch (error) {
|
|
7231
|
-
console.error(
|
|
7232
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7232
7233
|
process.exit(1);
|
|
7233
7234
|
}
|
|
7234
7235
|
});
|
|
@@ -7246,7 +7247,7 @@ program.command("inspect").description("Inspect a single component from compiled
|
|
|
7246
7247
|
if (options.json) {
|
|
7247
7248
|
console.log(JSON.stringify({ error: error instanceof Error ? error.message : "Inspect failed" }));
|
|
7248
7249
|
} else {
|
|
7249
|
-
console.error(
|
|
7250
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7250
7251
|
}
|
|
7251
7252
|
process.exit(1);
|
|
7252
7253
|
}
|
|
@@ -7265,7 +7266,7 @@ program.command("discover").description("List and filter components from compile
|
|
|
7265
7266
|
if (options.json) {
|
|
7266
7267
|
console.log(JSON.stringify({ error: error instanceof Error ? error.message : "Discover failed" }));
|
|
7267
7268
|
} else {
|
|
7268
|
-
console.error(
|
|
7269
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7269
7270
|
}
|
|
7270
7271
|
process.exit(1);
|
|
7271
7272
|
}
|
|
@@ -7284,14 +7285,14 @@ program.command("perf").description("Profile component bundle sizes and performa
|
|
|
7284
7285
|
process.exit(1);
|
|
7285
7286
|
}
|
|
7286
7287
|
} catch (error) {
|
|
7287
|
-
console.error(
|
|
7288
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7288
7289
|
process.exit(1);
|
|
7289
7290
|
}
|
|
7290
7291
|
});
|
|
7291
7292
|
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) => {
|
|
7292
7293
|
try {
|
|
7293
7294
|
const { config, configDir } = await loadConfig(options.config);
|
|
7294
|
-
const { runTestCommand, listTests } = await import("./test-
|
|
7295
|
+
const { runTestCommand, listTests } = await import("./test-64Z5BKBA.js");
|
|
7295
7296
|
if (options.list) {
|
|
7296
7297
|
await listTests(config, configDir, {
|
|
7297
7298
|
component: options.component,
|
|
@@ -7324,12 +7325,13 @@ program.command("test").description("Run interaction tests for fragments with pl
|
|
|
7324
7325
|
});
|
|
7325
7326
|
process.exit(exitCode);
|
|
7326
7327
|
} catch (error) {
|
|
7327
|
-
console.error(
|
|
7328
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7328
7329
|
process.exit(1);
|
|
7329
7330
|
}
|
|
7330
7331
|
});
|
|
7331
7332
|
program.command("doctor").description("Diagnose design system configuration issues").option("--root <dir>", "Project root directory", process.cwd()).option("--json", "Output results as JSON").option("--fix", "Auto-fix issues where possible").action(async (options) => {
|
|
7332
7333
|
try {
|
|
7334
|
+
const { doctor } = await import("./doctor-BDPMYYE6.js");
|
|
7333
7335
|
const result = await doctor({
|
|
7334
7336
|
root: options.root,
|
|
7335
7337
|
json: options.json,
|
|
@@ -7339,15 +7341,15 @@ program.command("doctor").description("Diagnose design system configuration issu
|
|
|
7339
7341
|
process.exit(1);
|
|
7340
7342
|
}
|
|
7341
7343
|
} catch (error) {
|
|
7342
|
-
console.error(
|
|
7344
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7343
7345
|
process.exit(1);
|
|
7344
7346
|
}
|
|
7345
7347
|
});
|
|
7346
7348
|
var governCmd = program.command("govern").description(EXPERIMENTAL ? "AI UI governance checks" : "AI UI governance checks (preview \u2014 set FRAGMENTS_EXPERIMENTAL=1)").hook("preAction", () => {
|
|
7347
7349
|
if (!EXPERIMENTAL) {
|
|
7348
|
-
console.log(
|
|
7350
|
+
console.log(pc25.yellow(`
|
|
7349
7351
|
Fragments governance is not yet publicly available.`));
|
|
7350
|
-
console.log(
|
|
7352
|
+
console.log(pc25.dim(` Set FRAGMENTS_EXPERIMENTAL=1 to enable preview features.
|
|
7351
7353
|
`));
|
|
7352
7354
|
process.exit(1);
|
|
7353
7355
|
}
|
|
@@ -7362,7 +7364,7 @@ governCmd.command("check").description("Validate a UISpec against governance pol
|
|
|
7362
7364
|
});
|
|
7363
7365
|
process.exit(exitCode);
|
|
7364
7366
|
} catch (error) {
|
|
7365
|
-
console.error(
|
|
7367
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7366
7368
|
process.exit(1);
|
|
7367
7369
|
}
|
|
7368
7370
|
});
|
|
@@ -7370,7 +7372,7 @@ governCmd.command("init").description("Generate a fragments.config.ts with gover
|
|
|
7370
7372
|
try {
|
|
7371
7373
|
await governInit({ output: options.output });
|
|
7372
7374
|
} catch (error) {
|
|
7373
|
-
console.error(
|
|
7375
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7374
7376
|
process.exit(1);
|
|
7375
7377
|
}
|
|
7376
7378
|
});
|
|
@@ -7378,7 +7380,7 @@ governCmd.command("report").description("Summarize governance audit log").action
|
|
|
7378
7380
|
try {
|
|
7379
7381
|
await governReport();
|
|
7380
7382
|
} catch (error) {
|
|
7381
|
-
console.error(
|
|
7383
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7382
7384
|
process.exit(1);
|
|
7383
7385
|
}
|
|
7384
7386
|
});
|
|
@@ -7386,13 +7388,13 @@ governCmd.command("connect").description("Connect your project to the Fragments
|
|
|
7386
7388
|
try {
|
|
7387
7389
|
await governConnect();
|
|
7388
7390
|
} catch (error) {
|
|
7389
|
-
console.error(
|
|
7391
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7390
7392
|
process.exit(1);
|
|
7391
7393
|
}
|
|
7392
7394
|
});
|
|
7393
7395
|
governCmd.command("scan").description("Scan JSX/TSX codebase for governance violations").option("-d, --dir <path>", "Root directory (default: auto-detect)").option("-c, --config <path>", "Path to govern.config.ts").option("-f, --format <format>", "Output format: summary, json, sarif", "summary").option("-q, --quiet", "Suppress non-error output").action(async (options) => {
|
|
7394
7396
|
try {
|
|
7395
|
-
const { governScan } = await import("./govern-scan-
|
|
7397
|
+
const { governScan } = await import("./govern-scan-OYFZYOQW.js");
|
|
7396
7398
|
const { exitCode } = await governScan({
|
|
7397
7399
|
dir: options.dir,
|
|
7398
7400
|
config: options.config,
|
|
@@ -7401,13 +7403,13 @@ governCmd.command("scan").description("Scan JSX/TSX codebase for governance viol
|
|
|
7401
7403
|
});
|
|
7402
7404
|
process.exit(exitCode);
|
|
7403
7405
|
} catch (error) {
|
|
7404
|
-
console.error(
|
|
7406
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7405
7407
|
process.exit(1);
|
|
7406
7408
|
}
|
|
7407
7409
|
});
|
|
7408
7410
|
governCmd.command("watch").description("Watch JSX/TSX files and re-check on changes").option("-d, --dir <path>", "Root directory (default: auto-detect)").option("-c, --config <path>", "Path to govern.config.ts").option("-q, --quiet", "Suppress non-error output").option("--debounce <ms>", "Debounce interval in ms", "300").action(async (options) => {
|
|
7409
7411
|
try {
|
|
7410
|
-
const { governWatch } = await import("./govern-scan-
|
|
7412
|
+
const { governWatch } = await import("./govern-scan-OYFZYOQW.js");
|
|
7411
7413
|
await governWatch({
|
|
7412
7414
|
dir: options.dir,
|
|
7413
7415
|
config: options.config,
|
|
@@ -7415,7 +7417,22 @@ governCmd.command("watch").description("Watch JSX/TSX files and re-check on chan
|
|
|
7415
7417
|
debounce: parseInt(options.debounce, 10)
|
|
7416
7418
|
});
|
|
7417
7419
|
} catch (error) {
|
|
7418
|
-
console.error(
|
|
7420
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7421
|
+
process.exit(1);
|
|
7422
|
+
}
|
|
7423
|
+
});
|
|
7424
|
+
governCmd.command("push-contracts").description("Push component contracts to Fragments Cloud").option("-i, --input <path>", "Path to fragments.json (default: ./fragments.json)").option("--url <url>", "Fragments Cloud URL").option("--api-key <key>", "API key (default: FRAGMENTS_API_KEY env var)").option("-q, --quiet", "Suppress non-error output").action(async (options) => {
|
|
7425
|
+
try {
|
|
7426
|
+
const { pushContracts } = await import("./push-contracts-WY32TFP6.js");
|
|
7427
|
+
const { exitCode } = await pushContracts({
|
|
7428
|
+
input: options.input,
|
|
7429
|
+
url: options.url,
|
|
7430
|
+
apiKey: options.apiKey,
|
|
7431
|
+
quiet: options.quiet
|
|
7432
|
+
});
|
|
7433
|
+
process.exit(exitCode);
|
|
7434
|
+
} catch (error) {
|
|
7435
|
+
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7419
7436
|
process.exit(1);
|
|
7420
7437
|
}
|
|
7421
7438
|
});
|