@fragments-sdk/cli 0.15.10 → 0.16.0
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/bin.js +896 -787
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-6SQPP47U.js → chunk-77AAP6R6.js} +532 -31
- package/dist/chunk-77AAP6R6.js.map +1 -0
- package/dist/{chunk-5JF26E55.js → chunk-ACFVKMVZ.js} +11 -11
- package/dist/{chunk-BJE3425I.js → chunk-ACX7YWZW.js} +2 -2
- package/dist/{chunk-ONUP6Z4W.js → chunk-G6UVWMFU.js} +8 -8
- package/dist/{chunk-2WXKALIG.js → chunk-OZZ4SVZX.js} +2 -2
- package/dist/{chunk-32LIWN2P.js → chunk-SJFSG7QF.js} +582 -261
- package/dist/chunk-SJFSG7QF.js.map +1 -0
- package/dist/{chunk-HQ6A6DTV.js → chunk-XRADMHMV.js} +315 -1089
- package/dist/chunk-XRADMHMV.js.map +1 -0
- package/dist/core/index.js +53 -1
- package/dist/{create-EXURTBKK.js → create-3ZFYQB3T.js} +2 -2
- package/dist/{doctor-BDPMYYE6.js → doctor-4IDUM7HI.js} +2 -2
- package/dist/{generate-PVOLUAAC.js → generate-VNUUWVWQ.js} +4 -4
- package/dist/{govern-scan-DW4QUAYD.js → govern-scan-HTACKYPF.js} +158 -120
- package/dist/govern-scan-HTACKYPF.js.map +1 -0
- package/dist/index.js +6 -7
- package/dist/index.js.map +1 -1
- package/dist/{init-SSGUSP7Z.js → init-PXFRAQ64.js} +5 -5
- package/dist/mcp-bin.js +2 -2
- package/dist/{scan-PKSYSTRR.js → scan-L4GWGEZX.js} +5 -6
- package/dist/{scan-generate-VY27PIOX.js → scan-generate-74EYSAGH.js} +4 -4
- package/dist/{service-QJGWUIVL.js → service-VELQHEWV.js} +12 -14
- package/dist/{snapshot-WIJMEIFT.js → snapshot-DT4B6DPR.js} +2 -2
- package/dist/{static-viewer-7QIBQZRC.js → static-viewer-E4OJWFDJ.js} +3 -3
- package/dist/{test-64Z5BKBA.js → test-QJY2QO4X.js} +3 -3
- package/dist/{token-normalizer-TEPOVBPV.js → token-normalizer-56H4242J.js} +2 -2
- package/dist/{tokens-NZWFQIAB.js → tokens-K6URXFPK.js} +7 -8
- package/dist/{tokens-NZWFQIAB.js.map → tokens-K6URXFPK.js.map} +1 -1
- package/dist/{tokens-generate-5JQSJ27E.js → tokens-generate-EL6IN536.js} +2 -2
- package/package.json +7 -6
- package/src/bin.ts +49 -88
- 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__/context-cloud.test.ts +291 -0
- package/src/commands/__tests__/govern-scan.test.ts +185 -0
- package/src/commands/__tests__/govern.test.ts +1 -0
- package/src/commands/context-cloud.ts +355 -0
- package/src/commands/govern-scan-report.ts +170 -0
- package/src/commands/govern-scan.ts +42 -147
- package/src/commands/govern.ts +0 -157
- package/dist/chunk-32LIWN2P.js.map +0 -1
- package/dist/chunk-6SQPP47U.js.map +0 -1
- package/dist/chunk-HQ6A6DTV.js.map +0 -1
- package/dist/chunk-MHIBEEW4.js +0 -511
- package/dist/chunk-MHIBEEW4.js.map +0 -1
- package/dist/govern-scan-DW4QUAYD.js.map +0 -1
- package/dist/init-cloud-3DNKPWFB.js +0 -304
- package/dist/init-cloud-3DNKPWFB.js.map +0 -1
- package/dist/node-37AUE74M.js +0 -65
- package/dist/push-contracts-WY32TFP6.js +0 -84
- package/dist/push-contracts-WY32TFP6.js.map +0 -1
- package/dist/static-viewer-7QIBQZRC.js.map +0 -1
- package/dist/token-parser-32KOIOFN.js +0 -22
- package/dist/token-parser-32KOIOFN.js.map +0 -1
- package/dist/tokens-push-HY3KO36V.js +0 -148
- package/dist/tokens-push-HY3KO36V.js.map +0 -1
- package/src/commands/init-cloud.ts +0 -382
- package/src/commands/push-contracts.ts +0 -112
- package/src/commands/tokens-push.ts +0 -199
- /package/dist/{chunk-5JF26E55.js.map → chunk-ACFVKMVZ.js.map} +0 -0
- /package/dist/{chunk-BJE3425I.js.map → chunk-ACX7YWZW.js.map} +0 -0
- /package/dist/{chunk-ONUP6Z4W.js.map → chunk-G6UVWMFU.js.map} +0 -0
- /package/dist/{chunk-2WXKALIG.js.map → chunk-OZZ4SVZX.js.map} +0 -0
- /package/dist/{create-EXURTBKK.js.map → create-3ZFYQB3T.js.map} +0 -0
- /package/dist/{doctor-BDPMYYE6.js.map → doctor-4IDUM7HI.js.map} +0 -0
- /package/dist/{generate-PVOLUAAC.js.map → generate-VNUUWVWQ.js.map} +0 -0
- /package/dist/{init-SSGUSP7Z.js.map → init-PXFRAQ64.js.map} +0 -0
- /package/dist/{node-37AUE74M.js.map → scan-L4GWGEZX.js.map} +0 -0
- /package/dist/{scan-generate-VY27PIOX.js.map → scan-generate-74EYSAGH.js.map} +0 -0
- /package/dist/{scan-PKSYSTRR.js.map → service-VELQHEWV.js.map} +0 -0
- /package/dist/{snapshot-WIJMEIFT.js.map → snapshot-DT4B6DPR.js.map} +0 -0
- /package/dist/{service-QJGWUIVL.js.map → static-viewer-E4OJWFDJ.js.map} +0 -0
- /package/dist/{test-64Z5BKBA.js.map → test-QJY2QO4X.js.map} +0 -0
- /package/dist/{token-normalizer-TEPOVBPV.js.map → token-normalizer-56H4242J.js.map} +0 -0
- /package/dist/{tokens-generate-5JQSJ27E.js.map → tokens-generate-EL6IN536.js.map} +0 -0
package/dist/bin.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
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";
|
|
6
|
-
import {
|
|
7
|
-
scan
|
|
8
|
-
} from "./chunk-ONUP6Z4W.js";
|
|
9
3
|
import {
|
|
10
4
|
createAIClient,
|
|
11
5
|
detectProvider,
|
|
12
6
|
getApiKey
|
|
13
7
|
} from "./chunk-SXTKFDCR.js";
|
|
8
|
+
import {
|
|
9
|
+
setup
|
|
10
|
+
} from "./chunk-ACX7YWZW.js";
|
|
14
11
|
import {
|
|
15
12
|
runAnalyzeCommand,
|
|
16
13
|
runDiffCommand,
|
|
@@ -20,7 +17,19 @@ import {
|
|
|
20
17
|
validateDrift,
|
|
21
18
|
validateSchema,
|
|
22
19
|
validateSnippets
|
|
23
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-ACFVKMVZ.js";
|
|
21
|
+
import {
|
|
22
|
+
projectFields
|
|
23
|
+
} from "./chunk-T47OLCSF.js";
|
|
24
|
+
import {
|
|
25
|
+
scan
|
|
26
|
+
} from "./chunk-G6UVWMFU.js";
|
|
27
|
+
import {
|
|
28
|
+
discoverFragmentFiles,
|
|
29
|
+
loadConfig,
|
|
30
|
+
loadFragmentFile,
|
|
31
|
+
parseFragmentFile
|
|
32
|
+
} from "./chunk-XRADMHMV.js";
|
|
24
33
|
import {
|
|
25
34
|
FigmaClient,
|
|
26
35
|
StorageManager,
|
|
@@ -34,26 +43,18 @@ import {
|
|
|
34
43
|
parseAllStories,
|
|
35
44
|
renderAllComponentVariants,
|
|
36
45
|
shutdownSharedPool
|
|
37
|
-
} from "./chunk-
|
|
38
|
-
import {
|
|
39
|
-
projectFields
|
|
40
|
-
} from "./chunk-T47OLCSF.js";
|
|
46
|
+
} from "./chunk-77AAP6R6.js";
|
|
41
47
|
import "./chunk-D2CDBRNU.js";
|
|
42
|
-
import {
|
|
43
|
-
discoverFragmentFiles,
|
|
44
|
-
loadConfig,
|
|
45
|
-
loadFragmentFile,
|
|
46
|
-
parseFragmentFile
|
|
47
|
-
} from "./chunk-HQ6A6DTV.js";
|
|
48
48
|
import {
|
|
49
49
|
BRAND,
|
|
50
50
|
budgetBar,
|
|
51
|
+
bundleManifestSchema,
|
|
52
|
+
bundleTargetSchema,
|
|
51
53
|
classifyComplexity,
|
|
52
54
|
formatBytes,
|
|
53
55
|
generateContext,
|
|
54
56
|
resolvePerformanceConfig
|
|
55
|
-
} from "./chunk-
|
|
56
|
-
import "./chunk-MHIBEEW4.js";
|
|
57
|
+
} from "./chunk-SJFSG7QF.js";
|
|
57
58
|
import {
|
|
58
59
|
getScanStats,
|
|
59
60
|
scanCodebase
|
|
@@ -62,10 +63,10 @@ import "./chunk-7DZC4YEV.js";
|
|
|
62
63
|
|
|
63
64
|
// src/bin.ts
|
|
64
65
|
import { Command } from "commander";
|
|
65
|
-
import
|
|
66
|
+
import pc26 from "picocolors";
|
|
66
67
|
import { readFileSync } from "fs";
|
|
67
68
|
import { fileURLToPath } from "url";
|
|
68
|
-
import { dirname as
|
|
69
|
+
import { dirname as dirname7, join as join11 } from "path";
|
|
69
70
|
|
|
70
71
|
// src/commands/validate.ts
|
|
71
72
|
import pc from "picocolors";
|
|
@@ -367,23 +368,288 @@ async function context(options = {}) {
|
|
|
367
368
|
};
|
|
368
369
|
}
|
|
369
370
|
|
|
370
|
-
// src/commands/
|
|
371
|
+
// src/commands/context-cloud.ts
|
|
372
|
+
import { confirm } from "@inquirer/prompts";
|
|
373
|
+
import {
|
|
374
|
+
access,
|
|
375
|
+
mkdir,
|
|
376
|
+
readFile as readFile2,
|
|
377
|
+
writeFile
|
|
378
|
+
} from "fs/promises";
|
|
379
|
+
import { dirname, resolve as resolve2 } from "path";
|
|
371
380
|
import pc4 from "picocolors";
|
|
381
|
+
import { strFromU8, unzipSync } from "fflate";
|
|
382
|
+
var DEFAULT_CLOUD_URL = process.env.FRAGMENTS_CLOUD_URL ?? "https://app.usefragments.com";
|
|
383
|
+
var MANAGED_START = "<!-- BEGIN FRAGMENTS DESIGN SYSTEM -->";
|
|
384
|
+
var MANAGED_END = "<!-- END FRAGMENTS DESIGN SYSTEM -->";
|
|
385
|
+
var ROOT_FILE_TARGETS = {
|
|
386
|
+
agents: {
|
|
387
|
+
helperPath: ".fragments/instructions/agents.md",
|
|
388
|
+
rootPath: "AGENTS.md"
|
|
389
|
+
},
|
|
390
|
+
claude: {
|
|
391
|
+
helperPath: ".fragments/instructions/claude-code.md",
|
|
392
|
+
rootPath: "CLAUDE.md"
|
|
393
|
+
},
|
|
394
|
+
copilot: {
|
|
395
|
+
helperPath: ".fragments/instructions/copilot.md",
|
|
396
|
+
rootPath: ".github/copilot-instructions.md"
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
function parseManifestOrThrow(payload, sourceLabel) {
|
|
400
|
+
let parsed;
|
|
401
|
+
try {
|
|
402
|
+
parsed = JSON.parse(payload);
|
|
403
|
+
} catch {
|
|
404
|
+
throw new Error(`Invalid ${sourceLabel} Fragments manifest JSON.`);
|
|
405
|
+
}
|
|
406
|
+
const result = bundleManifestSchema.safeParse(parsed);
|
|
407
|
+
if (result.success) {
|
|
408
|
+
return result.data;
|
|
409
|
+
}
|
|
410
|
+
const schemaVersion = parsed && typeof parsed === "object" && "schemaVersion" in parsed && typeof parsed.schemaVersion === "number" ? parsed.schemaVersion : null;
|
|
411
|
+
if (schemaVersion !== null) {
|
|
412
|
+
throw new Error(
|
|
413
|
+
`Unsupported Fragments bundle schemaVersion ${schemaVersion}. Upgrade your CLI.`
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
throw new Error(`Invalid ${sourceLabel} Fragments manifest.`);
|
|
417
|
+
}
|
|
418
|
+
function resolveApiKey(explicit) {
|
|
419
|
+
const apiKey = explicit ?? process.env.FRAGMENTS_API_KEY;
|
|
420
|
+
if (!apiKey) {
|
|
421
|
+
throw new Error(
|
|
422
|
+
"Missing Fragments Cloud API key. Set FRAGMENTS_API_KEY or pass --api-key."
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
return apiKey;
|
|
426
|
+
}
|
|
427
|
+
function resolveTargets(csv) {
|
|
428
|
+
if (!csv) return null;
|
|
429
|
+
const targets = csv.split(",").map((value) => value.trim()).filter(Boolean);
|
|
430
|
+
for (const target of targets) {
|
|
431
|
+
bundleTargetSchema.parse(target);
|
|
432
|
+
}
|
|
433
|
+
return Array.from(new Set(targets));
|
|
434
|
+
}
|
|
435
|
+
function managedBlock(content) {
|
|
436
|
+
return `${MANAGED_START}
|
|
437
|
+
${content.trim()}
|
|
438
|
+
${MANAGED_END}
|
|
439
|
+
`;
|
|
440
|
+
}
|
|
441
|
+
function upsertManagedBlock(existing, content) {
|
|
442
|
+
const block = managedBlock(content);
|
|
443
|
+
if (existing.includes(MANAGED_START) && existing.includes(MANAGED_END)) {
|
|
444
|
+
return existing.replace(
|
|
445
|
+
new RegExp(
|
|
446
|
+
`${MANAGED_START}[\\s\\S]*?${MANAGED_END}\\n?`,
|
|
447
|
+
"m"
|
|
448
|
+
),
|
|
449
|
+
block
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
return existing.trim() ? `${existing.trimEnd()}
|
|
453
|
+
|
|
454
|
+
${block}` : block;
|
|
455
|
+
}
|
|
456
|
+
async function fileExists(path) {
|
|
457
|
+
try {
|
|
458
|
+
await access(path);
|
|
459
|
+
return true;
|
|
460
|
+
} catch {
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
async function writeManagedFile(path, content, dryRun) {
|
|
465
|
+
if (dryRun) return;
|
|
466
|
+
await mkdir(dirname(path), { recursive: true });
|
|
467
|
+
await writeFile(path, content, "utf-8");
|
|
468
|
+
}
|
|
469
|
+
async function fetchBundle(args) {
|
|
470
|
+
const url = new URL("/api/bundle", DEFAULT_CLOUD_URL);
|
|
471
|
+
if (args.targets?.length) {
|
|
472
|
+
url.searchParams.set("targets", args.targets.join(","));
|
|
473
|
+
}
|
|
474
|
+
const response = await fetch(url, {
|
|
475
|
+
headers: {
|
|
476
|
+
Authorization: `Bearer ${args.apiKey}`
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
if (!response.ok) {
|
|
480
|
+
const payload = await response.json().catch(() => null);
|
|
481
|
+
throw new Error(payload?.message ?? payload?.error ?? "Bundle download failed.");
|
|
482
|
+
}
|
|
483
|
+
try {
|
|
484
|
+
const archive = unzipSync(new Uint8Array(await response.arrayBuffer()));
|
|
485
|
+
return Object.fromEntries(
|
|
486
|
+
Object.entries(archive).map(([path, bytes]) => [path, strFromU8(bytes)])
|
|
487
|
+
);
|
|
488
|
+
} catch {
|
|
489
|
+
throw new Error("Bundle download was corrupted, try again.");
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
async function fetchRemoteManifest(apiKey) {
|
|
493
|
+
const url = new URL("/api/bundle-artifact", DEFAULT_CLOUD_URL);
|
|
494
|
+
url.searchParams.set("artifact", "manifest");
|
|
495
|
+
const response = await fetch(url, {
|
|
496
|
+
headers: {
|
|
497
|
+
Authorization: `Bearer ${apiKey}`
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
if (!response.ok) {
|
|
501
|
+
const payload2 = await response.json().catch(() => null);
|
|
502
|
+
throw new Error(payload2?.message ?? payload2?.error ?? "Unable to fetch remote manifest.");
|
|
503
|
+
}
|
|
504
|
+
const payload = await response.json();
|
|
505
|
+
return parseManifestOrThrow(payload.content, "remote");
|
|
506
|
+
}
|
|
507
|
+
function resolveRootFileMode(options) {
|
|
508
|
+
if (options.rootFiles) {
|
|
509
|
+
return options.rootFiles;
|
|
510
|
+
}
|
|
511
|
+
if (options.yes || !process.stdout.isTTY) {
|
|
512
|
+
return "never";
|
|
513
|
+
}
|
|
514
|
+
return "prompt";
|
|
515
|
+
}
|
|
516
|
+
async function maybePatchRootFile(args) {
|
|
517
|
+
const config = ROOT_FILE_TARGETS[args.target];
|
|
518
|
+
if (!config) return;
|
|
519
|
+
const snippet = args.files[config.helperPath];
|
|
520
|
+
if (!snippet) return;
|
|
521
|
+
const rootPath = resolve2(args.projectRoot, config.rootPath);
|
|
522
|
+
const exists = await fileExists(rootPath);
|
|
523
|
+
if (args.mode === "never") {
|
|
524
|
+
console.log(
|
|
525
|
+
pc4.dim(
|
|
526
|
+
` \u2022 Next step: paste ${config.helperPath} into ${config.rootPath} if you want repo-level instructions.`
|
|
527
|
+
)
|
|
528
|
+
);
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
if (args.mode === "prompt" && exists) {
|
|
532
|
+
const approved = await confirm({
|
|
533
|
+
message: `Update ${config.rootPath} with a Fragments-managed block?`,
|
|
534
|
+
default: true
|
|
535
|
+
});
|
|
536
|
+
if (!approved) {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if (args.mode === "prompt" && !exists) {
|
|
541
|
+
console.log(
|
|
542
|
+
pc4.dim(
|
|
543
|
+
` \u2022 ${config.rootPath} does not exist. Keeping snippet-only output in ${config.helperPath}.`
|
|
544
|
+
)
|
|
545
|
+
);
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
const nextContent = exists ? upsertManagedBlock(await readFile2(rootPath, "utf-8"), snippet) : managedBlock(snippet);
|
|
549
|
+
await writeManagedFile(rootPath, nextContent, args.dryRun);
|
|
550
|
+
console.log(pc4.green(` \u2022 Updated ${config.rootPath}`));
|
|
551
|
+
}
|
|
552
|
+
async function maybeUpdateGitignore(projectRoot, dryRun) {
|
|
553
|
+
const gitignorePath = resolve2(projectRoot, ".gitignore");
|
|
554
|
+
const existing = await fileExists(gitignorePath) ? await readFile2(gitignorePath, "utf-8") : "";
|
|
555
|
+
if (existing.includes(".fragments/")) {
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
const next = existing.trimEnd() ? `${existing.trimEnd()}
|
|
559
|
+
.fragments/
|
|
560
|
+
` : ".fragments/\n";
|
|
561
|
+
await writeManagedFile(gitignorePath, next, dryRun);
|
|
562
|
+
}
|
|
563
|
+
async function contextInstallCloud(options) {
|
|
564
|
+
const projectRoot = resolve2(process.cwd(), options.cwd ?? ".");
|
|
565
|
+
const apiKey = resolveApiKey(options.apiKey);
|
|
566
|
+
const targets = resolveTargets(options.targets);
|
|
567
|
+
const files = await fetchBundle({ apiKey, targets });
|
|
568
|
+
const manifest = parseManifestOrThrow(
|
|
569
|
+
files[".fragments/manifest.json"] ?? "{}",
|
|
570
|
+
"downloaded"
|
|
571
|
+
);
|
|
572
|
+
const mode = resolveRootFileMode(options);
|
|
573
|
+
console.log(
|
|
574
|
+
pc4.cyan(
|
|
575
|
+
`Installing Fragments bundle (${manifest.totalComponents} components, revision ${manifest.catalogRevision})`
|
|
576
|
+
)
|
|
577
|
+
);
|
|
578
|
+
for (const [relativePath, content] of Object.entries(files).sort(
|
|
579
|
+
([a], [b]) => a.localeCompare(b)
|
|
580
|
+
)) {
|
|
581
|
+
const absolutePath = resolve2(projectRoot, relativePath);
|
|
582
|
+
if (!options.dryRun) {
|
|
583
|
+
await mkdir(dirname(absolutePath), { recursive: true });
|
|
584
|
+
await writeFile(absolutePath, content, "utf-8");
|
|
585
|
+
}
|
|
586
|
+
console.log(pc4.green(` \u2022 Wrote ${relativePath}`));
|
|
587
|
+
}
|
|
588
|
+
if (options.gitignoreFragments) {
|
|
589
|
+
await maybeUpdateGitignore(projectRoot, options.dryRun);
|
|
590
|
+
console.log(
|
|
591
|
+
pc4.yellow(
|
|
592
|
+
" \u2022 Added .fragments/ to .gitignore. This disables committed offline context by default."
|
|
593
|
+
)
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
for (const target of ["agents", "claude", "copilot"]) {
|
|
597
|
+
await maybePatchRootFile({
|
|
598
|
+
projectRoot,
|
|
599
|
+
target,
|
|
600
|
+
files,
|
|
601
|
+
mode,
|
|
602
|
+
dryRun: options.dryRun
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
console.log(
|
|
606
|
+
pc4.dim(
|
|
607
|
+
`Status: ${options.dryRun ? "dry run only \u2014 no files written" : "bundle installed successfully"}`
|
|
608
|
+
)
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
async function contextStatusCloud(options) {
|
|
612
|
+
const projectRoot = resolve2(process.cwd(), options.cwd ?? ".");
|
|
613
|
+
const manifestPath = resolve2(projectRoot, ".fragments/manifest.json");
|
|
614
|
+
if (!await fileExists(manifestPath)) {
|
|
615
|
+
console.log(pc4.yellow("Status: missing"));
|
|
616
|
+
console.log(pc4.dim("No local .fragments/manifest.json was found."));
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
const parsedLocalManifest = parseManifestOrThrow(
|
|
620
|
+
await readFile2(manifestPath, "utf-8"),
|
|
621
|
+
"local"
|
|
622
|
+
);
|
|
623
|
+
const remoteManifest = await fetchRemoteManifest(resolveApiKey(options.apiKey));
|
|
624
|
+
const status = parsedLocalManifest.catalogRevision === remoteManifest.catalogRevision ? "up-to-date" : "outdated";
|
|
625
|
+
console.log(pc4.cyan(`Status: ${status}`));
|
|
626
|
+
console.log(
|
|
627
|
+
pc4.dim(` Local revision: ${parsedLocalManifest.catalogRevision}`)
|
|
628
|
+
);
|
|
629
|
+
console.log(pc4.dim(` Remote revision: ${remoteManifest.catalogRevision}`));
|
|
630
|
+
console.log(
|
|
631
|
+
pc4.dim(` Local updated: ${parsedLocalManifest.catalogUpdatedAt}`)
|
|
632
|
+
);
|
|
633
|
+
console.log(pc4.dim(` Remote updated: ${remoteManifest.catalogUpdatedAt}`));
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// src/commands/list.ts
|
|
637
|
+
import pc5 from "picocolors";
|
|
372
638
|
async function list(options = {}) {
|
|
373
639
|
const { config, configDir } = await loadConfig(options.config);
|
|
374
640
|
const files = await discoverFragmentFiles(config, configDir);
|
|
375
|
-
console.log(
|
|
641
|
+
console.log(pc5.cyan(`
|
|
376
642
|
${BRAND.name} - Discovered Fragments
|
|
377
643
|
`));
|
|
378
644
|
if (files.length === 0) {
|
|
379
|
-
console.log(
|
|
380
|
-
console.log(
|
|
645
|
+
console.log(pc5.yellow("No fragment files found.\n"));
|
|
646
|
+
console.log(pc5.dim(`Looking for: ${config.include.join(", ")}`));
|
|
381
647
|
return { success: true, files: [] };
|
|
382
648
|
}
|
|
383
649
|
for (const file of files) {
|
|
384
|
-
console.log(` ${
|
|
650
|
+
console.log(` ${pc5.dim("\u2022")} ${file.relativePath}`);
|
|
385
651
|
}
|
|
386
|
-
console.log(
|
|
652
|
+
console.log(pc5.dim(`
|
|
387
653
|
${files.length} fragment(s) found
|
|
388
654
|
`));
|
|
389
655
|
return { success: true, files };
|
|
@@ -392,11 +658,11 @@ ${BRAND.name} - Discovered Fragments
|
|
|
392
658
|
// src/commands/reset.ts
|
|
393
659
|
import { stat, rm, unlink } from "fs/promises";
|
|
394
660
|
import { join, relative } from "path";
|
|
395
|
-
import
|
|
661
|
+
import pc6 from "picocolors";
|
|
396
662
|
import fg from "fast-glob";
|
|
397
663
|
async function reset(options = {}) {
|
|
398
664
|
const { yes = false, dryRun = false } = options;
|
|
399
|
-
console.log(
|
|
665
|
+
console.log(pc6.cyan(`
|
|
400
666
|
${BRAND.name} Reset
|
|
401
667
|
`));
|
|
402
668
|
const projectRoot = process.cwd();
|
|
@@ -436,7 +702,7 @@ ${BRAND.name} Reset
|
|
|
436
702
|
}
|
|
437
703
|
} catch {
|
|
438
704
|
}
|
|
439
|
-
console.log(
|
|
705
|
+
console.log(pc6.dim("Scanning for generated files...\n"));
|
|
440
706
|
for (const pattern of fragmentPatterns) {
|
|
441
707
|
const matches = await fg(pattern, {
|
|
442
708
|
cwd: projectRoot,
|
|
@@ -460,10 +726,10 @@ ${BRAND.name} Reset
|
|
|
460
726
|
}
|
|
461
727
|
}
|
|
462
728
|
if (filesToDelete.length === 0 && dirsToDelete.length === 0) {
|
|
463
|
-
console.log(
|
|
729
|
+
console.log(pc6.yellow("Nothing to reset. No generated files found.\n"));
|
|
464
730
|
return { success: true, deletedFiles: 0, deletedDirs: 0 };
|
|
465
731
|
}
|
|
466
|
-
console.log(
|
|
732
|
+
console.log(pc6.dim("The following will be deleted:\n"));
|
|
467
733
|
for (const dir of dirsToDelete) {
|
|
468
734
|
const relativePath = relative(projectRoot, dir);
|
|
469
735
|
console.log(` \u{1F4C1} ${relativePath}/`);
|
|
@@ -477,44 +743,44 @@ ${BRAND.name} Reset
|
|
|
477
743
|
console.log(` \u{1F4C4} ${fragmentFiles.length} fragment file(s) (*${BRAND.fileExtension})`);
|
|
478
744
|
if (fragmentFiles.length <= 5) {
|
|
479
745
|
for (const f of fragmentFiles) {
|
|
480
|
-
console.log(
|
|
746
|
+
console.log(pc6.dim(` ${relative(projectRoot, f)}`));
|
|
481
747
|
}
|
|
482
748
|
} else {
|
|
483
749
|
for (const f of fragmentFiles.slice(0, 3)) {
|
|
484
|
-
console.log(
|
|
750
|
+
console.log(pc6.dim(` ${relative(projectRoot, f)}`));
|
|
485
751
|
}
|
|
486
|
-
console.log(
|
|
752
|
+
console.log(pc6.dim(` ... and ${fragmentFiles.length - 3} more`));
|
|
487
753
|
}
|
|
488
754
|
}
|
|
489
755
|
if (mdxFilesFound.length > 0) {
|
|
490
756
|
console.log(` \u{1F4C4} ${mdxFilesFound.length} documentation file(s) (*.mdx)`);
|
|
491
757
|
if (mdxFilesFound.length <= 5) {
|
|
492
758
|
for (const f of mdxFilesFound) {
|
|
493
|
-
console.log(
|
|
759
|
+
console.log(pc6.dim(` ${relative(projectRoot, f)}`));
|
|
494
760
|
}
|
|
495
761
|
} else {
|
|
496
762
|
for (const f of mdxFilesFound.slice(0, 3)) {
|
|
497
|
-
console.log(
|
|
763
|
+
console.log(pc6.dim(` ${relative(projectRoot, f)}`));
|
|
498
764
|
}
|
|
499
|
-
console.log(
|
|
765
|
+
console.log(pc6.dim(` ... and ${mdxFilesFound.length - 3} more`));
|
|
500
766
|
}
|
|
501
767
|
}
|
|
502
768
|
for (const f of otherFiles) {
|
|
503
769
|
console.log(` \u{1F4C4} ${relative(projectRoot, f)}`);
|
|
504
770
|
}
|
|
505
771
|
const totalCount = filesToDelete.length + dirsToDelete.length;
|
|
506
|
-
console.log(
|
|
772
|
+
console.log(pc6.dim(`
|
|
507
773
|
Total: ${totalCount} item(s)
|
|
508
774
|
`));
|
|
509
775
|
if (dryRun) {
|
|
510
|
-
console.log(
|
|
776
|
+
console.log(pc6.yellow("[Dry run - no files were deleted]\n"));
|
|
511
777
|
return { success: true, deletedFiles: 0, deletedDirs: 0 };
|
|
512
778
|
}
|
|
513
779
|
let proceed = yes;
|
|
514
780
|
if (!proceed) {
|
|
515
|
-
const { confirm } = await import("@inquirer/prompts");
|
|
781
|
+
const { confirm: confirm2 } = await import("@inquirer/prompts");
|
|
516
782
|
try {
|
|
517
|
-
proceed = await
|
|
783
|
+
proceed = await confirm2({
|
|
518
784
|
message: `Delete ${totalCount} item(s)?`,
|
|
519
785
|
default: false
|
|
520
786
|
});
|
|
@@ -523,7 +789,7 @@ ${BRAND.name} Reset
|
|
|
523
789
|
}
|
|
524
790
|
}
|
|
525
791
|
if (!proceed) {
|
|
526
|
-
console.log(
|
|
792
|
+
console.log(pc6.dim("\nNo changes made.\n"));
|
|
527
793
|
return { success: true, deletedFiles: 0, deletedDirs: 0 };
|
|
528
794
|
}
|
|
529
795
|
console.log();
|
|
@@ -532,10 +798,10 @@ ${BRAND.name} Reset
|
|
|
532
798
|
try {
|
|
533
799
|
const relativePath = relative(projectRoot, dir);
|
|
534
800
|
await rm(dir, { recursive: true, force: true });
|
|
535
|
-
console.log(` ${
|
|
801
|
+
console.log(` ${pc6.green("\u2713")} Deleted ${relativePath}/`);
|
|
536
802
|
deletedDirs++;
|
|
537
803
|
} catch {
|
|
538
|
-
console.log(` ${
|
|
804
|
+
console.log(` ${pc6.red("\u2717")} Failed: ${relative(projectRoot, dir)}`);
|
|
539
805
|
}
|
|
540
806
|
}
|
|
541
807
|
let deletedCount = 0;
|
|
@@ -549,16 +815,16 @@ ${BRAND.name} Reset
|
|
|
549
815
|
}
|
|
550
816
|
}
|
|
551
817
|
if (deletedCount > 0) {
|
|
552
|
-
console.log(` ${
|
|
818
|
+
console.log(` ${pc6.green("\u2713")} Deleted ${deletedCount} file(s)`);
|
|
553
819
|
}
|
|
554
820
|
if (failedCount > 0) {
|
|
555
|
-
console.log(` ${
|
|
821
|
+
console.log(` ${pc6.red("\u2717")} Failed to delete ${failedCount} file(s)`);
|
|
556
822
|
}
|
|
557
|
-
console.log(
|
|
823
|
+
console.log(pc6.green(`
|
|
558
824
|
\u2713 Reset complete
|
|
559
825
|
`));
|
|
560
|
-
console.log(
|
|
561
|
-
console.log(
|
|
826
|
+
console.log(pc6.dim(`Config file retained: ${BRAND.configFile}`));
|
|
827
|
+
console.log(pc6.dim(`Run ${pc6.cyan(`${BRAND.cliCommand} init`)} to start fresh
|
|
562
828
|
`));
|
|
563
829
|
return {
|
|
564
830
|
success: true,
|
|
@@ -568,9 +834,9 @@ ${BRAND.name} Reset
|
|
|
568
834
|
}
|
|
569
835
|
|
|
570
836
|
// src/commands/compare.ts
|
|
571
|
-
import
|
|
572
|
-
import { writeFile, mkdir } from "fs/promises";
|
|
573
|
-
import { resolve as
|
|
837
|
+
import pc7 from "picocolors";
|
|
838
|
+
import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
839
|
+
import { resolve as resolve3, join as join2 } from "path";
|
|
574
840
|
async function compare(component, options = {}) {
|
|
575
841
|
const {
|
|
576
842
|
variant,
|
|
@@ -581,13 +847,13 @@ async function compare(component, options = {}) {
|
|
|
581
847
|
port = 6006
|
|
582
848
|
} = options;
|
|
583
849
|
if (!process.env.FIGMA_ACCESS_TOKEN) {
|
|
584
|
-
console.error(
|
|
585
|
-
console.log(
|
|
586
|
-
console.log(
|
|
850
|
+
console.error(pc7.red("\nFIGMA_ACCESS_TOKEN environment variable required."));
|
|
851
|
+
console.log(pc7.dim("Generate at: https://www.figma.com/developers/api#access-tokens"));
|
|
852
|
+
console.log(pc7.dim(" export FIGMA_ACCESS_TOKEN=figd_xxx"));
|
|
587
853
|
process.exit(1);
|
|
588
854
|
}
|
|
589
855
|
const baseUrl = `http://localhost:${port}`;
|
|
590
|
-
console.log(
|
|
856
|
+
console.log(pc7.cyan(`
|
|
591
857
|
${BRAND.name} Design Verification
|
|
592
858
|
`));
|
|
593
859
|
if (all) {
|
|
@@ -600,14 +866,14 @@ ${BRAND.name} Design Verification
|
|
|
600
866
|
componentsToCompare = await selectComponents(baseUrl);
|
|
601
867
|
}
|
|
602
868
|
if (componentsToCompare.length === 0) {
|
|
603
|
-
console.log(
|
|
869
|
+
console.log(pc7.dim("\nNo components selected."));
|
|
604
870
|
return { success: true, passed: 0, failed: 0, skipped: 0 };
|
|
605
871
|
}
|
|
606
872
|
if (componentsToCompare.length === 1) {
|
|
607
|
-
console.log(
|
|
873
|
+
console.log(pc7.dim(`Comparing ${componentsToCompare[0]} to Figma design...
|
|
608
874
|
`));
|
|
609
875
|
} else {
|
|
610
|
-
console.log(
|
|
876
|
+
console.log(pc7.dim(`Comparing ${componentsToCompare.length} components to Figma designs...
|
|
611
877
|
`));
|
|
612
878
|
}
|
|
613
879
|
let passed = 0;
|
|
@@ -627,15 +893,15 @@ ${BRAND.name} Design Verification
|
|
|
627
893
|
const result = await response.json();
|
|
628
894
|
if (result.error) {
|
|
629
895
|
failed++;
|
|
630
|
-
console.log(`${
|
|
896
|
+
console.log(`${pc7.red("\u2717")} ${pc7.bold(comp)} - ${result.error}`);
|
|
631
897
|
continue;
|
|
632
898
|
}
|
|
633
899
|
if (result.match) {
|
|
634
900
|
passed++;
|
|
635
|
-
console.log(`${
|
|
901
|
+
console.log(`${pc7.green("\u2713")} ${pc7.bold(comp)} ${pc7.dim(`${result.diffPercentage}%`)}`);
|
|
636
902
|
} else {
|
|
637
903
|
failed++;
|
|
638
|
-
console.log(`${
|
|
904
|
+
console.log(`${pc7.red("\u2717")} ${pc7.bold(comp)} ${pc7.yellow(`${result.diffPercentage}%`)} ${pc7.dim(`(threshold: ${threshold}%)`)}`);
|
|
639
905
|
}
|
|
640
906
|
if (output && result.rendered && result.figma && result.diff) {
|
|
641
907
|
await saveImages(output, comp, result);
|
|
@@ -643,15 +909,15 @@ ${BRAND.name} Design Verification
|
|
|
643
909
|
}
|
|
644
910
|
if (componentsToCompare.length > 1) {
|
|
645
911
|
console.log();
|
|
646
|
-
console.log(
|
|
912
|
+
console.log(pc7.dim("\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\u2500\u2500"));
|
|
647
913
|
console.log(`
|
|
648
|
-
${
|
|
914
|
+
${pc7.green(`${passed} passed`)}, ${pc7.red(`${failed} failed`)}
|
|
649
915
|
`);
|
|
650
916
|
} else {
|
|
651
917
|
console.log();
|
|
652
918
|
}
|
|
653
919
|
if (output && componentsToCompare.length > 0) {
|
|
654
|
-
console.log(
|
|
920
|
+
console.log(pc7.dim(`Images saved to: ${output}/
|
|
655
921
|
`));
|
|
656
922
|
}
|
|
657
923
|
return {
|
|
@@ -662,7 +928,7 @@ ${pc6.green(`${passed} passed`)}, ${pc6.red(`${failed} failed`)}
|
|
|
662
928
|
};
|
|
663
929
|
}
|
|
664
930
|
async function compareAll(baseUrl, threshold, output) {
|
|
665
|
-
console.log(
|
|
931
|
+
console.log(pc7.dim("Comparing all components with Figma links...\n"));
|
|
666
932
|
const contextResp = await fetch(`${baseUrl}/fragments/context?format=json`);
|
|
667
933
|
if (!contextResp.ok) {
|
|
668
934
|
throw new Error("Failed to fetch fragments. Make sure dev server is running.");
|
|
@@ -676,9 +942,9 @@ async function compareAll(baseUrl, threshold, output) {
|
|
|
676
942
|
fragments = [];
|
|
677
943
|
}
|
|
678
944
|
if (fragments.length === 0) {
|
|
679
|
-
console.log(
|
|
680
|
-
console.log(
|
|
681
|
-
console.log(
|
|
945
|
+
console.log(pc7.yellow("No components found with Figma links."));
|
|
946
|
+
console.log(pc7.dim("Add figma field to your fragment definitions:"));
|
|
947
|
+
console.log(pc7.dim(' meta: { figma: "https://figma.com/file/..." }'));
|
|
682
948
|
return { success: true, passed: 0, failed: 0, skipped: 0 };
|
|
683
949
|
}
|
|
684
950
|
let passed = 0;
|
|
@@ -687,7 +953,7 @@ async function compareAll(baseUrl, threshold, output) {
|
|
|
687
953
|
for (const seg of fragments) {
|
|
688
954
|
if (!seg.figma) {
|
|
689
955
|
skipped++;
|
|
690
|
-
console.log(`${
|
|
956
|
+
console.log(`${pc7.dim("\u23ED\uFE0F")} ${pc7.dim(seg.name)} ${pc7.dim("(no figma link)")}`);
|
|
691
957
|
continue;
|
|
692
958
|
}
|
|
693
959
|
try {
|
|
@@ -703,29 +969,29 @@ async function compareAll(baseUrl, threshold, output) {
|
|
|
703
969
|
const result = await response.json();
|
|
704
970
|
if (result.error) {
|
|
705
971
|
failed++;
|
|
706
|
-
console.log(`${
|
|
972
|
+
console.log(`${pc7.red("\u2717")} ${pc7.bold(seg.name)} - ${result.error}`);
|
|
707
973
|
} else if (result.match) {
|
|
708
974
|
passed++;
|
|
709
|
-
console.log(`${
|
|
975
|
+
console.log(`${pc7.green("\u2713")} ${pc7.bold(seg.name)} ${pc7.dim(`${result.diffPercentage}%`)}`);
|
|
710
976
|
} else {
|
|
711
977
|
failed++;
|
|
712
|
-
console.log(`${
|
|
978
|
+
console.log(`${pc7.red("\u2717")} ${pc7.bold(seg.name)} ${pc7.yellow(`${result.diffPercentage}%`)} ${pc7.dim(`(threshold: ${threshold}%)`)}`);
|
|
713
979
|
}
|
|
714
980
|
if (output && result.rendered && result.figma && result.diff) {
|
|
715
981
|
await saveImages(output, seg.name, result);
|
|
716
982
|
}
|
|
717
983
|
} catch (error) {
|
|
718
984
|
failed++;
|
|
719
|
-
console.log(`${
|
|
985
|
+
console.log(`${pc7.red("\u2717")} ${pc7.bold(seg.name)} - ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
720
986
|
}
|
|
721
987
|
}
|
|
722
988
|
console.log();
|
|
723
|
-
console.log(
|
|
989
|
+
console.log(pc7.dim("\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\u2500\u2500"));
|
|
724
990
|
console.log(`
|
|
725
|
-
${
|
|
991
|
+
${pc7.green(`${passed} passed`)}, ${pc7.red(`${failed} failed`)}, ${pc7.dim(`${skipped} skipped`)}
|
|
726
992
|
`);
|
|
727
993
|
if (output) {
|
|
728
|
-
console.log(
|
|
994
|
+
console.log(pc7.dim(`Images saved to: ${output}/
|
|
729
995
|
`));
|
|
730
996
|
}
|
|
731
997
|
return {
|
|
@@ -736,7 +1002,7 @@ ${pc6.green(`${passed} passed`)}, ${pc6.red(`${failed} failed`)}, ${pc6.dim(`${s
|
|
|
736
1002
|
};
|
|
737
1003
|
}
|
|
738
1004
|
async function selectComponents(baseUrl) {
|
|
739
|
-
console.log(
|
|
1005
|
+
console.log(pc7.dim("Fetching components with Figma links...\n"));
|
|
740
1006
|
const contextResp = await fetch(`${baseUrl}/fragments/context?format=json`);
|
|
741
1007
|
if (!contextResp.ok) {
|
|
742
1008
|
throw new Error("Failed to fetch fragments. Make sure dev server is running.");
|
|
@@ -750,9 +1016,9 @@ async function selectComponents(baseUrl) {
|
|
|
750
1016
|
fragments = [];
|
|
751
1017
|
}
|
|
752
1018
|
if (fragments.length === 0) {
|
|
753
|
-
console.log(
|
|
754
|
-
console.log(
|
|
755
|
-
console.log(
|
|
1019
|
+
console.log(pc7.yellow("No components found with Figma links."));
|
|
1020
|
+
console.log(pc7.dim("Add figma field to your fragment definitions:"));
|
|
1021
|
+
console.log(pc7.dim(' meta: { figma: "https://figma.com/file/..." }'));
|
|
756
1022
|
return [];
|
|
757
1023
|
}
|
|
758
1024
|
const { checkbox } = await import("@inquirer/prompts");
|
|
@@ -768,16 +1034,16 @@ async function selectComponents(baseUrl) {
|
|
|
768
1034
|
pageSize: 15
|
|
769
1035
|
});
|
|
770
1036
|
} catch {
|
|
771
|
-
console.log(
|
|
1037
|
+
console.log(pc7.dim("\nNo changes made."));
|
|
772
1038
|
return [];
|
|
773
1039
|
}
|
|
774
1040
|
}
|
|
775
1041
|
async function saveImages(outputDir, component, result) {
|
|
776
|
-
const dir =
|
|
777
|
-
await
|
|
1042
|
+
const dir = resolve3(process.cwd(), outputDir);
|
|
1043
|
+
await mkdir2(dir, { recursive: true });
|
|
778
1044
|
const saveImage = async (data, filename) => {
|
|
779
1045
|
const base64 = data.replace("data:image/png;base64,", "");
|
|
780
|
-
await
|
|
1046
|
+
await writeFile2(join2(dir, filename), Buffer.from(base64, "base64"));
|
|
781
1047
|
};
|
|
782
1048
|
if (result.rendered) {
|
|
783
1049
|
await saveImage(result.rendered, `${component}-rendered.png`);
|
|
@@ -791,9 +1057,9 @@ async function saveImages(outputDir, component, result) {
|
|
|
791
1057
|
}
|
|
792
1058
|
|
|
793
1059
|
// src/commands/verify.ts
|
|
794
|
-
import
|
|
795
|
-
import { readFile as
|
|
796
|
-
import { resolve as
|
|
1060
|
+
import pc9 from "picocolors";
|
|
1061
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1062
|
+
import { resolve as resolve4 } from "path";
|
|
797
1063
|
import { existsSync } from "fs";
|
|
798
1064
|
|
|
799
1065
|
// src/shared/dev-server-client.ts
|
|
@@ -918,7 +1184,7 @@ function createDevServerClient(port = 6006) {
|
|
|
918
1184
|
}
|
|
919
1185
|
|
|
920
1186
|
// src/shared/command-wrapper.ts
|
|
921
|
-
import
|
|
1187
|
+
import pc8 from "picocolors";
|
|
922
1188
|
|
|
923
1189
|
// src/commands/verify.ts
|
|
924
1190
|
async function verify(component, options = {}) {
|
|
@@ -930,10 +1196,10 @@ async function verify(component, options = {}) {
|
|
|
930
1196
|
let totalCompliance = 0;
|
|
931
1197
|
let componentCount = 0;
|
|
932
1198
|
if (!ci) {
|
|
933
|
-
console.log(
|
|
1199
|
+
console.log(pc9.cyan(`
|
|
934
1200
|
${BRAND.name} Compliance Verification
|
|
935
1201
|
`));
|
|
936
|
-
console.log(
|
|
1202
|
+
console.log(pc9.dim(`Minimum compliance: ${minCompliance}%
|
|
937
1203
|
`));
|
|
938
1204
|
}
|
|
939
1205
|
if (ci) {
|
|
@@ -956,7 +1222,7 @@ ${BRAND.name} Compliance Verification
|
|
|
956
1222
|
if (ci) {
|
|
957
1223
|
console.log(JSON.stringify(error));
|
|
958
1224
|
} else {
|
|
959
|
-
console.log(
|
|
1225
|
+
console.log(pc9.red(error.error));
|
|
960
1226
|
}
|
|
961
1227
|
process.exit(1);
|
|
962
1228
|
}
|
|
@@ -979,19 +1245,19 @@ ${BRAND.name} Compliance Verification
|
|
|
979
1245
|
totalCompliance += complianceResult.compliance;
|
|
980
1246
|
componentCount++;
|
|
981
1247
|
if (!ci) {
|
|
982
|
-
const icon = passed ?
|
|
983
|
-
const complianceStr = passed ?
|
|
1248
|
+
const icon = passed ? pc9.green("\u2713") : pc9.red("\u2717");
|
|
1249
|
+
const complianceStr = passed ? pc9.green(`${complianceResult.compliance}%`) : pc9.red(`${complianceResult.compliance}%`);
|
|
984
1250
|
console.log(` ${icon} ${seg.name} ${complianceStr}`);
|
|
985
1251
|
if (!passed && complianceResult.violations.length > 0) {
|
|
986
1252
|
const violationsToShow = complianceResult.violations.slice(0, 3);
|
|
987
1253
|
for (const v of violationsToShow) {
|
|
988
|
-
console.log(
|
|
1254
|
+
console.log(pc9.dim(` - ${v.property}: ${v.issue}`));
|
|
989
1255
|
if (v.suggestion) {
|
|
990
|
-
console.log(
|
|
1256
|
+
console.log(pc9.dim(` ${pc9.cyan("\u2192")} ${v.suggestion}`));
|
|
991
1257
|
}
|
|
992
1258
|
}
|
|
993
1259
|
if (complianceResult.violations.length > 3) {
|
|
994
|
-
console.log(
|
|
1260
|
+
console.log(pc9.dim(` ... and ${complianceResult.violations.length - 3} more`));
|
|
995
1261
|
}
|
|
996
1262
|
}
|
|
997
1263
|
}
|
|
@@ -1010,7 +1276,7 @@ ${BRAND.name} Compliance Verification
|
|
|
1010
1276
|
usingTokens: 0
|
|
1011
1277
|
});
|
|
1012
1278
|
if (!ci) {
|
|
1013
|
-
console.log(` ${
|
|
1279
|
+
console.log(` ${pc9.red("\u2717")} ${seg.name} ${pc9.red("error")}`);
|
|
1014
1280
|
}
|
|
1015
1281
|
}
|
|
1016
1282
|
}
|
|
@@ -1034,12 +1300,12 @@ ${BRAND.name} Compliance Verification
|
|
|
1034
1300
|
} else {
|
|
1035
1301
|
console.log();
|
|
1036
1302
|
if (allPassed) {
|
|
1037
|
-
console.log(
|
|
1303
|
+
console.log(pc9.green(`\u2713 All ${componentCount} component(s) meet ${minCompliance}% compliance threshold`));
|
|
1038
1304
|
} else {
|
|
1039
1305
|
const failedCount = results.filter((r) => !r.passed).length;
|
|
1040
|
-
console.log(
|
|
1306
|
+
console.log(pc9.red(`\u2717 ${failedCount} component(s) below ${minCompliance}% compliance threshold`));
|
|
1041
1307
|
}
|
|
1042
|
-
console.log(
|
|
1308
|
+
console.log(pc9.dim(` Average compliance: ${summary.compliance}%
|
|
1043
1309
|
`));
|
|
1044
1310
|
}
|
|
1045
1311
|
return summary;
|
|
@@ -1115,7 +1381,7 @@ function computeMetadataScore(fragment) {
|
|
|
1115
1381
|
async function verifyFromLocalFragments(configPath, minCompliance, component) {
|
|
1116
1382
|
const { config, configDir } = await loadConfig(configPath);
|
|
1117
1383
|
const outFile = config.outFile ?? "fragments.json";
|
|
1118
|
-
const fragmentsPath =
|
|
1384
|
+
const fragmentsPath = resolve4(configDir ?? process.cwd(), outFile);
|
|
1119
1385
|
if (!existsSync(fragmentsPath)) {
|
|
1120
1386
|
const error = {
|
|
1121
1387
|
error: `fragments.json not found at ${fragmentsPath}. Run "fragments build" first.`
|
|
@@ -1123,7 +1389,7 @@ async function verifyFromLocalFragments(configPath, minCompliance, component) {
|
|
|
1123
1389
|
console.log(JSON.stringify(error));
|
|
1124
1390
|
process.exit(1);
|
|
1125
1391
|
}
|
|
1126
|
-
const raw = await
|
|
1392
|
+
const raw = await readFile3(fragmentsPath, "utf-8");
|
|
1127
1393
|
const data = JSON.parse(raw);
|
|
1128
1394
|
let entries = Object.entries(data.fragments).filter(
|
|
1129
1395
|
([, frag]) => !frag.filePath.includes("node_modules")
|
|
@@ -1175,14 +1441,14 @@ async function verifyFromLocalFragments(configPath, minCompliance, component) {
|
|
|
1175
1441
|
}
|
|
1176
1442
|
|
|
1177
1443
|
// src/commands/audit.ts
|
|
1178
|
-
import
|
|
1444
|
+
import pc10 from "picocolors";
|
|
1179
1445
|
async function audit(options = {}) {
|
|
1180
1446
|
const { config: configPath, json = false, sort = "compliance", port = 6006 } = options;
|
|
1181
1447
|
await loadConfig(configPath);
|
|
1182
1448
|
const client = createDevServerClient(port);
|
|
1183
1449
|
const audits = [];
|
|
1184
1450
|
if (!json) {
|
|
1185
|
-
console.log(
|
|
1451
|
+
console.log(pc10.cyan(`
|
|
1186
1452
|
${BRAND.name} Design System Audit
|
|
1187
1453
|
`));
|
|
1188
1454
|
}
|
|
@@ -1198,7 +1464,7 @@ ${BRAND.name} Design System Audit
|
|
|
1198
1464
|
if (json) {
|
|
1199
1465
|
console.log(JSON.stringify({ error: "No fragments found", components: [] }));
|
|
1200
1466
|
} else {
|
|
1201
|
-
console.log(
|
|
1467
|
+
console.log(pc10.yellow("No fragments found.\n"));
|
|
1202
1468
|
}
|
|
1203
1469
|
return {
|
|
1204
1470
|
totalComponents: 0,
|
|
@@ -1213,7 +1479,7 @@ ${BRAND.name} Design System Audit
|
|
|
1213
1479
|
};
|
|
1214
1480
|
}
|
|
1215
1481
|
if (!json) {
|
|
1216
|
-
console.log(
|
|
1482
|
+
console.log(pc10.dim(`Auditing ${fragments.length} component(s)...
|
|
1217
1483
|
`));
|
|
1218
1484
|
}
|
|
1219
1485
|
for (const seg of fragments) {
|
|
@@ -1268,24 +1534,24 @@ ${BRAND.name} Design System Audit
|
|
|
1268
1534
|
if (json) {
|
|
1269
1535
|
console.log(JSON.stringify(summary, null, 2));
|
|
1270
1536
|
} else {
|
|
1271
|
-
console.log(
|
|
1272
|
-
console.log(
|
|
1537
|
+
console.log(pc10.bold("Component".padEnd(30) + "Compliance".padEnd(12) + "Hardcoded".padEnd(12) + "Properties"));
|
|
1538
|
+
console.log(pc10.dim("\u2500".repeat(66)));
|
|
1273
1539
|
for (const auditItem of audits) {
|
|
1274
|
-
const complianceColor = auditItem.compliance >= 90 ?
|
|
1540
|
+
const complianceColor = auditItem.compliance >= 90 ? pc10.green : auditItem.compliance >= 70 ? pc10.yellow : pc10.red;
|
|
1275
1541
|
console.log(
|
|
1276
1542
|
auditItem.name.padEnd(30) + complianceColor(`${auditItem.compliance}%`.padEnd(12)) + String(auditItem.hardcoded).padEnd(12) + String(auditItem.totalProperties)
|
|
1277
1543
|
);
|
|
1278
1544
|
}
|
|
1279
|
-
console.log(
|
|
1545
|
+
console.log(pc10.dim("\u2500".repeat(66)));
|
|
1280
1546
|
console.log();
|
|
1281
|
-
console.log(
|
|
1547
|
+
console.log(pc10.bold("Summary:"));
|
|
1282
1548
|
console.log(` Total components: ${audits.length}`);
|
|
1283
1549
|
console.log(` Average compliance: ${summary.averageCompliance}%`);
|
|
1284
1550
|
console.log(` Total hardcoded values: ${summary.stats.totalHardcoded}`);
|
|
1285
1551
|
console.log(` Total properties checked: ${summary.stats.totalProperties}`);
|
|
1286
1552
|
if (audits.some((a) => a.compliance < 100)) {
|
|
1287
1553
|
console.log();
|
|
1288
|
-
console.log(
|
|
1554
|
+
console.log(pc10.dim(`Run ${pc10.cyan(`${BRAND.cliCommand} verify <component>`)} for detailed compliance info.`));
|
|
1289
1555
|
}
|
|
1290
1556
|
console.log();
|
|
1291
1557
|
}
|
|
@@ -1294,7 +1560,7 @@ ${BRAND.name} Design System Audit
|
|
|
1294
1560
|
|
|
1295
1561
|
// src/commands/a11y.ts
|
|
1296
1562
|
import fs from "fs";
|
|
1297
|
-
import
|
|
1563
|
+
import pc11 from "picocolors";
|
|
1298
1564
|
|
|
1299
1565
|
// src/commands/a11y-report.ts
|
|
1300
1566
|
function generateA11yReport(summary) {
|
|
@@ -1923,7 +2189,7 @@ async function a11y(options = {}) {
|
|
|
1923
2189
|
const componentResults = [];
|
|
1924
2190
|
const isJsonOutput = format === "json" || json;
|
|
1925
2191
|
if (!isJsonOutput && format !== "github") {
|
|
1926
|
-
console.log(
|
|
2192
|
+
console.log(pc11.cyan(`
|
|
1927
2193
|
${BRAND.name} Accessibility Report
|
|
1928
2194
|
`));
|
|
1929
2195
|
}
|
|
@@ -1939,7 +2205,7 @@ ${BRAND.name} Accessibility Report
|
|
|
1939
2205
|
if (isJsonOutput) {
|
|
1940
2206
|
console.log(JSON.stringify({ error: "No fragments found", components: [] }));
|
|
1941
2207
|
} else {
|
|
1942
|
-
console.log(
|
|
2208
|
+
console.log(pc11.yellow("No fragments found.\n"));
|
|
1943
2209
|
}
|
|
1944
2210
|
return {
|
|
1945
2211
|
totalComponents: 0,
|
|
@@ -1960,12 +2226,12 @@ ${BRAND.name} Accessibility Report
|
|
|
1960
2226
|
if (isJsonOutput) {
|
|
1961
2227
|
console.log(JSON.stringify({ error }));
|
|
1962
2228
|
} else {
|
|
1963
|
-
console.log(
|
|
2229
|
+
console.log(pc11.red(error));
|
|
1964
2230
|
}
|
|
1965
2231
|
throw new Error(error);
|
|
1966
2232
|
}
|
|
1967
2233
|
if (!isJsonOutput && format !== "github") {
|
|
1968
|
-
console.log(
|
|
2234
|
+
console.log(pc11.dim(`Checking ${componentsToCheck.length} component(s) for accessibility issues...
|
|
1969
2235
|
`));
|
|
1970
2236
|
}
|
|
1971
2237
|
for (const seg of componentsToCheck) {
|
|
@@ -2038,50 +2304,50 @@ ${BRAND.name} Accessibility Report
|
|
|
2038
2304
|
} else if (isJsonOutput) {
|
|
2039
2305
|
console.log(JSON.stringify(summary, null, 2));
|
|
2040
2306
|
} else {
|
|
2041
|
-
console.log(
|
|
2307
|
+
console.log(pc11.bold(
|
|
2042
2308
|
"Component".padEnd(20) + "Variants".padEnd(10) + "Violations".padEnd(12) + "Critical".padEnd(10) + "Serious".padEnd(10) + "Status"
|
|
2043
2309
|
));
|
|
2044
|
-
console.log(
|
|
2310
|
+
console.log(pc11.dim("\u2500".repeat(72)));
|
|
2045
2311
|
for (const result of componentResults) {
|
|
2046
|
-
const statusColor = result.status === "PASS" ?
|
|
2312
|
+
const statusColor = result.status === "PASS" ? pc11.green : result.status === "WARN" ? pc11.yellow : pc11.red;
|
|
2047
2313
|
const variantCount = result.results.length || 1;
|
|
2048
2314
|
console.log(
|
|
2049
2315
|
result.component.padEnd(20) + String(variantCount).padEnd(10) + String(result.totalViolations).padEnd(12) + String(result.totalCritical).padEnd(10) + String(result.totalSerious).padEnd(10) + statusColor(result.status)
|
|
2050
2316
|
);
|
|
2051
2317
|
}
|
|
2052
|
-
console.log(
|
|
2318
|
+
console.log(pc11.dim("\u2500".repeat(72)));
|
|
2053
2319
|
console.log();
|
|
2054
2320
|
const categories = [
|
|
2055
|
-
{ label: "Critical", count: totalCritical, color:
|
|
2056
|
-
{ label: "Serious", count: totalSerious, color:
|
|
2057
|
-
{ label: "Moderate", count: totalModerate, color:
|
|
2058
|
-
{ label: "Minor", count: totalMinor, color:
|
|
2321
|
+
{ label: "Critical", count: totalCritical, color: pc11.red },
|
|
2322
|
+
{ label: "Serious", count: totalSerious, color: pc11.red },
|
|
2323
|
+
{ label: "Moderate", count: totalModerate, color: pc11.yellow },
|
|
2324
|
+
{ label: "Minor", count: totalMinor, color: pc11.dim }
|
|
2059
2325
|
];
|
|
2060
2326
|
for (const cat of categories) {
|
|
2061
2327
|
if (cat.count > 0) {
|
|
2062
2328
|
const dots = ".".repeat(Math.max(1, 30 - cat.label.length));
|
|
2063
|
-
console.log(` ${cat.label} ${
|
|
2329
|
+
console.log(` ${cat.label} ${pc11.dim(dots)} ${cat.color(String(cat.count))}`);
|
|
2064
2330
|
}
|
|
2065
2331
|
}
|
|
2066
2332
|
const { score, aaPercent, aaaPercent } = summary.score;
|
|
2067
2333
|
console.log();
|
|
2068
|
-
console.log(
|
|
2334
|
+
console.log(pc11.bold(` Score: ${score}/100`));
|
|
2069
2335
|
console.log(` AA compliance .... ${aaPercent}%`);
|
|
2070
2336
|
console.log(` AAA compliance ... ${aaaPercent}%`);
|
|
2071
2337
|
console.log(` Standard ......... WCAG ${standard}`);
|
|
2072
2338
|
console.log();
|
|
2073
|
-
console.log(
|
|
2339
|
+
console.log(pc11.bold("Summary:"));
|
|
2074
2340
|
console.log(` ${accessibleComponents}/${componentResults.length} components accessible (${summary.accessiblePercent}%)`);
|
|
2075
2341
|
console.log(` Total violations: ${totalViolations} (${totalCritical} critical, ${totalSerious} serious, ${totalModerate} moderate, ${totalMinor} minor)`);
|
|
2076
2342
|
if (!summary.passed) {
|
|
2077
2343
|
console.log();
|
|
2078
|
-
console.log(
|
|
2344
|
+
console.log(pc11.red("x Accessibility check failed - critical/serious violations found"));
|
|
2079
2345
|
} else if (totalViolations > 0) {
|
|
2080
2346
|
console.log();
|
|
2081
|
-
console.log(
|
|
2347
|
+
console.log(pc11.yellow("! Minor/moderate violations found - consider fixing for better accessibility"));
|
|
2082
2348
|
} else {
|
|
2083
2349
|
console.log();
|
|
2084
|
-
console.log(
|
|
2350
|
+
console.log(pc11.green("v All components pass accessibility checks"));
|
|
2085
2351
|
}
|
|
2086
2352
|
console.log();
|
|
2087
2353
|
}
|
|
@@ -2089,7 +2355,7 @@ ${BRAND.name} Accessibility Report
|
|
|
2089
2355
|
const outputPath = options.output ?? "a11y-report.html";
|
|
2090
2356
|
const html = generateA11yReport(summary);
|
|
2091
2357
|
fs.writeFileSync(outputPath, html, "utf-8");
|
|
2092
|
-
console.log(
|
|
2358
|
+
console.log(pc11.green("v Report generated: " + outputPath));
|
|
2093
2359
|
}
|
|
2094
2360
|
if (ci && !summary.passed) {
|
|
2095
2361
|
throw new Error(`Accessibility check failed: ${totalCritical} critical, ${totalSerious} serious violations found`);
|
|
@@ -2098,22 +2364,22 @@ ${BRAND.name} Accessibility Report
|
|
|
2098
2364
|
}
|
|
2099
2365
|
|
|
2100
2366
|
// src/commands/storygen.ts
|
|
2101
|
-
import { writeFile as
|
|
2102
|
-
import { resolve as
|
|
2103
|
-
import
|
|
2367
|
+
import { writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
2368
|
+
import { resolve as resolve5, join as join3, relative as relative2 } from "path";
|
|
2369
|
+
import pc12 from "picocolors";
|
|
2104
2370
|
async function storygen(options = {}) {
|
|
2105
2371
|
const { config: configPath, output = ".storybook/generated", watch = false } = options;
|
|
2106
2372
|
const { config, configDir } = await loadConfig(configPath);
|
|
2107
|
-
console.log(
|
|
2373
|
+
console.log(pc12.cyan(`
|
|
2108
2374
|
${BRAND.name} Story Generator
|
|
2109
2375
|
`));
|
|
2110
2376
|
const fragmentFiles = await discoverFragmentFiles(config, configDir);
|
|
2111
2377
|
if (fragmentFiles.length === 0) {
|
|
2112
|
-
console.log(
|
|
2378
|
+
console.log(pc12.yellow("No fragment files found.\n"));
|
|
2113
2379
|
return { success: true, generated: 0, outputDir: output };
|
|
2114
2380
|
}
|
|
2115
|
-
const outputDir =
|
|
2116
|
-
await
|
|
2381
|
+
const outputDir = resolve5(configDir, output);
|
|
2382
|
+
await mkdir3(outputDir, { recursive: true });
|
|
2117
2383
|
let generated = 0;
|
|
2118
2384
|
const generateStory = async (file) => {
|
|
2119
2385
|
try {
|
|
@@ -2122,15 +2388,15 @@ ${BRAND.name} Story Generator
|
|
|
2122
2388
|
const storyContent = generateCSF3Story(fragment, file.relativePath);
|
|
2123
2389
|
const storyName = `${fragment.meta.name}.stories.tsx`;
|
|
2124
2390
|
const storyPath = join3(outputDir, storyName);
|
|
2125
|
-
await
|
|
2126
|
-
console.log(`${
|
|
2391
|
+
await writeFile3(storyPath, storyContent);
|
|
2392
|
+
console.log(`${pc12.green("\u2713")} Generated ${storyName}`);
|
|
2127
2393
|
return true;
|
|
2128
2394
|
} catch (error) {
|
|
2129
|
-
console.log(`${
|
|
2395
|
+
console.log(`${pc12.red("\u2717")} Failed: ${file.relativePath} - ${error instanceof Error ? error.message : error}`);
|
|
2130
2396
|
return false;
|
|
2131
2397
|
}
|
|
2132
2398
|
};
|
|
2133
|
-
console.log(
|
|
2399
|
+
console.log(pc12.dim(`Generating stories to ${relative2(process.cwd(), outputDir)}/
|
|
2134
2400
|
`));
|
|
2135
2401
|
for (const file of fragmentFiles) {
|
|
2136
2402
|
if (await generateStory(file)) {
|
|
@@ -2138,10 +2404,10 @@ ${BRAND.name} Story Generator
|
|
|
2138
2404
|
}
|
|
2139
2405
|
}
|
|
2140
2406
|
console.log();
|
|
2141
|
-
console.log(
|
|
2407
|
+
console.log(pc12.green(`\u2713 Generated ${generated} story file(s)
|
|
2142
2408
|
`));
|
|
2143
2409
|
if (watch) {
|
|
2144
|
-
console.log(
|
|
2410
|
+
console.log(pc12.dim("Watching for fragment changes... (Ctrl+C to stop)\n"));
|
|
2145
2411
|
const chokidar = await import("chokidar");
|
|
2146
2412
|
const patterns = fragmentFiles.map((f) => f.absolutePath);
|
|
2147
2413
|
const watcher = chokidar.watch(patterns, {
|
|
@@ -2151,13 +2417,13 @@ ${BRAND.name} Story Generator
|
|
|
2151
2417
|
watcher.on("change", async (changedPath) => {
|
|
2152
2418
|
const file = fragmentFiles.find((f) => f.absolutePath === changedPath);
|
|
2153
2419
|
if (file) {
|
|
2154
|
-
console.log(
|
|
2420
|
+
console.log(pc12.dim(`
|
|
2155
2421
|
Changed: ${relative2(process.cwd(), changedPath)}`));
|
|
2156
2422
|
await generateStory(file);
|
|
2157
2423
|
}
|
|
2158
2424
|
});
|
|
2159
2425
|
watcher.on("add", async (addedPath) => {
|
|
2160
|
-
console.log(
|
|
2426
|
+
console.log(pc12.dim(`
|
|
2161
2427
|
Added: ${relative2(process.cwd(), addedPath)}`));
|
|
2162
2428
|
const newFiles = await discoverFragmentFiles(config, configDir);
|
|
2163
2429
|
const file = newFiles.find((f) => f.absolutePath === addedPath);
|
|
@@ -2241,12 +2507,12 @@ ${storyExports.join("\n")}
|
|
|
2241
2507
|
}
|
|
2242
2508
|
|
|
2243
2509
|
// src/commands/metrics.ts
|
|
2244
|
-
import
|
|
2510
|
+
import pc13 from "picocolors";
|
|
2245
2511
|
async function metrics(component, options = {}) {
|
|
2246
2512
|
const { config: configPath, days = 30, json = false } = options;
|
|
2247
2513
|
const { configDir } = await loadConfig(configPath);
|
|
2248
2514
|
const store = createMetricsStore(configDir);
|
|
2249
|
-
console.log(
|
|
2515
|
+
console.log(pc13.cyan(`
|
|
2250
2516
|
${BRAND.name} Compliance Metrics
|
|
2251
2517
|
`));
|
|
2252
2518
|
const trend = await store.getTrend(component || "all", {
|
|
@@ -2258,31 +2524,31 @@ ${BRAND.name} Compliance Metrics
|
|
|
2258
2524
|
return { success: true, trend };
|
|
2259
2525
|
}
|
|
2260
2526
|
const title = component ? `Component: ${component}` : "System-wide";
|
|
2261
|
-
console.log(
|
|
2262
|
-
console.log(
|
|
2527
|
+
console.log(pc13.bold(title));
|
|
2528
|
+
console.log(pc13.dim(`Last ${days} days
|
|
2263
2529
|
`));
|
|
2264
2530
|
if (trend.dataPoints.length === 0) {
|
|
2265
|
-
console.log(
|
|
2266
|
-
console.log(
|
|
2267
|
-
console.log(
|
|
2531
|
+
console.log(pc13.yellow("No metrics data found.\n"));
|
|
2532
|
+
console.log(pc13.dim("Metrics are recorded automatically when running verification commands."));
|
|
2533
|
+
console.log(pc13.dim(`Try running: ${pc13.cyan(`${BRAND.cliCommand} verify --ci`)}
|
|
2268
2534
|
`));
|
|
2269
2535
|
return { success: true, trend };
|
|
2270
2536
|
}
|
|
2271
2537
|
const sparkline = store.generateSparkline(trend.dataPoints);
|
|
2272
|
-
console.log(
|
|
2538
|
+
console.log(pc13.bold("Trend: ") + sparkline);
|
|
2273
2539
|
console.log();
|
|
2274
|
-
const trendColor = trend.trend === "improving" ?
|
|
2540
|
+
const trendColor = trend.trend === "improving" ? pc13.green : trend.trend === "declining" ? pc13.red : pc13.dim;
|
|
2275
2541
|
const trendIcon = trend.trend === "improving" ? "\u2191" : trend.trend === "declining" ? "\u2193" : "\u2192";
|
|
2276
|
-
console.log(` Average compliance: ${
|
|
2542
|
+
console.log(` Average compliance: ${pc13.bold(`${trend.averageCompliance}%`)}`);
|
|
2277
2543
|
console.log(` Direction: ${trendColor(`${trendIcon} ${trend.trend}`)}`);
|
|
2278
2544
|
console.log(` Data points: ${trend.dataPoints.length}`);
|
|
2279
2545
|
console.log();
|
|
2280
2546
|
const recent = trend.dataPoints.slice(-5);
|
|
2281
2547
|
if (recent.length > 0) {
|
|
2282
|
-
console.log(
|
|
2548
|
+
console.log(pc13.dim("Recent data:"));
|
|
2283
2549
|
for (const point of recent) {
|
|
2284
|
-
const complianceColor = point.compliance >= 90 ?
|
|
2285
|
-
console.log(` ${
|
|
2550
|
+
const complianceColor = point.compliance >= 90 ? pc13.green : point.compliance >= 70 ? pc13.yellow : pc13.red;
|
|
2551
|
+
console.log(` ${pc13.dim(point.date)} ${complianceColor(`${point.compliance}%`)} ${pc13.dim(`(${point.violations} violations)`)}`);
|
|
2286
2552
|
}
|
|
2287
2553
|
console.log();
|
|
2288
2554
|
}
|
|
@@ -2292,7 +2558,7 @@ ${BRAND.name} Compliance Metrics
|
|
|
2292
2558
|
// src/commands/baseline.ts
|
|
2293
2559
|
import { readdir as readdir2, rm as rm2 } from "fs/promises";
|
|
2294
2560
|
import { join as join4, relative as relative3 } from "path";
|
|
2295
|
-
import
|
|
2561
|
+
import pc14 from "picocolors";
|
|
2296
2562
|
async function baseline(action, component, options = {}) {
|
|
2297
2563
|
const { config: configPath, variant, all = false, theme = "light", port = 6006 } = options;
|
|
2298
2564
|
const { config, configDir } = await loadConfig(configPath);
|
|
@@ -2301,7 +2567,7 @@ async function baseline(action, component, options = {}) {
|
|
|
2301
2567
|
viewport: config.screenshots?.viewport
|
|
2302
2568
|
});
|
|
2303
2569
|
await storage.initialize();
|
|
2304
|
-
console.log(
|
|
2570
|
+
console.log(pc14.cyan(`
|
|
2305
2571
|
${BRAND.name} Baseline Manager
|
|
2306
2572
|
`));
|
|
2307
2573
|
const baseUrl = `http://localhost:${port}`;
|
|
@@ -2313,8 +2579,8 @@ ${BRAND.name} Baseline Manager
|
|
|
2313
2579
|
case "delete":
|
|
2314
2580
|
return deleteBaseline(component, options, configDir);
|
|
2315
2581
|
default:
|
|
2316
|
-
console.log(
|
|
2317
|
-
console.log(
|
|
2582
|
+
console.log(pc14.red(`Unknown action: ${action}`));
|
|
2583
|
+
console.log(pc14.dim("Available actions: update, list, delete\n"));
|
|
2318
2584
|
process.exit(1);
|
|
2319
2585
|
}
|
|
2320
2586
|
}
|
|
@@ -2329,7 +2595,7 @@ async function updateBaseline(component, options, config, configDir, baseUrl) {
|
|
|
2329
2595
|
const contextData = JSON.parse(await contextResp.text());
|
|
2330
2596
|
const fragments = contextData.components || [];
|
|
2331
2597
|
if (fragments.length === 0) {
|
|
2332
|
-
console.log(
|
|
2598
|
+
console.log(pc14.yellow("No components found.\n"));
|
|
2333
2599
|
return { success: true, action: "update", count: 0 };
|
|
2334
2600
|
}
|
|
2335
2601
|
component = await select({
|
|
@@ -2341,25 +2607,25 @@ async function updateBaseline(component, options, config, configDir, baseUrl) {
|
|
|
2341
2607
|
});
|
|
2342
2608
|
}
|
|
2343
2609
|
if (all) {
|
|
2344
|
-
console.log(
|
|
2610
|
+
console.log(pc14.dim("Updating all baselines...\n"));
|
|
2345
2611
|
await runScreenshotCommand(config, configDir, {
|
|
2346
2612
|
theme
|
|
2347
2613
|
});
|
|
2348
|
-
console.log(
|
|
2614
|
+
console.log(pc14.green("\n\u2713 All baselines updated\n"));
|
|
2349
2615
|
} else if (component) {
|
|
2350
|
-
console.log(
|
|
2616
|
+
console.log(pc14.dim(`Updating baselines for ${component}...
|
|
2351
2617
|
`));
|
|
2352
2618
|
await runScreenshotCommand(config, configDir, {
|
|
2353
2619
|
component,
|
|
2354
2620
|
variant,
|
|
2355
2621
|
theme
|
|
2356
2622
|
});
|
|
2357
|
-
console.log(
|
|
2623
|
+
console.log(pc14.green(`
|
|
2358
2624
|
\u2713 Baselines updated for ${component}
|
|
2359
2625
|
`));
|
|
2360
2626
|
}
|
|
2361
2627
|
const baselinesDir = join4(configDir, BRAND.dataDir, "baselines");
|
|
2362
|
-
console.log(
|
|
2628
|
+
console.log(pc14.dim(`Baselines directory: ${relative3(process.cwd(), baselinesDir)}
|
|
2363
2629
|
`));
|
|
2364
2630
|
return { success: true, action: "update" };
|
|
2365
2631
|
}
|
|
@@ -2369,22 +2635,22 @@ async function listBaselines(configDir) {
|
|
|
2369
2635
|
const files = await readdir2(baselinesDir, { recursive: true });
|
|
2370
2636
|
const pngFiles = files.filter((f) => f.endsWith(".png"));
|
|
2371
2637
|
if (pngFiles.length === 0) {
|
|
2372
|
-
console.log(
|
|
2373
|
-
console.log(
|
|
2638
|
+
console.log(pc14.yellow("No baselines found.\n"));
|
|
2639
|
+
console.log(pc14.dim(`Run ${pc14.cyan(`${BRAND.cliCommand} screenshot`)} to capture baselines.
|
|
2374
2640
|
`));
|
|
2375
2641
|
return { success: true, action: "list", count: 0 };
|
|
2376
2642
|
}
|
|
2377
|
-
console.log(
|
|
2643
|
+
console.log(pc14.bold("Baselines:\n"));
|
|
2378
2644
|
for (const file of pngFiles) {
|
|
2379
2645
|
console.log(` ${file}`);
|
|
2380
2646
|
}
|
|
2381
2647
|
console.log();
|
|
2382
|
-
console.log(
|
|
2648
|
+
console.log(pc14.dim(`Total: ${pngFiles.length} baseline(s)
|
|
2383
2649
|
`));
|
|
2384
2650
|
return { success: true, action: "list", count: pngFiles.length };
|
|
2385
2651
|
} catch {
|
|
2386
|
-
console.log(
|
|
2387
|
-
console.log(
|
|
2652
|
+
console.log(pc14.yellow("No baselines directory found.\n"));
|
|
2653
|
+
console.log(pc14.dim(`Run ${pc14.cyan(`${BRAND.cliCommand} screenshot`)} to capture baselines.
|
|
2388
2654
|
`));
|
|
2389
2655
|
return { success: true, action: "list", count: 0 };
|
|
2390
2656
|
}
|
|
@@ -2393,42 +2659,42 @@ async function deleteBaseline(component, options, configDir) {
|
|
|
2393
2659
|
const { all = false } = options;
|
|
2394
2660
|
const baselinesDir = join4(configDir, BRAND.dataDir, "baselines");
|
|
2395
2661
|
if (all) {
|
|
2396
|
-
const { confirm } = await import("@inquirer/prompts");
|
|
2397
|
-
const confirmed = await
|
|
2662
|
+
const { confirm: confirm2 } = await import("@inquirer/prompts");
|
|
2663
|
+
const confirmed = await confirm2({
|
|
2398
2664
|
message: "Delete ALL baselines? This cannot be undone.",
|
|
2399
2665
|
default: false
|
|
2400
2666
|
});
|
|
2401
2667
|
if (!confirmed) {
|
|
2402
|
-
console.log(
|
|
2668
|
+
console.log(pc14.dim("\nNo changes made.\n"));
|
|
2403
2669
|
return { success: true, action: "delete", count: 0 };
|
|
2404
2670
|
}
|
|
2405
2671
|
await rm2(baselinesDir, { recursive: true, force: true });
|
|
2406
|
-
console.log(
|
|
2672
|
+
console.log(pc14.green("\n\u2713 All baselines deleted\n"));
|
|
2407
2673
|
return { success: true, action: "delete" };
|
|
2408
2674
|
} else if (component) {
|
|
2409
2675
|
const componentDir = join4(baselinesDir, component);
|
|
2410
2676
|
try {
|
|
2411
2677
|
await rm2(componentDir, { recursive: true, force: true });
|
|
2412
|
-
console.log(
|
|
2678
|
+
console.log(pc14.green(`\u2713 Baselines deleted for ${component}
|
|
2413
2679
|
`));
|
|
2414
2680
|
return { success: true, action: "delete" };
|
|
2415
2681
|
} catch {
|
|
2416
|
-
console.log(
|
|
2682
|
+
console.log(pc14.yellow(`No baselines found for ${component}.
|
|
2417
2683
|
`));
|
|
2418
2684
|
return { success: true, action: "delete", count: 0 };
|
|
2419
2685
|
}
|
|
2420
2686
|
} else {
|
|
2421
|
-
console.log(
|
|
2687
|
+
console.log(pc14.yellow("Specify a component name or use --all flag.\n"));
|
|
2422
2688
|
return { success: false, action: "delete" };
|
|
2423
2689
|
}
|
|
2424
2690
|
}
|
|
2425
2691
|
|
|
2426
2692
|
// src/commands/add.ts
|
|
2427
|
-
import { writeFile as
|
|
2428
|
-
import { resolve as
|
|
2429
|
-
import
|
|
2693
|
+
import { writeFile as writeFile4, mkdir as mkdir5, access as access2 } from "fs/promises";
|
|
2694
|
+
import { resolve as resolve6, join as join5, relative as relative4 } from "path";
|
|
2695
|
+
import pc15 from "picocolors";
|
|
2430
2696
|
async function add(name, options = {}) {
|
|
2431
|
-
console.log(
|
|
2697
|
+
console.log(pc15.cyan(`
|
|
2432
2698
|
${BRAND.name} Component Scaffold
|
|
2433
2699
|
`));
|
|
2434
2700
|
let componentName = name;
|
|
@@ -2474,7 +2740,7 @@ ${BRAND.name} Component Scaffold
|
|
|
2474
2740
|
});
|
|
2475
2741
|
}
|
|
2476
2742
|
} catch {
|
|
2477
|
-
console.log(
|
|
2743
|
+
console.log(pc15.dim("\nNo changes made."));
|
|
2478
2744
|
process.exit(0);
|
|
2479
2745
|
}
|
|
2480
2746
|
}
|
|
@@ -2482,41 +2748,41 @@ ${BRAND.name} Component Scaffold
|
|
|
2482
2748
|
category = category || "components";
|
|
2483
2749
|
template = template || "display";
|
|
2484
2750
|
dir = dir || "src/components";
|
|
2485
|
-
const componentDir =
|
|
2751
|
+
const componentDir = resolve6(process.cwd(), dir, componentName);
|
|
2486
2752
|
const componentFile = join5(componentDir, `${componentName}.tsx`);
|
|
2487
2753
|
const fragmentFile = join5(componentDir, `${componentName}${BRAND.fileExtension}`);
|
|
2488
2754
|
const indexFile = join5(componentDir, "index.ts");
|
|
2489
2755
|
try {
|
|
2490
|
-
await
|
|
2491
|
-
console.log(
|
|
2492
|
-
console.log(
|
|
2756
|
+
await access2(componentDir);
|
|
2757
|
+
console.log(pc15.yellow(`Directory already exists: ${relative4(process.cwd(), componentDir)}`));
|
|
2758
|
+
console.log(pc15.dim("Use a different name or remove the existing directory."));
|
|
2493
2759
|
process.exit(1);
|
|
2494
2760
|
} catch {
|
|
2495
2761
|
}
|
|
2496
|
-
await
|
|
2762
|
+
await mkdir5(componentDir, { recursive: true });
|
|
2497
2763
|
if (generateComponent) {
|
|
2498
2764
|
const componentCode = generateComponentStub(componentName, template);
|
|
2499
|
-
await
|
|
2500
|
-
console.log(`${
|
|
2765
|
+
await writeFile4(componentFile, componentCode);
|
|
2766
|
+
console.log(`${pc15.green("\u2713")} Created ${relative4(process.cwd(), componentFile)}`);
|
|
2501
2767
|
}
|
|
2502
2768
|
const fragmentCode = generateFragmentStub(componentName, category, template);
|
|
2503
|
-
await
|
|
2504
|
-
console.log(`${
|
|
2769
|
+
await writeFile4(fragmentFile, fragmentCode);
|
|
2770
|
+
console.log(`${pc15.green("\u2713")} Created ${relative4(process.cwd(), fragmentFile)}`);
|
|
2505
2771
|
const indexCode = `export { ${componentName} } from './${componentName}.js';
|
|
2506
2772
|
`;
|
|
2507
|
-
await
|
|
2508
|
-
console.log(`${
|
|
2509
|
-
console.log(
|
|
2773
|
+
await writeFile4(indexFile, indexCode);
|
|
2774
|
+
console.log(`${pc15.green("\u2713")} Created ${relative4(process.cwd(), indexFile)}`);
|
|
2775
|
+
console.log(pc15.green(`
|
|
2510
2776
|
\u2713 Scaffolded ${componentName}
|
|
2511
2777
|
`));
|
|
2512
|
-
console.log(
|
|
2513
|
-
console.log(
|
|
2778
|
+
console.log(pc15.dim("\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\u2500\u2500"));
|
|
2779
|
+
console.log(pc15.bold("\nNext steps:"));
|
|
2514
2780
|
if (generateComponent) {
|
|
2515
2781
|
console.log(` 1. Implement ${componentName}.tsx`);
|
|
2516
2782
|
}
|
|
2517
2783
|
console.log(` 2. Fill in usage.when and usage.whenNot in the fragment file`);
|
|
2518
2784
|
console.log(` 3. Add variants with different prop combinations`);
|
|
2519
|
-
console.log(` 4. Run ${
|
|
2785
|
+
console.log(` 4. Run ${pc15.cyan(`${BRAND.cliCommand} dev`)} to preview`);
|
|
2520
2786
|
console.log();
|
|
2521
2787
|
return {
|
|
2522
2788
|
success: true,
|
|
@@ -2666,24 +2932,24 @@ export default defineFragment({
|
|
|
2666
2932
|
}
|
|
2667
2933
|
|
|
2668
2934
|
// src/commands/link/figma.ts
|
|
2669
|
-
import { readFile as
|
|
2670
|
-
import
|
|
2935
|
+
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
2936
|
+
import pc16 from "picocolors";
|
|
2671
2937
|
async function linkFigma(figmaUrl, options = {}) {
|
|
2672
2938
|
const { config: configPath, auto = false, dryRun = false, variants = true } = options;
|
|
2673
2939
|
if (!process.env.FIGMA_ACCESS_TOKEN) {
|
|
2674
|
-
console.error(
|
|
2675
|
-
console.log(
|
|
2676
|
-
console.log(
|
|
2940
|
+
console.error(pc16.red("\nFIGMA_ACCESS_TOKEN environment variable required."));
|
|
2941
|
+
console.log(pc16.dim("Generate at: https://www.figma.com/developers/api#access-tokens"));
|
|
2942
|
+
console.log(pc16.dim(" export FIGMA_ACCESS_TOKEN=figd_xxx"));
|
|
2677
2943
|
process.exit(1);
|
|
2678
2944
|
}
|
|
2679
|
-
console.log(
|
|
2945
|
+
console.log(pc16.cyan(`
|
|
2680
2946
|
${BRAND.name} Link Wizard
|
|
2681
2947
|
`));
|
|
2682
2948
|
const { config, configDir } = await loadConfig(configPath);
|
|
2683
2949
|
let fileUrl = figmaUrl || config.figmaFile;
|
|
2684
2950
|
if (!fileUrl) {
|
|
2685
2951
|
const { input } = await import("@inquirer/prompts");
|
|
2686
|
-
console.log(
|
|
2952
|
+
console.log(pc16.dim("Tip: Add `figmaFile` to fragments.config.ts to skip this prompt\n"));
|
|
2687
2953
|
try {
|
|
2688
2954
|
fileUrl = await input({
|
|
2689
2955
|
message: "Enter Figma file URL:",
|
|
@@ -2694,44 +2960,44 @@ ${BRAND.name} Link Wizard
|
|
|
2694
2960
|
}
|
|
2695
2961
|
});
|
|
2696
2962
|
} catch {
|
|
2697
|
-
console.log(
|
|
2963
|
+
console.log(pc16.dim("\nNo changes made."));
|
|
2698
2964
|
process.exit(0);
|
|
2699
2965
|
}
|
|
2700
2966
|
} else if (config.figmaFile && !figmaUrl) {
|
|
2701
|
-
console.log(
|
|
2967
|
+
console.log(pc16.dim(`Using Figma file from config: ${config.figmaFile}
|
|
2702
2968
|
`));
|
|
2703
2969
|
}
|
|
2704
2970
|
const figmaClient = new FigmaClient({ accessToken: process.env.FIGMA_ACCESS_TOKEN });
|
|
2705
|
-
console.log(
|
|
2971
|
+
console.log(pc16.dim("Parsing Figma URL..."));
|
|
2706
2972
|
const { fileKey } = figmaClient.parseFileUrl(fileUrl);
|
|
2707
|
-
console.log(
|
|
2973
|
+
console.log(pc16.dim("Fetching components from Figma..."));
|
|
2708
2974
|
const figmaData = await figmaClient.getFileComponents(fileKey);
|
|
2709
2975
|
const allFigmaComponents = figmaData.componentSets.length > 0 ? figmaData.componentSets : figmaData.components;
|
|
2710
2976
|
if (allFigmaComponents.length === 0) {
|
|
2711
|
-
console.log(
|
|
2712
|
-
console.log(
|
|
2977
|
+
console.log(pc16.yellow("\nNo components found in Figma file."));
|
|
2978
|
+
console.log(pc16.dim("Make sure the file contains published components."));
|
|
2713
2979
|
process.exit(0);
|
|
2714
2980
|
}
|
|
2715
2981
|
const componentType = figmaData.componentSets.length > 0 ? "component set" : "component";
|
|
2716
|
-
console.log(
|
|
2982
|
+
console.log(pc16.green(`\u2713 Found ${allFigmaComponents.length} Figma ${componentType}(s) in "${figmaData.fileName}"`));
|
|
2717
2983
|
if (figmaData.components.length > 0 && figmaData.componentSets.length > 0) {
|
|
2718
|
-
console.log(
|
|
2984
|
+
console.log(pc16.dim(` (${figmaData.components.length} individual components also available)
|
|
2719
2985
|
`));
|
|
2720
2986
|
} else {
|
|
2721
2987
|
console.log();
|
|
2722
2988
|
}
|
|
2723
2989
|
const fragmentFiles = await discoverFragmentFiles(config, configDir);
|
|
2724
2990
|
if (fragmentFiles.length === 0) {
|
|
2725
|
-
console.log(
|
|
2726
|
-
console.log(
|
|
2991
|
+
console.log(pc16.yellow("No fragment files found in codebase."));
|
|
2992
|
+
console.log(pc16.dim(`Looking for: ${config.include.join(", ")}`));
|
|
2727
2993
|
process.exit(0);
|
|
2728
2994
|
}
|
|
2729
|
-
console.log(
|
|
2995
|
+
console.log(pc16.dim(`Found ${fragmentFiles.length} fragment file(s)
|
|
2730
2996
|
`));
|
|
2731
2997
|
const fragments = [];
|
|
2732
2998
|
for (const file of fragmentFiles) {
|
|
2733
2999
|
try {
|
|
2734
|
-
const content = await
|
|
3000
|
+
const content = await readFile4(file.absolutePath, "utf-8");
|
|
2735
3001
|
const nameMatch = content.match(/name:\s*['"]([^'"]+)['"]/);
|
|
2736
3002
|
const hasFigma = /meta:\s*\{[^}]*figma:\s*['"]https?:/.test(content);
|
|
2737
3003
|
const fragmentVariants = extractVariants(content, nameMatch?.[1]);
|
|
@@ -2763,7 +3029,7 @@ ${BRAND.name} Link Wizard
|
|
|
2763
3029
|
if (bestMatch && bestScore >= 65) {
|
|
2764
3030
|
const alreadyLinked = fragment.hasFigma;
|
|
2765
3031
|
if (alreadyLinked && !auto) {
|
|
2766
|
-
console.log(
|
|
3032
|
+
console.log(pc16.dim(`\u23ED\uFE0F ${fragment.name} (already linked)`));
|
|
2767
3033
|
}
|
|
2768
3034
|
matches.push({ fragment, figmaComponent: bestMatch, score: bestScore, alreadyLinked });
|
|
2769
3035
|
} else {
|
|
@@ -2771,50 +3037,50 @@ ${BRAND.name} Link Wizard
|
|
|
2771
3037
|
}
|
|
2772
3038
|
}
|
|
2773
3039
|
if (unmatchedFragments.length > 0) {
|
|
2774
|
-
console.log(
|
|
3040
|
+
console.log(pc16.dim("Unmatched fragments:"));
|
|
2775
3041
|
for (const seg of unmatchedFragments) {
|
|
2776
|
-
console.log(` ${
|
|
3042
|
+
console.log(` ${pc16.dim("\u2022")} ${seg.name}`);
|
|
2777
3043
|
}
|
|
2778
3044
|
console.log();
|
|
2779
3045
|
}
|
|
2780
3046
|
const newMatches = matches.filter((m) => !m.alreadyLinked);
|
|
2781
3047
|
const alreadyLinkedMatches = matches.filter((m) => m.alreadyLinked);
|
|
2782
3048
|
if (matches.length === 0) {
|
|
2783
|
-
console.log(
|
|
2784
|
-
console.log(
|
|
3049
|
+
console.log(pc16.yellow("\nNo automatic matches found."));
|
|
3050
|
+
console.log(pc16.dim("You can manually add figma URLs to your fragment definitions."));
|
|
2785
3051
|
process.exit(0);
|
|
2786
3052
|
}
|
|
2787
3053
|
if (dryRun) {
|
|
2788
3054
|
if (newMatches.length > 0) {
|
|
2789
|
-
console.log(
|
|
3055
|
+
console.log(pc16.bold("\nMatched Components:\n"));
|
|
2790
3056
|
for (const match of newMatches) {
|
|
2791
|
-
const scoreColor = match.score === 100 ?
|
|
3057
|
+
const scoreColor = match.score === 100 ? pc16.green : pc16.yellow;
|
|
2792
3058
|
console.log(
|
|
2793
|
-
` ${
|
|
3059
|
+
` ${pc16.green("\u2713")} ${pc16.bold(match.fragment.name)} \u2192 ${match.figmaComponent.name} ${scoreColor(`(${Math.round(match.score)}%)`)}`
|
|
2794
3060
|
);
|
|
2795
3061
|
}
|
|
2796
3062
|
}
|
|
2797
|
-
console.log(
|
|
3063
|
+
console.log(pc16.yellow("\n[Dry run - no files were updated]"));
|
|
2798
3064
|
return { success: true, updated: 0, variantUpdates: 0 };
|
|
2799
3065
|
}
|
|
2800
3066
|
let selectedMatches = newMatches;
|
|
2801
3067
|
if (newMatches.length > 0) {
|
|
2802
3068
|
if (auto) {
|
|
2803
|
-
console.log(
|
|
3069
|
+
console.log(pc16.bold("\nMatched Components:\n"));
|
|
2804
3070
|
for (const match of newMatches) {
|
|
2805
|
-
const scoreColor = match.score === 100 ?
|
|
3071
|
+
const scoreColor = match.score === 100 ? pc16.green : pc16.yellow;
|
|
2806
3072
|
console.log(
|
|
2807
|
-
` ${
|
|
3073
|
+
` ${pc16.green("\u2713")} ${pc16.bold(match.fragment.name)} \u2192 ${match.figmaComponent.name} ${scoreColor(`(${Math.round(match.score)}%)`)}`
|
|
2808
3074
|
);
|
|
2809
3075
|
}
|
|
2810
3076
|
} else {
|
|
2811
3077
|
const { checkbox } = await import("@inquirer/prompts");
|
|
2812
|
-
console.log(
|
|
2813
|
-
console.log(
|
|
3078
|
+
console.log(pc16.bold("\nMatched Components:\n"));
|
|
3079
|
+
console.log(pc16.dim("Use \u2191/\u2193 to navigate, Space to toggle, Enter to confirm\n"));
|
|
2814
3080
|
const choices = newMatches.map((match) => {
|
|
2815
|
-
const scoreColor = match.score === 100 ?
|
|
3081
|
+
const scoreColor = match.score === 100 ? pc16.green : pc16.yellow;
|
|
2816
3082
|
return {
|
|
2817
|
-
name: `${
|
|
3083
|
+
name: `${pc16.bold(match.fragment.name)} \u2192 ${match.figmaComponent.name} ${scoreColor(`(${Math.round(match.score)}%)`)}`,
|
|
2818
3084
|
value: match,
|
|
2819
3085
|
checked: true
|
|
2820
3086
|
};
|
|
@@ -2826,7 +3092,7 @@ ${BRAND.name} Link Wizard
|
|
|
2826
3092
|
pageSize: 20
|
|
2827
3093
|
});
|
|
2828
3094
|
} catch {
|
|
2829
|
-
console.log(
|
|
3095
|
+
console.log(pc16.dim("\nNo changes made."));
|
|
2830
3096
|
return { success: true, updated: 0, variantUpdates: 0 };
|
|
2831
3097
|
}
|
|
2832
3098
|
}
|
|
@@ -2836,7 +3102,7 @@ ${BRAND.name} Link Wizard
|
|
|
2836
3102
|
for (const match of selectedMatches) {
|
|
2837
3103
|
if (match.alreadyLinked) continue;
|
|
2838
3104
|
try {
|
|
2839
|
-
let content = await
|
|
3105
|
+
let content = await readFile4(match.fragment.filePath, "utf-8");
|
|
2840
3106
|
const figmaUrlToInsert = figmaClient.buildNodeUrl(
|
|
2841
3107
|
match.figmaComponent.file_key,
|
|
2842
3108
|
match.figmaComponent.node_id,
|
|
@@ -2854,15 +3120,15 @@ ${BRAND.name} Link Wizard
|
|
|
2854
3120
|
figma: '${figmaUrlToInsert}',`
|
|
2855
3121
|
);
|
|
2856
3122
|
}
|
|
2857
|
-
await
|
|
3123
|
+
await writeFile5(match.fragment.filePath, content);
|
|
2858
3124
|
updated++;
|
|
2859
|
-
console.log(` ${
|
|
3125
|
+
console.log(` ${pc16.green("\u2713")} Updated ${match.fragment.relativePath}`);
|
|
2860
3126
|
} catch (error) {
|
|
2861
|
-
console.log(` ${
|
|
3127
|
+
console.log(` ${pc16.red("\u2717")} Failed to update ${match.fragment.relativePath}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
2862
3128
|
}
|
|
2863
3129
|
}
|
|
2864
3130
|
if (updated > 0) {
|
|
2865
|
-
console.log(
|
|
3131
|
+
console.log(pc16.green(`
|
|
2866
3132
|
\u2713 Updated ${updated} fragment file(s)
|
|
2867
3133
|
`));
|
|
2868
3134
|
}
|
|
@@ -2875,9 +3141,9 @@ ${BRAND.name} Link Wizard
|
|
|
2875
3141
|
fileKey
|
|
2876
3142
|
);
|
|
2877
3143
|
}
|
|
2878
|
-
console.log(
|
|
2879
|
-
console.log(
|
|
2880
|
-
console.log(` 1. Run ${
|
|
3144
|
+
console.log(pc16.dim("\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\u2500\u2500"));
|
|
3145
|
+
console.log(pc16.bold("\nNext steps:"));
|
|
3146
|
+
console.log(` 1. Run ${pc16.cyan(`${BRAND.cliCommand} compare --all`)} to verify designs`);
|
|
2881
3147
|
console.log(` 2. Add figmaProps mappings for prop-level linking`);
|
|
2882
3148
|
console.log();
|
|
2883
3149
|
return { success: true, updated, variantUpdates };
|
|
@@ -2954,12 +3220,12 @@ function calculateMatchScore(fragmentName, figmaName) {
|
|
|
2954
3220
|
return 0;
|
|
2955
3221
|
}
|
|
2956
3222
|
async function linkVariants(matches, figmaData, figmaClient, fileKey) {
|
|
2957
|
-
console.log(
|
|
3223
|
+
console.log(pc16.bold("\nLinking variants...\n"));
|
|
2958
3224
|
const matchedComponentSets = matches.map((m) => m.figmaComponent).filter((c) => figmaData.componentSets.some((cs) => cs.key === c.key));
|
|
2959
3225
|
if (matchedComponentSets.length === 0) {
|
|
2960
3226
|
return 0;
|
|
2961
3227
|
}
|
|
2962
|
-
console.log(
|
|
3228
|
+
console.log(pc16.dim("Fetching Figma variants..."));
|
|
2963
3229
|
const componentSetVariants = await figmaClient.getComponentSetVariants(fileKey, matchedComponentSets);
|
|
2964
3230
|
let variantUpdates = 0;
|
|
2965
3231
|
const { select } = await import("@inquirer/prompts");
|
|
@@ -2974,10 +3240,10 @@ async function linkVariants(matches, figmaData, figmaClient, fileKey) {
|
|
|
2974
3240
|
}
|
|
2975
3241
|
const fragmentVariants = match.fragment.variants.filter((v) => !v.hasFigma);
|
|
2976
3242
|
if (fragmentVariants.length === 0) {
|
|
2977
|
-
console.log(
|
|
3243
|
+
console.log(pc16.dim(` \u23ED\uFE0F ${match.fragment.name}: all variants already linked`));
|
|
2978
3244
|
continue;
|
|
2979
3245
|
}
|
|
2980
|
-
console.log(
|
|
3246
|
+
console.log(pc16.dim(` ${match.fragment.name}: ${csWithVariants.variants.length} Figma variants`));
|
|
2981
3247
|
for (const fragmentVariant of fragmentVariants) {
|
|
2982
3248
|
const variantMatches = [];
|
|
2983
3249
|
for (const fv of csWithVariants.variants) {
|
|
@@ -3003,7 +3269,7 @@ async function linkVariants(matches, figmaData, figmaClient, fileKey) {
|
|
|
3003
3269
|
figmaData.fileName
|
|
3004
3270
|
);
|
|
3005
3271
|
try {
|
|
3006
|
-
let content = await
|
|
3272
|
+
let content = await readFile4(match.fragment.filePath, "utf-8");
|
|
3007
3273
|
const namePattern = new RegExp(
|
|
3008
3274
|
`(name:\\s*['"]${escapeRegExp(fragmentVariant.name)}['"],?)`,
|
|
3009
3275
|
"g"
|
|
@@ -3015,14 +3281,14 @@ async function linkVariants(matches, figmaData, figmaClient, fileKey) {
|
|
|
3015
3281
|
return `${matchedStr}
|
|
3016
3282
|
figma: '${variantUrl}',`;
|
|
3017
3283
|
});
|
|
3018
|
-
await
|
|
3284
|
+
await writeFile5(match.fragment.filePath, content);
|
|
3019
3285
|
variantUpdates++;
|
|
3020
3286
|
console.log(
|
|
3021
|
-
` ${
|
|
3287
|
+
` ${pc16.green("\u2713")} ${fragmentVariant.name} \u2192 ${bestMatch.figmaVariant.name}`
|
|
3022
3288
|
);
|
|
3023
3289
|
} catch (error) {
|
|
3024
3290
|
console.log(
|
|
3025
|
-
` ${
|
|
3291
|
+
` ${pc16.red("\u2717")} ${fragmentVariant.name}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
3026
3292
|
);
|
|
3027
3293
|
}
|
|
3028
3294
|
} else if (variantMatches.length > 0) {
|
|
@@ -3044,7 +3310,7 @@ async function linkVariants(matches, figmaData, figmaClient, fileKey) {
|
|
|
3044
3310
|
selectedVariant.node_id,
|
|
3045
3311
|
figmaData.fileName
|
|
3046
3312
|
);
|
|
3047
|
-
let content = await
|
|
3313
|
+
let content = await readFile4(match.fragment.filePath, "utf-8");
|
|
3048
3314
|
const namePattern = new RegExp(
|
|
3049
3315
|
`(name:\\s*['"]${escapeRegExp(fragmentVariant.name)}['"],?)`,
|
|
3050
3316
|
"g"
|
|
@@ -3056,46 +3322,46 @@ async function linkVariants(matches, figmaData, figmaClient, fileKey) {
|
|
|
3056
3322
|
return `${matchedStr}
|
|
3057
3323
|
figma: '${variantUrl}',`;
|
|
3058
3324
|
});
|
|
3059
|
-
await
|
|
3325
|
+
await writeFile5(match.fragment.filePath, content);
|
|
3060
3326
|
variantUpdates++;
|
|
3061
3327
|
console.log(
|
|
3062
|
-
` ${
|
|
3328
|
+
` ${pc16.green("\u2713")} ${fragmentVariant.name} \u2192 ${selectedVariant.name}`
|
|
3063
3329
|
);
|
|
3064
3330
|
} else {
|
|
3065
|
-
console.log(` ${
|
|
3331
|
+
console.log(` ${pc16.dim("\u23ED\uFE0F")} ${fragmentVariant.name} (skipped)`);
|
|
3066
3332
|
}
|
|
3067
3333
|
} catch {
|
|
3068
|
-
console.log(` ${
|
|
3334
|
+
console.log(` ${pc16.dim("\u23ED\uFE0F")} ${fragmentVariant.name} (cancelled)`);
|
|
3069
3335
|
}
|
|
3070
3336
|
} else {
|
|
3071
|
-
console.log(` ${
|
|
3337
|
+
console.log(` ${pc16.yellow("?")} ${fragmentVariant.name}: no matching Figma variant`);
|
|
3072
3338
|
}
|
|
3073
3339
|
}
|
|
3074
3340
|
}
|
|
3075
3341
|
if (variantUpdates > 0) {
|
|
3076
|
-
console.log(
|
|
3342
|
+
console.log(pc16.green(`
|
|
3077
3343
|
\u2713 Linked ${variantUpdates} variant(s)
|
|
3078
3344
|
`));
|
|
3079
3345
|
} else {
|
|
3080
|
-
console.log(
|
|
3346
|
+
console.log(pc16.dim("\nNo variant updates made.\n"));
|
|
3081
3347
|
}
|
|
3082
3348
|
return variantUpdates;
|
|
3083
3349
|
}
|
|
3084
3350
|
|
|
3085
3351
|
// src/commands/link/storybook.ts
|
|
3086
|
-
import { writeFile as
|
|
3087
|
-
import { join as join8, dirname as
|
|
3088
|
-
import
|
|
3352
|
+
import { writeFile as writeFile7, mkdir as mkdir7 } from "fs/promises";
|
|
3353
|
+
import { join as join8, dirname as dirname4, relative as relative6 } from "path";
|
|
3354
|
+
import pc17 from "picocolors";
|
|
3089
3355
|
|
|
3090
3356
|
// src/migrate/migrate.ts
|
|
3091
|
-
import { writeFile as
|
|
3092
|
-
import { join as join6, dirname, relative as relative5 } from "path";
|
|
3357
|
+
import { writeFile as writeFile6, mkdir as mkdir6, access as access3, stat as stat2 } from "fs/promises";
|
|
3358
|
+
import { join as join6, dirname as dirname2, relative as relative5 } from "path";
|
|
3093
3359
|
import fg2 from "fast-glob";
|
|
3094
3360
|
|
|
3095
3361
|
// src/migrate/parser.ts
|
|
3096
|
-
import { readFile as
|
|
3362
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
3097
3363
|
async function parseStoryFile(filePath) {
|
|
3098
|
-
const content = await
|
|
3364
|
+
const content = await readFile5(filePath, "utf-8");
|
|
3099
3365
|
return parseStoryContent(content, filePath);
|
|
3100
3366
|
}
|
|
3101
3367
|
function parseStoryContent(content, filePath) {
|
|
@@ -4157,7 +4423,7 @@ function escapeString(str) {
|
|
|
4157
4423
|
|
|
4158
4424
|
// src/migrate/detect.ts
|
|
4159
4425
|
import { existsSync as existsSync2 } from "fs";
|
|
4160
|
-
import { readFile as
|
|
4426
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
4161
4427
|
import { join as join7 } from "path";
|
|
4162
4428
|
import fg3 from "fast-glob";
|
|
4163
4429
|
var CONFIG_FILES = [
|
|
@@ -4186,7 +4452,7 @@ async function parseStorybookConfig(configPath) {
|
|
|
4186
4452
|
let framework;
|
|
4187
4453
|
let builder;
|
|
4188
4454
|
try {
|
|
4189
|
-
const content = await
|
|
4455
|
+
const content = await readFile6(configPath, "utf-8");
|
|
4190
4456
|
const storiesMatch = content.match(
|
|
4191
4457
|
/stories:\s*\[([^\]]+)\]/s
|
|
4192
4458
|
);
|
|
@@ -4269,45 +4535,45 @@ async function discoverStoryFiles(projectRoot, patterns) {
|
|
|
4269
4535
|
// src/commands/link/storybook.ts
|
|
4270
4536
|
async function linkStorybook(options = {}) {
|
|
4271
4537
|
const { out, yes = false, dryRun = false, verbose = false } = options;
|
|
4272
|
-
console.log(
|
|
4538
|
+
console.log(pc17.cyan(`
|
|
4273
4539
|
${BRAND.name} Storybook Link
|
|
4274
4540
|
`));
|
|
4275
4541
|
const projectRoot = process.cwd();
|
|
4276
|
-
console.log(
|
|
4542
|
+
console.log(pc17.dim("Detecting Storybook configuration..."));
|
|
4277
4543
|
const sbConfig = await detectStorybookConfig(projectRoot);
|
|
4278
4544
|
if (!sbConfig) {
|
|
4279
|
-
console.log(
|
|
4280
|
-
console.log(
|
|
4281
|
-
console.log(
|
|
4545
|
+
console.log(pc17.yellow("\nNo Storybook configuration found."));
|
|
4546
|
+
console.log(pc17.dim("Looking for: .storybook/main.ts, .storybook/main.js, etc."));
|
|
4547
|
+
console.log(pc17.dim("\nUse --config to specify a custom path."));
|
|
4282
4548
|
process.exit(1);
|
|
4283
4549
|
}
|
|
4284
|
-
console.log(
|
|
4550
|
+
console.log(pc17.green(`\u2713 Found: ${sbConfig.configPath}`));
|
|
4285
4551
|
if (sbConfig.framework) {
|
|
4286
|
-
console.log(
|
|
4552
|
+
console.log(pc17.dim(` Framework: ${sbConfig.framework}`));
|
|
4287
4553
|
}
|
|
4288
4554
|
if (sbConfig.errors?.length) {
|
|
4289
4555
|
for (const err of sbConfig.errors) {
|
|
4290
|
-
console.log(
|
|
4556
|
+
console.log(pc17.yellow(` Warning: ${err}`));
|
|
4291
4557
|
}
|
|
4292
4558
|
}
|
|
4293
|
-
console.log(
|
|
4559
|
+
console.log(pc17.dim("\nDiscovering story files..."));
|
|
4294
4560
|
const storyFiles = await discoverStoryFiles(projectRoot, sbConfig.storyPatterns);
|
|
4295
4561
|
if (storyFiles.length === 0) {
|
|
4296
|
-
console.log(
|
|
4297
|
-
console.log(
|
|
4562
|
+
console.log(pc17.yellow("\nNo story files found."));
|
|
4563
|
+
console.log(pc17.dim(`Patterns: ${sbConfig.storyPatterns.join(", ")}`));
|
|
4298
4564
|
process.exit(1);
|
|
4299
4565
|
}
|
|
4300
|
-
console.log(
|
|
4566
|
+
console.log(pc17.green(`\u2713 Found ${storyFiles.length} story file(s)
|
|
4301
4567
|
`));
|
|
4302
4568
|
const previews = [];
|
|
4303
4569
|
let parseErrors = 0;
|
|
4304
4570
|
const total = storyFiles.length;
|
|
4305
|
-
console.log(
|
|
4571
|
+
console.log(pc17.dim(`Analyzing ${total} stories...
|
|
4306
4572
|
`));
|
|
4307
4573
|
for (let i = 0; i < storyFiles.length; i++) {
|
|
4308
4574
|
const storyFile = storyFiles[i];
|
|
4309
4575
|
const relativePath = relative6(projectRoot, storyFile);
|
|
4310
|
-
process.stdout.write(`\r ${
|
|
4576
|
+
process.stdout.write(`\r ${pc17.dim(`[${i + 1}/${total}]`)} ${relativePath.slice(0, 60).padEnd(60)}`);
|
|
4311
4577
|
try {
|
|
4312
4578
|
const parsed = await parseStoryFile(storyFile);
|
|
4313
4579
|
const result = convertToFragment(parsed);
|
|
@@ -4325,49 +4591,49 @@ ${BRAND.name} Storybook Link
|
|
|
4325
4591
|
parseErrors++;
|
|
4326
4592
|
if (verbose) {
|
|
4327
4593
|
process.stdout.write("\n");
|
|
4328
|
-
console.log(
|
|
4594
|
+
console.log(pc17.red(`\u2717 ${relativePath}: ${error instanceof Error ? error.message : "Parse error"}`));
|
|
4329
4595
|
}
|
|
4330
4596
|
}
|
|
4331
4597
|
}
|
|
4332
4598
|
process.stdout.write("\r" + " ".repeat(80) + "\r");
|
|
4333
|
-
console.log(
|
|
4334
|
-
console.log(
|
|
4335
|
-
console.log(
|
|
4336
|
-
console.log(
|
|
4599
|
+
console.log(pc17.bold("Preview:\n"));
|
|
4600
|
+
console.log(pc17.dim("\u250C\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\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\u2510"));
|
|
4601
|
+
console.log(pc17.dim("\u2502") + " Component".padEnd(20) + pc17.dim("\u2502") + " Stories".padEnd(10) + pc17.dim("\u2502") + " Props".padEnd(8) + pc17.dim("\u2502") + " Confidence".padEnd(13) + pc17.dim("\u2502"));
|
|
4602
|
+
console.log(pc17.dim("\u251C\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\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\u2524"));
|
|
4337
4603
|
for (const item of previews) {
|
|
4338
|
-
const confColor = item.confidence >= 0.8 ?
|
|
4604
|
+
const confColor = item.confidence >= 0.8 ? pc17.green : item.confidence >= 0.5 ? pc17.yellow : pc17.red;
|
|
4339
4605
|
const confLabel = item.confidence >= 0.8 ? "high" : item.confidence >= 0.5 ? "medium" : "low";
|
|
4340
4606
|
console.log(
|
|
4341
|
-
|
|
4607
|
+
pc17.dim("\u2502") + ` ${item.componentName}`.padEnd(20).slice(0, 20) + pc17.dim("\u2502") + ` ${item.variantCount}`.padEnd(10) + pc17.dim("\u2502") + ` ${item.propCount}`.padEnd(8) + pc17.dim("\u2502") + ` ${confColor(confLabel)}`.padEnd(13 + (confColor === pc17.green ? 10 : confColor === pc17.yellow ? 11 : 9)) + pc17.dim("\u2502")
|
|
4342
4608
|
);
|
|
4343
4609
|
}
|
|
4344
|
-
console.log(
|
|
4610
|
+
console.log(pc17.dim("\u2514\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\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\u2518"));
|
|
4345
4611
|
if (parseErrors > 0) {
|
|
4346
|
-
console.log(
|
|
4612
|
+
console.log(pc17.yellow(`
|
|
4347
4613
|
${parseErrors} file(s) could not be parsed (use --verbose for details)`));
|
|
4348
4614
|
}
|
|
4349
4615
|
if (previews.length === 0) {
|
|
4350
|
-
console.log(
|
|
4616
|
+
console.log(pc17.yellow("\nNo stories could be parsed successfully."));
|
|
4351
4617
|
return { success: true, generated: 0 };
|
|
4352
4618
|
}
|
|
4353
4619
|
if (dryRun) {
|
|
4354
|
-
console.log(
|
|
4620
|
+
console.log(pc17.dim(`
|
|
4355
4621
|
${previews.length} fragment file(s) would be created`));
|
|
4356
|
-
console.log(
|
|
4622
|
+
console.log(pc17.yellow("\n[Dry run - no files were written]"));
|
|
4357
4623
|
return { success: true, generated: 0 };
|
|
4358
4624
|
}
|
|
4359
4625
|
let selectedPreviews = previews;
|
|
4360
4626
|
if (yes) {
|
|
4361
|
-
console.log(
|
|
4627
|
+
console.log(pc17.dim(`
|
|
4362
4628
|
${previews.length} fragment file(s) will be created`));
|
|
4363
4629
|
} else {
|
|
4364
4630
|
const { checkbox } = await import("@inquirer/prompts");
|
|
4365
|
-
console.log(
|
|
4631
|
+
console.log(pc17.dim("\nUse \u2191/\u2193 to navigate, Space to toggle, Enter to confirm\n"));
|
|
4366
4632
|
const choices = previews.map((item) => {
|
|
4367
|
-
const confColor = item.confidence >= 0.8 ?
|
|
4633
|
+
const confColor = item.confidence >= 0.8 ? pc17.green : item.confidence >= 0.5 ? pc17.yellow : pc17.red;
|
|
4368
4634
|
const confLabel = item.confidence >= 0.8 ? "high" : item.confidence >= 0.5 ? "medium" : "low";
|
|
4369
4635
|
return {
|
|
4370
|
-
name: `${
|
|
4636
|
+
name: `${pc17.bold(item.componentName)} ${pc17.dim(`(${item.variantCount} stories, ${confLabel} confidence)`)}`,
|
|
4371
4637
|
value: item,
|
|
4372
4638
|
checked: true
|
|
4373
4639
|
};
|
|
@@ -4379,16 +4645,16 @@ ${previews.length} fragment file(s) will be created`));
|
|
|
4379
4645
|
pageSize: 15
|
|
4380
4646
|
});
|
|
4381
4647
|
} catch {
|
|
4382
|
-
console.log(
|
|
4648
|
+
console.log(pc17.dim("\nNo changes made."));
|
|
4383
4649
|
return { success: true, generated: 0 };
|
|
4384
4650
|
}
|
|
4385
4651
|
if (selectedPreviews.length === 0) {
|
|
4386
|
-
console.log(
|
|
4652
|
+
console.log(pc17.dim("\nNo stories selected. No changes made."));
|
|
4387
4653
|
return { success: true, generated: 0 };
|
|
4388
4654
|
}
|
|
4389
4655
|
}
|
|
4390
4656
|
const genTotal = selectedPreviews.length;
|
|
4391
|
-
console.log(
|
|
4657
|
+
console.log(pc17.dim(`
|
|
4392
4658
|
Generating ${genTotal} fragment file(s)...
|
|
4393
4659
|
`));
|
|
4394
4660
|
let generated = 0;
|
|
@@ -4400,32 +4666,32 @@ Generating ${genTotal} fragment file(s)...
|
|
|
4400
4666
|
const parsed = await parseStoryFile(storyFile);
|
|
4401
4667
|
const result = convertToFragment(parsed);
|
|
4402
4668
|
const outputFile = out ? join8(projectRoot, out, preview.sourceFile.replace(/\.stories\.(tsx?|jsx?)$/, BRAND.fileExtension)) : result.outputFile;
|
|
4403
|
-
await
|
|
4404
|
-
await
|
|
4669
|
+
await mkdir7(dirname4(outputFile), { recursive: true });
|
|
4670
|
+
await writeFile7(outputFile, result.code);
|
|
4405
4671
|
generated++;
|
|
4406
|
-
console.log(`${
|
|
4672
|
+
console.log(`${pc17.dim(`[${i + 1}/${genTotal}]`)} ${pc17.green("\u2713")} ${result.componentName}`);
|
|
4407
4673
|
} catch (error) {
|
|
4408
4674
|
genErrors++;
|
|
4409
|
-
console.log(`${
|
|
4675
|
+
console.log(`${pc17.dim(`[${i + 1}/${genTotal}]`)} ${pc17.red("\u2717")} ${preview.componentName}`);
|
|
4410
4676
|
}
|
|
4411
4677
|
}
|
|
4412
|
-
console.log(
|
|
4678
|
+
console.log(pc17.green(`
|
|
4413
4679
|
\u2713 Generated ${generated} fragment file(s)
|
|
4414
4680
|
`));
|
|
4415
|
-
console.log(
|
|
4416
|
-
console.log(
|
|
4681
|
+
console.log(pc17.dim("\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\u2500\u2500"));
|
|
4682
|
+
console.log(pc17.bold("\nNext steps:"));
|
|
4417
4683
|
console.log(` 1. Review generated ${BRAND.fileExtension} files`);
|
|
4418
4684
|
console.log(` 2. Fill in usage.when and usage.whenNot fields`);
|
|
4419
|
-
console.log(` 3. Run ${
|
|
4420
|
-
console.log(` 4. Run ${
|
|
4685
|
+
console.log(` 3. Run ${pc17.cyan(`${BRAND.cliCommand} build`)} to compile`);
|
|
4686
|
+
console.log(` 4. Run ${pc17.cyan(`${BRAND.cliCommand} dev`)} to view your design system`);
|
|
4421
4687
|
console.log();
|
|
4422
4688
|
return { success: true, generated };
|
|
4423
4689
|
}
|
|
4424
4690
|
|
|
4425
4691
|
// src/commands/enhance.ts
|
|
4426
|
-
import
|
|
4427
|
-
import { readFile as
|
|
4428
|
-
import { resolve as
|
|
4692
|
+
import pc18 from "picocolors";
|
|
4693
|
+
import { readFile as readFile7, writeFile as writeFile8 } from "fs/promises";
|
|
4694
|
+
import { resolve as resolve7, relative as relative7, join as join9 } from "path";
|
|
4429
4695
|
var DEFAULT_MODELS = {
|
|
4430
4696
|
anthropic: "claude-sonnet-4-20250514",
|
|
4431
4697
|
openai: "gpt-4o",
|
|
@@ -4450,15 +4716,15 @@ async function enhance(options = {}) {
|
|
|
4450
4716
|
const isQuiet = format === "quiet";
|
|
4451
4717
|
const isContextMode = contextOnly || format === "context" || provider === "none";
|
|
4452
4718
|
const config = await loadConfig(configPath);
|
|
4453
|
-
const rootDir =
|
|
4719
|
+
const rootDir = resolve7(root);
|
|
4454
4720
|
if (isInteractive) {
|
|
4455
|
-
console.log(
|
|
4721
|
+
console.log(pc18.cyan(`
|
|
4456
4722
|
${BRAND.name} AI Enhancement
|
|
4457
4723
|
`));
|
|
4458
4724
|
if (isContextMode) {
|
|
4459
|
-
console.log(
|
|
4725
|
+
console.log(pc18.dim("Running in context-only mode (for use with IDE AI like Cursor)\n"));
|
|
4460
4726
|
} else {
|
|
4461
|
-
console.log(
|
|
4727
|
+
console.log(pc18.dim(`Using ${provider} API with model: ${model}
|
|
4462
4728
|
`));
|
|
4463
4729
|
}
|
|
4464
4730
|
}
|
|
@@ -4468,13 +4734,13 @@ ${BRAND.name} AI Enhancement
|
|
|
4468
4734
|
if (format === "json") {
|
|
4469
4735
|
console.log(JSON.stringify({ success: false, error }));
|
|
4470
4736
|
} else if (!isQuiet) {
|
|
4471
|
-
console.error(
|
|
4472
|
-
console.log(
|
|
4737
|
+
console.error(pc18.red("Error:"), error);
|
|
4738
|
+
console.log(pc18.dim("\nTip: Use --context-only to generate prompts for Cursor/Copilot/etc."));
|
|
4473
4739
|
}
|
|
4474
4740
|
return { success: false, enhanced: [], totalTokens: 0, estimatedCost: 0 };
|
|
4475
4741
|
}
|
|
4476
4742
|
if (isInteractive) {
|
|
4477
|
-
console.log(
|
|
4743
|
+
console.log(pc18.dim("Phase 1: Scanning codebase for usage patterns..."));
|
|
4478
4744
|
}
|
|
4479
4745
|
let usageAnalysis;
|
|
4480
4746
|
try {
|
|
@@ -4491,34 +4757,34 @@ ${BRAND.name} AI Enhancement
|
|
|
4491
4757
|
if (isInteractive) {
|
|
4492
4758
|
process.stdout.write("\r" + " ".repeat(60) + "\r");
|
|
4493
4759
|
const stats = getScanStats(usageAnalysis);
|
|
4494
|
-
console.log(
|
|
4760
|
+
console.log(pc18.green(` Found ${stats.totalUsages} usages across ${stats.totalFiles} files`));
|
|
4495
4761
|
}
|
|
4496
4762
|
} catch (error) {
|
|
4497
4763
|
const msg = error instanceof Error ? error.message : String(error);
|
|
4498
4764
|
if (format === "json") {
|
|
4499
4765
|
console.log(JSON.stringify({ success: false, error: `Scan failed: ${msg}` }));
|
|
4500
4766
|
} else if (!isQuiet) {
|
|
4501
|
-
console.error(
|
|
4767
|
+
console.error(pc18.red("Scan failed:"), msg);
|
|
4502
4768
|
}
|
|
4503
4769
|
return { success: false, enhanced: [], totalTokens: 0, estimatedCost: 0 };
|
|
4504
4770
|
}
|
|
4505
4771
|
if (isInteractive) {
|
|
4506
|
-
console.log(
|
|
4772
|
+
console.log(pc18.dim("Phase 2: Parsing Storybook stories..."));
|
|
4507
4773
|
}
|
|
4508
4774
|
let storyFiles;
|
|
4509
4775
|
try {
|
|
4510
4776
|
storyFiles = await parseAllStories(rootDir);
|
|
4511
4777
|
if (isInteractive) {
|
|
4512
|
-
console.log(
|
|
4778
|
+
console.log(pc18.green(` Found ${storyFiles.size} story files`));
|
|
4513
4779
|
}
|
|
4514
4780
|
} catch {
|
|
4515
4781
|
storyFiles = /* @__PURE__ */ new Map();
|
|
4516
4782
|
if (isInteractive) {
|
|
4517
|
-
console.log(
|
|
4783
|
+
console.log(pc18.yellow(" No Storybook stories found"));
|
|
4518
4784
|
}
|
|
4519
4785
|
}
|
|
4520
4786
|
if (isInteractive) {
|
|
4521
|
-
console.log(
|
|
4787
|
+
console.log(pc18.dim("Phase 3: Loading fragment files..."));
|
|
4522
4788
|
}
|
|
4523
4789
|
const fragmentFiles = await findFragmentFiles(rootDir);
|
|
4524
4790
|
if (fragmentFiles.length === 0) {
|
|
@@ -4526,12 +4792,12 @@ ${BRAND.name} AI Enhancement
|
|
|
4526
4792
|
if (format === "json") {
|
|
4527
4793
|
console.log(JSON.stringify({ success: false, error: msg }));
|
|
4528
4794
|
} else if (!isQuiet) {
|
|
4529
|
-
console.log(
|
|
4795
|
+
console.log(pc18.yellow(msg));
|
|
4530
4796
|
}
|
|
4531
4797
|
return { success: false, enhanced: [], totalTokens: 0, estimatedCost: 0 };
|
|
4532
4798
|
}
|
|
4533
4799
|
if (isInteractive) {
|
|
4534
|
-
console.log(
|
|
4800
|
+
console.log(pc18.green(` Found ${fragmentFiles.length} fragment files`));
|
|
4535
4801
|
}
|
|
4536
4802
|
let componentsToEnhance;
|
|
4537
4803
|
if (component && component !== "all") {
|
|
@@ -4540,7 +4806,7 @@ ${BRAND.name} AI Enhancement
|
|
|
4540
4806
|
componentsToEnhance = fragmentFiles.map((f) => extractComponentName(f));
|
|
4541
4807
|
}
|
|
4542
4808
|
if (isInteractive) {
|
|
4543
|
-
console.log(
|
|
4809
|
+
console.log(pc18.dim("Phase 4: Extracting props from TypeScript interfaces..."));
|
|
4544
4810
|
}
|
|
4545
4811
|
const propsExtractions = /* @__PURE__ */ new Map();
|
|
4546
4812
|
for (const compName of componentsToEnhance) {
|
|
@@ -4575,18 +4841,18 @@ ${BRAND.name} AI Enhancement
|
|
|
4575
4841
|
}
|
|
4576
4842
|
if (isInteractive) {
|
|
4577
4843
|
const propsCount = Array.from(propsExtractions.values()).reduce((sum, ext) => sum + ext.props.length, 0);
|
|
4578
|
-
console.log(
|
|
4844
|
+
console.log(pc18.green(` Extracted ${propsCount} props from ${propsExtractions.size} component files`));
|
|
4579
4845
|
}
|
|
4580
4846
|
const renderedVariants = /* @__PURE__ */ new Map();
|
|
4581
4847
|
if (renderVariants) {
|
|
4582
4848
|
if (isInteractive) {
|
|
4583
|
-
console.log(
|
|
4849
|
+
console.log(pc18.dim("Phase 5: Rendering variants from Storybook..."));
|
|
4584
4850
|
}
|
|
4585
4851
|
const storybookRunning = await checkStorybookRunning(storybookUrl);
|
|
4586
4852
|
if (!storybookRunning) {
|
|
4587
4853
|
if (isInteractive) {
|
|
4588
|
-
console.log(
|
|
4589
|
-
console.log(
|
|
4854
|
+
console.log(pc18.yellow(` Storybook not running at ${storybookUrl}. Skipping variant rendering.`));
|
|
4855
|
+
console.log(pc18.dim(` Start Storybook with: npm run storybook (or yarn storybook)`));
|
|
4590
4856
|
}
|
|
4591
4857
|
} else {
|
|
4592
4858
|
let totalVariants = 0;
|
|
@@ -4601,16 +4867,16 @@ ${BRAND.name} AI Enhancement
|
|
|
4601
4867
|
failedVariants += result.failed.length;
|
|
4602
4868
|
} catch (err) {
|
|
4603
4869
|
if (isInteractive) {
|
|
4604
|
-
console.log(
|
|
4870
|
+
console.log(pc18.dim(` ${compName}: Failed to render (${err.message})`));
|
|
4605
4871
|
}
|
|
4606
4872
|
}
|
|
4607
4873
|
}
|
|
4608
4874
|
if (isInteractive) {
|
|
4609
4875
|
if (totalVariants > 0) {
|
|
4610
|
-
console.log(
|
|
4876
|
+
console.log(pc18.green(` Rendered ${totalVariants} variants from ${renderedVariants.size} components`));
|
|
4611
4877
|
}
|
|
4612
4878
|
if (failedVariants > 0) {
|
|
4613
|
-
console.log(
|
|
4879
|
+
console.log(pc18.yellow(` ${failedVariants} variant(s) failed to render`));
|
|
4614
4880
|
}
|
|
4615
4881
|
}
|
|
4616
4882
|
await shutdownSharedPool();
|
|
@@ -4636,7 +4902,7 @@ ${BRAND.name} AI Enhancement
|
|
|
4636
4902
|
return handleContextOnlyMode(contexts, format, isInteractive);
|
|
4637
4903
|
}
|
|
4638
4904
|
if (isInteractive) {
|
|
4639
|
-
console.log(
|
|
4905
|
+
console.log(pc18.dim(`
|
|
4640
4906
|
Phase 6: Generating AI enhancements for ${componentsToEnhance.length} component(s)...
|
|
4641
4907
|
`));
|
|
4642
4908
|
}
|
|
@@ -4654,7 +4920,7 @@ Phase 6: Generating AI enhancements for ${componentsToEnhance.length} component(
|
|
|
4654
4920
|
reason: context2.usageAnalysis?.totalUsages === 1 ? "Only 1 usage found" : "No usage data found"
|
|
4655
4921
|
});
|
|
4656
4922
|
if (isInteractive) {
|
|
4657
|
-
console.log(
|
|
4923
|
+
console.log(pc18.dim(` ${compName}: Skipped (insufficient data)`));
|
|
4658
4924
|
}
|
|
4659
4925
|
continue;
|
|
4660
4926
|
}
|
|
@@ -4672,7 +4938,7 @@ Phase 6: Generating AI enhancements for ${componentsToEnhance.length} component(
|
|
|
4672
4938
|
});
|
|
4673
4939
|
totalTokens += result.tokensUsed;
|
|
4674
4940
|
if (isInteractive) {
|
|
4675
|
-
process.stdout.write(`\r ${compName}: ${
|
|
4941
|
+
process.stdout.write(`\r ${compName}: ${pc18.green("Done")} (${result.suggestions.when.length} when, ${result.suggestions.whenNot.length} whenNot)
|
|
4676
4942
|
`);
|
|
4677
4943
|
}
|
|
4678
4944
|
} catch (error) {
|
|
@@ -4686,7 +4952,7 @@ Phase 6: Generating AI enhancements for ${componentsToEnhance.length} component(
|
|
|
4686
4952
|
reason: `AI error: ${msg}`
|
|
4687
4953
|
});
|
|
4688
4954
|
if (isInteractive) {
|
|
4689
|
-
process.stdout.write(`\r ${compName}: ${
|
|
4955
|
+
process.stdout.write(`\r ${compName}: ${pc18.red("Failed")} - ${msg}
|
|
4690
4956
|
`);
|
|
4691
4957
|
}
|
|
4692
4958
|
}
|
|
@@ -4694,7 +4960,7 @@ Phase 6: Generating AI enhancements for ${componentsToEnhance.length} component(
|
|
|
4694
4960
|
const estimatedCost = calculateCost(provider, totalTokens);
|
|
4695
4961
|
if (!dryRun) {
|
|
4696
4962
|
if (isInteractive) {
|
|
4697
|
-
console.log(
|
|
4963
|
+
console.log(pc18.dim("\nPhase 7: Updating fragment files..."));
|
|
4698
4964
|
}
|
|
4699
4965
|
for (const result of enhanced) {
|
|
4700
4966
|
if (result.skipped || result.added.when.length === 0 && result.added.whenNot.length === 0) {
|
|
@@ -4705,11 +4971,11 @@ Phase 6: Generating AI enhancements for ${componentsToEnhance.length} component(
|
|
|
4705
4971
|
try {
|
|
4706
4972
|
await updateFragmentFile(fragmentFile, result.added);
|
|
4707
4973
|
if (isInteractive) {
|
|
4708
|
-
console.log(
|
|
4974
|
+
console.log(pc18.green(` Updated: ${relative7(rootDir, fragmentFile)}`));
|
|
4709
4975
|
}
|
|
4710
4976
|
} catch {
|
|
4711
4977
|
if (isInteractive) {
|
|
4712
|
-
console.log(
|
|
4978
|
+
console.log(pc18.red(` Failed to update: ${relative7(rootDir, fragmentFile)}`));
|
|
4713
4979
|
}
|
|
4714
4980
|
}
|
|
4715
4981
|
}
|
|
@@ -4724,7 +4990,7 @@ Phase 6: Generating AI enhancements for ${componentsToEnhance.length} component(
|
|
|
4724
4990
|
}, null, 2));
|
|
4725
4991
|
} else if (isInteractive) {
|
|
4726
4992
|
console.log();
|
|
4727
|
-
console.log(
|
|
4993
|
+
console.log(pc18.bold("Summary:"));
|
|
4728
4994
|
console.log(` Components processed: ${componentsToEnhance.length}`);
|
|
4729
4995
|
console.log(` Successfully enhanced: ${successCount}`);
|
|
4730
4996
|
console.log(` Skipped: ${enhanced.filter((e) => e.skipped).length}`);
|
|
@@ -4732,7 +4998,7 @@ Phase 6: Generating AI enhancements for ${componentsToEnhance.length} component(
|
|
|
4732
4998
|
console.log(` Estimated cost: $${estimatedCost.toFixed(4)}`);
|
|
4733
4999
|
if (dryRun) {
|
|
4734
5000
|
console.log();
|
|
4735
|
-
console.log(
|
|
5001
|
+
console.log(pc18.yellow("Dry run - no files were modified"));
|
|
4736
5002
|
}
|
|
4737
5003
|
console.log();
|
|
4738
5004
|
}
|
|
@@ -4756,7 +5022,7 @@ function handleContextOnlyMode(contexts, format, isInteractive) {
|
|
|
4756
5022
|
if (format === "json") {
|
|
4757
5023
|
console.log(JSON.stringify({ success: false, error: "No components with sufficient usage data" }));
|
|
4758
5024
|
} else if (isInteractive) {
|
|
4759
|
-
console.log(
|
|
5025
|
+
console.log(pc18.yellow("\nNo components with sufficient usage data to analyze."));
|
|
4760
5026
|
}
|
|
4761
5027
|
return { success: false, enhanced: [], totalTokens: 0, estimatedCost: 0 };
|
|
4762
5028
|
}
|
|
@@ -4785,14 +5051,14 @@ For each component, provide your response in JSON format:
|
|
|
4785
5051
|
instructions: "Copy this prompt into your IDE AI (Cursor, Copilot, etc.) to generate suggestions"
|
|
4786
5052
|
}, null, 2));
|
|
4787
5053
|
} else {
|
|
4788
|
-
console.log(
|
|
4789
|
-
console.log(
|
|
4790
|
-
console.log(
|
|
5054
|
+
console.log(pc18.bold("\n\u{1F4CB} AI Context Generated\n"));
|
|
5055
|
+
console.log(pc18.dim("Copy the following prompt into your IDE AI (Cursor, Copilot, etc.):\n"));
|
|
5056
|
+
console.log(pc18.dim("\u2500".repeat(60)));
|
|
4791
5057
|
console.log(fullContext);
|
|
4792
|
-
console.log(
|
|
5058
|
+
console.log(pc18.dim("\u2500".repeat(60)));
|
|
4793
5059
|
console.log();
|
|
4794
|
-
console.log(
|
|
4795
|
-
console.log(
|
|
5060
|
+
console.log(pc18.green("Tip: In Cursor, press Cmd+L to open chat and paste this prompt."));
|
|
5061
|
+
console.log(pc18.dim("After getting suggestions, manually update your fragment files."));
|
|
4796
5062
|
console.log();
|
|
4797
5063
|
}
|
|
4798
5064
|
return {
|
|
@@ -4935,7 +5201,7 @@ function extractComponentName(filePath) {
|
|
|
4935
5201
|
return match ? match[1] : "";
|
|
4936
5202
|
}
|
|
4937
5203
|
async function updateFragmentFile(filePath, suggestions) {
|
|
4938
|
-
const content = await
|
|
5204
|
+
const content = await readFile7(filePath, "utf-8");
|
|
4939
5205
|
let updated = content;
|
|
4940
5206
|
if (suggestions.when.length > 0) {
|
|
4941
5207
|
const whenItems = suggestions.when.map((s) => ` "${s}"`).join(",\n");
|
|
@@ -4984,34 +5250,34 @@ ${whenNotItems}
|
|
|
4984
5250
|
}
|
|
4985
5251
|
}
|
|
4986
5252
|
if (updated !== content) {
|
|
4987
|
-
await
|
|
5253
|
+
await writeFile8(filePath, updated, "utf-8");
|
|
4988
5254
|
}
|
|
4989
5255
|
}
|
|
4990
5256
|
|
|
4991
5257
|
// src/commands/graph.ts
|
|
4992
|
-
import
|
|
4993
|
-
import { readFile as
|
|
4994
|
-
import { resolve as
|
|
5258
|
+
import pc19 from "picocolors";
|
|
5259
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
5260
|
+
import { resolve as resolve8 } from "path";
|
|
4995
5261
|
import {
|
|
4996
5262
|
ComponentGraphEngine,
|
|
4997
5263
|
deserializeGraph
|
|
4998
5264
|
} from "@fragments-sdk/context/graph";
|
|
4999
5265
|
async function graph(component, options) {
|
|
5000
5266
|
const { config, configDir } = await loadConfig(options.config);
|
|
5001
|
-
const outputPath =
|
|
5267
|
+
const outputPath = resolve8(configDir, config.outFile ?? BRAND.outFile);
|
|
5002
5268
|
let data;
|
|
5003
5269
|
try {
|
|
5004
|
-
const content = await
|
|
5270
|
+
const content = await readFile8(outputPath, "utf-8");
|
|
5005
5271
|
data = JSON.parse(content);
|
|
5006
5272
|
} catch {
|
|
5007
5273
|
console.error(
|
|
5008
|
-
|
|
5274
|
+
pc19.red(`Error: Could not load ${BRAND.outFile}. Run \`${BRAND.cliCommand} build\` first.`)
|
|
5009
5275
|
);
|
|
5010
5276
|
process.exit(1);
|
|
5011
5277
|
}
|
|
5012
5278
|
if (!data.graph) {
|
|
5013
5279
|
console.error(
|
|
5014
|
-
|
|
5280
|
+
pc19.red(`Error: No graph data in ${BRAND.outFile}. Rebuild with the latest CLI.`)
|
|
5015
5281
|
);
|
|
5016
5282
|
process.exit(1);
|
|
5017
5283
|
}
|
|
@@ -5025,11 +5291,11 @@ async function graph(component, options) {
|
|
|
5025
5291
|
const edgeTypes = options.edgeTypes ? options.edgeTypes.split(",") : void 0;
|
|
5026
5292
|
const needsComponent = ["dependencies", "dependents", "impact", "path", "composition", "alternatives"];
|
|
5027
5293
|
if (needsComponent.includes(mode) && !component) {
|
|
5028
|
-
console.error(
|
|
5294
|
+
console.error(pc19.red(`Error: "${mode}" mode requires a component name.`));
|
|
5029
5295
|
process.exit(1);
|
|
5030
5296
|
}
|
|
5031
5297
|
if (component && !engine.hasNode(component)) {
|
|
5032
|
-
console.error(
|
|
5298
|
+
console.error(pc19.red(`Error: Component "${component}" not found in graph.`));
|
|
5033
5299
|
process.exit(1);
|
|
5034
5300
|
}
|
|
5035
5301
|
switch (mode) {
|
|
@@ -5039,18 +5305,18 @@ async function graph(component, options) {
|
|
|
5039
5305
|
console.log(JSON.stringify(health, null, 2));
|
|
5040
5306
|
return;
|
|
5041
5307
|
}
|
|
5042
|
-
console.log(
|
|
5043
|
-
console.log(` ${
|
|
5044
|
-
console.log(` ${
|
|
5045
|
-
console.log(` ${
|
|
5046
|
-
console.log(` ${
|
|
5047
|
-
console.log(` ${
|
|
5048
|
-
console.log(` ${
|
|
5308
|
+
console.log(pc19.bold("\nComponent Graph Health\n"));
|
|
5309
|
+
console.log(` ${pc19.cyan("Nodes:")} ${health.nodeCount}`);
|
|
5310
|
+
console.log(` ${pc19.cyan("Edges:")} ${health.edgeCount}`);
|
|
5311
|
+
console.log(` ${pc19.cyan("Avg degree:")} ${health.averageDegree}`);
|
|
5312
|
+
console.log(` ${pc19.cyan("Islands:")} ${health.connectedComponents.length}`);
|
|
5313
|
+
console.log(` ${pc19.cyan("Coverage:")} ${health.compositionCoverage}% in blocks`);
|
|
5314
|
+
console.log(` ${pc19.cyan("Orphans:")} ${health.orphans.length > 0 ? health.orphans.join(", ") : pc19.green("none")}`);
|
|
5049
5315
|
if (health.hubs.length > 0) {
|
|
5050
5316
|
console.log(`
|
|
5051
|
-
${
|
|
5317
|
+
${pc19.bold("Top hubs:")}`);
|
|
5052
5318
|
for (const hub of health.hubs.slice(0, 5)) {
|
|
5053
|
-
console.log(` ${
|
|
5319
|
+
console.log(` ${pc19.yellow(hub.name)} \u2014 ${hub.degree} connections`);
|
|
5054
5320
|
}
|
|
5055
5321
|
}
|
|
5056
5322
|
console.log();
|
|
@@ -5062,14 +5328,14 @@ async function graph(component, options) {
|
|
|
5062
5328
|
console.log(JSON.stringify({ component, dependencies: deps }, null, 2));
|
|
5063
5329
|
return;
|
|
5064
5330
|
}
|
|
5065
|
-
console.log(
|
|
5331
|
+
console.log(pc19.bold(`
|
|
5066
5332
|
Dependencies of ${component}
|
|
5067
5333
|
`));
|
|
5068
5334
|
if (deps.length === 0) {
|
|
5069
5335
|
console.log(" No outgoing dependencies.");
|
|
5070
5336
|
} else {
|
|
5071
5337
|
for (const dep of deps) {
|
|
5072
|
-
console.log(` ${
|
|
5338
|
+
console.log(` ${pc19.yellow(dep.target)} ${pc19.dim(`(${dep.type})`)}${dep.note ? ` \u2014 ${dep.note}` : ""}`);
|
|
5073
5339
|
}
|
|
5074
5340
|
}
|
|
5075
5341
|
console.log();
|
|
@@ -5081,14 +5347,14 @@ Dependencies of ${component}
|
|
|
5081
5347
|
console.log(JSON.stringify({ component, dependents: deps }, null, 2));
|
|
5082
5348
|
return;
|
|
5083
5349
|
}
|
|
5084
|
-
console.log(
|
|
5350
|
+
console.log(pc19.bold(`
|
|
5085
5351
|
Dependents of ${component}
|
|
5086
5352
|
`));
|
|
5087
5353
|
if (deps.length === 0) {
|
|
5088
5354
|
console.log(" No incoming dependents.");
|
|
5089
5355
|
} else {
|
|
5090
5356
|
for (const dep of deps) {
|
|
5091
|
-
console.log(` ${
|
|
5357
|
+
console.log(` ${pc19.yellow(dep.source)} ${pc19.dim(`(${dep.type})`)}${dep.note ? ` \u2014 ${dep.note}` : ""}`);
|
|
5092
5358
|
}
|
|
5093
5359
|
}
|
|
5094
5360
|
console.log();
|
|
@@ -5100,28 +5366,28 @@ Dependents of ${component}
|
|
|
5100
5366
|
console.log(JSON.stringify(result, null, 2));
|
|
5101
5367
|
return;
|
|
5102
5368
|
}
|
|
5103
|
-
console.log(
|
|
5369
|
+
console.log(pc19.bold(`
|
|
5104
5370
|
Impact analysis: ${component}
|
|
5105
5371
|
`));
|
|
5106
|
-
console.log(` ${
|
|
5372
|
+
console.log(` ${pc19.red(`${result.totalAffected}`)} affected components, ${pc19.red(`${result.affectedBlocks.length}`)} affected blocks
|
|
5107
5373
|
`);
|
|
5108
5374
|
if (result.affected.length > 0) {
|
|
5109
|
-
console.log(` ${
|
|
5375
|
+
console.log(` ${pc19.bold("Affected components:")}`);
|
|
5110
5376
|
for (const entry of result.affected) {
|
|
5111
5377
|
const indent = " ".repeat(entry.depth + 1);
|
|
5112
|
-
console.log(`${indent}${
|
|
5378
|
+
console.log(`${indent}${pc19.yellow(entry.component)} ${pc19.dim(`(depth ${entry.depth}, via ${entry.edgeType})`)}`);
|
|
5113
5379
|
}
|
|
5114
5380
|
}
|
|
5115
5381
|
if (result.affectedBlocks.length > 0) {
|
|
5116
5382
|
console.log(`
|
|
5117
|
-
${
|
|
5383
|
+
${pc19.bold("Affected blocks:")} ${result.affectedBlocks.join(", ")}`);
|
|
5118
5384
|
}
|
|
5119
5385
|
console.log();
|
|
5120
5386
|
break;
|
|
5121
5387
|
}
|
|
5122
5388
|
case "path": {
|
|
5123
5389
|
if (!options.target) {
|
|
5124
|
-
console.error(
|
|
5390
|
+
console.error(pc19.red("Error: --target is required for path mode."));
|
|
5125
5391
|
process.exit(1);
|
|
5126
5392
|
}
|
|
5127
5393
|
const result = engine.path(component, options.target);
|
|
@@ -5129,15 +5395,15 @@ Impact analysis: ${component}
|
|
|
5129
5395
|
console.log(JSON.stringify(result, null, 2));
|
|
5130
5396
|
return;
|
|
5131
5397
|
}
|
|
5132
|
-
console.log(
|
|
5398
|
+
console.log(pc19.bold(`
|
|
5133
5399
|
Path: ${component} \u2192 ${options.target}
|
|
5134
5400
|
`));
|
|
5135
5401
|
if (!result.found) {
|
|
5136
|
-
console.log(` ${
|
|
5402
|
+
console.log(` ${pc19.red("No path found.")}`);
|
|
5137
5403
|
} else {
|
|
5138
|
-
console.log(` ${result.path.map((n) =>
|
|
5404
|
+
console.log(` ${result.path.map((n) => pc19.yellow(n)).join(pc19.dim(" \u2192 "))}`);
|
|
5139
5405
|
if (result.edges.length > 0) {
|
|
5140
|
-
console.log(` ${
|
|
5406
|
+
console.log(` ${pc19.dim(`(${result.edges.map((e) => e.type).join(" \u2192 ")})`)}`);
|
|
5141
5407
|
}
|
|
5142
5408
|
}
|
|
5143
5409
|
console.log();
|
|
@@ -5149,25 +5415,25 @@ Path: ${component} \u2192 ${options.target}
|
|
|
5149
5415
|
console.log(JSON.stringify(tree, null, 2));
|
|
5150
5416
|
return;
|
|
5151
5417
|
}
|
|
5152
|
-
console.log(
|
|
5418
|
+
console.log(pc19.bold(`
|
|
5153
5419
|
Composition: ${component}
|
|
5154
5420
|
`));
|
|
5155
|
-
console.log(` ${
|
|
5421
|
+
console.log(` ${pc19.cyan("Pattern:")} ${tree.compositionPattern ?? "unknown"}`);
|
|
5156
5422
|
if (tree.parent) {
|
|
5157
|
-
console.log(` ${
|
|
5423
|
+
console.log(` ${pc19.cyan("Parent:")} ${tree.parent}`);
|
|
5158
5424
|
}
|
|
5159
5425
|
if (tree.subComponents.length > 0) {
|
|
5160
|
-
console.log(` ${
|
|
5426
|
+
console.log(` ${pc19.cyan("Sub-components:")}`);
|
|
5161
5427
|
for (const sub of tree.subComponents) {
|
|
5162
5428
|
const isChild = tree.children.includes(sub);
|
|
5163
|
-
console.log(` ${
|
|
5429
|
+
console.log(` ${pc19.yellow(sub)}${isChild ? pc19.dim(" (child)") : ""}`);
|
|
5164
5430
|
}
|
|
5165
5431
|
}
|
|
5166
5432
|
if (tree.siblings.length > 0) {
|
|
5167
|
-
console.log(` ${
|
|
5433
|
+
console.log(` ${pc19.cyan("Siblings:")} ${tree.siblings.join(", ")}`);
|
|
5168
5434
|
}
|
|
5169
5435
|
if (tree.blocks.length > 0) {
|
|
5170
|
-
console.log(` ${
|
|
5436
|
+
console.log(` ${pc19.cyan("In blocks:")} ${tree.blocks.join(", ")}`);
|
|
5171
5437
|
}
|
|
5172
5438
|
console.log();
|
|
5173
5439
|
break;
|
|
@@ -5178,14 +5444,14 @@ Composition: ${component}
|
|
|
5178
5444
|
console.log(JSON.stringify({ component, alternatives: alts }, null, 2));
|
|
5179
5445
|
return;
|
|
5180
5446
|
}
|
|
5181
|
-
console.log(
|
|
5447
|
+
console.log(pc19.bold(`
|
|
5182
5448
|
Alternatives for ${component}
|
|
5183
5449
|
`));
|
|
5184
5450
|
if (alts.length === 0) {
|
|
5185
5451
|
console.log(" No known alternatives.");
|
|
5186
5452
|
} else {
|
|
5187
5453
|
for (const alt of alts) {
|
|
5188
|
-
console.log(` ${
|
|
5454
|
+
console.log(` ${pc19.yellow(alt.component)}${alt.note ? ` \u2014 ${alt.note}` : ""}`);
|
|
5189
5455
|
}
|
|
5190
5456
|
}
|
|
5191
5457
|
console.log();
|
|
@@ -5197,17 +5463,17 @@ Alternatives for ${component}
|
|
|
5197
5463
|
console.log(JSON.stringify({ islands }, null, 2));
|
|
5198
5464
|
return;
|
|
5199
5465
|
}
|
|
5200
|
-
console.log(
|
|
5466
|
+
console.log(pc19.bold(`
|
|
5201
5467
|
Connected Islands (${islands.length})
|
|
5202
5468
|
`));
|
|
5203
5469
|
for (let i = 0; i < islands.length; i++) {
|
|
5204
|
-
console.log(` ${
|
|
5470
|
+
console.log(` ${pc19.cyan(`Island ${i + 1}`)} (${islands[i].length} components): ${islands[i].join(", ")}`);
|
|
5205
5471
|
}
|
|
5206
5472
|
console.log();
|
|
5207
5473
|
break;
|
|
5208
5474
|
}
|
|
5209
5475
|
default:
|
|
5210
|
-
console.error(
|
|
5476
|
+
console.error(pc19.red(`Unknown mode: "${mode}". Valid: health, dependencies, dependents, impact, path, composition, alternatives, islands`));
|
|
5211
5477
|
process.exit(1);
|
|
5212
5478
|
}
|
|
5213
5479
|
if (format === "dot") {
|
|
@@ -5225,9 +5491,9 @@ Connected Islands (${islands.length})
|
|
|
5225
5491
|
}
|
|
5226
5492
|
|
|
5227
5493
|
// src/commands/inspect.ts
|
|
5228
|
-
import
|
|
5229
|
-
import { readFile as
|
|
5230
|
-
import { resolve as
|
|
5494
|
+
import pc20 from "picocolors";
|
|
5495
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
5496
|
+
import { resolve as resolve9 } from "path";
|
|
5231
5497
|
function levenshtein(a, b) {
|
|
5232
5498
|
const m = a.length;
|
|
5233
5499
|
const n = b.length;
|
|
@@ -5260,14 +5526,14 @@ function findClosestMatch(input, candidates, maxDistance = 3) {
|
|
|
5260
5526
|
}
|
|
5261
5527
|
async function inspect(component, options) {
|
|
5262
5528
|
const { config, configDir } = await loadConfig(options.config);
|
|
5263
|
-
const outputPath =
|
|
5529
|
+
const outputPath = resolve9(configDir, config.outFile ?? BRAND.outFile);
|
|
5264
5530
|
let data;
|
|
5265
5531
|
try {
|
|
5266
|
-
const content = await
|
|
5532
|
+
const content = await readFile9(outputPath, "utf-8");
|
|
5267
5533
|
data = JSON.parse(content);
|
|
5268
5534
|
} catch {
|
|
5269
5535
|
console.error(
|
|
5270
|
-
|
|
5536
|
+
pc20.red(`Error: Could not load ${BRAND.outFile}. Run \`${BRAND.cliCommand} build\` first.`)
|
|
5271
5537
|
);
|
|
5272
5538
|
process.exit(1);
|
|
5273
5539
|
}
|
|
@@ -5279,10 +5545,10 @@ async function inspect(component, options) {
|
|
|
5279
5545
|
const closest = findClosestMatch(component, allNames);
|
|
5280
5546
|
const suggestion = closest ? ` Did you mean "${closest}"?` : "";
|
|
5281
5547
|
console.error(
|
|
5282
|
-
|
|
5548
|
+
pc20.red(`Error: Component "${component}" not found.${suggestion}`)
|
|
5283
5549
|
);
|
|
5284
5550
|
console.error(
|
|
5285
|
-
|
|
5551
|
+
pc20.dim(`Use \`${BRAND.cliCommand} discover\` to see available components.`)
|
|
5286
5552
|
);
|
|
5287
5553
|
process.exit(1);
|
|
5288
5554
|
}
|
|
@@ -5301,7 +5567,7 @@ async function inspect(component, options) {
|
|
|
5301
5567
|
variants = filtered;
|
|
5302
5568
|
} else {
|
|
5303
5569
|
console.error(
|
|
5304
|
-
|
|
5570
|
+
pc20.red(
|
|
5305
5571
|
`Error: Variant "${options.variant}" not found for ${component}. Available: ${fragment.variants.map((v) => v.name).join(", ")}`
|
|
5306
5572
|
)
|
|
5307
5573
|
);
|
|
@@ -5370,59 +5636,59 @@ async function inspect(component, options) {
|
|
|
5370
5636
|
return;
|
|
5371
5637
|
}
|
|
5372
5638
|
const meta = fragment.meta;
|
|
5373
|
-
console.log(
|
|
5639
|
+
console.log(pc20.bold(`
|
|
5374
5640
|
${meta.name}
|
|
5375
5641
|
`));
|
|
5376
|
-
console.log(` ${
|
|
5377
|
-
console.log(` ${
|
|
5642
|
+
console.log(` ${pc20.cyan("Category:")} ${meta.category}`);
|
|
5643
|
+
console.log(` ${pc20.cyan("Status:")} ${meta.status ?? "stable"}`);
|
|
5378
5644
|
if (meta.description) {
|
|
5379
|
-
console.log(` ${
|
|
5645
|
+
console.log(` ${pc20.cyan("Description:")} ${meta.description}`);
|
|
5380
5646
|
}
|
|
5381
5647
|
if (meta.tags && meta.tags.length > 0) {
|
|
5382
|
-
console.log(` ${
|
|
5648
|
+
console.log(` ${pc20.cyan("Tags:")} ${meta.tags.join(", ")}`);
|
|
5383
5649
|
}
|
|
5384
5650
|
const propEntries = Object.entries(fragment.props ?? {});
|
|
5385
5651
|
if (propEntries.length > 0) {
|
|
5386
|
-
console.log(
|
|
5652
|
+
console.log(pc20.bold("\n Props\n"));
|
|
5387
5653
|
console.log(
|
|
5388
|
-
|
|
5654
|
+
pc20.dim(
|
|
5389
5655
|
` ${"Name".padEnd(24)} ${"Type".padEnd(20)} ${"Required".padEnd(10)} ${"Default"}`
|
|
5390
5656
|
)
|
|
5391
5657
|
);
|
|
5392
|
-
console.log(
|
|
5658
|
+
console.log(pc20.dim(` ${"\u2500".repeat(70)}`));
|
|
5393
5659
|
const displayProps = verbosity === "compact" ? propEntries.slice(0, 5) : propEntries;
|
|
5394
5660
|
for (const [name, prop] of displayProps) {
|
|
5395
5661
|
const propName = name.length > 22 ? name.slice(0, 19) + "..." : name;
|
|
5396
5662
|
const propType = (prop.type ?? "").length > 18 ? (prop.type ?? "").slice(0, 15) + "..." : prop.type ?? "";
|
|
5397
5663
|
console.log(
|
|
5398
|
-
` ${
|
|
5664
|
+
` ${pc20.cyan(propName.padEnd(24))} ${propType.padEnd(20)} ${(prop.required ? "yes" : "no").padEnd(10)} ${pc20.dim(String(prop.default ?? "-"))}`
|
|
5399
5665
|
);
|
|
5400
5666
|
}
|
|
5401
5667
|
if (verbosity === "compact" && propEntries.length > 5) {
|
|
5402
|
-
console.log(
|
|
5668
|
+
console.log(pc20.dim(` ... and ${propEntries.length - 5} more`));
|
|
5403
5669
|
}
|
|
5404
5670
|
}
|
|
5405
5671
|
if (variants.length > 0) {
|
|
5406
|
-
console.log(
|
|
5672
|
+
console.log(pc20.bold("\n Variants\n"));
|
|
5407
5673
|
for (const v of variants) {
|
|
5408
|
-
console.log(` ${
|
|
5674
|
+
console.log(` ${pc20.yellow(v.name)}${v.description ? pc20.dim(` \u2014 ${v.description}`) : ""}`);
|
|
5409
5675
|
}
|
|
5410
5676
|
}
|
|
5411
5677
|
if (verbosity !== "compact") {
|
|
5412
5678
|
const when = fragment.usage?.when ?? [];
|
|
5413
5679
|
const whenNot = fragment.usage?.whenNot ?? [];
|
|
5414
5680
|
if (when.length > 0 || whenNot.length > 0) {
|
|
5415
|
-
console.log(
|
|
5681
|
+
console.log(pc20.bold("\n Usage Guidelines\n"));
|
|
5416
5682
|
if (when.length > 0) {
|
|
5417
|
-
console.log(` ${
|
|
5683
|
+
console.log(` ${pc20.green("When to use:")}`);
|
|
5418
5684
|
for (const w of when) {
|
|
5419
|
-
console.log(` ${
|
|
5685
|
+
console.log(` ${pc20.dim("\u2022")} ${w}`);
|
|
5420
5686
|
}
|
|
5421
5687
|
}
|
|
5422
5688
|
if (whenNot.length > 0) {
|
|
5423
|
-
console.log(` ${
|
|
5689
|
+
console.log(` ${pc20.red("When NOT to use:")}`);
|
|
5424
5690
|
for (const w of whenNot) {
|
|
5425
|
-
console.log(` ${
|
|
5691
|
+
console.log(` ${pc20.dim("\u2022")} ${w}`);
|
|
5426
5692
|
}
|
|
5427
5693
|
}
|
|
5428
5694
|
}
|
|
@@ -5431,19 +5697,19 @@ ${meta.name}
|
|
|
5431
5697
|
}
|
|
5432
5698
|
|
|
5433
5699
|
// src/commands/discover.ts
|
|
5434
|
-
import
|
|
5435
|
-
import { readFile as
|
|
5436
|
-
import { resolve as
|
|
5700
|
+
import pc21 from "picocolors";
|
|
5701
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
5702
|
+
import { resolve as resolve10 } from "path";
|
|
5437
5703
|
async function discover(options) {
|
|
5438
5704
|
const { config, configDir } = await loadConfig(options.config);
|
|
5439
|
-
const outputPath =
|
|
5705
|
+
const outputPath = resolve10(configDir, config.outFile ?? BRAND.outFile);
|
|
5440
5706
|
let data;
|
|
5441
5707
|
try {
|
|
5442
|
-
const content = await
|
|
5708
|
+
const content = await readFile10(outputPath, "utf-8");
|
|
5443
5709
|
data = JSON.parse(content);
|
|
5444
5710
|
} catch {
|
|
5445
5711
|
console.error(
|
|
5446
|
-
|
|
5712
|
+
pc21.red(`Error: Could not load ${BRAND.outFile}. Run \`${BRAND.cliCommand} build\` first.`)
|
|
5447
5713
|
);
|
|
5448
5714
|
process.exit(1);
|
|
5449
5715
|
}
|
|
@@ -5490,53 +5756,53 @@ async function discover(options) {
|
|
|
5490
5756
|
return;
|
|
5491
5757
|
}
|
|
5492
5758
|
if (fragments.length === 0) {
|
|
5493
|
-
console.log(
|
|
5759
|
+
console.log(pc21.yellow("\nNo components found matching the given filters.\n"));
|
|
5494
5760
|
return;
|
|
5495
5761
|
}
|
|
5496
|
-
console.log(
|
|
5762
|
+
console.log(pc21.bold(`
|
|
5497
5763
|
${BRAND.name} Components (${fragments.length})
|
|
5498
5764
|
`));
|
|
5499
5765
|
console.log(
|
|
5500
|
-
|
|
5766
|
+
pc21.dim(
|
|
5501
5767
|
` ${"Name".padEnd(28)} ${"Category".padEnd(16)} ${"Status".padEnd(14)} ${"Variants".padEnd(10)} ${"Props"}`
|
|
5502
5768
|
)
|
|
5503
5769
|
);
|
|
5504
|
-
console.log(
|
|
5770
|
+
console.log(pc21.dim(` ${"\u2500".repeat(80)}`));
|
|
5505
5771
|
for (const f of fragments) {
|
|
5506
5772
|
const name = f.meta.name.length > 26 ? f.meta.name.slice(0, 23) + "..." : f.meta.name;
|
|
5507
5773
|
const category = (f.meta.category ?? "").padEnd(16);
|
|
5508
5774
|
const status = f.meta.status ?? "stable";
|
|
5509
|
-
const statusColored = status === "stable" ?
|
|
5775
|
+
const statusColored = status === "stable" ? pc21.green(status.padEnd(14)) : status === "deprecated" ? pc21.red(status.padEnd(14)) : status === "beta" ? pc21.yellow(status.padEnd(14)) : pc21.cyan(status.padEnd(14));
|
|
5510
5776
|
const variantCount = String(f.variants.length).padEnd(10);
|
|
5511
5777
|
const propCount = String(Object.keys(f.props ?? {}).length);
|
|
5512
5778
|
console.log(
|
|
5513
|
-
` ${
|
|
5779
|
+
` ${pc21.cyan(name.padEnd(28))} ${pc21.dim(category)} ${statusColored} ${variantCount} ${propCount}`
|
|
5514
5780
|
);
|
|
5515
5781
|
}
|
|
5516
|
-
console.log(
|
|
5782
|
+
console.log(pc21.dim(`
|
|
5517
5783
|
${"\u2500".repeat(80)}`));
|
|
5518
5784
|
console.log(
|
|
5519
|
-
|
|
5785
|
+
pc21.dim(` ${fragments.length} component(s) across ${categories.length} categories`)
|
|
5520
5786
|
);
|
|
5521
5787
|
if (categories.length > 0) {
|
|
5522
|
-
console.log(
|
|
5788
|
+
console.log(pc21.dim(` Categories: ${categories.join(", ")}`));
|
|
5523
5789
|
}
|
|
5524
5790
|
console.log();
|
|
5525
5791
|
}
|
|
5526
5792
|
|
|
5527
5793
|
// src/commands/perf.ts
|
|
5528
|
-
import
|
|
5529
|
-
import { readFile as
|
|
5530
|
-
import { resolve as
|
|
5794
|
+
import pc22 from "picocolors";
|
|
5795
|
+
import { readFile as readFile11, writeFile as writeFile9 } from "fs/promises";
|
|
5796
|
+
import { resolve as resolve12 } from "path";
|
|
5531
5797
|
|
|
5532
5798
|
// src/core/bundle-measurer.ts
|
|
5533
5799
|
import { build as build2 } from "esbuild";
|
|
5534
5800
|
import { gzipSync } from "zlib";
|
|
5535
|
-
import { resolve as
|
|
5801
|
+
import { resolve as resolve11, dirname as dirname5, join as join10, basename } from "path";
|
|
5536
5802
|
import { existsSync as existsSync3 } from "fs";
|
|
5537
5803
|
function resolveEntryPoint(fragmentFilePath, configDir) {
|
|
5538
|
-
const absPath =
|
|
5539
|
-
const dir =
|
|
5804
|
+
const absPath = resolve11(configDir, fragmentFilePath);
|
|
5805
|
+
const dir = dirname5(absPath);
|
|
5540
5806
|
const candidates = ["index.tsx", "index.ts", "index.jsx", "index.js"];
|
|
5541
5807
|
for (const candidate of candidates) {
|
|
5542
5808
|
const path = join10(dir, candidate);
|
|
@@ -5578,7 +5844,7 @@ function groupImportsByDirectDep(metafile, entryPoint) {
|
|
|
5578
5844
|
let entryKey;
|
|
5579
5845
|
for (const key of Object.keys(inputs)) {
|
|
5580
5846
|
if (key === entryPoint || entryPoint.endsWith(key) || key.endsWith(basename(entryPoint))) {
|
|
5581
|
-
const entryDir =
|
|
5847
|
+
const entryDir = dirname5(entryPoint);
|
|
5582
5848
|
if (key.includes(basename(entryDir))) {
|
|
5583
5849
|
entryKey = key;
|
|
5584
5850
|
break;
|
|
@@ -5586,7 +5852,7 @@ function groupImportsByDirectDep(metafile, entryPoint) {
|
|
|
5586
5852
|
}
|
|
5587
5853
|
}
|
|
5588
5854
|
if (!entryKey) {
|
|
5589
|
-
const entryBasename = basename(
|
|
5855
|
+
const entryBasename = basename(dirname5(entryPoint));
|
|
5590
5856
|
for (const key of Object.keys(inputs)) {
|
|
5591
5857
|
if (key.includes(`/${entryBasename}/index.`)) {
|
|
5592
5858
|
entryKey = key;
|
|
@@ -5779,10 +6045,10 @@ async function perf(options) {
|
|
|
5779
6045
|
const shouldWrite = options.write !== false;
|
|
5780
6046
|
const { config, configDir } = await loadConfig(configPath);
|
|
5781
6047
|
const perfConfig = resolvePerformanceConfig(config.performance ?? "standard");
|
|
5782
|
-
const outFile =
|
|
6048
|
+
const outFile = resolve12(configDir, config.outFile ?? BRAND.outFile);
|
|
5783
6049
|
let data;
|
|
5784
6050
|
try {
|
|
5785
|
-
data = JSON.parse(await
|
|
6051
|
+
data = JSON.parse(await readFile11(outFile, "utf-8"));
|
|
5786
6052
|
} catch {
|
|
5787
6053
|
throw new Error(
|
|
5788
6054
|
`Could not read ${BRAND.outFile}. Run \`${BRAND.cliCommand} build\` first.`
|
|
@@ -5801,18 +6067,18 @@ async function perf(options) {
|
|
|
5801
6067
|
fragments = { [match[0]]: match[1] };
|
|
5802
6068
|
}
|
|
5803
6069
|
if (!json) {
|
|
5804
|
-
console.log(
|
|
6070
|
+
console.log(pc22.cyan(`
|
|
5805
6071
|
${BRAND.name} Performance Profiler
|
|
5806
6072
|
`));
|
|
5807
|
-
console.log(
|
|
5808
|
-
console.log(
|
|
6073
|
+
console.log(pc22.dim(`Preset: ${perfConfig.preset} (${formatBytes(perfConfig.budgets.bundleSize)} budget)`));
|
|
6074
|
+
console.log(pc22.dim(`Measuring ${Object.keys(fragments).length} component(s)...
|
|
5809
6075
|
`));
|
|
5810
6076
|
}
|
|
5811
6077
|
const result = await measureBundleSizes(fragments, configDir, {
|
|
5812
6078
|
concurrency: concurrency ?? 4,
|
|
5813
6079
|
perfConfig,
|
|
5814
6080
|
onProgress: !json ? (done, total, name) => {
|
|
5815
|
-
process.stdout.write(`\r${
|
|
6081
|
+
process.stdout.write(`\r${pc22.dim(`[${done}/${total}]`)} ${name}`);
|
|
5816
6082
|
} : void 0
|
|
5817
6083
|
});
|
|
5818
6084
|
if (!json) {
|
|
@@ -5850,49 +6116,49 @@ ${BRAND.name} Performance Profiler
|
|
|
5850
6116
|
} else {
|
|
5851
6117
|
const nameWidth = Math.max(20, ...perfResults.map((r) => r.name.length)) + 2;
|
|
5852
6118
|
console.log(
|
|
5853
|
-
|
|
6119
|
+
pc22.bold(
|
|
5854
6120
|
"Component".padEnd(nameWidth) + "Gzip".padStart(8) + "Raw".padStart(10) + "Budget".padStart(8) + "Tier".padStart(14) + " Bar"
|
|
5855
6121
|
)
|
|
5856
6122
|
);
|
|
5857
|
-
console.log(
|
|
6123
|
+
console.log(pc22.dim("\u2500".repeat(nameWidth + 60)));
|
|
5858
6124
|
for (const { name, data: d } of perfResults) {
|
|
5859
|
-
const tierColor = d.complexity === "lightweight" ?
|
|
5860
|
-
const budgetColor = d.overBudget ?
|
|
6125
|
+
const tierColor = d.complexity === "lightweight" ? pc22.green : d.complexity === "moderate" ? pc22.yellow : pc22.red;
|
|
6126
|
+
const budgetColor = d.overBudget ? pc22.red : pc22.green;
|
|
5861
6127
|
console.log(
|
|
5862
6128
|
name.padEnd(nameWidth) + formatBytes(d.bundleSize).padStart(8) + formatBytes(d.rawSize).padStart(10) + budgetColor(`${d.budgetPercent}%`.padStart(8)) + tierColor(d.complexity.padStart(14)) + " " + budgetBar(d.budgetPercent)
|
|
5863
6129
|
);
|
|
5864
6130
|
if (d.imports?.length && (detail || d.overBudget)) {
|
|
5865
6131
|
for (const imp of d.imports.slice(0, 5)) {
|
|
5866
6132
|
const barWidth = Math.max(1, Math.round(imp.percent / 5));
|
|
5867
|
-
const impBar =
|
|
6133
|
+
const impBar = pc22.dim("\u2588".repeat(barWidth));
|
|
5868
6134
|
console.log(
|
|
5869
|
-
|
|
6135
|
+
pc22.dim(" \u2514\u2500\u2500 ") + pc22.dim(imp.path.length > 50 ? "\u2026" + imp.path.slice(-49) : imp.path) + pc22.dim(` \u2192 ${formatBytes(imp.bytes)} (${imp.percent}%) `) + impBar
|
|
5870
6136
|
);
|
|
5871
6137
|
}
|
|
5872
6138
|
if (d.imports.length > 5) {
|
|
5873
|
-
console.log(
|
|
6139
|
+
console.log(pc22.dim(` \u2514\u2500\u2500 \u2026 and ${d.imports.length - 5} more files`));
|
|
5874
6140
|
}
|
|
5875
6141
|
}
|
|
5876
6142
|
}
|
|
5877
|
-
console.log(
|
|
6143
|
+
console.log(pc22.dim("\u2500".repeat(nameWidth + 60)));
|
|
5878
6144
|
if (result.errors.length > 0) {
|
|
5879
|
-
console.log(
|
|
6145
|
+
console.log(pc22.yellow(`
|
|
5880
6146
|
${result.errors.length} component(s) could not be measured:`));
|
|
5881
6147
|
for (const err of result.errors.slice(0, 5)) {
|
|
5882
|
-
console.log(
|
|
6148
|
+
console.log(pc22.dim(` ${err.name}: ${err.error}`));
|
|
5883
6149
|
}
|
|
5884
6150
|
if (result.errors.length > 5) {
|
|
5885
|
-
console.log(
|
|
6151
|
+
console.log(pc22.dim(` ... and ${result.errors.length - 5} more`));
|
|
5886
6152
|
}
|
|
5887
6153
|
}
|
|
5888
6154
|
console.log(`
|
|
5889
|
-
${
|
|
6155
|
+
${pc22.bold("Summary")}`);
|
|
5890
6156
|
console.log(` Total: ${perfResults.length} components measured in ${(result.elapsed / 1e3).toFixed(1)}s`);
|
|
5891
|
-
console.log(` Tiers: ${
|
|
6157
|
+
console.log(` Tiers: ${pc22.green(`${tiers.lightweight} lightweight`)} \xB7 ${pc22.yellow(`${tiers.moderate} moderate`)} \xB7 ${pc22.red(`${tiers.heavy} heavy`)}`);
|
|
5892
6158
|
if (overBudgetCount > 0) {
|
|
5893
|
-
console.log(
|
|
6159
|
+
console.log(pc22.red(` Over budget: ${overBudgetCount} component(s) exceed ${formatBytes(perfConfig.budgets.bundleSize)} budget`));
|
|
5894
6160
|
} else {
|
|
5895
|
-
console.log(
|
|
6161
|
+
console.log(pc22.green(" All components within budget"));
|
|
5896
6162
|
}
|
|
5897
6163
|
console.log("");
|
|
5898
6164
|
}
|
|
@@ -5909,9 +6175,9 @@ ${pc21.bold("Summary")}`);
|
|
|
5909
6175
|
overBudget: overBudgetCount,
|
|
5910
6176
|
tiers
|
|
5911
6177
|
};
|
|
5912
|
-
await
|
|
6178
|
+
await writeFile9(outFile, JSON.stringify(data));
|
|
5913
6179
|
if (!json) {
|
|
5914
|
-
console.log(
|
|
6180
|
+
console.log(pc22.dim(`Results written to ${BRAND.outFile}`));
|
|
5915
6181
|
}
|
|
5916
6182
|
}
|
|
5917
6183
|
return {
|
|
@@ -5922,44 +6188,44 @@ ${pc21.bold("Summary")}`);
|
|
|
5922
6188
|
}
|
|
5923
6189
|
|
|
5924
6190
|
// src/commands/sync.ts
|
|
5925
|
-
import
|
|
5926
|
-
import { readFile as
|
|
6191
|
+
import pc23 from "picocolors";
|
|
6192
|
+
import { readFile as readFile12, writeFile as writeFile10 } from "fs/promises";
|
|
5927
6193
|
import { resolveComponentSourcePath } from "@fragments-sdk/extract";
|
|
5928
6194
|
import { createComponentExtractor } from "@fragments-sdk/extract";
|
|
5929
6195
|
async function sync(options = {}) {
|
|
5930
6196
|
const { config, configDir } = await loadConfig(options.config);
|
|
5931
|
-
console.log(
|
|
6197
|
+
console.log(pc23.cyan(`
|
|
5932
6198
|
${BRAND.name} Sync
|
|
5933
6199
|
`));
|
|
5934
6200
|
if (options.dryRun) {
|
|
5935
|
-
console.log(
|
|
6201
|
+
console.log(pc23.dim("Dry run \u2014 no files will be modified.\n"));
|
|
5936
6202
|
}
|
|
5937
6203
|
const result = await runSync(config, configDir, options);
|
|
5938
6204
|
if (result.updated.length > 0) {
|
|
5939
6205
|
const verb = options.dryRun ? "Would update" : "Updated";
|
|
5940
|
-
console.log(
|
|
6206
|
+
console.log(pc23.bold(`${verb} ${result.updated.length} fragment(s):
|
|
5941
6207
|
`));
|
|
5942
6208
|
for (const comp of result.updated) {
|
|
5943
|
-
console.log(` ${
|
|
6209
|
+
console.log(` ${pc23.green("\u2713")} ${pc23.bold(comp.name)} ${pc23.dim(`(${comp.file})`)}`);
|
|
5944
6210
|
for (const change of comp.changes) {
|
|
5945
|
-
console.log(` ${
|
|
6211
|
+
console.log(` ${pc23.dim("\u2022")} ${change}`);
|
|
5946
6212
|
}
|
|
5947
6213
|
}
|
|
5948
6214
|
console.log();
|
|
5949
6215
|
}
|
|
5950
6216
|
if (result.skipped.length > 0) {
|
|
5951
|
-
console.log(
|
|
6217
|
+
console.log(pc23.dim(`Skipped ${result.skipped.length}: ${result.skipped.map((s) => s.name).join(", ")}
|
|
5952
6218
|
`));
|
|
5953
6219
|
}
|
|
5954
6220
|
if (result.errors.length > 0) {
|
|
5955
|
-
console.log(
|
|
6221
|
+
console.log(pc23.red(pc23.bold("Errors:")));
|
|
5956
6222
|
for (const err of result.errors) {
|
|
5957
|
-
console.log(` ${
|
|
6223
|
+
console.log(` ${pc23.red("\u2717")} ${pc23.bold(err.file)}: ${err.message}`);
|
|
5958
6224
|
}
|
|
5959
6225
|
console.log();
|
|
5960
6226
|
}
|
|
5961
6227
|
if (result.updated.length === 0 && result.errors.length === 0) {
|
|
5962
|
-
console.log(
|
|
6228
|
+
console.log(pc23.green("All fragments are in sync \u2014 nothing to update.\n"));
|
|
5963
6229
|
}
|
|
5964
6230
|
return result;
|
|
5965
6231
|
}
|
|
@@ -5978,7 +6244,7 @@ async function runSync(config, configDir, options) {
|
|
|
5978
6244
|
const fragment = await loadFragmentFile(file.absolutePath);
|
|
5979
6245
|
if (!fragment?.meta?.name) continue;
|
|
5980
6246
|
if (options.component && fragment.meta.name !== options.component) continue;
|
|
5981
|
-
const fileContent = await
|
|
6247
|
+
const fileContent = await readFile12(file.absolutePath, "utf-8");
|
|
5982
6248
|
const parsed = parseFragmentFile(fileContent, file.absolutePath);
|
|
5983
6249
|
if (!parsed.componentImport) {
|
|
5984
6250
|
skipped.push({ name: fragment.meta.name, reason: "No component import found" });
|
|
@@ -6000,7 +6266,7 @@ async function runSync(config, configDir, options) {
|
|
|
6000
6266
|
continue;
|
|
6001
6267
|
}
|
|
6002
6268
|
if (!options.dryRun) {
|
|
6003
|
-
await
|
|
6269
|
+
await writeFile10(file.absolutePath, patch.updatedContent, "utf-8");
|
|
6004
6270
|
}
|
|
6005
6271
|
updated.push({
|
|
6006
6272
|
name: fragment.meta.name,
|
|
@@ -6140,9 +6406,9 @@ function escapeRegex(str) {
|
|
|
6140
6406
|
}
|
|
6141
6407
|
|
|
6142
6408
|
// src/commands/govern.ts
|
|
6143
|
-
import
|
|
6409
|
+
import pc24 from "picocolors";
|
|
6144
6410
|
async function governCheck(options = {}) {
|
|
6145
|
-
console.log(
|
|
6411
|
+
console.log(pc24.cyan(`
|
|
6146
6412
|
${BRAND.name} Governance Check
|
|
6147
6413
|
`));
|
|
6148
6414
|
const { cliCheck, formatVerdict } = await import("@fragments-sdk/govern");
|
|
@@ -6153,20 +6419,20 @@ ${BRAND.name} Governance Check
|
|
|
6153
6419
|
console.log();
|
|
6154
6420
|
}
|
|
6155
6421
|
if (result.verdict.passed) {
|
|
6156
|
-
console.log(
|
|
6422
|
+
console.log(pc24.green(`\u2713 Governance check passed (score: ${result.verdict.score}/100)
|
|
6157
6423
|
`));
|
|
6158
6424
|
} else {
|
|
6159
|
-
console.log(
|
|
6425
|
+
console.log(pc24.red(`\u2717 Governance check failed (score: ${result.verdict.score}/100)
|
|
6160
6426
|
`));
|
|
6161
6427
|
}
|
|
6162
6428
|
return { exitCode: result.exitCode };
|
|
6163
6429
|
}
|
|
6164
6430
|
async function governInit(options = {}) {
|
|
6165
|
-
const { writeFile:
|
|
6431
|
+
const { writeFile: writeFile12 } = await import("fs/promises");
|
|
6166
6432
|
const { existsSync: existsSync4 } = await import("fs");
|
|
6167
|
-
const { resolve:
|
|
6433
|
+
const { resolve: resolve15 } = await import("path");
|
|
6168
6434
|
const { generateConfigTemplate } = await import("@fragments-sdk/govern");
|
|
6169
|
-
const { findTailwindConfig } = await import("./token-normalizer-
|
|
6435
|
+
const { findTailwindConfig } = await import("./token-normalizer-56H4242J.js");
|
|
6170
6436
|
const cwd = process.cwd();
|
|
6171
6437
|
const detected = {
|
|
6172
6438
|
tokenIncludes: [],
|
|
@@ -6177,7 +6443,7 @@ async function governInit(options = {}) {
|
|
|
6177
6443
|
const { relative: relative9 } = await import("path");
|
|
6178
6444
|
detected.tokenIncludes.push(relative9(cwd, tailwindPath));
|
|
6179
6445
|
detected.projectType = "tailwind";
|
|
6180
|
-
console.log(
|
|
6446
|
+
console.log(pc24.dim(` Detected Tailwind config: ${detected.tokenIncludes[0]}`));
|
|
6181
6447
|
}
|
|
6182
6448
|
if (detected.tokenIncludes.length === 0) {
|
|
6183
6449
|
const candidates = [
|
|
@@ -6193,175 +6459,47 @@ async function governInit(options = {}) {
|
|
|
6193
6459
|
"src/tokens.json"
|
|
6194
6460
|
];
|
|
6195
6461
|
for (const candidate of candidates) {
|
|
6196
|
-
if (existsSync4(
|
|
6462
|
+
if (existsSync4(resolve15(cwd, candidate))) {
|
|
6197
6463
|
detected.tokenIncludes.push(candidate);
|
|
6198
6464
|
detected.projectType = candidate.endsWith(".scss") ? "scss" : candidate.endsWith(".json") ? "dtcg" : "css";
|
|
6199
|
-
console.log(
|
|
6465
|
+
console.log(pc24.dim(` Detected token source: ${candidate}`));
|
|
6200
6466
|
}
|
|
6201
6467
|
}
|
|
6202
6468
|
}
|
|
6203
6469
|
if (detected.tokenIncludes.length === 0) {
|
|
6204
|
-
console.log(
|
|
6470
|
+
console.log(pc24.dim(" No token sources auto-detected. You can add them manually."));
|
|
6205
6471
|
}
|
|
6206
|
-
const outputPath =
|
|
6472
|
+
const outputPath = resolve15(options.output ?? "fragments.config.ts");
|
|
6207
6473
|
const template = generateConfigTemplate({ tokenIncludes: detected.tokenIncludes });
|
|
6208
|
-
await
|
|
6209
|
-
console.log(
|
|
6474
|
+
await writeFile12(outputPath, template, "utf-8");
|
|
6475
|
+
console.log(pc24.green(`
|
|
6210
6476
|
\u2713 Created ${outputPath}
|
|
6211
6477
|
`));
|
|
6212
6478
|
if (detected.tokenIncludes.length > 0) {
|
|
6213
6479
|
console.log(
|
|
6214
|
-
|
|
6480
|
+
pc24.dim(` Token sources configured: ${detected.tokenIncludes.join(", ")}`)
|
|
6215
6481
|
);
|
|
6216
6482
|
}
|
|
6217
6483
|
console.log(
|
|
6218
|
-
|
|
6219
|
-
);
|
|
6220
|
-
}
|
|
6221
|
-
async function governConnect() {
|
|
6222
|
-
const { readFile: readFile13, writeFile: writeFile11, appendFile } = await import("fs/promises");
|
|
6223
|
-
const { existsSync: existsSync4 } = await import("fs");
|
|
6224
|
-
const { resolve: resolve14 } = await import("path");
|
|
6225
|
-
const { platform } = await import("os");
|
|
6226
|
-
const { exec } = await import("child_process");
|
|
6227
|
-
const { password, confirm } = await import("@inquirer/prompts");
|
|
6228
|
-
const cloudUrl = process.env.FRAGMENTS_URL ?? "https://app.usefragments.com";
|
|
6229
|
-
console.log(pc23.cyan(`
|
|
6230
|
-
${BRAND.name} \u2014 Connect to Cloud
|
|
6231
|
-
`));
|
|
6232
|
-
console.log(
|
|
6233
|
-
pc23.dim(" This will connect your project to the Fragments dashboard\n") + pc23.dim(" for centralized audit tracking and team visibility.\n")
|
|
6484
|
+
pc24.dim(" Run ") + pc24.cyan("fragments govern scan") + pc24.dim(" to check your project.\n")
|
|
6234
6485
|
);
|
|
6235
|
-
console.log(pc23.bold(" Step 1 of 3: Get your API key\n"));
|
|
6236
|
-
const dashboardUrl = `${cloudUrl}/api-keys`;
|
|
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
|
|
6239
|
-
`));
|
|
6240
|
-
const os = platform();
|
|
6241
|
-
const openCmd = os === "darwin" ? `open "${dashboardUrl}"` : os === "win32" ? `start "" "${dashboardUrl}"` : `xdg-open "${dashboardUrl}"`;
|
|
6242
|
-
exec(openCmd);
|
|
6243
|
-
let apiKey;
|
|
6244
|
-
let orgName;
|
|
6245
|
-
while (true) {
|
|
6246
|
-
apiKey = await password({
|
|
6247
|
-
message: "Paste your API key:",
|
|
6248
|
-
mask: "*"
|
|
6249
|
-
});
|
|
6250
|
-
if (!apiKey.trim()) {
|
|
6251
|
-
console.log(pc23.yellow("\n API key cannot be empty. Please try again.\n"));
|
|
6252
|
-
continue;
|
|
6253
|
-
}
|
|
6254
|
-
console.log(pc23.dim("\n Verifying..."));
|
|
6255
|
-
try {
|
|
6256
|
-
const response = await fetch(`${cloudUrl}/api/verify`, {
|
|
6257
|
-
headers: { Authorization: `Bearer ${apiKey.trim()}` }
|
|
6258
|
-
});
|
|
6259
|
-
if (!response.ok) {
|
|
6260
|
-
console.log(pc23.red(`
|
|
6261
|
-
\u2717 Invalid API key (HTTP ${response.status}). Please try again.
|
|
6262
|
-
`));
|
|
6263
|
-
continue;
|
|
6264
|
-
}
|
|
6265
|
-
const data = await response.json();
|
|
6266
|
-
if (!data.valid) {
|
|
6267
|
-
console.log(pc23.red("\n \u2717 API key not recognized. Please try again.\n"));
|
|
6268
|
-
continue;
|
|
6269
|
-
}
|
|
6270
|
-
orgName = data.orgName ?? "your organization";
|
|
6271
|
-
console.log(pc23.green(`
|
|
6272
|
-
\u2713 Connected to "${orgName}" (verified)
|
|
6273
|
-
`));
|
|
6274
|
-
break;
|
|
6275
|
-
} catch (error) {
|
|
6276
|
-
console.log(
|
|
6277
|
-
pc23.red("\n \u2717 Could not reach the dashboard.")
|
|
6278
|
-
);
|
|
6279
|
-
console.log(
|
|
6280
|
-
pc23.dim(` ${error instanceof Error ? error.message : "Network error"}
|
|
6281
|
-
`)
|
|
6282
|
-
);
|
|
6283
|
-
continue;
|
|
6284
|
-
}
|
|
6285
|
-
}
|
|
6286
|
-
console.log(pc23.bold(" Step 2 of 3: Save configuration\n"));
|
|
6287
|
-
const saveToEnv = await confirm({
|
|
6288
|
-
message: "Save API key to .env file?",
|
|
6289
|
-
default: true
|
|
6290
|
-
});
|
|
6291
|
-
if (saveToEnv) {
|
|
6292
|
-
const envPath = resolve14(".env");
|
|
6293
|
-
const envEntry = `FRAGMENTS_API_KEY=${apiKey.trim()}`;
|
|
6294
|
-
if (existsSync4(envPath)) {
|
|
6295
|
-
const envContent = await readFile13(envPath, "utf-8");
|
|
6296
|
-
if (envContent.includes("FRAGMENTS_API_KEY=")) {
|
|
6297
|
-
const updated = envContent.replace(
|
|
6298
|
-
/^FRAGMENTS_API_KEY=.*$/m,
|
|
6299
|
-
envEntry
|
|
6300
|
-
);
|
|
6301
|
-
await writeFile11(envPath, updated, "utf-8");
|
|
6302
|
-
console.log(pc23.green(" \u2713 Updated FRAGMENTS_API_KEY in .env"));
|
|
6303
|
-
} else {
|
|
6304
|
-
await appendFile(envPath, `
|
|
6305
|
-
${envEntry}
|
|
6306
|
-
`, "utf-8");
|
|
6307
|
-
console.log(pc23.green(" \u2713 Added FRAGMENTS_API_KEY to .env"));
|
|
6308
|
-
}
|
|
6309
|
-
} else {
|
|
6310
|
-
await writeFile11(envPath, `${envEntry}
|
|
6311
|
-
`, "utf-8");
|
|
6312
|
-
console.log(pc23.green(" \u2713 Created .env with FRAGMENTS_API_KEY"));
|
|
6313
|
-
}
|
|
6314
|
-
if (cloudUrl !== "https://app.usefragments.com") {
|
|
6315
|
-
const envContent = await readFile13(envPath, "utf-8");
|
|
6316
|
-
if (!envContent.includes("FRAGMENTS_URL=")) {
|
|
6317
|
-
await appendFile(envPath, `FRAGMENTS_URL=${cloudUrl}
|
|
6318
|
-
`, "utf-8");
|
|
6319
|
-
console.log(pc23.green(` \u2713 Added FRAGMENTS_URL to .env`));
|
|
6320
|
-
}
|
|
6321
|
-
}
|
|
6322
|
-
const gitignorePath = resolve14(".gitignore");
|
|
6323
|
-
if (existsSync4(gitignorePath)) {
|
|
6324
|
-
const gitignore = await readFile13(gitignorePath, "utf-8");
|
|
6325
|
-
if (!gitignore.split("\n").some((line) => line.trim() === ".env")) {
|
|
6326
|
-
await appendFile(gitignorePath, "\n.env\n", "utf-8");
|
|
6327
|
-
console.log(pc23.green(" \u2713 Added .env to .gitignore"));
|
|
6328
|
-
}
|
|
6329
|
-
} else {
|
|
6330
|
-
await writeFile11(gitignorePath, ".env\n", "utf-8");
|
|
6331
|
-
console.log(pc23.green(" \u2713 Created .gitignore with .env entry"));
|
|
6332
|
-
}
|
|
6333
|
-
}
|
|
6334
|
-
console.log(pc23.bold("\n Step 3 of 3: Config check\n"));
|
|
6335
|
-
const { findGovernConfig } = await import("@fragments-sdk/govern");
|
|
6336
|
-
const configPath = findGovernConfig();
|
|
6337
|
-
if (configPath) {
|
|
6338
|
-
console.log(pc23.green(` \u2713 Found govern config: ${configPath}`));
|
|
6339
|
-
} else {
|
|
6340
|
-
console.log(
|
|
6341
|
-
pc23.yellow(" No govern config found \u2014 run `fragments govern init` to create one")
|
|
6342
|
-
);
|
|
6343
|
-
}
|
|
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
|
|
6347
|
-
`));
|
|
6348
6486
|
}
|
|
6349
6487
|
async function governReport() {
|
|
6350
|
-
const { readFile:
|
|
6351
|
-
console.log(
|
|
6488
|
+
const { readFile: readFile14 } = await import("fs/promises");
|
|
6489
|
+
console.log(pc24.cyan(`
|
|
6352
6490
|
${BRAND.name} Governance Report
|
|
6353
6491
|
`));
|
|
6354
6492
|
let raw;
|
|
6355
6493
|
try {
|
|
6356
|
-
raw = await
|
|
6494
|
+
raw = await readFile14(".govern-audit.jsonl", "utf-8");
|
|
6357
6495
|
} catch {
|
|
6358
|
-
console.log(
|
|
6359
|
-
console.log(
|
|
6496
|
+
console.log(pc24.yellow("No audit log found (.govern-audit.jsonl)\n"));
|
|
6497
|
+
console.log(pc24.dim("Run `fragments govern check` to generate audit data.\n"));
|
|
6360
6498
|
return;
|
|
6361
6499
|
}
|
|
6362
6500
|
const lines = raw.trim().split("\n").filter(Boolean);
|
|
6363
6501
|
if (lines.length === 0) {
|
|
6364
|
-
console.log(
|
|
6502
|
+
console.log(pc24.yellow("Audit log is empty.\n"));
|
|
6365
6503
|
return;
|
|
6366
6504
|
}
|
|
6367
6505
|
let totalScore = 0;
|
|
@@ -6386,13 +6524,13 @@ ${BRAND.name} Governance Report
|
|
|
6386
6524
|
}
|
|
6387
6525
|
|
|
6388
6526
|
// src/commands/migrate-contract.ts
|
|
6389
|
-
import
|
|
6527
|
+
import pc25 from "picocolors";
|
|
6390
6528
|
import fg4 from "fast-glob";
|
|
6391
|
-
import { resolve as
|
|
6529
|
+
import { resolve as resolve14 } from "path";
|
|
6392
6530
|
|
|
6393
6531
|
// src/migrate/fragment-to-contract.ts
|
|
6394
|
-
import { readFile as
|
|
6395
|
-
import { resolve as
|
|
6532
|
+
import { readFile as readFile13, writeFile as writeFile11 } from "fs/promises";
|
|
6533
|
+
import { resolve as resolve13, dirname as dirname6, relative as relative8 } from "path";
|
|
6396
6534
|
import { createComponentExtractor as createComponentExtractor2 } from "@fragments-sdk/extract";
|
|
6397
6535
|
import { resolveComponentSourcePath as resolveComponentSourcePath2 } from "@fragments-sdk/extract";
|
|
6398
6536
|
function compilePropsSummaryFromExtracted(props) {
|
|
@@ -6431,8 +6569,8 @@ function compilePropsSummaryFromDocs(props) {
|
|
|
6431
6569
|
}
|
|
6432
6570
|
async function migrateFragmentToContract(fragmentPath, configDir, options) {
|
|
6433
6571
|
const warnings = [];
|
|
6434
|
-
const absFragmentPath =
|
|
6435
|
-
const content = await
|
|
6572
|
+
const absFragmentPath = resolve13(fragmentPath);
|
|
6573
|
+
const content = await readFile13(absFragmentPath, "utf-8");
|
|
6436
6574
|
const parsed = parseFragmentFile(content, fragmentPath);
|
|
6437
6575
|
warnings.push(...parsed.warnings);
|
|
6438
6576
|
if (!parsed.meta.name) {
|
|
@@ -6466,8 +6604,8 @@ async function migrateFragmentToContract(fragmentPath, configDir, options) {
|
|
|
6466
6604
|
warnings.push(`Could not resolve component source path: ${parsed.componentImport}`);
|
|
6467
6605
|
}
|
|
6468
6606
|
} else {
|
|
6469
|
-
const fragDir =
|
|
6470
|
-
sourcePath = relative8(configDir,
|
|
6607
|
+
const fragDir = dirname6(absFragmentPath);
|
|
6608
|
+
sourcePath = relative8(configDir, resolve13(fragDir, "index.tsx"));
|
|
6471
6609
|
warnings.push("No component import found; assuming ./index.tsx");
|
|
6472
6610
|
}
|
|
6473
6611
|
const exportName = parsed.componentName ?? parsed.meta.name;
|
|
@@ -6561,7 +6699,7 @@ async function migrateFragmentToContract(fragmentPath, configDir, options) {
|
|
|
6561
6699
|
};
|
|
6562
6700
|
const contractPath = absFragmentPath.replace(/\.fragment\.tsx?$/, ".contract.json");
|
|
6563
6701
|
if (!options?.dryRun) {
|
|
6564
|
-
await
|
|
6702
|
+
await writeFile11(contractPath, JSON.stringify(contract, null, 2) + "\n");
|
|
6565
6703
|
}
|
|
6566
6704
|
return { contractPath, contract, warnings };
|
|
6567
6705
|
}
|
|
@@ -6570,20 +6708,20 @@ async function migrateFragmentToContract(fragmentPath, configDir, options) {
|
|
|
6570
6708
|
async function migrateContract(options) {
|
|
6571
6709
|
const { config, configDir } = await loadConfig(options.config);
|
|
6572
6710
|
const pattern = options.glob ?? "src/**/*.fragment.tsx";
|
|
6573
|
-
console.log(
|
|
6711
|
+
console.log(pc25.blue(`Migrating fragment files matching: ${pattern}`));
|
|
6574
6712
|
if (options.dryRun) {
|
|
6575
|
-
console.log(
|
|
6713
|
+
console.log(pc25.yellow("(dry run \u2014 no files will be written)"));
|
|
6576
6714
|
}
|
|
6577
6715
|
const files = await fg4(pattern, { cwd: configDir, absolute: true });
|
|
6578
6716
|
if (files.length === 0) {
|
|
6579
|
-
console.log(
|
|
6717
|
+
console.log(pc25.yellow("No fragment files found matching pattern."));
|
|
6580
6718
|
return { migrated: 0, failed: 0, warnings: 0 };
|
|
6581
6719
|
}
|
|
6582
|
-
console.log(
|
|
6720
|
+
console.log(pc25.dim(`Found ${files.length} fragment file(s)`));
|
|
6583
6721
|
let migrated = 0;
|
|
6584
6722
|
let failed = 0;
|
|
6585
6723
|
let totalWarnings = 0;
|
|
6586
|
-
const tsconfigPath = options.tsconfig ?
|
|
6724
|
+
const tsconfigPath = options.tsconfig ? resolve14(options.tsconfig) : void 0;
|
|
6587
6725
|
for (const file of files) {
|
|
6588
6726
|
try {
|
|
6589
6727
|
const result = await migrateFragmentToContract(file, configDir, {
|
|
@@ -6592,31 +6730,31 @@ async function migrateContract(options) {
|
|
|
6592
6730
|
});
|
|
6593
6731
|
migrated++;
|
|
6594
6732
|
const verb = options.dryRun ? "Would migrate" : "Migrated";
|
|
6595
|
-
console.log(
|
|
6733
|
+
console.log(pc25.green(` \u2713 ${verb}: ${result.contractPath}`));
|
|
6596
6734
|
if (result.warnings.length > 0) {
|
|
6597
6735
|
totalWarnings += result.warnings.length;
|
|
6598
6736
|
for (const w of result.warnings) {
|
|
6599
|
-
console.log(
|
|
6737
|
+
console.log(pc25.yellow(` \u26A0 ${w}`));
|
|
6600
6738
|
}
|
|
6601
6739
|
}
|
|
6602
6740
|
} catch (error) {
|
|
6603
6741
|
failed++;
|
|
6604
|
-
console.log(
|
|
6605
|
-
console.log(
|
|
6742
|
+
console.log(pc25.red(` \u2717 Failed: ${file}`));
|
|
6743
|
+
console.log(pc25.dim(` ${error instanceof Error ? error.message : String(error)}`));
|
|
6606
6744
|
}
|
|
6607
6745
|
}
|
|
6608
6746
|
console.log("");
|
|
6609
|
-
console.log(
|
|
6610
|
-
console.log(` ${
|
|
6747
|
+
console.log(pc25.bold("Migration summary:"));
|
|
6748
|
+
console.log(` ${pc25.green(`${migrated} migrated`)} ${pc25.red(`${failed} failed`)} ${pc25.yellow(`${totalWarnings} warnings`)}`);
|
|
6611
6749
|
if (!options.dryRun && migrated > 0) {
|
|
6612
6750
|
console.log("");
|
|
6613
|
-
console.log(
|
|
6751
|
+
console.log(pc25.dim("Run `fragments build` to verify migrated contracts compile correctly."));
|
|
6614
6752
|
}
|
|
6615
6753
|
return { migrated, failed, warnings: totalWarnings };
|
|
6616
6754
|
}
|
|
6617
6755
|
|
|
6618
6756
|
// src/bin.ts
|
|
6619
|
-
var __dirname =
|
|
6757
|
+
var __dirname = dirname7(fileURLToPath(import.meta.url));
|
|
6620
6758
|
var pkg = JSON.parse(readFileSync(join11(__dirname, "../package.json"), "utf-8"));
|
|
6621
6759
|
var EXPERIMENTAL = process.env.FRAGMENTS_EXPERIMENTAL === "1";
|
|
6622
6760
|
var program = new Command();
|
|
@@ -6628,7 +6766,7 @@ program.command("validate").description("Validate fragment files").option("-c, -
|
|
|
6628
6766
|
process.exit(1);
|
|
6629
6767
|
}
|
|
6630
6768
|
} catch (error) {
|
|
6631
|
-
console.error(
|
|
6769
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6632
6770
|
process.exit(1);
|
|
6633
6771
|
}
|
|
6634
6772
|
});
|
|
@@ -6644,7 +6782,7 @@ program.command("sync").description("Auto-update fragment files from component s
|
|
|
6644
6782
|
process.exit(1);
|
|
6645
6783
|
}
|
|
6646
6784
|
} catch (error) {
|
|
6647
|
-
console.error(
|
|
6785
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6648
6786
|
process.exit(1);
|
|
6649
6787
|
}
|
|
6650
6788
|
});
|
|
@@ -6666,12 +6804,39 @@ program.command("build").description(`Build compiled ${BRAND.outFile} and ${BRAN
|
|
|
6666
6804
|
process.exit(1);
|
|
6667
6805
|
}
|
|
6668
6806
|
} catch (error) {
|
|
6669
|
-
console.error(
|
|
6807
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6670
6808
|
process.exit(1);
|
|
6671
6809
|
}
|
|
6672
6810
|
});
|
|
6673
|
-
program.command("context").description("Generate AI-ready context
|
|
6811
|
+
program.command("context [action]").description("Generate AI-ready context or install a Fragments Cloud bundle").option("-c, --config <path>", "Path to config file").option("-i, --input <path>", `Path to ${BRAND.outFile} (builds if not provided)`).option("-f, --format <format>", "Output format (markdown/json)", "markdown").option("--compact", "Minimal output for token efficiency").option("--code", "Include code examples").option("--relations", "Include component relationships").option("--tokens", "Only output token estimate").option("--cloud", "Use Fragments Cloud bundle endpoints").option("--api-key <key>", "Fragments Cloud API key (or use FRAGMENTS_API_KEY)").option("--targets <list>", "Comma-separated helper targets (cursor,agents,claude,copilot)").option("--cwd <path>", "Project root for install/status").option("--dry-run", "Preview file writes without modifying the repo").option("-y, --yes", "Non-interactive mode").option("--root-files <mode>", "Root instruction file mode (prompt|never|patch)").option("--gitignore-fragments", "Add .fragments/ to .gitignore after install").action(async (action, options) => {
|
|
6674
6812
|
try {
|
|
6813
|
+
if (options.cloud && action === "install") {
|
|
6814
|
+
await contextInstallCloud({
|
|
6815
|
+
apiKey: options.apiKey,
|
|
6816
|
+
targets: options.targets,
|
|
6817
|
+
cwd: options.cwd,
|
|
6818
|
+
dryRun: options.dryRun,
|
|
6819
|
+
yes: options.yes,
|
|
6820
|
+
rootFiles: options.rootFiles,
|
|
6821
|
+
gitignoreFragments: options.gitignoreFragments
|
|
6822
|
+
});
|
|
6823
|
+
return;
|
|
6824
|
+
}
|
|
6825
|
+
if (options.cloud && action === "status") {
|
|
6826
|
+
await contextStatusCloud({
|
|
6827
|
+
apiKey: options.apiKey,
|
|
6828
|
+
cwd: options.cwd
|
|
6829
|
+
});
|
|
6830
|
+
return;
|
|
6831
|
+
}
|
|
6832
|
+
if (action === "install" || action === "status") {
|
|
6833
|
+
throw new Error(
|
|
6834
|
+
`context ${action} requires --cloud`
|
|
6835
|
+
);
|
|
6836
|
+
}
|
|
6837
|
+
if (action && action !== "install" && action !== "status") {
|
|
6838
|
+
throw new Error(`Unknown context action: ${action}`);
|
|
6839
|
+
}
|
|
6675
6840
|
const result = await context({
|
|
6676
6841
|
config: options.config,
|
|
6677
6842
|
input: options.input,
|
|
@@ -6685,7 +6850,7 @@ program.command("context").description("Generate AI-ready context for your desig
|
|
|
6685
6850
|
process.exit(1);
|
|
6686
6851
|
}
|
|
6687
6852
|
} catch (error) {
|
|
6688
|
-
console.error(
|
|
6853
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6689
6854
|
process.exit(1);
|
|
6690
6855
|
}
|
|
6691
6856
|
});
|
|
@@ -6712,7 +6877,7 @@ program.command("ai").description("Generate context optimized for AI assistants
|
|
|
6712
6877
|
}
|
|
6713
6878
|
}
|
|
6714
6879
|
} catch (error) {
|
|
6715
|
-
console.error(
|
|
6880
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6716
6881
|
process.exit(1);
|
|
6717
6882
|
}
|
|
6718
6883
|
});
|
|
@@ -6720,7 +6885,7 @@ program.command("list").description("List all discovered fragment files").option
|
|
|
6720
6885
|
try {
|
|
6721
6886
|
await list(options);
|
|
6722
6887
|
} catch (error) {
|
|
6723
|
-
console.error(
|
|
6888
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6724
6889
|
process.exit(1);
|
|
6725
6890
|
}
|
|
6726
6891
|
});
|
|
@@ -6728,7 +6893,7 @@ program.command("reset").description("Reset to initial state (delete all generat
|
|
|
6728
6893
|
try {
|
|
6729
6894
|
await reset(options);
|
|
6730
6895
|
} catch (error) {
|
|
6731
|
-
console.error(
|
|
6896
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6732
6897
|
process.exit(1);
|
|
6733
6898
|
}
|
|
6734
6899
|
});
|
|
@@ -6742,7 +6907,7 @@ linkCommand.command("figma").argument("[figma-url]", "Figma file URL to link com
|
|
|
6742
6907
|
variants: options.variants
|
|
6743
6908
|
});
|
|
6744
6909
|
} catch (error) {
|
|
6745
|
-
console.error(
|
|
6910
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6746
6911
|
process.exit(1);
|
|
6747
6912
|
}
|
|
6748
6913
|
});
|
|
@@ -6757,7 +6922,7 @@ linkCommand.command("storybook").description("Bootstrap fragments from existing
|
|
|
6757
6922
|
exclude: options.exclude
|
|
6758
6923
|
});
|
|
6759
6924
|
} catch (error) {
|
|
6760
|
-
console.error(
|
|
6925
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6761
6926
|
process.exit(1);
|
|
6762
6927
|
}
|
|
6763
6928
|
});
|
|
@@ -6777,7 +6942,7 @@ program.command("screenshot").description("Capture screenshots of component vari
|
|
|
6777
6942
|
process.exit(1);
|
|
6778
6943
|
}
|
|
6779
6944
|
} catch (error) {
|
|
6780
|
-
console.error(
|
|
6945
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6781
6946
|
process.exit(1);
|
|
6782
6947
|
}
|
|
6783
6948
|
});
|
|
@@ -6796,7 +6961,7 @@ program.command("diff").argument("[component]", "Component name to diff (optiona
|
|
|
6796
6961
|
process.exit(1);
|
|
6797
6962
|
}
|
|
6798
6963
|
} catch (error) {
|
|
6799
|
-
console.error(
|
|
6964
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6800
6965
|
process.exit(1);
|
|
6801
6966
|
}
|
|
6802
6967
|
});
|
|
@@ -6815,8 +6980,8 @@ program.command("compare").argument("[component]", "Component name to compare").
|
|
|
6815
6980
|
process.exit(1);
|
|
6816
6981
|
}
|
|
6817
6982
|
} catch (error) {
|
|
6818
|
-
console.error(
|
|
6819
|
-
console.log(
|
|
6983
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6984
|
+
console.log(pc26.dim(`
|
|
6820
6985
|
Make sure a dev server is running on the expected port.`));
|
|
6821
6986
|
process.exit(1);
|
|
6822
6987
|
}
|
|
@@ -6835,7 +7000,7 @@ program.command("analyze").description("Analyze design system and generate repor
|
|
|
6835
7000
|
process.exit(1);
|
|
6836
7001
|
}
|
|
6837
7002
|
} catch (error) {
|
|
6838
|
-
console.error(
|
|
7003
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6839
7004
|
process.exit(1);
|
|
6840
7005
|
}
|
|
6841
7006
|
});
|
|
@@ -6854,7 +7019,7 @@ program.command("verify").argument("[component]", "Component name to verify (opt
|
|
|
6854
7019
|
if (options.ci) {
|
|
6855
7020
|
console.log(JSON.stringify({ error: error instanceof Error ? error.message : "Verification failed" }));
|
|
6856
7021
|
} else {
|
|
6857
|
-
console.error(
|
|
7022
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6858
7023
|
}
|
|
6859
7024
|
process.exit(1);
|
|
6860
7025
|
}
|
|
@@ -6871,7 +7036,7 @@ program.command("audit").description("Scan all fragments and show compliance met
|
|
|
6871
7036
|
if (options.json) {
|
|
6872
7037
|
console.log(JSON.stringify({ error: error instanceof Error ? error.message : "Audit failed" }));
|
|
6873
7038
|
} else {
|
|
6874
|
-
console.error(
|
|
7039
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6875
7040
|
}
|
|
6876
7041
|
process.exit(1);
|
|
6877
7042
|
}
|
|
@@ -6893,7 +7058,7 @@ program.command("a11y").description("Run accessibility checks on all component v
|
|
|
6893
7058
|
if (options.json) {
|
|
6894
7059
|
console.log(JSON.stringify({ error: error instanceof Error ? error.message : "A11y check failed" }));
|
|
6895
7060
|
} else {
|
|
6896
|
-
console.error(
|
|
7061
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6897
7062
|
}
|
|
6898
7063
|
process.exit(1);
|
|
6899
7064
|
}
|
|
@@ -6916,7 +7081,7 @@ program.command("enhance").description("AI-powered documentation generation from
|
|
|
6916
7081
|
if (options.format === "json") {
|
|
6917
7082
|
console.log(JSON.stringify({ success: false, error: error instanceof Error ? error.message : "Enhance failed" }));
|
|
6918
7083
|
} else {
|
|
6919
|
-
console.error(
|
|
7084
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6920
7085
|
}
|
|
6921
7086
|
process.exit(1);
|
|
6922
7087
|
}
|
|
@@ -6937,7 +7102,7 @@ program.command("scan").description(`Zero-config ${BRAND.outFile} generation fro
|
|
|
6937
7102
|
process.exit(1);
|
|
6938
7103
|
}
|
|
6939
7104
|
} catch (error) {
|
|
6940
|
-
console.error(
|
|
7105
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6941
7106
|
process.exit(1);
|
|
6942
7107
|
}
|
|
6943
7108
|
});
|
|
@@ -6953,7 +7118,7 @@ program.command("migrate-contract").description("Migrate .fragment.tsx files to
|
|
|
6953
7118
|
process.exit(1);
|
|
6954
7119
|
}
|
|
6955
7120
|
} catch (error) {
|
|
6956
|
-
console.error(
|
|
7121
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6957
7122
|
process.exit(1);
|
|
6958
7123
|
}
|
|
6959
7124
|
});
|
|
@@ -6966,7 +7131,7 @@ program.command("storygen").description("Generate Storybook stories from fragmen
|
|
|
6966
7131
|
format: options.format
|
|
6967
7132
|
});
|
|
6968
7133
|
} catch (error) {
|
|
6969
|
-
console.error(
|
|
7134
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6970
7135
|
process.exit(1);
|
|
6971
7136
|
}
|
|
6972
7137
|
});
|
|
@@ -6978,7 +7143,7 @@ program.command("metrics").argument("[component]", "Component name (optional, sh
|
|
|
6978
7143
|
json: options.json
|
|
6979
7144
|
});
|
|
6980
7145
|
} catch (error) {
|
|
6981
|
-
console.error(
|
|
7146
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6982
7147
|
process.exit(1);
|
|
6983
7148
|
}
|
|
6984
7149
|
});
|
|
@@ -6992,9 +7157,9 @@ program.command("baseline").description("Manage visual regression baselines").ar
|
|
|
6992
7157
|
port: options.port
|
|
6993
7158
|
});
|
|
6994
7159
|
} catch (error) {
|
|
6995
|
-
console.error(
|
|
7160
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
6996
7161
|
if (action === "update") {
|
|
6997
|
-
console.log(
|
|
7162
|
+
console.log(pc26.dim(`
|
|
6998
7163
|
Make sure a dev server is running on the expected port.`));
|
|
6999
7164
|
}
|
|
7000
7165
|
process.exit(1);
|
|
@@ -7002,27 +7167,27 @@ Make sure a dev server is running on the expected port.`));
|
|
|
7002
7167
|
});
|
|
7003
7168
|
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) => {
|
|
7004
7169
|
try {
|
|
7005
|
-
const { generateViewerFromJson } = await import("./static-viewer-
|
|
7170
|
+
const { generateViewerFromJson } = await import("./static-viewer-E4OJWFDJ.js");
|
|
7006
7171
|
const fs2 = await import("fs/promises");
|
|
7007
7172
|
const path = await import("path");
|
|
7008
7173
|
const inputPath = path.resolve(process.cwd(), options.input);
|
|
7009
7174
|
const outputPath = path.resolve(process.cwd(), options.output);
|
|
7010
|
-
console.log(
|
|
7175
|
+
console.log(pc26.cyan(`
|
|
7011
7176
|
${BRAND.name} Viewer Generator
|
|
7012
7177
|
`));
|
|
7013
7178
|
try {
|
|
7014
7179
|
await fs2.access(inputPath);
|
|
7015
7180
|
} catch {
|
|
7016
|
-
console.log(
|
|
7017
|
-
console.log(
|
|
7018
|
-
Run ${
|
|
7181
|
+
console.log(pc26.red(`Error: ${options.input} not found.`));
|
|
7182
|
+
console.log(pc26.dim(`
|
|
7183
|
+
Run ${pc26.cyan(`${BRAND.cliCommand} build`)} first to generate ${BRAND.outFile}
|
|
7019
7184
|
`));
|
|
7020
7185
|
process.exit(1);
|
|
7021
7186
|
}
|
|
7022
|
-
console.log(
|
|
7187
|
+
console.log(pc26.dim(`Reading: ${options.input}`));
|
|
7023
7188
|
const html = await generateViewerFromJson(inputPath);
|
|
7024
7189
|
await fs2.writeFile(outputPath, html);
|
|
7025
|
-
console.log(
|
|
7190
|
+
console.log(pc26.green(`
|
|
7026
7191
|
\u2713 Generated: ${options.output}
|
|
7027
7192
|
`));
|
|
7028
7193
|
if (options.open) {
|
|
@@ -7031,7 +7196,7 @@ Run ${pc25.cyan(`${BRAND.cliCommand} build`)} first to generate ${BRAND.outFile}
|
|
|
7031
7196
|
exec(`${openCmd} "${outputPath}"`);
|
|
7032
7197
|
}
|
|
7033
7198
|
} catch (error) {
|
|
7034
|
-
console.error(
|
|
7199
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7035
7200
|
process.exit(1);
|
|
7036
7201
|
}
|
|
7037
7202
|
});
|
|
@@ -7044,13 +7209,13 @@ program.command("add").argument("[name]", 'Component name (e.g., "Button", "Text
|
|
|
7044
7209
|
component: options.component
|
|
7045
7210
|
});
|
|
7046
7211
|
} catch (error) {
|
|
7047
|
-
console.error(
|
|
7212
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7048
7213
|
process.exit(1);
|
|
7049
7214
|
}
|
|
7050
7215
|
});
|
|
7051
7216
|
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("--no-mcp", "Skip MCP server configuration").option("-y, --yes", "Skip interactive prompts").option("--no-git", "Skip git initialization").action(async (name, options) => {
|
|
7052
7217
|
try {
|
|
7053
|
-
const { create } = await import("./create-
|
|
7218
|
+
const { create } = await import("./create-3ZFYQB3T.js");
|
|
7054
7219
|
const result = await create({
|
|
7055
7220
|
name,
|
|
7056
7221
|
template: options.template,
|
|
@@ -7064,11 +7229,11 @@ program.command("create").argument("[name]", "Project name").description("Create
|
|
|
7064
7229
|
noGit: !options.git
|
|
7065
7230
|
});
|
|
7066
7231
|
if (!result.success) {
|
|
7067
|
-
if (result.error) console.error(
|
|
7232
|
+
if (result.error) console.error(pc26.red(`Error: ${result.error}`));
|
|
7068
7233
|
process.exit(1);
|
|
7069
7234
|
}
|
|
7070
7235
|
} catch (error) {
|
|
7071
|
-
console.error(
|
|
7236
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7072
7237
|
process.exit(1);
|
|
7073
7238
|
}
|
|
7074
7239
|
});
|
|
@@ -7085,34 +7250,14 @@ program.command("setup").description("Configure @fragments-sdk/ui in a consumer
|
|
|
7085
7250
|
process.exit(1);
|
|
7086
7251
|
}
|
|
7087
7252
|
} catch (error) {
|
|
7088
|
-
console.error(
|
|
7253
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7089
7254
|
process.exit(1);
|
|
7090
7255
|
}
|
|
7091
7256
|
});
|
|
7092
7257
|
var initCmd = program.command("init").description("Initialize fragments in a project (zero-config by default)").option("--force", "Overwrite existing config").option("-y, --yes", "Non-interactive mode (now the default)").option("--configure", "Interactive mode for theme seeds, snapshots, etc.").option("--metadata-only", "Generate config and metadata without modifying runtime app setup").option("--govern", "Alias for --metadata-only").option("--scan <path>", "Scan a TypeScript component directory and generate fragment files").option("--enrich", "Use AI to fill knowledge fields during --scan (requires API key)").option("--dry-run", "Show what --enrich would generate without calling API").option("--provider <provider>", "AI provider for enrichment: anthropic or openai").option("--api-key <key>", "API key for AI enrichment").option("--model <model>", "Override AI model for enrichment");
|
|
7093
|
-
if (EXPERIMENTAL) {
|
|
7094
|
-
initCmd.option("--cloud", "Set up Fragments Cloud governance (zero-config browser auth)").option("--cloud-url <url>", "Cloud dashboard URL (default: https://app.usefragments.com)").option("--port <port>", "Localhost port for auth callback (default: 9876)").option("--auth-only", "Only authenticate, skip project setup").option("--skip-check", "Skip running the first governance check");
|
|
7095
|
-
}
|
|
7096
7258
|
initCmd.action(async (options) => {
|
|
7097
7259
|
try {
|
|
7098
|
-
|
|
7099
|
-
if (!EXPERIMENTAL) {
|
|
7100
|
-
console.log(pc25.yellow(`
|
|
7101
|
-
Fragments Cloud is not yet publicly available.`));
|
|
7102
|
-
console.log(pc25.dim(` Set FRAGMENTS_EXPERIMENTAL=1 to enable preview features.
|
|
7103
|
-
`));
|
|
7104
|
-
process.exit(1);
|
|
7105
|
-
}
|
|
7106
|
-
const { initCloud } = await import("./init-cloud-3DNKPWFB.js");
|
|
7107
|
-
await initCloud({
|
|
7108
|
-
url: options.cloudUrl,
|
|
7109
|
-
port: options.port ? Number(options.port) : void 0,
|
|
7110
|
-
authOnly: options.authOnly,
|
|
7111
|
-
skipCheck: options.skipCheck
|
|
7112
|
-
});
|
|
7113
|
-
return;
|
|
7114
|
-
}
|
|
7115
|
-
const { init } = await import("./init-SSGUSP7Z.js");
|
|
7260
|
+
const { init } = await import("./init-PXFRAQ64.js");
|
|
7116
7261
|
const result = await init({
|
|
7117
7262
|
projectRoot: process.cwd(),
|
|
7118
7263
|
force: options.force,
|
|
@@ -7128,20 +7273,20 @@ initCmd.action(async (options) => {
|
|
|
7128
7273
|
govern: options.govern
|
|
7129
7274
|
});
|
|
7130
7275
|
if (!result.success) {
|
|
7131
|
-
console.error(
|
|
7276
|
+
console.error(pc26.red("\nInit failed with errors:"));
|
|
7132
7277
|
for (const error of result.errors) {
|
|
7133
|
-
console.error(
|
|
7278
|
+
console.error(pc26.red(` - ${error}`));
|
|
7134
7279
|
}
|
|
7135
7280
|
process.exit(1);
|
|
7136
7281
|
}
|
|
7137
7282
|
} catch (error) {
|
|
7138
|
-
console.error(
|
|
7283
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7139
7284
|
process.exit(1);
|
|
7140
7285
|
}
|
|
7141
7286
|
});
|
|
7142
7287
|
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) => {
|
|
7143
7288
|
try {
|
|
7144
|
-
const { snapshot } = await import("./snapshot-
|
|
7289
|
+
const { snapshot } = await import("./snapshot-DT4B6DPR.js");
|
|
7145
7290
|
const result = await snapshot({
|
|
7146
7291
|
port: options.port,
|
|
7147
7292
|
update: options.update,
|
|
@@ -7153,14 +7298,14 @@ program.command("snapshot").description("Run visual snapshot tests per component
|
|
|
7153
7298
|
process.exit(1);
|
|
7154
7299
|
}
|
|
7155
7300
|
} catch (error) {
|
|
7156
|
-
console.error(
|
|
7301
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7157
7302
|
process.exit(1);
|
|
7158
7303
|
}
|
|
7159
7304
|
});
|
|
7160
7305
|
var tokensCmd = program.command("tokens").description("Design token discovery, listing, and generation");
|
|
7161
7306
|
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) => {
|
|
7162
7307
|
try {
|
|
7163
|
-
const { tokens } = await import("./tokens-
|
|
7308
|
+
const { tokens } = await import("./tokens-K6URXFPK.js");
|
|
7164
7309
|
const result = await tokens({
|
|
7165
7310
|
config: options.config,
|
|
7166
7311
|
json: options.json,
|
|
@@ -7173,13 +7318,13 @@ tokensCmd.command("list", { isDefault: true }).description("Discover and list de
|
|
|
7173
7318
|
process.exit(1);
|
|
7174
7319
|
}
|
|
7175
7320
|
} catch (error) {
|
|
7176
|
-
console.error(
|
|
7321
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7177
7322
|
process.exit(1);
|
|
7178
7323
|
}
|
|
7179
7324
|
});
|
|
7180
7325
|
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) => {
|
|
7181
7326
|
try {
|
|
7182
|
-
const { tokensGenerate } = await import("./tokens-generate-
|
|
7327
|
+
const { tokensGenerate } = await import("./tokens-generate-EL6IN536.js");
|
|
7183
7328
|
await tokensGenerate({
|
|
7184
7329
|
from: options.from,
|
|
7185
7330
|
format: options.format,
|
|
@@ -7189,27 +7334,13 @@ tokensCmd.command("generate").description("Generate CSS, SCSS, Tailwind, or Figm
|
|
|
7189
7334
|
verbose: options.verbose
|
|
7190
7335
|
});
|
|
7191
7336
|
} catch (error) {
|
|
7192
|
-
console.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);
|
|
7337
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7207
7338
|
process.exit(1);
|
|
7208
7339
|
}
|
|
7209
7340
|
});
|
|
7210
7341
|
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) => {
|
|
7211
7342
|
try {
|
|
7212
|
-
const { generate } = await import("./generate-
|
|
7343
|
+
const { generate } = await import("./generate-VNUUWVWQ.js");
|
|
7213
7344
|
const result = await generate({
|
|
7214
7345
|
projectRoot: process.cwd(),
|
|
7215
7346
|
component,
|
|
@@ -7217,11 +7348,11 @@ program.command("generate").description("Generate fragment files from component
|
|
|
7217
7348
|
componentPattern: options.pattern
|
|
7218
7349
|
});
|
|
7219
7350
|
if (!result.success) {
|
|
7220
|
-
console.error(
|
|
7351
|
+
console.error(pc26.red("\nGenerate completed with errors"));
|
|
7221
7352
|
process.exit(1);
|
|
7222
7353
|
}
|
|
7223
7354
|
} catch (error) {
|
|
7224
|
-
console.error(
|
|
7355
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7225
7356
|
process.exit(1);
|
|
7226
7357
|
}
|
|
7227
7358
|
});
|
|
@@ -7229,7 +7360,7 @@ program.command("graph").description("Query the component relationship graph").a
|
|
|
7229
7360
|
try {
|
|
7230
7361
|
await graph(component, options);
|
|
7231
7362
|
} catch (error) {
|
|
7232
|
-
console.error(
|
|
7363
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7233
7364
|
process.exit(1);
|
|
7234
7365
|
}
|
|
7235
7366
|
});
|
|
@@ -7247,7 +7378,7 @@ program.command("inspect").description("Inspect a single component from compiled
|
|
|
7247
7378
|
if (options.json) {
|
|
7248
7379
|
console.log(JSON.stringify({ error: error instanceof Error ? error.message : "Inspect failed" }));
|
|
7249
7380
|
} else {
|
|
7250
|
-
console.error(
|
|
7381
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7251
7382
|
}
|
|
7252
7383
|
process.exit(1);
|
|
7253
7384
|
}
|
|
@@ -7266,7 +7397,7 @@ program.command("discover").description("List and filter components from compile
|
|
|
7266
7397
|
if (options.json) {
|
|
7267
7398
|
console.log(JSON.stringify({ error: error instanceof Error ? error.message : "Discover failed" }));
|
|
7268
7399
|
} else {
|
|
7269
|
-
console.error(
|
|
7400
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7270
7401
|
}
|
|
7271
7402
|
process.exit(1);
|
|
7272
7403
|
}
|
|
@@ -7285,14 +7416,14 @@ program.command("perf").description("Profile component bundle sizes and performa
|
|
|
7285
7416
|
process.exit(1);
|
|
7286
7417
|
}
|
|
7287
7418
|
} catch (error) {
|
|
7288
|
-
console.error(
|
|
7419
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7289
7420
|
process.exit(1);
|
|
7290
7421
|
}
|
|
7291
7422
|
});
|
|
7292
7423
|
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) => {
|
|
7293
7424
|
try {
|
|
7294
7425
|
const { config, configDir } = await loadConfig(options.config);
|
|
7295
|
-
const { runTestCommand, listTests } = await import("./test-
|
|
7426
|
+
const { runTestCommand, listTests } = await import("./test-QJY2QO4X.js");
|
|
7296
7427
|
if (options.list) {
|
|
7297
7428
|
await listTests(config, configDir, {
|
|
7298
7429
|
component: options.component,
|
|
@@ -7325,13 +7456,13 @@ program.command("test").description("Run interaction tests for fragments with pl
|
|
|
7325
7456
|
});
|
|
7326
7457
|
process.exit(exitCode);
|
|
7327
7458
|
} catch (error) {
|
|
7328
|
-
console.error(
|
|
7459
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7329
7460
|
process.exit(1);
|
|
7330
7461
|
}
|
|
7331
7462
|
});
|
|
7332
7463
|
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) => {
|
|
7333
7464
|
try {
|
|
7334
|
-
const { doctor } = await import("./doctor-
|
|
7465
|
+
const { doctor } = await import("./doctor-4IDUM7HI.js");
|
|
7335
7466
|
const result = await doctor({
|
|
7336
7467
|
root: options.root,
|
|
7337
7468
|
json: options.json,
|
|
@@ -7341,15 +7472,15 @@ program.command("doctor").description("Diagnose design system configuration issu
|
|
|
7341
7472
|
process.exit(1);
|
|
7342
7473
|
}
|
|
7343
7474
|
} catch (error) {
|
|
7344
|
-
console.error(
|
|
7475
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7345
7476
|
process.exit(1);
|
|
7346
7477
|
}
|
|
7347
7478
|
});
|
|
7348
7479
|
var governCmd = program.command("govern").description(EXPERIMENTAL ? "AI UI governance checks" : "AI UI governance checks (preview \u2014 set FRAGMENTS_EXPERIMENTAL=1)").hook("preAction", () => {
|
|
7349
7480
|
if (!EXPERIMENTAL) {
|
|
7350
|
-
console.log(
|
|
7481
|
+
console.log(pc26.yellow(`
|
|
7351
7482
|
Fragments governance is not yet publicly available.`));
|
|
7352
|
-
console.log(
|
|
7483
|
+
console.log(pc26.dim(` Set FRAGMENTS_EXPERIMENTAL=1 to enable preview features.
|
|
7353
7484
|
`));
|
|
7354
7485
|
process.exit(1);
|
|
7355
7486
|
}
|
|
@@ -7364,7 +7495,7 @@ governCmd.command("check").description("Validate a UISpec against governance pol
|
|
|
7364
7495
|
});
|
|
7365
7496
|
process.exit(exitCode);
|
|
7366
7497
|
} catch (error) {
|
|
7367
|
-
console.error(
|
|
7498
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7368
7499
|
process.exit(1);
|
|
7369
7500
|
}
|
|
7370
7501
|
});
|
|
@@ -7372,7 +7503,7 @@ governCmd.command("init").description("Generate a fragments.config.ts with gover
|
|
|
7372
7503
|
try {
|
|
7373
7504
|
await governInit({ output: options.output });
|
|
7374
7505
|
} catch (error) {
|
|
7375
|
-
console.error(
|
|
7506
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7376
7507
|
process.exit(1);
|
|
7377
7508
|
}
|
|
7378
7509
|
});
|
|
@@ -7380,36 +7511,29 @@ governCmd.command("report").description("Summarize governance audit log").action
|
|
|
7380
7511
|
try {
|
|
7381
7512
|
await governReport();
|
|
7382
7513
|
} catch (error) {
|
|
7383
|
-
console.error(
|
|
7384
|
-
process.exit(1);
|
|
7385
|
-
}
|
|
7386
|
-
});
|
|
7387
|
-
governCmd.command("connect").description("Connect your project to the Fragments Govern cloud dashboard").action(async () => {
|
|
7388
|
-
try {
|
|
7389
|
-
await governConnect();
|
|
7390
|
-
} catch (error) {
|
|
7391
|
-
console.error(pc25.red("Error:"), error instanceof Error ? error.message : error);
|
|
7514
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7392
7515
|
process.exit(1);
|
|
7393
7516
|
}
|
|
7394
7517
|
});
|
|
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) => {
|
|
7518
|
+
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("-r, --report <path>", "Write an aggregated machine-readable JSON report").option("-q, --quiet", "Suppress non-error output").action(async (options) => {
|
|
7396
7519
|
try {
|
|
7397
|
-
const { governScan } = await import("./govern-scan-
|
|
7520
|
+
const { governScan } = await import("./govern-scan-HTACKYPF.js");
|
|
7398
7521
|
const { exitCode } = await governScan({
|
|
7399
7522
|
dir: options.dir,
|
|
7400
7523
|
config: options.config,
|
|
7401
7524
|
format: options.format,
|
|
7525
|
+
report: options.report,
|
|
7402
7526
|
quiet: options.quiet
|
|
7403
7527
|
});
|
|
7404
7528
|
process.exit(exitCode);
|
|
7405
7529
|
} catch (error) {
|
|
7406
|
-
console.error(
|
|
7530
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7407
7531
|
process.exit(1);
|
|
7408
7532
|
}
|
|
7409
7533
|
});
|
|
7410
7534
|
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) => {
|
|
7411
7535
|
try {
|
|
7412
|
-
const { governWatch } = await import("./govern-scan-
|
|
7536
|
+
const { governWatch } = await import("./govern-scan-HTACKYPF.js");
|
|
7413
7537
|
await governWatch({
|
|
7414
7538
|
dir: options.dir,
|
|
7415
7539
|
config: options.config,
|
|
@@ -7417,22 +7541,7 @@ governCmd.command("watch").description("Watch JSX/TSX files and re-check on chan
|
|
|
7417
7541
|
debounce: parseInt(options.debounce, 10)
|
|
7418
7542
|
});
|
|
7419
7543
|
} catch (error) {
|
|
7420
|
-
console.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);
|
|
7544
|
+
console.error(pc26.red("Error:"), error instanceof Error ? error.message : error);
|
|
7436
7545
|
process.exit(1);
|
|
7437
7546
|
}
|
|
7438
7547
|
});
|