@fragments-sdk/cli 0.7.17 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/bin.js +227 -53
  2. package/dist/bin.js.map +1 -1
  3. package/dist/{chunk-QLTLLQBI.js → chunk-2JIKCJX3.js} +312 -24
  4. package/dist/chunk-2JIKCJX3.js.map +1 -0
  5. package/dist/{chunk-57OW43NL.js → chunk-CJEGT3WD.js} +2 -2
  6. package/dist/{chunk-7CRC46HV.js → chunk-GOVI6COW.js} +13 -3
  7. package/dist/chunk-GOVI6COW.js.map +1 -0
  8. package/dist/{chunk-WLXFE6XW.js → chunk-NGIMCIK2.js} +60 -2
  9. package/dist/chunk-NGIMCIK2.js.map +1 -0
  10. package/dist/{chunk-M42XIHPV.js → chunk-WI6SLMSO.js} +2 -2
  11. package/dist/core/index.d.ts +110 -3
  12. package/dist/core/index.js +12 -2
  13. package/dist/{defineFragment-BI9KoPrs.d.ts → defineFragment-D0UTve-I.d.ts} +9 -0
  14. package/dist/{generate-ICIPKCKV.js → generate-35OIMW4Y.js} +4 -4
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.js +4 -4
  17. package/dist/{init-DIZ6UNBL.js → init-KFYN37ZY.js} +4 -4
  18. package/dist/mcp-bin.js +67 -3
  19. package/dist/mcp-bin.js.map +1 -1
  20. package/dist/{scan-X3DI2X5G.js → scan-65RH3QMM.js} +5 -5
  21. package/dist/{service-JEWWTSKI.js → service-A5GIGGGK.js} +3 -3
  22. package/dist/{static-viewer-JIWCYKVK.js → static-viewer-NSODM5VX.js} +3 -3
  23. package/dist/{test-36UELXTE.js → test-RPWZAYSJ.js} +3 -3
  24. package/dist/{tokens-K2AGUUOJ.js → tokens-NIXSZRX7.js} +4 -4
  25. package/dist/{viewer-QKIAPTPG.js → viewer-HZK4BSDK.js} +43 -12
  26. package/dist/viewer-HZK4BSDK.js.map +1 -0
  27. package/package.json +3 -3
  28. package/src/bin.ts +32 -0
  29. package/src/build.ts +47 -0
  30. package/src/commands/perf.ts +249 -0
  31. package/src/core/bundle-measurer.ts +421 -0
  32. package/src/core/index.ts +16 -0
  33. package/src/core/performance-presets.ts +142 -0
  34. package/src/core/schema.ts +10 -0
  35. package/src/core/types.ts +6 -0
  36. package/src/mcp/server.ts +77 -0
  37. package/src/viewer/components/BottomPanel.tsx +8 -0
  38. package/src/viewer/components/PerformancePanel.tsx +301 -0
  39. package/src/viewer/hooks/useAppState.ts +1 -1
  40. package/src/viewer/vite-plugin.ts +36 -0
  41. package/dist/chunk-7CRC46HV.js.map +0 -1
  42. package/dist/chunk-QLTLLQBI.js.map +0 -1
  43. package/dist/chunk-WLXFE6XW.js.map +0 -1
  44. package/dist/viewer-QKIAPTPG.js.map +0 -1
  45. /package/dist/{chunk-57OW43NL.js.map → chunk-CJEGT3WD.js.map} +0 -0
  46. /package/dist/{chunk-M42XIHPV.js.map → chunk-WI6SLMSO.js.map} +0 -0
  47. /package/dist/{generate-ICIPKCKV.js.map → generate-35OIMW4Y.js.map} +0 -0
  48. /package/dist/{init-DIZ6UNBL.js.map → init-KFYN37ZY.js.map} +0 -0
  49. /package/dist/{scan-X3DI2X5G.js.map → scan-65RH3QMM.js.map} +0 -0
  50. /package/dist/{service-JEWWTSKI.js.map → service-A5GIGGGK.js.map} +0 -0
  51. /package/dist/{static-viewer-JIWCYKVK.js.map → static-viewer-NSODM5VX.js.map} +0 -0
  52. /package/dist/{test-36UELXTE.js.map → test-RPWZAYSJ.js.map} +0 -0
  53. /package/dist/{tokens-K2AGUUOJ.js.map → tokens-NIXSZRX7.js.map} +0 -0
@@ -4,7 +4,7 @@ import {
4
4
  generateRegistry,
5
5
  loadFragmentFile,
6
6
  parseFragmentFile
7
- } from "./chunk-57OW43NL.js";
7
+ } from "./chunk-CJEGT3WD.js";
8
8
  import {
9
9
  discoverBlockFiles,
10
10
  discoverComponentFiles,
@@ -23,12 +23,14 @@ import {
23
23
  getGrade
24
24
  } from "./chunk-YMPGYEWK.js";
25
25
  import {
26
+ classifyComplexity,
26
27
  compileBlock,
27
- parseTokenFile
28
- } from "./chunk-WLXFE6XW.js";
28
+ parseTokenFile,
29
+ resolvePerformanceConfig
30
+ } from "./chunk-NGIMCIK2.js";
29
31
  import {
30
32
  fragmentDefinitionSchema
31
- } from "./chunk-7CRC46HV.js";
33
+ } from "./chunk-GOVI6COW.js";
32
34
  import {
33
35
  BRAND,
34
36
  DEFAULTS
@@ -598,8 +600,8 @@ async function validateSnippets(config, configDir, options = {}) {
598
600
 
599
601
  // src/build.ts
600
602
  import { readFile as readFile2, writeFile, mkdir } from "fs/promises";
601
- import { resolve as resolve4, join as join4 } from "path";
602
- import { existsSync as existsSync5 } from "fs";
603
+ import { resolve as resolve5, join as join5 } from "path";
604
+ import { existsSync as existsSync6 } from "fs";
603
605
 
604
606
  // src/core/token-resolver.ts
605
607
  import { resolve, dirname, basename } from "path";
@@ -1384,6 +1386,252 @@ function findComponentIndex(componentDir, componentName) {
1384
1386
 
1385
1387
  // src/build.ts
1386
1388
  import { serializeGraph } from "@fragments-sdk/context/graph";
1389
+
1390
+ // src/core/bundle-measurer.ts
1391
+ import { build } from "esbuild";
1392
+ import { gzipSync } from "zlib";
1393
+ import { resolve as resolve4, dirname as dirname3, join as join4, basename as basename3 } from "path";
1394
+ import { existsSync as existsSync5 } from "fs";
1395
+ function resolveEntryPoint(fragmentFilePath, configDir) {
1396
+ const absPath = resolve4(configDir, fragmentFilePath);
1397
+ const dir = dirname3(absPath);
1398
+ const candidates = ["index.tsx", "index.ts", "index.jsx", "index.js"];
1399
+ for (const candidate of candidates) {
1400
+ const path = join4(dir, candidate);
1401
+ if (existsSync5(path)) return path;
1402
+ }
1403
+ return null;
1404
+ }
1405
+ function labelForPath(filePath) {
1406
+ const lastNmIdx = filePath.lastIndexOf("node_modules/");
1407
+ if (lastNmIdx >= 0) {
1408
+ const afterNm = filePath.slice(lastNmIdx + "node_modules/".length);
1409
+ if (afterNm.startsWith("@")) {
1410
+ const parts = afterNm.split("/");
1411
+ return parts.slice(0, 2).join("/");
1412
+ }
1413
+ return afterNm.split("/")[0];
1414
+ }
1415
+ const componentsIdx = filePath.indexOf("components/");
1416
+ if (componentsIdx >= 0) {
1417
+ const afterComponents = filePath.slice(componentsIdx + "components/".length);
1418
+ const componentName = afterComponents.split("/")[0];
1419
+ return componentName;
1420
+ }
1421
+ const srcIdx = filePath.indexOf("src/");
1422
+ if (srcIdx >= 0) return filePath.slice(srcIdx);
1423
+ return filePath;
1424
+ }
1425
+ function groupImportsByDirectDep(metafile, entryPoint) {
1426
+ const inputs = metafile.inputs;
1427
+ const outputKey = Object.keys(metafile.outputs)[0];
1428
+ const outputMeta = outputKey ? metafile.outputs[outputKey] : void 0;
1429
+ if (!outputMeta?.inputs) return [];
1430
+ const bytesMap = /* @__PURE__ */ new Map();
1431
+ for (const [path, info] of Object.entries(outputMeta.inputs)) {
1432
+ if (info.bytesInOutput > 0) {
1433
+ bytesMap.set(path, info.bytesInOutput);
1434
+ }
1435
+ }
1436
+ let entryKey;
1437
+ for (const key of Object.keys(inputs)) {
1438
+ if (key === entryPoint || entryPoint.endsWith(key) || key.endsWith(basename3(entryPoint))) {
1439
+ const entryDir = dirname3(entryPoint);
1440
+ if (key.includes(basename3(entryDir))) {
1441
+ entryKey = key;
1442
+ break;
1443
+ }
1444
+ }
1445
+ }
1446
+ if (!entryKey) {
1447
+ const entryBasename = basename3(dirname3(entryPoint));
1448
+ for (const key of Object.keys(inputs)) {
1449
+ if (key.includes(`/${entryBasename}/index.`)) {
1450
+ entryKey = key;
1451
+ break;
1452
+ }
1453
+ }
1454
+ }
1455
+ if (!entryKey || !inputs[entryKey]) {
1456
+ return groupByPackage(bytesMap);
1457
+ }
1458
+ const directImports = inputs[entryKey].imports.map((imp) => imp.path).filter((p) => inputs[p]);
1459
+ const claimed = /* @__PURE__ */ new Set();
1460
+ claimed.add(entryKey);
1461
+ const groupMap = /* @__PURE__ */ new Map();
1462
+ for (const directPath of directImports) {
1463
+ if (claimed.has(directPath)) continue;
1464
+ const queue = [directPath];
1465
+ const reachable = /* @__PURE__ */ new Set();
1466
+ while (queue.length > 0) {
1467
+ const current = queue.pop();
1468
+ if (reachable.has(current) || claimed.has(current)) continue;
1469
+ reachable.add(current);
1470
+ claimed.add(current);
1471
+ const entry = inputs[current];
1472
+ if (entry?.imports) {
1473
+ for (const imp of entry.imports) {
1474
+ if (inputs[imp.path] && !claimed.has(imp.path)) {
1475
+ queue.push(imp.path);
1476
+ }
1477
+ }
1478
+ }
1479
+ }
1480
+ let totalBytes = 0;
1481
+ for (const path of reachable) {
1482
+ totalBytes += bytesMap.get(path) ?? 0;
1483
+ }
1484
+ if (totalBytes > 0) {
1485
+ const label = labelForPath(directPath);
1486
+ const existing = groupMap.get(label);
1487
+ if (existing) {
1488
+ existing.bytes += totalBytes;
1489
+ } else {
1490
+ const entry = { path: label, bytes: totalBytes };
1491
+ groupMap.set(label, entry);
1492
+ }
1493
+ }
1494
+ }
1495
+ const entryLabel = labelForPath(entryKey);
1496
+ const selfLabel = entryLabel + " (self)";
1497
+ let selfBytes = bytesMap.get(entryKey) ?? 0;
1498
+ const siblingGroup = groupMap.get(entryLabel);
1499
+ if (siblingGroup) {
1500
+ selfBytes += siblingGroup.bytes;
1501
+ groupMap.delete(entryLabel);
1502
+ }
1503
+ if (selfBytes > 0) {
1504
+ groupMap.set(selfLabel, { path: selfLabel, bytes: selfBytes });
1505
+ }
1506
+ let unclaimedBytes = 0;
1507
+ for (const [path, bytes] of bytesMap) {
1508
+ if (!claimed.has(path)) unclaimedBytes += bytes;
1509
+ }
1510
+ if (unclaimedBytes > 0) {
1511
+ groupMap.set("(other)", { path: "(other)", bytes: unclaimedBytes });
1512
+ }
1513
+ return [...groupMap.values()].sort((a, b) => b.bytes - a.bytes);
1514
+ }
1515
+ function groupByPackage(bytesMap) {
1516
+ const groups = /* @__PURE__ */ new Map();
1517
+ for (const [path, bytes] of bytesMap) {
1518
+ const label = labelForPath(path);
1519
+ const key = label.includes("/") && !label.startsWith("components/") ? label.split("/").slice(0, label.startsWith("@") ? 2 : 1).join("/") : label;
1520
+ groups.set(key, (groups.get(key) ?? 0) + bytes);
1521
+ }
1522
+ return [...groups.entries()].map(([path, bytes]) => ({ path, bytes })).sort((a, b) => b.bytes - a.bytes);
1523
+ }
1524
+ async function measureSingleComponent(entryPoint, name) {
1525
+ const result = await build({
1526
+ entryPoints: [entryPoint],
1527
+ bundle: true,
1528
+ write: false,
1529
+ minify: true,
1530
+ metafile: true,
1531
+ format: "esm",
1532
+ target: "es2020",
1533
+ platform: "browser",
1534
+ treeShaking: true,
1535
+ external: [
1536
+ "react",
1537
+ "react-dom",
1538
+ "react/jsx-runtime",
1539
+ "react/jsx-dev-runtime",
1540
+ // Optional peer deps — excluded from measurement
1541
+ "recharts",
1542
+ "shiki",
1543
+ "react-day-picker",
1544
+ "@tanstack/react-table",
1545
+ "date-fns",
1546
+ "@base-ui-components/*",
1547
+ "@base-ui/react/*"
1548
+ ],
1549
+ loader: {
1550
+ ".scss": "empty",
1551
+ ".css": "empty",
1552
+ ".svg": "empty",
1553
+ ".png": "empty",
1554
+ ".jpg": "empty",
1555
+ ".gif": "empty",
1556
+ ".woff": "empty",
1557
+ ".woff2": "empty",
1558
+ ".ttf": "empty",
1559
+ ".eot": "empty"
1560
+ },
1561
+ logLevel: "silent"
1562
+ });
1563
+ const output = result.outputFiles[0];
1564
+ const rawBytes = output.contents.byteLength;
1565
+ const gzipBytes = gzipSync(output.contents).byteLength;
1566
+ const imports = result.metafile ? groupImportsByDirectDep(result.metafile, entryPoint) : void 0;
1567
+ return { name, rawBytes, gzipBytes, imports };
1568
+ }
1569
+ async function measureBundleSizes(fragments, configDir, options = {}) {
1570
+ const concurrency = options.concurrency ?? 4;
1571
+ const measurements = /* @__PURE__ */ new Map();
1572
+ const errors = [];
1573
+ const start = Date.now();
1574
+ const entries = [];
1575
+ for (const [name, fragment] of Object.entries(fragments)) {
1576
+ const entryPoint = resolveEntryPoint(fragment.filePath, configDir);
1577
+ if (entryPoint) {
1578
+ entries.push({ name, entryPoint });
1579
+ } else {
1580
+ errors.push({
1581
+ name,
1582
+ error: `Could not resolve entry point from ${fragment.filePath}`
1583
+ });
1584
+ }
1585
+ }
1586
+ let completed = 0;
1587
+ for (let i = 0; i < entries.length; i += concurrency) {
1588
+ const batch = entries.slice(i, i + concurrency);
1589
+ const results = await Promise.allSettled(
1590
+ batch.map(
1591
+ ({ name, entryPoint }) => measureSingleComponent(entryPoint, name)
1592
+ )
1593
+ );
1594
+ for (let j = 0; j < results.length; j++) {
1595
+ const result = results[j];
1596
+ const { name } = batch[j];
1597
+ completed++;
1598
+ if (result.status === "fulfilled") {
1599
+ measurements.set(name, result.value);
1600
+ } else {
1601
+ errors.push({
1602
+ name,
1603
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
1604
+ });
1605
+ }
1606
+ options.onProgress?.(completed, entries.length, name);
1607
+ }
1608
+ }
1609
+ return {
1610
+ measurements,
1611
+ errors,
1612
+ elapsed: Date.now() - start
1613
+ };
1614
+ }
1615
+ function toPerformanceData(measurement, config, contractBudget) {
1616
+ const budget = contractBudget ?? config.budgets.bundleSize;
1617
+ const budgetPercent = Math.round(measurement.gzipBytes / budget * 100);
1618
+ const imports = measurement.imports?.slice(0, 10).map((imp) => ({
1619
+ path: imp.path,
1620
+ bytes: imp.bytes,
1621
+ percent: Math.round(imp.bytes / measurement.rawBytes * 100)
1622
+ }));
1623
+ return {
1624
+ bundleSize: measurement.gzipBytes,
1625
+ rawSize: measurement.rawBytes,
1626
+ complexity: classifyComplexity(measurement.gzipBytes),
1627
+ budgetPercent,
1628
+ overBudget: budgetPercent > 100,
1629
+ measuredAt: (/* @__PURE__ */ new Date()).toISOString(),
1630
+ ...imports && imports.length > 0 ? { imports } : {}
1631
+ };
1632
+ }
1633
+
1634
+ // src/build.ts
1387
1635
  function normalizeParsedProps(parsedProps) {
1388
1636
  return Object.fromEntries(
1389
1637
  Object.entries(parsedProps).map(([name, prop]) => [
@@ -1593,7 +1841,7 @@ async function buildFragments(config, configDir) {
1593
1841
  (t) => t.value && (t.value.includes("#{") || t.value.includes("$"))
1594
1842
  );
1595
1843
  if (unresolved.length > 0 && tokenFiles.length > 0) {
1596
- const tokensDir = resolve4(configDir, tokenFiles[0].relativePath, "..");
1844
+ const tokensDir = resolve5(configDir, tokenFiles[0].relativePath, "..");
1597
1845
  const sassResolved = await resolveTokensWithSass(
1598
1846
  unresolved,
1599
1847
  tokensDir
@@ -1615,15 +1863,15 @@ async function buildFragments(config, configDir) {
1615
1863
  } catch {
1616
1864
  }
1617
1865
  let packageName;
1618
- const pkgJsonPath = resolve4(configDir, "package.json");
1619
- if (existsSync5(pkgJsonPath)) {
1866
+ const pkgJsonPath = resolve5(configDir, "package.json");
1867
+ if (existsSync6(pkgJsonPath)) {
1620
1868
  try {
1621
1869
  const pkg = JSON.parse(await readFile2(pkgJsonPath, "utf-8"));
1622
1870
  if (pkg.name) packageName = pkg.name;
1623
1871
  } catch {
1624
1872
  }
1625
1873
  }
1626
- const componentDir = resolve4(configDir, "src", "components");
1874
+ const componentDir = resolve5(configDir, "src", "components");
1627
1875
  let graphData;
1628
1876
  try {
1629
1877
  const graphResult = await buildComponentGraph(fragments, blocks, componentDir);
@@ -1654,6 +1902,43 @@ async function buildFragments(config, configDir) {
1654
1902
  warning: `Graph extraction failed: ${error instanceof Error ? error.message : String(error)}`
1655
1903
  });
1656
1904
  }
1905
+ let performanceSummary;
1906
+ if (config.performance) {
1907
+ try {
1908
+ const perfConfig = resolvePerformanceConfig(config.performance);
1909
+ const perfResult = await measureBundleSizes(fragments, configDir, {
1910
+ perfConfig
1911
+ });
1912
+ const tiers = { lightweight: 0, moderate: 0, heavy: 0 };
1913
+ let overBudgetCount = 0;
1914
+ for (const [name, measurement] of perfResult.measurements) {
1915
+ const fragment = fragments[name];
1916
+ const contractBudget = fragment?.contract?.performanceBudget;
1917
+ const perfData = toPerformanceData(measurement, perfConfig, contractBudget);
1918
+ fragment.performance = perfData;
1919
+ tiers[perfData.complexity]++;
1920
+ if (perfData.overBudget) overBudgetCount++;
1921
+ }
1922
+ performanceSummary = {
1923
+ preset: perfConfig.preset,
1924
+ budget: perfConfig.budgets.bundleSize,
1925
+ total: perfResult.measurements.size,
1926
+ overBudget: overBudgetCount,
1927
+ tiers
1928
+ };
1929
+ for (const err of perfResult.errors) {
1930
+ warnings.push({
1931
+ file: "performance",
1932
+ warning: `Could not measure ${err.name}: ${err.error}`
1933
+ });
1934
+ }
1935
+ } catch (error) {
1936
+ warnings.push({
1937
+ file: "performance",
1938
+ warning: `Performance measurement failed: ${error instanceof Error ? error.message : String(error)}`
1939
+ });
1940
+ }
1941
+ }
1657
1942
  const output = {
1658
1943
  version: "1.0.0",
1659
1944
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -1661,9 +1946,10 @@ async function buildFragments(config, configDir) {
1661
1946
  fragments,
1662
1947
  ...Object.keys(blocks).length > 0 && { blocks },
1663
1948
  ...tokens && { tokens },
1664
- ...graphData && { graph: graphData }
1949
+ ...graphData && { graph: graphData },
1950
+ ...performanceSummary && { performanceSummary }
1665
1951
  };
1666
- const outputPath = resolve4(configDir, config.outFile ?? BRAND.outFile);
1952
+ const outputPath = resolve5(configDir, config.outFile ?? BRAND.outFile);
1667
1953
  await writeFile(outputPath, JSON.stringify(output));
1668
1954
  return {
1669
1955
  success: errors.length === 0,
@@ -1674,8 +1960,8 @@ async function buildFragments(config, configDir) {
1674
1960
  };
1675
1961
  }
1676
1962
  async function buildFragmentsDir(config, configDir) {
1677
- const fragmentsDir = join4(configDir, BRAND.dataDir);
1678
- const componentsDir = join4(fragmentsDir, BRAND.componentsDir);
1963
+ const fragmentsDir = join5(configDir, BRAND.dataDir);
1964
+ const componentsDir = join5(fragmentsDir, BRAND.componentsDir);
1679
1965
  await mkdir(fragmentsDir, { recursive: true });
1680
1966
  await mkdir(componentsDir, { recursive: true });
1681
1967
  const registryResult = await generateRegistry({
@@ -1687,9 +1973,9 @@ async function buildFragmentsDir(config, configDir) {
1687
1973
  });
1688
1974
  const errors = [...registryResult.errors];
1689
1975
  const warnings = [...registryResult.warnings];
1690
- const indexPath = join4(fragmentsDir, "index.json");
1976
+ const indexPath = join5(fragmentsDir, "index.json");
1691
1977
  await writeFile(indexPath, JSON.stringify(registryResult.index, null, 2));
1692
- const registryPath = join4(fragmentsDir, BRAND.registryFile);
1978
+ const registryPath = join5(fragmentsDir, BRAND.registryFile);
1693
1979
  await writeFile(registryPath, JSON.stringify(registryResult.registry, null, 2));
1694
1980
  const contextResult = generateContextMd(registryResult.registry, {
1695
1981
  format: "markdown",
@@ -1701,7 +1987,7 @@ async function buildFragmentsDir(config, configDir) {
1701
1987
  code: false
1702
1988
  }
1703
1989
  });
1704
- const contextPath = join4(fragmentsDir, BRAND.contextFile);
1990
+ const contextPath = join5(fragmentsDir, BRAND.contextFile);
1705
1991
  await writeFile(contextPath, contextResult.content);
1706
1992
  return {
1707
1993
  success: errors.length === 0,
@@ -2054,9 +2340,9 @@ ${BRAND.name} Diff
2054
2340
  }
2055
2341
 
2056
2342
  // src/analyze.ts
2057
- import { existsSync as existsSync6 } from "fs";
2343
+ import { existsSync as existsSync7 } from "fs";
2058
2344
  import { readFile as readFile3, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
2059
- import { join as join5, dirname as dirname3 } from "path";
2345
+ import { join as join6, dirname as dirname4 } from "path";
2060
2346
  import pc3 from "picocolors";
2061
2347
  async function runAnalyzeCommand(config, configDir, options = {}) {
2062
2348
  const format = options.format ?? "html";
@@ -2064,8 +2350,8 @@ async function runAnalyzeCommand(config, configDir, options = {}) {
2064
2350
  console.log(pc3.cyan(`
2065
2351
  ${BRAND.name} Analyzer
2066
2352
  `));
2067
- const fragmentsPath = join5(configDir, config.outFile ?? "fragments.json");
2068
- if (!existsSync6(fragmentsPath)) {
2353
+ const fragmentsPath = join6(configDir, config.outFile ?? "fragments.json");
2354
+ if (!existsSync7(fragmentsPath)) {
2069
2355
  console.log(pc3.red(`\u2717 No fragments.json found. Run \`${BRAND.cliCommand} build\` first.
2070
2356
  `));
2071
2357
  return {
@@ -2081,7 +2367,7 @@ ${BRAND.name} Analyzer
2081
2367
  let outputPath;
2082
2368
  if (format === "html" || format === "json") {
2083
2369
  outputPath = options.output ?? getDefaultOutputPath(format, configDir);
2084
- await mkdir2(dirname3(outputPath), { recursive: true });
2370
+ await mkdir2(dirname4(outputPath), { recursive: true });
2085
2371
  if (format === "html") {
2086
2372
  const html = generateHtmlReport(analytics);
2087
2373
  await writeFile2(outputPath, html);
@@ -2160,7 +2446,7 @@ function colorizeScore(score) {
2160
2446
  }
2161
2447
  function getDefaultOutputPath(format, configDir) {
2162
2448
  const filename = format === "html" ? "fragments-report.html" : "fragments-report.json";
2163
- return join5(configDir, filename);
2449
+ return join6(configDir, filename);
2164
2450
  }
2165
2451
  async function openInBrowser(path) {
2166
2452
  const { platform } = await import("os");
@@ -2223,10 +2509,12 @@ export {
2223
2509
  validateCoverage,
2224
2510
  validateAll,
2225
2511
  validateSnippets,
2512
+ measureBundleSizes,
2513
+ toPerformanceData,
2226
2514
  buildFragments,
2227
2515
  buildFragmentsDir,
2228
2516
  runScreenshotCommand,
2229
2517
  runDiffCommand,
2230
2518
  runAnalyzeCommand
2231
2519
  };
2232
- //# sourceMappingURL=chunk-QLTLLQBI.js.map
2520
+ //# sourceMappingURL=chunk-2JIKCJX3.js.map