aem-eds-kit 0.4.7 → 0.4.9

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/eds.js CHANGED
@@ -1424,15 +1424,451 @@ var init_init = __esm({
1424
1424
  }
1425
1425
  });
1426
1426
 
1427
+ // src/lib/migrate/clientlib.ts
1428
+ import { existsSync as existsSync5 } from "fs";
1429
+ import { readFile as readFile3, readdir } from "fs/promises";
1430
+ import path7 from "path";
1431
+ async function listFiles(dir) {
1432
+ const out = [];
1433
+ const walk3 = async (d) => {
1434
+ for (const e of await readdir(d, { withFileTypes: true })) {
1435
+ const p = path7.join(d, e.name);
1436
+ if (e.isDirectory()) await walk3(p);
1437
+ else out.push(p);
1438
+ }
1439
+ };
1440
+ if (existsSync5(dir)) await walk3(dir);
1441
+ return out;
1442
+ }
1443
+ async function findClientlibDir(componentDir) {
1444
+ if (!existsSync5(componentDir)) return null;
1445
+ const entries = await readdir(componentDir, { withFileTypes: true });
1446
+ const match = entries.find((e) => e.isDirectory() && /clientlib/i.test(e.name));
1447
+ if (match) return path7.join(componentDir, match.name);
1448
+ const hasCss = entries.some((e) => e.isFile() && e.name.endsWith(".css"));
1449
+ return hasCss ? componentDir : null;
1450
+ }
1451
+ async function readClientlibCss(clientlibDir) {
1452
+ const all = await listFiles(clientlibDir);
1453
+ const preprocessed = all.filter((f) => /\.(less|scss|sass)$/i.test(f)).map((f) => path7.relative(clientlibDir, f));
1454
+ let cssFiles;
1455
+ const cssTxt = path7.join(clientlibDir, "css.txt");
1456
+ if (existsSync5(cssTxt)) {
1457
+ const lines = (await readFile3(cssTxt, "utf-8")).split("\n").map((l) => l.trim());
1458
+ let base = clientlibDir;
1459
+ const listed = [];
1460
+ for (const l of lines) {
1461
+ if (!l) continue;
1462
+ const baseMatch = l.match(/^#base\s*=\s*(.+)$/);
1463
+ if (baseMatch) base = path7.join(clientlibDir, baseMatch[1].trim());
1464
+ else if (!l.startsWith("#")) listed.push(l);
1465
+ }
1466
+ cssFiles = listed.map((rel) => path7.join(base, rel)).filter((f) => existsSync5(f) && f.endsWith(".css"));
1467
+ } else {
1468
+ cssFiles = all.filter((f) => f.endsWith(".css"));
1469
+ }
1470
+ const parts = [];
1471
+ for (const f of cssFiles) parts.push(await readFile3(f, "utf-8"));
1472
+ return {
1473
+ css: parts.join("\n\n"),
1474
+ files: cssFiles.map((f) => path7.relative(clientlibDir, f)),
1475
+ preprocessed
1476
+ };
1477
+ }
1478
+ var init_clientlib = __esm({
1479
+ "src/lib/migrate/clientlib.ts"() {
1480
+ "use strict";
1481
+ init_esm_shims();
1482
+ }
1483
+ });
1484
+
1485
+ // src/lib/migrate/dialog.ts
1486
+ import { XMLParser } from "fast-xml-parser";
1487
+ function tagOf(n) {
1488
+ return Object.keys(n).find((k) => k !== ":@") ?? "";
1489
+ }
1490
+ function childrenOf(n) {
1491
+ const t = tagOf(n);
1492
+ const v = n[t];
1493
+ return Array.isArray(v) ? v : [];
1494
+ }
1495
+ function attrsOf(n) {
1496
+ return n[":@"] ?? {};
1497
+ }
1498
+ function stripType(v) {
1499
+ return typeof v === "string" ? v.replace(/^\{[^}]+\}/, "") : v;
1500
+ }
1501
+ function attr(n, name) {
1502
+ return stripType(attrsOf(n)[`@_${name}`]);
1503
+ }
1504
+ function leafComponent(rt, n) {
1505
+ const multiple = attr(n, "multiple") === "true" || attr(n, "multiple") === "{Boolean}true";
1506
+ if (rt.endsWith("/form/textfield")) return "text";
1507
+ if (rt.endsWith("/form/textarea")) return "textarea";
1508
+ if (rt.includes("/richtext")) return "richtext";
1509
+ if (rt.endsWith("/form/numberfield")) return "number";
1510
+ if (rt.endsWith("/form/checkbox")) return "boolean";
1511
+ if (rt.endsWith("/form/radiogroup")) return "radio-group";
1512
+ if (rt.endsWith("/form/select")) return multiple ? "multiselect" : "select";
1513
+ if (rt.endsWith("/form/datepicker")) return "date-time";
1514
+ if (rt.endsWith("/form/pathfield") || rt.endsWith("/form/pathbrowser")) return "aem-content";
1515
+ if (rt.includes("/fileupload") || rt.endsWith("/form/fileupload")) return "reference";
1516
+ if (rt.includes("tagfield") || rt.includes("tagpicker") || rt.includes("tagbrowser"))
1517
+ return "aem-tag";
1518
+ if (rt.includes("contentfragment")) return "aem-content-fragment";
1519
+ if (rt.includes("experiencefragment")) return "aem-experience-fragment";
1520
+ return null;
1521
+ }
1522
+ function isWrapper(rt) {
1523
+ return rt.includes("/foundation/container") || rt.includes("/foundation/fixedcolumns") || rt.includes("/foundation/well") || rt.includes("/foundation/tabs") || rt.includes("/foundation/form/fieldset") || rt.includes("/foundation/accordion");
1524
+ }
1525
+ function valueTypeFor(c) {
1526
+ switch (c) {
1527
+ case "number":
1528
+ return "number";
1529
+ case "boolean":
1530
+ return "boolean";
1531
+ case "date-time":
1532
+ return "date";
1533
+ case "checkbox-group":
1534
+ return "string[]";
1535
+ case "aem-content":
1536
+ case "aem-content-fragment":
1537
+ case "aem-experience-fragment":
1538
+ return void 0;
1539
+ default:
1540
+ return "string";
1541
+ }
1542
+ }
1543
+ function cleanName(raw, fallback) {
1544
+ const n = (raw ?? "").replace(/^\.\//, "").trim();
1545
+ return n || fallback;
1546
+ }
1547
+ function readOptions(fieldNode) {
1548
+ const items = childrenOf(fieldNode).find((c) => tagOf(c) === "items");
1549
+ if (!items) return [];
1550
+ return childrenOf(items).map((opt) => {
1551
+ const value = attr(opt, "value") ?? "";
1552
+ const name = attr(opt, "text") ?? attr(opt, "jcr:title") ?? value;
1553
+ return { name, value };
1554
+ });
1555
+ }
1556
+ function walk(nodes, out, unmapped, counter) {
1557
+ for (const node of nodes) {
1558
+ const rt = attr(node, "sling:resourceType") ?? "";
1559
+ if (rt.endsWith("/foundation/tabs")) {
1560
+ const items = childrenOf(node).find((c) => tagOf(c) === "items");
1561
+ for (const tab of items ? childrenOf(items) : []) {
1562
+ const label = attr(tab, "jcr:title");
1563
+ out.push({ component: "tab", name: cleanName(void 0, tagOf(tab)), label });
1564
+ walk(childrenOf(tab), out, unmapped, counter);
1565
+ }
1566
+ continue;
1567
+ }
1568
+ if (rt.endsWith("/form/multifield")) {
1569
+ const sub = [];
1570
+ walk(childrenOf(node), sub, unmapped, counter);
1571
+ const name = cleanName(attr(node, "name"), `items${counter.i++}`);
1572
+ out.push({
1573
+ component: "container",
1574
+ name,
1575
+ label: attr(node, "fieldLabel") ?? "Items",
1576
+ multi: true,
1577
+ collapsible: true,
1578
+ fields: sub
1579
+ });
1580
+ continue;
1581
+ }
1582
+ const comp = rt ? leafComponent(rt, node) : null;
1583
+ if (comp) {
1584
+ const name = cleanName(attr(node, "name"), `field${counter.i++}`);
1585
+ const field = { component: comp, name };
1586
+ const vt = valueTypeFor(comp);
1587
+ if (vt) field.valueType = vt;
1588
+ const label = attr(node, "fieldLabel") ?? attr(node, "text");
1589
+ if (label) field.label = label;
1590
+ if (comp === "select" || comp === "radio-group" || comp === "multiselect") {
1591
+ const options = readOptions(node);
1592
+ if (options.length) field.options = options;
1593
+ }
1594
+ const def = attr(node, "value") ?? (comp === "boolean" ? attr(node, "checked") : void 0);
1595
+ if (def !== void 0 && def !== "") field.value = comp === "boolean" ? def === "true" : def;
1596
+ out.push(field);
1597
+ continue;
1598
+ }
1599
+ if (rt && attr(node, "name") && !isWrapper(rt)) unmapped.add(rt);
1600
+ walk(childrenOf(node), out, unmapped, counter);
1601
+ }
1602
+ }
1603
+ function parseDialog(xml) {
1604
+ const parser = new XMLParser({
1605
+ ignoreAttributes: false,
1606
+ attributeNamePrefix: "@_",
1607
+ preserveOrder: true,
1608
+ removeNSPrefix: false,
1609
+ trimValues: true
1610
+ });
1611
+ const tree = parser.parse(xml);
1612
+ const root = tree.find((n) => tagOf(n) === "jcr:root");
1613
+ const fields = [];
1614
+ const unmapped = /* @__PURE__ */ new Set();
1615
+ if (root) walk(childrenOf(root), fields, unmapped, { i: 1 });
1616
+ return { fields, unmapped: [...unmapped] };
1617
+ }
1618
+ var init_dialog = __esm({
1619
+ "src/lib/migrate/dialog.ts"() {
1620
+ "use strict";
1621
+ init_esm_shims();
1622
+ }
1623
+ });
1624
+
1625
+ // src/lib/migrate/component.ts
1626
+ import { existsSync as existsSync6 } from "fs";
1627
+ import { readFile as readFile4 } from "fs/promises";
1628
+ import path8 from "path";
1629
+ import { XMLParser as XMLParser2 } from "fast-xml-parser";
1630
+ function findDialogFile(componentDir) {
1631
+ const candidates = [
1632
+ path8.join(componentDir, "_cq_dialog", ".content.xml"),
1633
+ path8.join(componentDir, "_cq_dialog.xml"),
1634
+ path8.join(componentDir, "cq:dialog", ".content.xml")
1635
+ ];
1636
+ return candidates.find((c) => existsSync6(c)) ?? null;
1637
+ }
1638
+ async function readComponentTitle(componentDir) {
1639
+ const file = path8.join(componentDir, ".content.xml");
1640
+ if (!existsSync6(file)) return void 0;
1641
+ try {
1642
+ const parser = new XMLParser2({ ignoreAttributes: false, attributeNamePrefix: "@_" });
1643
+ const parsed = parser.parse(await readFile4(file, "utf-8"));
1644
+ const root = parsed["jcr:root"] ?? {};
1645
+ const title = root["@_jcr:title"];
1646
+ return typeof title === "string" ? title.replace(/^\{[^}]+\}/, "") : void 0;
1647
+ } catch {
1648
+ return void 0;
1649
+ }
1650
+ }
1651
+ async function readDialogFields(componentDir) {
1652
+ const dialog = findDialogFile(componentDir);
1653
+ if (!dialog) return null;
1654
+ return parseDialog(await readFile4(dialog, "utf-8"));
1655
+ }
1656
+ function migratedModelFile(blockName, title, fields) {
1657
+ const model = {
1658
+ definitions: [
1659
+ {
1660
+ title,
1661
+ id: blockName,
1662
+ plugins: {
1663
+ xwalk: {
1664
+ page: {
1665
+ resourceType: "core/franklin/components/block/v1/block",
1666
+ template: { name: title, model: blockName }
1667
+ }
1668
+ }
1669
+ }
1670
+ }
1671
+ ],
1672
+ models: [{ id: blockName, fields }],
1673
+ filters: []
1674
+ };
1675
+ return `${JSON.stringify(model, null, 2)}
1676
+ `;
1677
+ }
1678
+ function migratedBlockJs(blockName, fields) {
1679
+ const hasContainer = fields.some((f) => f.component === "container" && f.multi);
1680
+ const body = hasContainer ? ` // This block was migrated from a classic AEM component with a multifield.
1681
+ // Each child row is one repeated item - enhance them here.
1682
+ [...block.children].forEach((row) => {
1683
+ row.classList.add('${blockName}-item');
1684
+ });` : ` // Migrated from a classic AEM component. The authored fields arrive as
1685
+ // rows; read and enhance them here (port the old HTL logic as needed).`;
1686
+ return `// Block: ${blockName}
1687
+ // Migrated from a classic AEM component by \`eds migrate component\`.
1688
+ // TODO: port the component's HTL/JS behaviour into this decorator.
1689
+ export default function decorate(block) {
1690
+ block.classList.add('${blockName}');
1691
+ ${body}
1692
+ }
1693
+ `;
1694
+ }
1695
+ function migratedBlockCss(blockName) {
1696
+ return `/* Block: ${blockName} */
1697
+ /* Migrated from a classic AEM component. Bring the component's styles here */
1698
+ /* (from its clientlib or the rendered page) and keep them scoped to the block. */
1699
+ .${blockName} {
1700
+ }
1701
+ `;
1702
+ }
1703
+ var init_component = __esm({
1704
+ "src/lib/migrate/component.ts"() {
1705
+ "use strict";
1706
+ init_esm_shims();
1707
+ init_dialog();
1708
+ }
1709
+ });
1710
+
1711
+ // src/commands/migrate.ts
1712
+ var migrate_exports = {};
1713
+ __export(migrate_exports, {
1714
+ migrateComponent: () => migrateComponent
1715
+ });
1716
+ import { existsSync as existsSync7 } from "fs";
1717
+ import { mkdir as mkdir3, writeFile as writeFile5 } from "fs/promises";
1718
+ import path9 from "path";
1719
+ function clientlibBlockCss(blockName, css, files) {
1720
+ return `/* Block: ${blockName} */
1721
+ /* Migrated from the component clientlib: ${files.join(", ")}. */
1722
+ /* TODO: review selectors and scope them under .${blockName} (and port any LESS/SCSS). */
1723
+
1724
+ ${css.trim()}
1725
+ `;
1726
+ }
1727
+ function previewFields(fields, depth = 0) {
1728
+ const lines = [];
1729
+ for (const f of fields) {
1730
+ const indent = " ".repeat(depth);
1731
+ if (f.component === "tab") {
1732
+ lines.push(`${indent}${brand.blue("\u25B8")} ${f.label ?? f.name}`);
1733
+ } else {
1734
+ const opts = f.options?.length ? ` (${f.options.length} options)` : "";
1735
+ const multi = f.multi ? " \xB7multi" : "";
1736
+ const label = f.label ? `- ${f.label}` : "";
1737
+ lines.push(`${indent}${brand.accent(f.component)}${multi} ${f.name}${opts} ${label}`);
1738
+ if (f.fields?.length) lines.push(...previewFields(f.fields, depth + 1));
1739
+ }
1740
+ }
1741
+ return lines;
1742
+ }
1743
+ function countLeaves(fields) {
1744
+ let n = 0;
1745
+ for (const f of fields) {
1746
+ if (f.component === "tab") continue;
1747
+ n += 1;
1748
+ if (f.fields) n += countLeaves(f.fields);
1749
+ }
1750
+ return n;
1751
+ }
1752
+ async function migrateComponent(dirArg, options = {}) {
1753
+ const projectRoot = findProjectRoot();
1754
+ if (!projectRoot) {
1755
+ logger.error("Not inside an EDS project (no EDS project markers found).");
1756
+ process.exitCode = 1;
1757
+ return;
1758
+ }
1759
+ await logger.logoOnceAnimated("Migrate component");
1760
+ const { input, confirm } = await import("@inquirer/prompts");
1761
+ const interactive = !options.yes;
1762
+ let componentDir = dirArg;
1763
+ if (!componentDir && interactive) {
1764
+ componentDir = await input({
1765
+ message: "Path to the classic component folder",
1766
+ validate: (v) => existsSync7(v.trim()) || "Folder not found"
1767
+ });
1768
+ }
1769
+ if (!componentDir) {
1770
+ logger.error("Provide the path to the classic component folder.");
1771
+ process.exitCode = 1;
1772
+ return;
1773
+ }
1774
+ componentDir = path9.resolve(componentDir.trim());
1775
+ if (!findDialogFile(componentDir)) {
1776
+ logger.error(`No Touch UI dialog (_cq_dialog/.content.xml) found in ${componentDir}.`);
1777
+ process.exitCode = 1;
1778
+ return;
1779
+ }
1780
+ const parsed = await readDialogFields(componentDir);
1781
+ if (!parsed || parsed.fields.length === 0) {
1782
+ logger.warn("Dialog found but no mappable fields were detected.");
1783
+ if (parsed?.unmapped.length)
1784
+ logger.info(`Unmapped resourceTypes: ${parsed.unmapped.join(", ")}`);
1785
+ process.exitCode = 1;
1786
+ return;
1787
+ }
1788
+ const title = await readComponentTitle(componentDir) ?? path9.basename(componentDir);
1789
+ const blockName = options.name && slug2(options.name) || (interactive ? slug2(await input({ message: "Block name", default: slug2(title) })) : slug2(title));
1790
+ logger.info(heading("Mapped fields", `${countLeaves(parsed.fields)} from dialog`));
1791
+ for (const line of previewFields(parsed.fields)) logger.info(` ${line}`);
1792
+ if (parsed.unmapped.length) {
1793
+ logger.info("");
1794
+ logger.warn(`Skipped (unrecognised): ${parsed.unmapped.join(", ")}`);
1795
+ }
1796
+ logger.info("");
1797
+ if (interactive) {
1798
+ const go = await confirm({ message: `Generate block "${blockName}"?`, default: true });
1799
+ if (!go) {
1800
+ logger.info("Cancelled.");
1801
+ return;
1802
+ }
1803
+ }
1804
+ const blockDir = path9.join(projectRoot, "blocks", blockName);
1805
+ if (existsSync7(blockDir)) {
1806
+ logger.error(
1807
+ `Block "${blockName}" already exists at blocks/${blockName}. Use --name to pick another.`
1808
+ );
1809
+ process.exitCode = 1;
1810
+ return;
1811
+ }
1812
+ const clibDir = options.clientlib ? path9.resolve(options.clientlib.trim()) : await findClientlibDir(componentDir);
1813
+ let cssContent = migratedBlockCss(blockName);
1814
+ let cssMessage = "empty - add styles from clientlib / rendered CSS";
1815
+ let preprocessedNote = [];
1816
+ if (clibDir && existsSync7(clibDir)) {
1817
+ const lib = await readClientlibCss(clibDir);
1818
+ if (lib.css.trim()) {
1819
+ cssContent = clientlibBlockCss(blockName, lib.css, lib.files);
1820
+ cssMessage = `from clientlib (${lib.files.length} file(s)) - review & scope`;
1821
+ }
1822
+ preprocessedNote = lib.preprocessed;
1823
+ }
1824
+ await mkdir3(blockDir, { recursive: true });
1825
+ await writeFile5(
1826
+ path9.join(blockDir, `${blockName}.js`),
1827
+ migratedBlockJs(blockName, parsed.fields)
1828
+ );
1829
+ await writeFile5(path9.join(blockDir, `${blockName}.css`), cssContent);
1830
+ await writeFile5(
1831
+ path9.join(blockDir, `_${blockName}.json`),
1832
+ migratedModelFile(blockName, title, parsed.fields)
1833
+ );
1834
+ logger.info(heading("Created"));
1835
+ const files = [`${blockName}.js`, `${blockName}.css`, `_${blockName}.json`];
1836
+ const w = columnWidth(files);
1837
+ logger.info(accentLine(`${blockName}.js`, "decorator stub (port HTL logic)", w));
1838
+ logger.info(accentLine(`${blockName}.css`, cssMessage, w));
1839
+ logger.info(accentLine(`_${blockName}.json`, "Universal Editor model", w));
1840
+ if (preprocessedNote.length) {
1841
+ logger.info("");
1842
+ logger.warn(`LESS/SCSS found (needs a build, not copied): ${preprocessedNote.join(", ")}`);
1843
+ }
1844
+ logger.info("");
1845
+ logger.info(
1846
+ box([`"${blockName}" migrated. Next: add CSS, then \`eds block preview ${blockName}\``])
1847
+ );
1848
+ }
1849
+ var slug2;
1850
+ var init_migrate = __esm({
1851
+ "src/commands/migrate.ts"() {
1852
+ "use strict";
1853
+ init_esm_shims();
1854
+ init_logger();
1855
+ init_clientlib();
1856
+ init_component();
1857
+ init_project();
1858
+ init_ui();
1859
+ slug2 = (s) => s.trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
1860
+ }
1861
+ });
1862
+
1427
1863
  // src/commands/block/create.ts
1428
1864
  var create_exports = {};
1429
1865
  __export(create_exports, {
1430
1866
  createBlock: () => createBlock,
1431
1867
  ueModelFor: () => ueModelFor
1432
1868
  });
1433
- import { existsSync as existsSync5 } from "fs";
1434
- import { mkdir as mkdir3, writeFile as writeFile5 } from "fs/promises";
1435
- import path7 from "path";
1869
+ import { existsSync as existsSync8 } from "fs";
1870
+ import { mkdir as mkdir4, writeFile as writeFile6 } from "fs/promises";
1871
+ import path10 from "path";
1436
1872
  import chalk4 from "chalk";
1437
1873
  function titleCase(name) {
1438
1874
  return name.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
@@ -1511,13 +1947,13 @@ async function createBlock(name, options = {}) {
1511
1947
  process.exitCode = 1;
1512
1948
  return;
1513
1949
  }
1514
- const blocksDir = path7.join(projectRoot, "blocks", name);
1515
- if (existsSync5(blocksDir)) {
1950
+ const blocksDir = path10.join(projectRoot, "blocks", name);
1951
+ if (existsSync8(blocksDir)) {
1516
1952
  logger.error(`Block "${name}" already exists at ${blocksDir}`);
1517
1953
  process.exitCode = 1;
1518
1954
  return;
1519
1955
  }
1520
- await mkdir3(blocksDir, { recursive: true });
1956
+ await mkdir4(blocksDir, { recursive: true });
1521
1957
  const jsContent = `export default function decorate(block) {
1522
1958
  // TODO: implement ${name} block
1523
1959
  }
@@ -1526,14 +1962,14 @@ async function createBlock(name, options = {}) {
1526
1962
  /* TODO: style ${name} block */
1527
1963
  }
1528
1964
  `;
1529
- await writeFile5(path7.join(blocksDir, `${name}.js`), jsContent);
1530
- await writeFile5(path7.join(blocksDir, `${name}.css`), cssContent);
1965
+ await writeFile6(path10.join(blocksDir, `${name}.js`), jsContent);
1966
+ await writeFile6(path10.join(blocksDir, `${name}.css`), cssContent);
1531
1967
  const withUe = options.ueModel !== false;
1532
1968
  if (withUe) {
1533
- await writeFile5(path7.join(blocksDir, `_${name}.json`), ueModelFor(name));
1969
+ await writeFile6(path10.join(blocksDir, `_${name}.json`), ueModelFor(name));
1534
1970
  }
1535
1971
  logger.success(
1536
- `Block "${name}" created at ${chalk4.cyan(path7.relative(projectRoot, blocksDir))}${withUe ? chalk4.dim(" (+ Universal Editor model)") : ""}`
1972
+ `Block "${name}" created at ${chalk4.cyan(path10.relative(projectRoot, blocksDir))}${withUe ? chalk4.dim(" (+ Universal Editor model)") : ""}`
1537
1973
  );
1538
1974
  }
1539
1975
  var init_create = __esm({
@@ -1547,10 +1983,10 @@ var init_create = __esm({
1547
1983
  });
1548
1984
 
1549
1985
  // src/lib/agents/detect.ts
1550
- import { existsSync as existsSync6 } from "fs";
1551
- import { readFile as readFile3 } from "fs/promises";
1986
+ import { existsSync as existsSync9 } from "fs";
1987
+ import { readFile as readFile5 } from "fs/promises";
1552
1988
  import { homedir } from "os";
1553
- import path8 from "path";
1989
+ import path11 from "path";
1554
1990
  async function whichBinary(name) {
1555
1991
  const { execaCommand } = await import("execa");
1556
1992
  try {
@@ -1578,8 +2014,8 @@ function findFigmaServerKey(node) {
1578
2014
  async function findFigmaMcpServer(configPaths) {
1579
2015
  for (const configPath of configPaths) {
1580
2016
  try {
1581
- if (!existsSync6(configPath)) continue;
1582
- const content = await readFile3(configPath, "utf-8");
2017
+ if (!existsSync9(configPath)) continue;
2018
+ const content = await readFile5(configPath, "utf-8");
1583
2019
  try {
1584
2020
  const key = findFigmaServerKey(JSON.parse(content));
1585
2021
  if (key) return key;
@@ -1601,7 +2037,7 @@ async function detectAgent(preferred) {
1601
2037
  return {
1602
2038
  type: def.type,
1603
2039
  path: binPath,
1604
- configDir: existsSync6(def.configDir) ? def.configDir : void 0,
2040
+ configDir: existsSync9(def.configDir) ? def.configDir : void 0,
1605
2041
  hasFigmaMcp: !!figmaMcpServerName,
1606
2042
  figmaMcpServerName
1607
2043
  };
@@ -1616,7 +2052,7 @@ async function detectAgent(preferred) {
1616
2052
  return {
1617
2053
  type: def.type,
1618
2054
  path: binPath,
1619
- configDir: existsSync6(def.configDir) ? def.configDir : void 0,
2055
+ configDir: existsSync9(def.configDir) ? def.configDir : void 0,
1620
2056
  hasFigmaMcp: !!figmaMcpServerName,
1621
2057
  figmaMcpServerName
1622
2058
  };
@@ -1633,7 +2069,7 @@ async function detectAllAgents() {
1633
2069
  agents.push({
1634
2070
  type: def.type,
1635
2071
  path: binPath,
1636
- configDir: existsSync6(def.configDir) ? def.configDir : void 0,
2072
+ configDir: existsSync9(def.configDir) ? def.configDir : void 0,
1637
2073
  hasFigmaMcp: !!figmaMcpServerName,
1638
2074
  figmaMcpServerName
1639
2075
  });
@@ -1651,22 +2087,22 @@ var init_detect = __esm({
1651
2087
  {
1652
2088
  type: "claude",
1653
2089
  binary: "claude",
1654
- configDir: path8.join(homedir(), ".claude"),
2090
+ configDir: path11.join(homedir(), ".claude"),
1655
2091
  mcpConfigPaths: [
1656
- path8.join(homedir(), ".claude", "plugins.json"),
1657
- path8.join(homedir(), ".claude.json")
2092
+ path11.join(homedir(), ".claude", "plugins.json"),
2093
+ path11.join(homedir(), ".claude.json")
1658
2094
  ]
1659
2095
  },
1660
2096
  {
1661
2097
  type: "cursor",
1662
2098
  binary: "cursor",
1663
- configDir: path8.join(homedir(), ".cursor"),
1664
- mcpConfigPaths: [path8.join(homedir(), ".cursor", "mcp.json")]
2099
+ configDir: path11.join(homedir(), ".cursor"),
2100
+ mcpConfigPaths: [path11.join(homedir(), ".cursor", "mcp.json")]
1665
2101
  },
1666
2102
  {
1667
2103
  type: "codex",
1668
2104
  binary: "codex",
1669
- configDir: path8.join(homedir(), ".codex"),
2105
+ configDir: path11.join(homedir(), ".codex"),
1670
2106
  mcpConfigPaths: []
1671
2107
  }
1672
2108
  ];
@@ -1674,23 +2110,23 @@ var init_detect = __esm({
1674
2110
  });
1675
2111
 
1676
2112
  // src/lib/agents/invoke.ts
1677
- import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile6 } from "fs/promises";
2113
+ import { mkdir as mkdir5, readFile as readFile6, writeFile as writeFile7 } from "fs/promises";
1678
2114
  import { tmpdir } from "os";
1679
- import path9 from "path";
2115
+ import path12 from "path";
1680
2116
  import readline from "readline";
1681
2117
  import chalk5 from "chalk";
1682
2118
  async function savePromptToFile(prompt, projectRoot) {
1683
- const base = projectRoot ? path9.join(projectRoot, "tmp") : tmpdir();
1684
- const dir = path9.join(base, "eds-cli");
1685
- await mkdir4(dir, { recursive: true });
2119
+ const base = projectRoot ? path12.join(projectRoot, "tmp") : tmpdir();
2120
+ const dir = path12.join(base, "eds-cli");
2121
+ await mkdir5(dir, { recursive: true });
1686
2122
  const filename = `from-design-${Date.now()}.md`;
1687
- const filePath = path9.join(dir, filename);
1688
- await writeFile6(filePath, prompt, "utf-8");
2123
+ const filePath = path12.join(dir, filename);
2124
+ await writeFile7(filePath, prompt, "utf-8");
1689
2125
  return filePath;
1690
2126
  }
1691
2127
  async function invokeAgent(agent, promptFile, cwd) {
1692
2128
  const { execa } = await import("execa");
1693
- const prompt = await readFile4(promptFile, "utf-8");
2129
+ const prompt = await readFile6(promptFile, "utf-8");
1694
2130
  try {
1695
2131
  switch (agent.type) {
1696
2132
  case "claude": {
@@ -1830,7 +2266,7 @@ function toolLabel(name, input, cwd) {
1830
2266
  const rel = (key) => {
1831
2267
  const p = input?.[key];
1832
2268
  if (typeof p !== "string") return "";
1833
- return p.startsWith(cwd) ? path9.relative(cwd, p) : path9.basename(p);
2269
+ return p.startsWith(cwd) ? path12.relative(cwd, p) : path12.basename(p);
1834
2270
  };
1835
2271
  if (name.includes("figma")) {
1836
2272
  const short = name.split("__").pop() ?? name;
@@ -1861,13 +2297,13 @@ var init_invoke = __esm({
1861
2297
  });
1862
2298
 
1863
2299
  // src/lib/config.ts
1864
- import { existsSync as existsSync7 } from "fs";
1865
- import { readFile as readFile5 } from "fs/promises";
2300
+ import { existsSync as existsSync10 } from "fs";
2301
+ import { readFile as readFile7 } from "fs/promises";
1866
2302
  import { homedir as homedir2 } from "os";
1867
- import path10 from "path";
2303
+ import path13 from "path";
1868
2304
  async function loadJsonFile(filePath) {
1869
2305
  try {
1870
- const content = await readFile5(filePath, "utf-8");
2306
+ const content = await readFile7(filePath, "utf-8");
1871
2307
  return JSON.parse(content);
1872
2308
  } catch {
1873
2309
  return void 0;
@@ -1875,7 +2311,7 @@ async function loadJsonFile(filePath) {
1875
2311
  }
1876
2312
  async function loadConfig(projectRoot) {
1877
2313
  const globalRaw = await loadJsonFile(GLOBAL_CONFIG_PATH);
1878
- const projectRaw = projectRoot ? await loadJsonFile(path10.join(projectRoot, PROJECT_CONFIG_NAME)) : void 0;
2314
+ const projectRaw = projectRoot ? await loadJsonFile(path13.join(projectRoot, PROJECT_CONFIG_NAME)) : void 0;
1879
2315
  const merged = {
1880
2316
  ...typeof globalRaw === "object" && globalRaw !== null ? globalRaw : {},
1881
2317
  ...typeof projectRaw === "object" && projectRaw !== null ? projectRaw : {}
@@ -1894,7 +2330,7 @@ function globalConfigPath() {
1894
2330
  return GLOBAL_CONFIG_PATH;
1895
2331
  }
1896
2332
  function globalConfigExists() {
1897
- return existsSync7(GLOBAL_CONFIG_PATH);
2333
+ return existsSync10(GLOBAL_CONFIG_PATH);
1898
2334
  }
1899
2335
  var GLOBAL_CONFIG_DIR, GLOBAL_CONFIG_PATH, PROJECT_CONFIG_NAME;
1900
2336
  var init_config = __esm({
@@ -1903,8 +2339,8 @@ var init_config = __esm({
1903
2339
  init_esm_shims();
1904
2340
  init_logger();
1905
2341
  init_schemas();
1906
- GLOBAL_CONFIG_DIR = path10.join(homedir2(), ".eds");
1907
- GLOBAL_CONFIG_PATH = path10.join(GLOBAL_CONFIG_DIR, "config.json");
2342
+ GLOBAL_CONFIG_DIR = path13.join(homedir2(), ".eds");
2343
+ GLOBAL_CONFIG_PATH = path13.join(GLOBAL_CONFIG_DIR, "config.json");
1908
2344
  PROJECT_CONFIG_NAME = ".edsrc.json";
1909
2345
  }
1910
2346
  });
@@ -1954,9 +2390,9 @@ var init_node_id = __esm({
1954
2390
  });
1955
2391
 
1956
2392
  // src/lib/figma/prompt-builder.ts
1957
- import { existsSync as existsSync8 } from "fs";
1958
- import { readFile as readFile6 } from "fs/promises";
1959
- import path11 from "path";
2393
+ import { existsSync as existsSync11 } from "fs";
2394
+ import { readFile as readFile8 } from "fs/promises";
2395
+ import path14 from "path";
1960
2396
  function contentSourceGuidance(source, cfHint, blockName) {
1961
2397
  if (!source || source === "document" || source === "ue") return "";
1962
2398
  const q = cfHint ? ` (model / persisted query: \`${cfHint}\`)` : "";
@@ -2168,10 +2604,10 @@ ${tokensHint}
2168
2604
  `;
2169
2605
  }
2170
2606
  async function getTokensHint(projectRoot) {
2171
- const stylesPath = path11.join(projectRoot, "styles", "styles.css");
2172
- if (!existsSync8(stylesPath)) return "";
2607
+ const stylesPath = path14.join(projectRoot, "styles", "styles.css");
2608
+ if (!existsSync11(stylesPath)) return "";
2173
2609
  try {
2174
- const css = await readFile6(stylesPath, "utf-8");
2610
+ const css = await readFile8(stylesPath, "utf-8");
2175
2611
  const rootMatch = css.match(/:root\s*\{([^}]+)\}/);
2176
2612
  if (!rootMatch) return "";
2177
2613
  const props = rootMatch[1].split("\n").filter((line) => line.trim().startsWith("--")).map((line) => line.trim()).join("\n ");
@@ -2311,11 +2747,11 @@ var init_harness = __esm({
2311
2747
  });
2312
2748
 
2313
2749
  // src/lib/preview/server.ts
2314
- import { createReadStream, existsSync as existsSync9, statSync } from "fs";
2750
+ import { createReadStream, existsSync as existsSync12, statSync } from "fs";
2315
2751
  import http from "http";
2316
- import path12 from "path";
2752
+ import path15 from "path";
2317
2753
  function startServer(root, routes, port, host = "127.0.0.1") {
2318
- const resolvedRoot = path12.resolve(root);
2754
+ const resolvedRoot = path15.resolve(root);
2319
2755
  const clients = /* @__PURE__ */ new Set();
2320
2756
  const server = http.createServer((req, res) => {
2321
2757
  const urlPath = decodeURIComponent((req.url ?? "/").split("?")[0]);
@@ -2341,19 +2777,19 @@ function startServer(root, routes, port, host = "127.0.0.1") {
2341
2777
  res.end(route.body);
2342
2778
  return;
2343
2779
  }
2344
- const filePath = path12.join(resolvedRoot, urlPath);
2780
+ const filePath = path15.join(resolvedRoot, urlPath);
2345
2781
  if (!filePath.startsWith(resolvedRoot)) {
2346
2782
  res.writeHead(403);
2347
2783
  res.end("Forbidden");
2348
2784
  return;
2349
2785
  }
2350
- if (!existsSync9(filePath) || !statSync(filePath).isFile()) {
2786
+ if (!existsSync12(filePath) || !statSync(filePath).isFile()) {
2351
2787
  res.writeHead(404, { "content-type": "text/plain" });
2352
2788
  res.end(`Not found: ${urlPath}`);
2353
2789
  return;
2354
2790
  }
2355
2791
  res.writeHead(200, {
2356
- "content-type": MIME[path12.extname(filePath).toLowerCase()] ?? "application/octet-stream"
2792
+ "content-type": MIME[path15.extname(filePath).toLowerCase()] ?? "application/octet-stream"
2357
2793
  });
2358
2794
  createReadStream(filePath).pipe(res);
2359
2795
  });
@@ -2417,9 +2853,9 @@ var preview_exports = {};
2417
2853
  __export(preview_exports, {
2418
2854
  previewBlock: () => previewBlock
2419
2855
  });
2420
- import { existsSync as existsSync10, watch } from "fs";
2421
- import { readFile as readFile7 } from "fs/promises";
2422
- import path13 from "path";
2856
+ import { existsSync as existsSync13, watch } from "fs";
2857
+ import { readFile as readFile9 } from "fs/promises";
2858
+ import path16 from "path";
2423
2859
  import chalk6 from "chalk";
2424
2860
  function labelFor(width) {
2425
2861
  if (width <= 480) return `${width} mobile`;
@@ -2472,30 +2908,30 @@ async function previewBlock(name, options = {}) {
2472
2908
  process.exitCode = 1;
2473
2909
  return;
2474
2910
  }
2475
- const blockDir = path13.join(projectRoot, "blocks", name);
2476
- if (!existsSync10(blockDir)) {
2911
+ const blockDir = path16.join(projectRoot, "blocks", name);
2912
+ if (!existsSync13(blockDir)) {
2477
2913
  logger.error(`Block "${name}" not found at blocks/${name}/.`);
2478
2914
  process.exitCode = 1;
2479
2915
  return;
2480
2916
  }
2481
- if (!existsSync10(path13.join(blockDir, `${name}.css`)) && !existsSync10(path13.join(blockDir, `${name}.js`))) {
2917
+ if (!existsSync13(path16.join(blockDir, `${name}.css`)) && !existsSync13(path16.join(blockDir, `${name}.js`))) {
2482
2918
  logger.error(`Block "${name}" has no ${name}.css / ${name}.js to preview.`);
2483
2919
  process.exitCode = 1;
2484
2920
  return;
2485
2921
  }
2486
2922
  let meta = null;
2487
- const metaPath = path13.join(blockDir, ".eds-meta.json");
2488
- if (existsSync10(metaPath)) {
2923
+ const metaPath = path16.join(blockDir, ".eds-meta.json");
2924
+ if (existsSync13(metaPath)) {
2489
2925
  try {
2490
- meta = JSON.parse(await readFile7(metaPath, "utf-8"));
2926
+ meta = JSON.parse(await readFile9(metaPath, "utf-8"));
2491
2927
  } catch {
2492
2928
  }
2493
2929
  }
2494
- const samplePath = path13.join(blockDir, `_${name}.preview.html`);
2930
+ const samplePath = path16.join(blockDir, `_${name}.preview.html`);
2495
2931
  let sampleContent;
2496
2932
  let placeholder = false;
2497
- if (existsSync10(samplePath)) {
2498
- sampleContent = await readFile7(samplePath, "utf-8");
2933
+ if (existsSync13(samplePath)) {
2934
+ sampleContent = await readFile9(samplePath, "utf-8");
2499
2935
  } else {
2500
2936
  sampleContent = defaultSample(name);
2501
2937
  placeholder = true;
@@ -2576,9 +3012,9 @@ var from_design_exports = {};
2576
3012
  __export(from_design_exports, {
2577
3013
  blockFromDesign: () => blockFromDesign
2578
3014
  });
2579
- import { existsSync as existsSync11 } from "fs";
2580
- import { readFile as readFile8, writeFile as writeFile7 } from "fs/promises";
2581
- import path14 from "path";
3015
+ import { existsSync as existsSync14 } from "fs";
3016
+ import { readFile as readFile10, writeFile as writeFile8 } from "fs/promises";
3017
+ import path17 from "path";
2582
3018
  import chalk7 from "chalk";
2583
3019
  import ora from "ora";
2584
3020
  async function previewRunning(port = 8777) {
@@ -2759,7 +3195,7 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
2759
3195
  screenshot: options.screenshot !== false
2760
3196
  });
2761
3197
  const promptFile = await savePromptToFile(prompt, projectRoot);
2762
- const hasTokens = existsSync11(path14.join(projectRoot, "styles", "styles.css"));
3198
+ const hasTokens = existsSync14(path17.join(projectRoot, "styles", "styles.css"));
2763
3199
  promptSpinner.succeed(
2764
3200
  hasTokens ? `Loaded design tokens ${chalk7.dim("styles/styles.css")}` : `Built prompt ${chalk7.dim("no project tokens found")}`
2765
3201
  );
@@ -2819,9 +3255,9 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
2819
3255
  process.exitCode = 1;
2820
3256
  return;
2821
3257
  }
2822
- const blockDir = path14.join(projectRoot, "blocks", blockName);
3258
+ const blockDir = path17.join(projectRoot, "blocks", blockName);
2823
3259
  const ok = await verifyGeneratedBlock(blockDir, blockName, options.ueModel !== false);
2824
- if (existsSync11(blockDir)) {
3260
+ if (existsSync14(blockDir)) {
2825
3261
  const meta = {
2826
3262
  provider,
2827
3263
  designRef,
@@ -2831,7 +3267,7 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
2831
3267
  promptVersion: PROMPT_VERSION,
2832
3268
  agentUsed: agent.type
2833
3269
  };
2834
- await writeFile7(path14.join(blockDir, ".eds-meta.json"), `${JSON.stringify(meta, null, 2)}
3270
+ await writeFile8(path17.join(blockDir, ".eds-meta.json"), `${JSON.stringify(meta, null, 2)}
2835
3271
  `);
2836
3272
  }
2837
3273
  if (!ok) {
@@ -2860,10 +3296,10 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
2860
3296
  }
2861
3297
  async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
2862
3298
  const checks = [];
2863
- const jsPath = path14.join(blockDir, `${blockName}.js`);
2864
- const cssPath = path14.join(blockDir, `${blockName}.css`);
2865
- const hasJs = existsSync11(jsPath);
2866
- const hasCss = existsSync11(cssPath);
3299
+ const jsPath = path17.join(blockDir, `${blockName}.js`);
3300
+ const cssPath = path17.join(blockDir, `${blockName}.css`);
3301
+ const hasJs = existsSync14(jsPath);
3302
+ const hasCss = existsSync14(cssPath);
2867
3303
  checks.push({
2868
3304
  status: hasJs ? "pass" : "fail",
2869
3305
  name: `${blockName}.js`,
@@ -2875,7 +3311,7 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
2875
3311
  message: hasCss ? "created" : "missing \u2014 agent did not write the CSS file"
2876
3312
  });
2877
3313
  if (hasJs) {
2878
- const js = await readFile8(jsPath, "utf-8");
3314
+ const js = await readFile10(jsPath, "utf-8");
2879
3315
  checks.push({
2880
3316
  status: /export\s+default/.test(js) ? "pass" : "fail",
2881
3317
  name: "decorate export",
@@ -2890,7 +3326,7 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
2890
3326
  }
2891
3327
  }
2892
3328
  if (hasCss) {
2893
- const css = await readFile8(cssPath, "utf-8");
3329
+ const css = await readFile10(cssPath, "utf-8");
2894
3330
  checks.push({
2895
3331
  status: css.includes(`.${blockName}`) ? "pass" : "warn",
2896
3332
  name: "scoped CSS",
@@ -2898,12 +3334,12 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
2898
3334
  });
2899
3335
  }
2900
3336
  if (withUeModel) {
2901
- const hasPerBlock = existsSync11(path14.join(blockDir, `_${blockName}.json`));
2902
- const modelsFile = path14.join(blockDir, "..", "..", "component-models.json");
3337
+ const hasPerBlock = existsSync14(path17.join(blockDir, `_${blockName}.json`));
3338
+ const modelsFile = path17.join(blockDir, "..", "..", "component-models.json");
2903
3339
  let hasAggregated = false;
2904
- if (!hasPerBlock && existsSync11(modelsFile)) {
3340
+ if (!hasPerBlock && existsSync14(modelsFile)) {
2905
3341
  try {
2906
- hasAggregated = (await readFile8(modelsFile, "utf-8")).includes(`"${blockName}"`);
3342
+ hasAggregated = (await readFile10(modelsFile, "utf-8")).includes(`"${blockName}"`);
2907
3343
  } catch {
2908
3344
  }
2909
3345
  }
@@ -2915,8 +3351,8 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
2915
3351
  });
2916
3352
  if (hasPerBlock && hasJs) {
2917
3353
  try {
2918
- const modelRaw = await readFile8(path14.join(blockDir, `_${blockName}.json`), "utf-8");
2919
- const js = await readFile8(jsPath, "utf-8");
3354
+ const modelRaw = await readFile10(path17.join(blockDir, `_${blockName}.json`), "utf-8");
3355
+ const js = await readFile10(jsPath, "utf-8");
2920
3356
  const isRepeatable = /"resourceType"\s*:\s*"[^"]*\/item"/.test(modelRaw) || /"filter"\s*:/.test(modelRaw) || /"multi"\s*:\s*true/.test(modelRaw);
2921
3357
  const iterates = /\.children/.test(js) || /\bfor\s*\(/.test(js) || /\.forEach\s*\(/.test(js);
2922
3358
  if (isRepeatable) {
@@ -2959,9 +3395,9 @@ var list_exports = {};
2959
3395
  __export(list_exports, {
2960
3396
  listBlocks: () => listBlocks
2961
3397
  });
2962
- import { existsSync as existsSync12 } from "fs";
2963
- import { readFile as readFile9, readdir } from "fs/promises";
2964
- import path15 from "path";
3398
+ import { existsSync as existsSync15 } from "fs";
3399
+ import { readFile as readFile11, readdir as readdir2 } from "fs/promises";
3400
+ import path18 from "path";
2965
3401
  import chalk8 from "chalk";
2966
3402
  async function listBlocks(options = {}) {
2967
3403
  const projectRoot = findProjectRoot();
@@ -2970,27 +3406,27 @@ async function listBlocks(options = {}) {
2970
3406
  process.exitCode = 1;
2971
3407
  return;
2972
3408
  }
2973
- const blocksDir = path15.join(projectRoot, "blocks");
2974
- if (!existsSync12(blocksDir)) {
3409
+ const blocksDir = path18.join(projectRoot, "blocks");
3410
+ if (!existsSync15(blocksDir)) {
2975
3411
  logger.info("No blocks/ directory found.");
2976
3412
  return;
2977
3413
  }
2978
- const entries = await readdir(blocksDir, { withFileTypes: true });
3414
+ const entries = await readdir2(blocksDir, { withFileTypes: true });
2979
3415
  const blocks = [];
2980
3416
  for (const entry of entries) {
2981
3417
  if (!entry.isDirectory()) continue;
2982
3418
  const name = entry.name;
2983
- const blockDir = path15.join(blocksDir, name);
3419
+ const blockDir = path18.join(blocksDir, name);
2984
3420
  const info = {
2985
3421
  name,
2986
- hasJs: existsSync12(path15.join(blockDir, `${name}.js`)),
2987
- hasCss: existsSync12(path15.join(blockDir, `${name}.css`)),
2988
- hasUeModel: existsSync12(path15.join(blockDir, `_${name}.json`))
3422
+ hasJs: existsSync15(path18.join(blockDir, `${name}.js`)),
3423
+ hasCss: existsSync15(path18.join(blockDir, `${name}.css`)),
3424
+ hasUeModel: existsSync15(path18.join(blockDir, `_${name}.json`))
2989
3425
  };
2990
- const metaFile = path15.join(blockDir, ".eds-meta.json");
2991
- if (existsSync12(metaFile)) {
3426
+ const metaFile = path18.join(blockDir, ".eds-meta.json");
3427
+ if (existsSync15(metaFile)) {
2992
3428
  try {
2993
- const meta = JSON.parse(await readFile9(metaFile, "utf-8"));
3429
+ const meta = JSON.parse(await readFile11(metaFile, "utf-8"));
2994
3430
  info.figmaSource = {
2995
3431
  fileKey: meta.figmaFileKey,
2996
3432
  nodeId: meta.figmaNodeId,
@@ -3140,9 +3576,9 @@ var integrate_exports = {};
3140
3576
  __export(integrate_exports, {
3141
3577
  integrate: () => integrate
3142
3578
  });
3143
- import { existsSync as existsSync13 } from "fs";
3144
- import { mkdir as mkdir5, readFile as readFile10, writeFile as writeFile8 } from "fs/promises";
3145
- import path16 from "path";
3579
+ import { existsSync as existsSync16 } from "fs";
3580
+ import { mkdir as mkdir6, readFile as readFile12, writeFile as writeFile9 } from "fs/promises";
3581
+ import path19 from "path";
3146
3582
  import chalk9 from "chalk";
3147
3583
  async function integrate(type) {
3148
3584
  const projectRoot = findProjectRoot();
@@ -3171,10 +3607,10 @@ async function integrate(type) {
3171
3607
  values[f.name] = answer.trim();
3172
3608
  }
3173
3609
  const block2 = wrapBlock(integration.name, integration.code(values));
3174
- const delayedPath = path16.join(projectRoot, "scripts", "delayed.js");
3175
- await mkdir5(path16.dirname(delayedPath), { recursive: true });
3176
- const existing = existsSync13(delayedPath) ? await readFile10(delayedPath, "utf-8") : DELAYED_HEADER;
3177
- await writeFile8(delayedPath, `${existing.replace(/\s*$/, "")}
3610
+ const delayedPath = path19.join(projectRoot, "scripts", "delayed.js");
3611
+ await mkdir6(path19.dirname(delayedPath), { recursive: true });
3612
+ const existing = existsSync16(delayedPath) ? await readFile12(delayedPath, "utf-8") : DELAYED_HEADER;
3613
+ await writeFile9(delayedPath, `${existing.replace(/\s*$/, "")}
3178
3614
  ${block2}`);
3179
3615
  logger.info(heading("Added"));
3180
3616
  logger.info(accentLine(integration.name, "\u2192 scripts/delayed.js"));
@@ -3215,8 +3651,8 @@ var setup_exports = {};
3215
3651
  __export(setup_exports, {
3216
3652
  figmaSetup: () => figmaSetup
3217
3653
  });
3218
- import { existsSync as existsSync14 } from "fs";
3219
- import { mkdir as mkdir6, writeFile as writeFile9 } from "fs/promises";
3654
+ import { existsSync as existsSync17 } from "fs";
3655
+ import { mkdir as mkdir7, writeFile as writeFile10 } from "fs/promises";
3220
3656
  import chalk10 from "chalk";
3221
3657
  async function figmaSetup() {
3222
3658
  const { select, confirm } = await import("@inquirer/prompts");
@@ -3296,14 +3732,14 @@ async function figmaSetup() {
3296
3732
  });
3297
3733
  if (shouldSave) {
3298
3734
  const configDir = globalConfigDir();
3299
- if (!existsSync14(configDir)) {
3300
- await mkdir6(configDir, { recursive: true });
3735
+ if (!existsSync17(configDir)) {
3736
+ await mkdir7(configDir, { recursive: true });
3301
3737
  }
3302
3738
  const config = {};
3303
3739
  if (selectedAgent !== "none") {
3304
3740
  config.agent = selectedAgent;
3305
3741
  }
3306
- await writeFile9(globalConfigPath(), `${JSON.stringify(config, null, 2)}
3742
+ await writeFile10(globalConfigPath(), `${JSON.stringify(config, null, 2)}
3307
3743
  `, {
3308
3744
  mode: 384
3309
3745
  });
@@ -3330,9 +3766,9 @@ var doctor_exports = {};
3330
3766
  __export(doctor_exports, {
3331
3767
  doctor: () => doctor
3332
3768
  });
3333
- import { existsSync as existsSync15 } from "fs";
3334
- import { readFile as readFile11, readdir as readdir2 } from "fs/promises";
3335
- import path17 from "path";
3769
+ import { existsSync as existsSync18 } from "fs";
3770
+ import { readFile as readFile13, readdir as readdir3 } from "fs/promises";
3771
+ import path20 from "path";
3336
3772
  import { parse as parseYaml } from "yaml";
3337
3773
  async function doctor(options = {}) {
3338
3774
  const projectRoot = findProjectRoot();
@@ -3354,7 +3790,7 @@ async function doctor(options = {}) {
3354
3790
  });
3355
3791
  if (project.hasFstab) {
3356
3792
  try {
3357
- const content = await readFile11(path17.join(projectRoot, "fstab.yaml"), "utf-8");
3793
+ const content = await readFile13(path20.join(projectRoot, "fstab.yaml"), "utf-8");
3358
3794
  parseYaml(content);
3359
3795
  results.push({ name: "fstab.yaml valid YAML", status: "pass", message: "Valid" });
3360
3796
  } catch {
@@ -3376,28 +3812,28 @@ async function doctor(options = {}) {
3376
3812
  message: project.hasBlocks ? "Found" : "Missing - no blocks yet"
3377
3813
  });
3378
3814
  if (project.hasBlocks) {
3379
- const blocksDir = path17.join(projectRoot, "blocks");
3380
- const entries = await readdir2(blocksDir, { withFileTypes: true });
3815
+ const blocksDir = path20.join(projectRoot, "blocks");
3816
+ const entries = await readdir3(blocksDir, { withFileTypes: true });
3381
3817
  let aggregatedModels = "";
3382
- const aggregatedPath = path17.join(projectRoot, "component-models.json");
3383
- if (existsSync15(aggregatedPath)) {
3818
+ const aggregatedPath = path20.join(projectRoot, "component-models.json");
3819
+ if (existsSync18(aggregatedPath)) {
3384
3820
  try {
3385
- aggregatedModels = await readFile11(aggregatedPath, "utf-8");
3821
+ aggregatedModels = await readFile13(aggregatedPath, "utf-8");
3386
3822
  } catch {
3387
3823
  }
3388
3824
  }
3389
3825
  for (const entry of entries) {
3390
3826
  if (!entry.isDirectory()) continue;
3391
3827
  const blockName = entry.name;
3392
- const jsFile = path17.join(blocksDir, blockName, `${blockName}.js`);
3393
- const hasJs = existsSync15(jsFile);
3828
+ const jsFile = path20.join(blocksDir, blockName, `${blockName}.js`);
3829
+ const hasJs = existsSync18(jsFile);
3394
3830
  results.push({
3395
3831
  name: `blocks/${blockName} has JS`,
3396
3832
  status: hasJs ? "pass" : "warn",
3397
3833
  message: hasJs ? "Found" : `Missing ${blockName}.js`
3398
3834
  });
3399
3835
  if (hasJs) {
3400
- const jsContent = await readFile11(jsFile, "utf-8");
3836
+ const jsContent = await readFile13(jsFile, "utf-8");
3401
3837
  if (jsContent.includes("console.log")) {
3402
3838
  results.push({
3403
3839
  name: `blocks/${blockName} no console.log`,
@@ -3406,17 +3842,17 @@ async function doctor(options = {}) {
3406
3842
  });
3407
3843
  }
3408
3844
  }
3409
- const hasPerBlockModel = existsSync15(path17.join(blocksDir, blockName, `_${blockName}.json`));
3845
+ const hasPerBlockModel = existsSync18(path20.join(blocksDir, blockName, `_${blockName}.json`));
3410
3846
  const hasAggregatedModel = aggregatedModels.includes(`"${blockName}"`);
3411
3847
  results.push({
3412
3848
  name: `blocks/${blockName} UE model`,
3413
3849
  status: hasPerBlockModel || hasAggregatedModel ? "pass" : "warn",
3414
3850
  message: hasPerBlockModel || hasAggregatedModel ? hasPerBlockModel ? `_${blockName}.json` : "in component-models.json" : "no Universal Editor model - authors can't edit this block in UE"
3415
3851
  });
3416
- const metaFile = path17.join(blocksDir, blockName, ".eds-meta.json");
3417
- if (existsSync15(metaFile)) {
3852
+ const metaFile = path20.join(blocksDir, blockName, ".eds-meta.json");
3853
+ if (existsSync18(metaFile)) {
3418
3854
  try {
3419
- const meta = JSON.parse(await readFile11(metaFile, "utf-8"));
3855
+ const meta = JSON.parse(await readFile13(metaFile, "utf-8"));
3420
3856
  const syncDate = new Date(meta.lastSyncedAt);
3421
3857
  const daysSinceSync = Math.floor(
3422
3858
  (Date.now() - syncDate.getTime()) / (1e3 * 60 * 60 * 24)
@@ -3436,10 +3872,10 @@ async function doctor(options = {}) {
3436
3872
  }
3437
3873
  }
3438
3874
  }
3439
- const queryFile = path17.join(projectRoot, "helix-query.yaml");
3440
- if (existsSync15(queryFile)) {
3875
+ const queryFile = path20.join(projectRoot, "helix-query.yaml");
3876
+ if (existsSync18(queryFile)) {
3441
3877
  try {
3442
- const content = await readFile11(queryFile, "utf-8");
3878
+ const content = await readFile13(queryFile, "utf-8");
3443
3879
  parseYaml(content);
3444
3880
  results.push({ name: "helix-query.yaml valid", status: "pass", message: "Valid YAML" });
3445
3881
  } catch {
@@ -3496,9 +3932,9 @@ var audit_loading_exports = {};
3496
3932
  __export(audit_loading_exports, {
3497
3933
  auditLoading: () => auditLoading
3498
3934
  });
3499
- import { existsSync as existsSync16 } from "fs";
3500
- import { readFile as readFile12, readdir as readdir3, stat as stat3 } from "fs/promises";
3501
- import path18 from "path";
3935
+ import { existsSync as existsSync19 } from "fs";
3936
+ import { readFile as readFile14, readdir as readdir4, stat as stat3 } from "fs/promises";
3937
+ import path21 from "path";
3502
3938
  import chalk11 from "chalk";
3503
3939
  async function auditLoading(options = {}) {
3504
3940
  const projectRoot = findProjectRoot();
@@ -3532,8 +3968,8 @@ async function auditLoading(options = {}) {
3532
3968
  printResults(results);
3533
3969
  }
3534
3970
  async function checkHeadHtml(root, results) {
3535
- const filePath = path18.join(root, "head.html");
3536
- if (!existsSync16(filePath)) {
3971
+ const filePath = path21.join(root, "head.html");
3972
+ if (!existsSync19(filePath)) {
3537
3973
  results.push({
3538
3974
  rule: "head.html exists",
3539
3975
  status: "warn",
@@ -3541,7 +3977,7 @@ async function checkHeadHtml(root, results) {
3541
3977
  });
3542
3978
  return;
3543
3979
  }
3544
- const content = await readFile12(filePath, "utf-8");
3980
+ const content = await readFile14(filePath, "utf-8");
3545
3981
  const lines = content.split("\n");
3546
3982
  for (let i = 0; i < lines.length; i++) {
3547
3983
  const line = lines[i];
@@ -3613,8 +4049,8 @@ async function checkHeadHtml(root, results) {
3613
4049
  }
3614
4050
  }
3615
4051
  async function checkScriptsJs(root, results) {
3616
- const filePath = path18.join(root, "scripts", "scripts.js");
3617
- if (!existsSync16(filePath)) {
4052
+ const filePath = path21.join(root, "scripts", "scripts.js");
4053
+ if (!existsSync19(filePath)) {
3618
4054
  results.push({
3619
4055
  rule: "scripts/scripts.js exists",
3620
4056
  status: "warn",
@@ -3622,7 +4058,7 @@ async function checkScriptsJs(root, results) {
3622
4058
  });
3623
4059
  return;
3624
4060
  }
3625
- const content = await readFile12(filePath, "utf-8");
4061
+ const content = await readFile14(filePath, "utf-8");
3626
4062
  const lines = content.split("\n");
3627
4063
  const hasLoadEager = /function\s+loadEager|loadEager\s*=/i.test(content);
3628
4064
  const hasLoadLazy = /function\s+loadLazy|loadLazy\s*=/i.test(content);
@@ -3700,8 +4136,8 @@ async function checkScriptsJs(root, results) {
3700
4136
  }
3701
4137
  }
3702
4138
  async function checkStylesCss(root, results) {
3703
- const filePath = path18.join(root, "styles", "styles.css");
3704
- if (!existsSync16(filePath)) return;
4139
+ const filePath = path21.join(root, "styles", "styles.css");
4140
+ if (!existsSync19(filePath)) return;
3705
4141
  const stats = await stat3(filePath);
3706
4142
  const sizeKb = stats.size / 1024;
3707
4143
  results.push({
@@ -3710,7 +4146,7 @@ async function checkStylesCss(root, results) {
3710
4146
  message: `${sizeKb.toFixed(1)} KB${sizeKb > 40 ? " \u2014 large CSS competes with 100KB LCP budget" : ""}`,
3711
4147
  file: "styles/styles.css"
3712
4148
  });
3713
- const content = await readFile12(filePath, "utf-8");
4149
+ const content = await readFile14(filePath, "utf-8");
3714
4150
  if (/@import\s/.test(content)) {
3715
4151
  results.push({
3716
4152
  rule: "No @import in styles.css",
@@ -3721,8 +4157,8 @@ async function checkStylesCss(root, results) {
3721
4157
  }
3722
4158
  }
3723
4159
  async function checkDelayedJs(root, results) {
3724
- const filePath = path18.join(root, "scripts", "delayed.js");
3725
- if (!existsSync16(filePath)) {
4160
+ const filePath = path21.join(root, "scripts", "delayed.js");
4161
+ if (!existsSync19(filePath)) {
3726
4162
  results.push({
3727
4163
  rule: "scripts/delayed.js exists",
3728
4164
  status: "info",
@@ -3730,7 +4166,7 @@ async function checkDelayedJs(root, results) {
3730
4166
  });
3731
4167
  return;
3732
4168
  }
3733
- const content = await readFile12(filePath, "utf-8");
4169
+ const content = await readFile14(filePath, "utf-8");
3734
4170
  const stripped = content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").trim();
3735
4171
  if (!stripped) {
3736
4172
  results.push({
@@ -3742,15 +4178,15 @@ async function checkDelayedJs(root, results) {
3742
4178
  }
3743
4179
  }
3744
4180
  async function checkBlocks(root, results) {
3745
- const blocksDir = path18.join(root, "blocks");
3746
- if (!existsSync16(blocksDir)) return;
3747
- const entries = await readdir3(blocksDir, { withFileTypes: true });
4181
+ const blocksDir = path21.join(root, "blocks");
4182
+ if (!existsSync19(blocksDir)) return;
4183
+ const entries = await readdir4(blocksDir, { withFileTypes: true });
3748
4184
  for (const entry of entries) {
3749
4185
  if (!entry.isDirectory()) continue;
3750
4186
  const blockName = entry.name;
3751
- const jsFile = path18.join(blocksDir, blockName, `${blockName}.js`);
3752
- if (!existsSync16(jsFile)) continue;
3753
- const content = await readFile12(jsFile, "utf-8");
4187
+ const jsFile = path21.join(blocksDir, blockName, `${blockName}.js`);
4188
+ if (!existsSync19(jsFile)) continue;
4189
+ const content = await readFile14(jsFile, "utf-8");
3754
4190
  const lines = content.split("\n");
3755
4191
  const heavyLibs = [
3756
4192
  "jquery",
@@ -3800,9 +4236,9 @@ async function checkBlocks(root, results) {
3800
4236
  for (const entry of entries) {
3801
4237
  if (!entry.isDirectory()) continue;
3802
4238
  const blockName = entry.name;
3803
- const cssFile = path18.join(blocksDir, blockName, `${blockName}.css`);
3804
- if (!existsSync16(cssFile)) continue;
3805
- const content = await readFile12(cssFile, "utf-8");
4239
+ const cssFile = path21.join(blocksDir, blockName, `${blockName}.css`);
4240
+ if (!existsSync19(cssFile)) continue;
4241
+ const content = await readFile14(cssFile, "utf-8");
3806
4242
  const selectorPattern = /^([a-z][a-z0-9-]*)\s*\{/gm;
3807
4243
  for (const selectorMatch of content.matchAll(selectorPattern)) {
3808
4244
  const selector = selectorMatch[1];
@@ -3818,8 +4254,8 @@ async function checkBlocks(root, results) {
3818
4254
  }
3819
4255
  }
3820
4256
  async function checkAemJs(root, results) {
3821
- const filePath = path18.join(root, "scripts", "aem.js");
3822
- if (!existsSync16(filePath)) {
4257
+ const filePath = path21.join(root, "scripts", "aem.js");
4258
+ if (!existsSync19(filePath)) {
3823
4259
  results.push({
3824
4260
  rule: "scripts/aem.js exists",
3825
4261
  status: "warn",
@@ -3891,17 +4327,17 @@ var audit_security_exports = {};
3891
4327
  __export(audit_security_exports, {
3892
4328
  auditSecurity: () => auditSecurity
3893
4329
  });
3894
- import { existsSync as existsSync17 } from "fs";
3895
- import { readFile as readFile13, readdir as readdir4 } from "fs/promises";
3896
- import path19 from "path";
4330
+ import { existsSync as existsSync20 } from "fs";
4331
+ import { readFile as readFile15, readdir as readdir5 } from "fs/promises";
4332
+ import path22 from "path";
3897
4333
  import chalk12 from "chalk";
3898
- async function* walk(dir) {
3899
- const entries = await readdir4(dir, { withFileTypes: true });
4334
+ async function* walk2(dir) {
4335
+ const entries = await readdir5(dir, { withFileTypes: true });
3900
4336
  for (const entry of entries) {
3901
- const full = path19.join(dir, entry.name);
4337
+ const full = path22.join(dir, entry.name);
3902
4338
  if (entry.isDirectory()) {
3903
4339
  if (IGNORE_DIRS.has(entry.name)) continue;
3904
- yield* walk(full);
4340
+ yield* walk2(full);
3905
4341
  } else if (entry.isFile()) {
3906
4342
  yield full;
3907
4343
  }
@@ -3932,11 +4368,11 @@ function scanContent(content, relFile, ext) {
3932
4368
  return findings;
3933
4369
  }
3934
4370
  async function auditDependencies(root) {
3935
- if (!existsSync17(path19.join(root, "package.json"))) {
4371
+ if (!existsSync20(path22.join(root, "package.json"))) {
3936
4372
  return { status: "skipped", note: "no package.json" };
3937
4373
  }
3938
- const hasLock = existsSync17(path19.join(root, "package-lock.json")) || existsSync17(path19.join(root, "npm-shrinkwrap.json"));
3939
- if (!hasLock && !existsSync17(path19.join(root, "node_modules"))) {
4374
+ const hasLock = existsSync20(path22.join(root, "package-lock.json")) || existsSync20(path22.join(root, "npm-shrinkwrap.json"));
4375
+ if (!hasLock && !existsSync20(path22.join(root, "node_modules"))) {
3940
4376
  return { status: "skipped", note: "no package-lock.json / node_modules \u2014 run npm install" };
3941
4377
  }
3942
4378
  try {
@@ -3967,12 +4403,12 @@ async function auditSecurity(options = {}) {
3967
4403
  return;
3968
4404
  }
3969
4405
  const findings = [];
3970
- for await (const file of walk(projectRoot)) {
3971
- const ext = path19.extname(file).slice(1).toLowerCase();
4406
+ for await (const file of walk2(projectRoot)) {
4407
+ const ext = path22.extname(file).slice(1).toLowerCase();
3972
4408
  if (!SCAN_EXT.has(ext)) continue;
3973
- const rel = path19.relative(projectRoot, file);
4409
+ const rel = path22.relative(projectRoot, file);
3974
4410
  if (rel.endsWith("aem.js") || rel.endsWith("lib-franklin.js")) continue;
3975
- const content = await readFile13(file, "utf-8");
4411
+ const content = await readFile15(file, "utf-8");
3976
4412
  findings.push(...scanContent(content, rel, ext));
3977
4413
  }
3978
4414
  const deps = await auditDependencies(projectRoot);
@@ -4390,13 +4826,13 @@ var menu_exports = {};
4390
4826
  __export(menu_exports, {
4391
4827
  mainMenu: () => mainMenu
4392
4828
  });
4393
- import { existsSync as existsSync18 } from "fs";
4394
- import { readdir as readdir5 } from "fs/promises";
4395
- import path20 from "path";
4829
+ import { existsSync as existsSync21 } from "fs";
4830
+ import { readdir as readdir6 } from "fs/promises";
4831
+ import path23 from "path";
4396
4832
  async function listBlockNames(projectRoot) {
4397
- const dir = path20.join(projectRoot, "blocks");
4398
- if (!existsSync18(dir)) return [];
4399
- const entries = await readdir5(dir, { withFileTypes: true });
4833
+ const dir = path23.join(projectRoot, "blocks");
4834
+ if (!existsSync21(dir)) return [];
4835
+ const entries = await readdir6(dir, { withFileTypes: true });
4400
4836
  return entries.filter((e) => e.isDirectory()).map((e) => e.name);
4401
4837
  }
4402
4838
  async function mainMenu() {
@@ -4407,6 +4843,7 @@ async function mainMenu() {
4407
4843
  choices: [
4408
4844
  { name: "Set up this project (authoring model, CI, naming)", value: "init" },
4409
4845
  { name: "Generate a block from a design (Figma/Stitch/Canva/Sketch)", value: "from-design" },
4846
+ { name: "Migrate a classic AEM component to EDS", value: "migrate" },
4410
4847
  { name: "Scaffold Universal Editor components", value: "scaffold" },
4411
4848
  { name: "Create an empty block", value: "create" },
4412
4849
  { name: "Add a third-party integration (GTM, chat, consent\u2026)", value: "integrate" },
@@ -4427,6 +4864,11 @@ async function mainMenu() {
4427
4864
  await blockFromDesign2(void 0, {});
4428
4865
  return;
4429
4866
  }
4867
+ case "migrate": {
4868
+ const { migrateComponent: migrateComponent2 } = await Promise.resolve().then(() => (init_migrate(), migrate_exports));
4869
+ await migrateComponent2(void 0, {});
4870
+ return;
4871
+ }
4430
4872
  case "scaffold": {
4431
4873
  const { scaffoldInteractive: scaffoldInteractive2 } = await Promise.resolve().then(() => (init_scaffold(), scaffold_exports));
4432
4874
  await scaffoldInteractive2();
@@ -4516,7 +4958,7 @@ import { Command } from "commander";
4516
4958
  // package.json
4517
4959
  var package_default = {
4518
4960
  name: "aem-eds-kit",
4519
- version: "0.4.7",
4961
+ version: "0.4.9",
4520
4962
  description: "CLI for AEM Edge Delivery Services with Figma MCP integration",
4521
4963
  type: "module",
4522
4964
  engines: {
@@ -4566,6 +5008,7 @@ var package_default = {
4566
5008
  chalk: "^5.4.0",
4567
5009
  commander: "^13.0.0",
4568
5010
  execa: "^9.5.0",
5011
+ "fast-xml-parser": "^5.11.2",
4569
5012
  ora: "^8.1.0",
4570
5013
  yaml: "^2.6.0",
4571
5014
  zod: "^3.24.0"
@@ -4598,6 +5041,11 @@ program.command("init").description("Set up a freshly cloned EDS boilerplate for
4598
5041
  const { initProject: initProject2 } = await Promise.resolve().then(() => (init_init(), init_exports));
4599
5042
  await initProject2(options);
4600
5043
  });
5044
+ var migrate = program.command("migrate").description("Migrate from classic AEM to EDS");
5045
+ migrate.command("component [dir]").description("Migrate a classic AEM component's dialog into an EDS block + UE model").option("--name <name>", "Block name (otherwise inferred from the component)").option("--clientlib <dir>", "Path to the component's clientlib (otherwise auto-detected)").option("--yes", "Skip confirmation prompts").action(async (dir, options) => {
5046
+ const { migrateComponent: migrateComponent2 } = await Promise.resolve().then(() => (init_migrate(), migrate_exports));
5047
+ await migrateComponent2(dir, options);
5048
+ });
4601
5049
  var block = program.command("block").description("Manage EDS blocks");
4602
5050
  block.command("create <name>").description("Scaffold a new EDS block (with Universal Editor model by default)").option("--no-ue-model", "Skip the Universal Editor model (generated by default)").action(async (name, options) => {
4603
5051
  const { createBlock: createBlock2 } = await Promise.resolve().then(() => (init_create(), create_exports));