aem-eds-kit 0.4.7 → 0.4.12
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/CHANGELOG.md +60 -1
- package/README.md +52 -4
- package/dist/bin/eds.js +964 -269
- package/dist/bin/eds.js.map +1 -1
- package/package.json +2 -1
package/dist/bin/eds.js
CHANGED
|
@@ -1424,16 +1424,582 @@ 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/lib/track.ts
|
|
1712
|
+
function trackSnippet(blockName, param, events) {
|
|
1713
|
+
const parts = [` // --- ${TRACK_MARKER} ---`];
|
|
1714
|
+
if (events.click) {
|
|
1715
|
+
parts.push(
|
|
1716
|
+
` ${param}.addEventListener('click', (ev) => {`,
|
|
1717
|
+
" const el = ev.target.closest('a, button');",
|
|
1718
|
+
" if (!el) return;",
|
|
1719
|
+
` trackEvent({ event: 'block_click', block: '${blockName}', label: el.dataset.track || el.textContent.trim() });`,
|
|
1720
|
+
" });"
|
|
1721
|
+
);
|
|
1722
|
+
}
|
|
1723
|
+
if (events.submit) {
|
|
1724
|
+
parts.push(
|
|
1725
|
+
` ${param}.addEventListener('submit', () => {`,
|
|
1726
|
+
` trackEvent({ event: 'form_submit', block: '${blockName}' });`,
|
|
1727
|
+
" });"
|
|
1728
|
+
);
|
|
1729
|
+
}
|
|
1730
|
+
return parts.join("\n");
|
|
1731
|
+
}
|
|
1732
|
+
function instrumentJs(js, blockName, events) {
|
|
1733
|
+
if (js.includes(TRACK_MARKER)) return { js, changed: false, reason: "already instrumented" };
|
|
1734
|
+
if (!events.click && !events.submit) return { js, changed: false, reason: "no events selected" };
|
|
1735
|
+
const decorate = js.match(
|
|
1736
|
+
/export default (?:async )?function decorate\s*\(\s*([A-Za-z_$][\w$]*)?[^)]*\)\s*\{/
|
|
1737
|
+
);
|
|
1738
|
+
if (!decorate || decorate.index === void 0) {
|
|
1739
|
+
return { js, changed: false, reason: "no `export default function decorate(...)` found" };
|
|
1740
|
+
}
|
|
1741
|
+
const param = decorate[1] || "block";
|
|
1742
|
+
let out = js;
|
|
1743
|
+
if (!out.includes("scripts/analytics.js")) {
|
|
1744
|
+
const imports = [...out.matchAll(/^import .*$/gm)];
|
|
1745
|
+
if (imports.length) {
|
|
1746
|
+
const last = imports[imports.length - 1];
|
|
1747
|
+
const at2 = (last.index ?? 0) + last[0].length;
|
|
1748
|
+
out = `${out.slice(0, at2)}
|
|
1749
|
+
${IMPORT_LINE}${out.slice(at2)}`;
|
|
1750
|
+
} else {
|
|
1751
|
+
out = `${IMPORT_LINE}
|
|
1752
|
+
|
|
1753
|
+
${out}`;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
const d2 = out.match(
|
|
1757
|
+
/export default (?:async )?function decorate\s*\(\s*([A-Za-z_$][\w$]*)?[^)]*\)\s*\{/
|
|
1758
|
+
);
|
|
1759
|
+
if (!d2 || d2.index === void 0)
|
|
1760
|
+
return { js: out, changed: false, reason: "decorate not found" };
|
|
1761
|
+
const at = d2.index + d2[0].length;
|
|
1762
|
+
const snippet = trackSnippet(blockName, param, events);
|
|
1763
|
+
out = `${out.slice(0, at)}
|
|
1764
|
+
${snippet}
|
|
1765
|
+
${out.slice(at)}`;
|
|
1766
|
+
return { js: out, changed: true };
|
|
1767
|
+
}
|
|
1768
|
+
var TRACK_MARKER, IMPORT_LINE;
|
|
1769
|
+
var init_track = __esm({
|
|
1770
|
+
"src/lib/track.ts"() {
|
|
1771
|
+
"use strict";
|
|
1772
|
+
init_esm_shims();
|
|
1773
|
+
TRACK_MARKER = "analytics (eds track)";
|
|
1774
|
+
IMPORT_LINE = "import { trackEvent } from '../../scripts/analytics.js';";
|
|
1775
|
+
}
|
|
1776
|
+
});
|
|
1777
|
+
|
|
1778
|
+
// src/commands/track.ts
|
|
1779
|
+
var track_exports = {};
|
|
1780
|
+
__export(track_exports, {
|
|
1781
|
+
trackBlock: () => trackBlock
|
|
1782
|
+
});
|
|
1783
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1784
|
+
import { readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
|
|
1785
|
+
import path9 from "path";
|
|
1786
|
+
import chalk4 from "chalk";
|
|
1787
|
+
async function trackBlock(blockName, options = {}) {
|
|
1788
|
+
const projectRoot = findProjectRoot();
|
|
1789
|
+
if (!projectRoot) {
|
|
1790
|
+
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
1791
|
+
process.exitCode = 1;
|
|
1792
|
+
return;
|
|
1793
|
+
}
|
|
1794
|
+
logger.logoOnce(logo("Track block"));
|
|
1795
|
+
const jsPath = path9.join(projectRoot, "blocks", blockName, `${blockName}.js`);
|
|
1796
|
+
if (!existsSync7(jsPath)) {
|
|
1797
|
+
logger.error(`Block "${blockName}" not found (no blocks/${blockName}/${blockName}.js).`);
|
|
1798
|
+
process.exitCode = 1;
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
const events = { click: options.click !== false, submit: options.submit !== false };
|
|
1802
|
+
const res = instrumentJs(await readFile5(jsPath, "utf-8"), blockName, events);
|
|
1803
|
+
if (!res.changed) {
|
|
1804
|
+
logger.warn(`Not instrumented: ${res.reason}.`);
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
await writeFile5(jsPath, res.js);
|
|
1808
|
+
logger.info(heading("Instrumented"));
|
|
1809
|
+
const picked = [events.click ? "block_click" : null, events.submit ? "form_submit" : null].filter(
|
|
1810
|
+
Boolean
|
|
1811
|
+
);
|
|
1812
|
+
logger.info(accentLine(`blocks/${blockName}/${blockName}.js`, picked.join(" + ")));
|
|
1813
|
+
if (!existsSync7(path9.join(projectRoot, "scripts", "analytics.js"))) {
|
|
1814
|
+
logger.info("");
|
|
1815
|
+
logger.warn("scripts/analytics.js is missing - run `eds integrate gtm` to create it.");
|
|
1816
|
+
}
|
|
1817
|
+
logger.info("");
|
|
1818
|
+
logger.info(chalk4.dim(" Events push to window.dataLayer via trackEvent - GTM picks them up."));
|
|
1819
|
+
}
|
|
1820
|
+
var init_track2 = __esm({
|
|
1821
|
+
"src/commands/track.ts"() {
|
|
1822
|
+
"use strict";
|
|
1823
|
+
init_esm_shims();
|
|
1824
|
+
init_logger();
|
|
1825
|
+
init_project();
|
|
1826
|
+
init_track();
|
|
1827
|
+
init_ui();
|
|
1828
|
+
}
|
|
1829
|
+
});
|
|
1830
|
+
|
|
1831
|
+
// src/commands/migrate.ts
|
|
1832
|
+
var migrate_exports = {};
|
|
1833
|
+
__export(migrate_exports, {
|
|
1834
|
+
migrateComponent: () => migrateComponent
|
|
1835
|
+
});
|
|
1836
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1837
|
+
import { mkdir as mkdir3, writeFile as writeFile6 } from "fs/promises";
|
|
1838
|
+
import path10 from "path";
|
|
1839
|
+
function clientlibBlockCss(blockName, css, files) {
|
|
1840
|
+
return `/* Block: ${blockName} */
|
|
1841
|
+
/* Migrated from the component clientlib: ${files.join(", ")}. */
|
|
1842
|
+
/* TODO: review selectors and scope them under .${blockName} (and port any LESS/SCSS). */
|
|
1843
|
+
|
|
1844
|
+
${css.trim()}
|
|
1845
|
+
`;
|
|
1846
|
+
}
|
|
1847
|
+
function previewFields(fields, depth = 0) {
|
|
1848
|
+
const lines = [];
|
|
1849
|
+
for (const f of fields) {
|
|
1850
|
+
const indent = " ".repeat(depth);
|
|
1851
|
+
if (f.component === "tab") {
|
|
1852
|
+
lines.push(`${indent}${brand.blue("\u25B8")} ${f.label ?? f.name}`);
|
|
1853
|
+
} else {
|
|
1854
|
+
const opts = f.options?.length ? ` (${f.options.length} options)` : "";
|
|
1855
|
+
const multi = f.multi ? " \xB7multi" : "";
|
|
1856
|
+
const label = f.label ? `- ${f.label}` : "";
|
|
1857
|
+
lines.push(`${indent}${brand.accent(f.component)}${multi} ${f.name}${opts} ${label}`);
|
|
1858
|
+
if (f.fields?.length) lines.push(...previewFields(f.fields, depth + 1));
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
return lines;
|
|
1862
|
+
}
|
|
1863
|
+
function countLeaves(fields) {
|
|
1864
|
+
let n = 0;
|
|
1865
|
+
for (const f of fields) {
|
|
1866
|
+
if (f.component === "tab") continue;
|
|
1867
|
+
n += 1;
|
|
1868
|
+
if (f.fields) n += countLeaves(f.fields);
|
|
1869
|
+
}
|
|
1870
|
+
return n;
|
|
1871
|
+
}
|
|
1872
|
+
async function migrateComponent(dirArg, options = {}) {
|
|
1873
|
+
const projectRoot = findProjectRoot();
|
|
1874
|
+
if (!projectRoot) {
|
|
1875
|
+
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
1876
|
+
process.exitCode = 1;
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
await logger.logoOnceAnimated("Migrate component");
|
|
1880
|
+
const { input, confirm } = await import("@inquirer/prompts");
|
|
1881
|
+
const interactive = !options.yes;
|
|
1882
|
+
let componentDir = dirArg;
|
|
1883
|
+
if (!componentDir && interactive) {
|
|
1884
|
+
componentDir = await input({
|
|
1885
|
+
message: "Path to the classic component folder",
|
|
1886
|
+
validate: (v) => existsSync8(v.trim()) || "Folder not found"
|
|
1887
|
+
});
|
|
1888
|
+
}
|
|
1889
|
+
if (!componentDir) {
|
|
1890
|
+
logger.error("Provide the path to the classic component folder.");
|
|
1891
|
+
process.exitCode = 1;
|
|
1892
|
+
return;
|
|
1893
|
+
}
|
|
1894
|
+
componentDir = path10.resolve(componentDir.trim());
|
|
1895
|
+
if (!findDialogFile(componentDir)) {
|
|
1896
|
+
logger.error(`No Touch UI dialog (_cq_dialog/.content.xml) found in ${componentDir}.`);
|
|
1897
|
+
process.exitCode = 1;
|
|
1898
|
+
return;
|
|
1899
|
+
}
|
|
1900
|
+
const parsed = await readDialogFields(componentDir);
|
|
1901
|
+
if (!parsed || parsed.fields.length === 0) {
|
|
1902
|
+
logger.warn("Dialog found but no mappable fields were detected.");
|
|
1903
|
+
if (parsed?.unmapped.length)
|
|
1904
|
+
logger.info(`Unmapped resourceTypes: ${parsed.unmapped.join(", ")}`);
|
|
1905
|
+
process.exitCode = 1;
|
|
1906
|
+
return;
|
|
1907
|
+
}
|
|
1908
|
+
const title = await readComponentTitle(componentDir) ?? path10.basename(componentDir);
|
|
1909
|
+
const blockName = options.name && slug2(options.name) || (interactive ? slug2(await input({ message: "Block name", default: slug2(title) })) : slug2(title));
|
|
1910
|
+
logger.info(heading("Mapped fields", `${countLeaves(parsed.fields)} from dialog`));
|
|
1911
|
+
for (const line of previewFields(parsed.fields)) logger.info(` ${line}`);
|
|
1912
|
+
if (parsed.unmapped.length) {
|
|
1913
|
+
logger.info("");
|
|
1914
|
+
logger.warn(`Skipped (unrecognised): ${parsed.unmapped.join(", ")}`);
|
|
1915
|
+
}
|
|
1916
|
+
logger.info("");
|
|
1917
|
+
if (interactive) {
|
|
1918
|
+
const go = await confirm({ message: `Generate block "${blockName}"?`, default: true });
|
|
1919
|
+
if (!go) {
|
|
1920
|
+
logger.info("Cancelled.");
|
|
1921
|
+
return;
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
const blockDir = path10.join(projectRoot, "blocks", blockName);
|
|
1925
|
+
if (existsSync8(blockDir)) {
|
|
1926
|
+
logger.error(
|
|
1927
|
+
`Block "${blockName}" already exists at blocks/${blockName}. Use --name to pick another.`
|
|
1928
|
+
);
|
|
1929
|
+
process.exitCode = 1;
|
|
1930
|
+
return;
|
|
1931
|
+
}
|
|
1932
|
+
const clibDir = options.clientlib ? path10.resolve(options.clientlib.trim()) : await findClientlibDir(componentDir);
|
|
1933
|
+
let cssContent = migratedBlockCss(blockName);
|
|
1934
|
+
let cssMessage = "empty - add styles from clientlib / rendered CSS";
|
|
1935
|
+
let preprocessedNote = [];
|
|
1936
|
+
if (clibDir && existsSync8(clibDir)) {
|
|
1937
|
+
const lib = await readClientlibCss(clibDir);
|
|
1938
|
+
if (lib.css.trim()) {
|
|
1939
|
+
cssContent = clientlibBlockCss(blockName, lib.css, lib.files);
|
|
1940
|
+
cssMessage = `from clientlib (${lib.files.length} file(s)) - review & scope`;
|
|
1941
|
+
}
|
|
1942
|
+
preprocessedNote = lib.preprocessed;
|
|
1943
|
+
}
|
|
1944
|
+
await mkdir3(blockDir, { recursive: true });
|
|
1945
|
+
await writeFile6(
|
|
1946
|
+
path10.join(blockDir, `${blockName}.js`),
|
|
1947
|
+
migratedBlockJs(blockName, parsed.fields)
|
|
1948
|
+
);
|
|
1949
|
+
await writeFile6(path10.join(blockDir, `${blockName}.css`), cssContent);
|
|
1950
|
+
await writeFile6(
|
|
1951
|
+
path10.join(blockDir, `_${blockName}.json`),
|
|
1952
|
+
migratedModelFile(blockName, title, parsed.fields)
|
|
1953
|
+
);
|
|
1954
|
+
logger.info(heading("Created"));
|
|
1955
|
+
const files = [`${blockName}.js`, `${blockName}.css`, `_${blockName}.json`];
|
|
1956
|
+
const w = columnWidth(files);
|
|
1957
|
+
logger.info(accentLine(`${blockName}.js`, "decorator stub (port HTL logic)", w));
|
|
1958
|
+
logger.info(accentLine(`${blockName}.css`, cssMessage, w));
|
|
1959
|
+
logger.info(accentLine(`_${blockName}.json`, "Universal Editor model", w));
|
|
1960
|
+
if (preprocessedNote.length) {
|
|
1961
|
+
logger.info("");
|
|
1962
|
+
logger.warn(`LESS/SCSS found (needs a build, not copied): ${preprocessedNote.join(", ")}`);
|
|
1963
|
+
}
|
|
1964
|
+
logger.info("");
|
|
1965
|
+
if (interactive) {
|
|
1966
|
+
const addTracking = await confirm({
|
|
1967
|
+
message: "Instrument this block for analytics (dataLayer)?",
|
|
1968
|
+
default: false
|
|
1969
|
+
});
|
|
1970
|
+
if (addTracking) {
|
|
1971
|
+
const { trackBlock: trackBlock2 } = await Promise.resolve().then(() => (init_track2(), track_exports));
|
|
1972
|
+
await trackBlock2(blockName, {});
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
logger.info(
|
|
1976
|
+
box([`"${blockName}" migrated. Next: add CSS, then \`eds block preview ${blockName}\``])
|
|
1977
|
+
);
|
|
1978
|
+
}
|
|
1979
|
+
var slug2;
|
|
1980
|
+
var init_migrate = __esm({
|
|
1981
|
+
"src/commands/migrate.ts"() {
|
|
1982
|
+
"use strict";
|
|
1983
|
+
init_esm_shims();
|
|
1984
|
+
init_logger();
|
|
1985
|
+
init_clientlib();
|
|
1986
|
+
init_component();
|
|
1987
|
+
init_project();
|
|
1988
|
+
init_ui();
|
|
1989
|
+
slug2 = (s) => s.trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1990
|
+
}
|
|
1991
|
+
});
|
|
1992
|
+
|
|
1427
1993
|
// src/commands/block/create.ts
|
|
1428
1994
|
var create_exports = {};
|
|
1429
1995
|
__export(create_exports, {
|
|
1430
1996
|
createBlock: () => createBlock,
|
|
1431
1997
|
ueModelFor: () => ueModelFor
|
|
1432
1998
|
});
|
|
1433
|
-
import { existsSync as
|
|
1434
|
-
import { mkdir as
|
|
1435
|
-
import
|
|
1436
|
-
import
|
|
1999
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2000
|
+
import { mkdir as mkdir4, writeFile as writeFile7 } from "fs/promises";
|
|
2001
|
+
import path11 from "path";
|
|
2002
|
+
import chalk5 from "chalk";
|
|
1437
2003
|
function titleCase(name) {
|
|
1438
2004
|
return name.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
1439
2005
|
}
|
|
@@ -1511,13 +2077,13 @@ async function createBlock(name, options = {}) {
|
|
|
1511
2077
|
process.exitCode = 1;
|
|
1512
2078
|
return;
|
|
1513
2079
|
}
|
|
1514
|
-
const blocksDir =
|
|
1515
|
-
if (
|
|
2080
|
+
const blocksDir = path11.join(projectRoot, "blocks", name);
|
|
2081
|
+
if (existsSync9(blocksDir)) {
|
|
1516
2082
|
logger.error(`Block "${name}" already exists at ${blocksDir}`);
|
|
1517
2083
|
process.exitCode = 1;
|
|
1518
2084
|
return;
|
|
1519
2085
|
}
|
|
1520
|
-
await
|
|
2086
|
+
await mkdir4(blocksDir, { recursive: true });
|
|
1521
2087
|
const jsContent = `export default function decorate(block) {
|
|
1522
2088
|
// TODO: implement ${name} block
|
|
1523
2089
|
}
|
|
@@ -1526,14 +2092,14 @@ async function createBlock(name, options = {}) {
|
|
|
1526
2092
|
/* TODO: style ${name} block */
|
|
1527
2093
|
}
|
|
1528
2094
|
`;
|
|
1529
|
-
await
|
|
1530
|
-
await
|
|
2095
|
+
await writeFile7(path11.join(blocksDir, `${name}.js`), jsContent);
|
|
2096
|
+
await writeFile7(path11.join(blocksDir, `${name}.css`), cssContent);
|
|
1531
2097
|
const withUe = options.ueModel !== false;
|
|
1532
2098
|
if (withUe) {
|
|
1533
|
-
await
|
|
2099
|
+
await writeFile7(path11.join(blocksDir, `_${name}.json`), ueModelFor(name));
|
|
1534
2100
|
}
|
|
1535
2101
|
logger.success(
|
|
1536
|
-
`Block "${name}" created at ${
|
|
2102
|
+
`Block "${name}" created at ${chalk5.cyan(path11.relative(projectRoot, blocksDir))}${withUe ? chalk5.dim(" (+ Universal Editor model)") : ""}`
|
|
1537
2103
|
);
|
|
1538
2104
|
}
|
|
1539
2105
|
var init_create = __esm({
|
|
@@ -1547,10 +2113,10 @@ var init_create = __esm({
|
|
|
1547
2113
|
});
|
|
1548
2114
|
|
|
1549
2115
|
// src/lib/agents/detect.ts
|
|
1550
|
-
import { existsSync as
|
|
1551
|
-
import { readFile as
|
|
2116
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2117
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
1552
2118
|
import { homedir } from "os";
|
|
1553
|
-
import
|
|
2119
|
+
import path12 from "path";
|
|
1554
2120
|
async function whichBinary(name) {
|
|
1555
2121
|
const { execaCommand } = await import("execa");
|
|
1556
2122
|
try {
|
|
@@ -1578,8 +2144,8 @@ function findFigmaServerKey(node) {
|
|
|
1578
2144
|
async function findFigmaMcpServer(configPaths) {
|
|
1579
2145
|
for (const configPath of configPaths) {
|
|
1580
2146
|
try {
|
|
1581
|
-
if (!
|
|
1582
|
-
const content = await
|
|
2147
|
+
if (!existsSync10(configPath)) continue;
|
|
2148
|
+
const content = await readFile6(configPath, "utf-8");
|
|
1583
2149
|
try {
|
|
1584
2150
|
const key = findFigmaServerKey(JSON.parse(content));
|
|
1585
2151
|
if (key) return key;
|
|
@@ -1601,7 +2167,7 @@ async function detectAgent(preferred) {
|
|
|
1601
2167
|
return {
|
|
1602
2168
|
type: def.type,
|
|
1603
2169
|
path: binPath,
|
|
1604
|
-
configDir:
|
|
2170
|
+
configDir: existsSync10(def.configDir) ? def.configDir : void 0,
|
|
1605
2171
|
hasFigmaMcp: !!figmaMcpServerName,
|
|
1606
2172
|
figmaMcpServerName
|
|
1607
2173
|
};
|
|
@@ -1616,7 +2182,7 @@ async function detectAgent(preferred) {
|
|
|
1616
2182
|
return {
|
|
1617
2183
|
type: def.type,
|
|
1618
2184
|
path: binPath,
|
|
1619
|
-
configDir:
|
|
2185
|
+
configDir: existsSync10(def.configDir) ? def.configDir : void 0,
|
|
1620
2186
|
hasFigmaMcp: !!figmaMcpServerName,
|
|
1621
2187
|
figmaMcpServerName
|
|
1622
2188
|
};
|
|
@@ -1633,7 +2199,7 @@ async function detectAllAgents() {
|
|
|
1633
2199
|
agents.push({
|
|
1634
2200
|
type: def.type,
|
|
1635
2201
|
path: binPath,
|
|
1636
|
-
configDir:
|
|
2202
|
+
configDir: existsSync10(def.configDir) ? def.configDir : void 0,
|
|
1637
2203
|
hasFigmaMcp: !!figmaMcpServerName,
|
|
1638
2204
|
figmaMcpServerName
|
|
1639
2205
|
});
|
|
@@ -1651,22 +2217,22 @@ var init_detect = __esm({
|
|
|
1651
2217
|
{
|
|
1652
2218
|
type: "claude",
|
|
1653
2219
|
binary: "claude",
|
|
1654
|
-
configDir:
|
|
2220
|
+
configDir: path12.join(homedir(), ".claude"),
|
|
1655
2221
|
mcpConfigPaths: [
|
|
1656
|
-
|
|
1657
|
-
|
|
2222
|
+
path12.join(homedir(), ".claude", "plugins.json"),
|
|
2223
|
+
path12.join(homedir(), ".claude.json")
|
|
1658
2224
|
]
|
|
1659
2225
|
},
|
|
1660
2226
|
{
|
|
1661
2227
|
type: "cursor",
|
|
1662
2228
|
binary: "cursor",
|
|
1663
|
-
configDir:
|
|
1664
|
-
mcpConfigPaths: [
|
|
2229
|
+
configDir: path12.join(homedir(), ".cursor"),
|
|
2230
|
+
mcpConfigPaths: [path12.join(homedir(), ".cursor", "mcp.json")]
|
|
1665
2231
|
},
|
|
1666
2232
|
{
|
|
1667
2233
|
type: "codex",
|
|
1668
2234
|
binary: "codex",
|
|
1669
|
-
configDir:
|
|
2235
|
+
configDir: path12.join(homedir(), ".codex"),
|
|
1670
2236
|
mcpConfigPaths: []
|
|
1671
2237
|
}
|
|
1672
2238
|
];
|
|
@@ -1674,27 +2240,27 @@ var init_detect = __esm({
|
|
|
1674
2240
|
});
|
|
1675
2241
|
|
|
1676
2242
|
// src/lib/agents/invoke.ts
|
|
1677
|
-
import { mkdir as
|
|
2243
|
+
import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile8 } from "fs/promises";
|
|
1678
2244
|
import { tmpdir } from "os";
|
|
1679
|
-
import
|
|
2245
|
+
import path13 from "path";
|
|
1680
2246
|
import readline from "readline";
|
|
1681
|
-
import
|
|
2247
|
+
import chalk6 from "chalk";
|
|
1682
2248
|
async function savePromptToFile(prompt, projectRoot) {
|
|
1683
|
-
const base = projectRoot ?
|
|
1684
|
-
const dir =
|
|
1685
|
-
await
|
|
2249
|
+
const base = projectRoot ? path13.join(projectRoot, "tmp") : tmpdir();
|
|
2250
|
+
const dir = path13.join(base, "eds-cli");
|
|
2251
|
+
await mkdir5(dir, { recursive: true });
|
|
1686
2252
|
const filename = `from-design-${Date.now()}.md`;
|
|
1687
|
-
const filePath =
|
|
1688
|
-
await
|
|
2253
|
+
const filePath = path13.join(dir, filename);
|
|
2254
|
+
await writeFile8(filePath, prompt, "utf-8");
|
|
1689
2255
|
return filePath;
|
|
1690
2256
|
}
|
|
1691
2257
|
async function invokeAgent(agent, promptFile, cwd) {
|
|
1692
2258
|
const { execa } = await import("execa");
|
|
1693
|
-
const prompt = await
|
|
2259
|
+
const prompt = await readFile7(promptFile, "utf-8");
|
|
1694
2260
|
try {
|
|
1695
2261
|
switch (agent.type) {
|
|
1696
2262
|
case "claude": {
|
|
1697
|
-
logger.info(`Running Claude Code ${
|
|
2263
|
+
logger.info(`Running Claude Code ${chalk6.dim("- live log:")}`);
|
|
1698
2264
|
const figmaServers = [
|
|
1699
2265
|
...agent.figmaMcpServerName ? [agent.figmaMcpServerName] : [],
|
|
1700
2266
|
"figma-sse",
|
|
@@ -1757,10 +2323,10 @@ async function renderClaudeStream(sub, cwd) {
|
|
|
1757
2323
|
const ora4 = (await import("ora")).default;
|
|
1758
2324
|
const start = Date.now();
|
|
1759
2325
|
const elapsed = () => `${Math.round((Date.now() - start) / 1e3)}s`;
|
|
1760
|
-
const spinner = ora4({ text: `thinking ${
|
|
2326
|
+
const spinner = ora4({ text: `thinking ${chalk6.dim(elapsed())}`, color: "yellow" }).start();
|
|
1761
2327
|
let activity = "thinking";
|
|
1762
2328
|
const timer = setInterval(() => {
|
|
1763
|
-
spinner.text = `${activity} ${
|
|
2329
|
+
spinner.text = `${activity} ${chalk6.dim(elapsed())}`;
|
|
1764
2330
|
}, 300);
|
|
1765
2331
|
const pending = /* @__PURE__ */ new Map();
|
|
1766
2332
|
const emit = (line) => {
|
|
@@ -1786,7 +2352,7 @@ async function renderClaudeStream(sub, cwd) {
|
|
|
1786
2352
|
for (const block2 of content) {
|
|
1787
2353
|
if (block2.type === "text" && "text" in block2) {
|
|
1788
2354
|
const first = block2.text.trim().split("\n")[0].slice(0, 100);
|
|
1789
|
-
if (first) emit(
|
|
2355
|
+
if (first) emit(chalk6.dim(first));
|
|
1790
2356
|
} else if (block2.type === "tool_use" && "name" in block2) {
|
|
1791
2357
|
const id = "id" in block2 && block2.id ? block2.id : `${Math.random()}`;
|
|
1792
2358
|
pending.set(id, {
|
|
@@ -1802,8 +2368,8 @@ async function renderClaudeStream(sub, cwd) {
|
|
|
1802
2368
|
const p = pending.get(block2.tool_use_id);
|
|
1803
2369
|
if (p) {
|
|
1804
2370
|
const dur = `${((Date.now() - p.start) / 1e3).toFixed(1)}s`;
|
|
1805
|
-
const failed = "is_error" in block2 && block2.is_error ? ` ${
|
|
1806
|
-
emit(`${p.label} ${
|
|
2371
|
+
const failed = "is_error" in block2 && block2.is_error ? ` ${chalk6.red("failed")}` : "";
|
|
2372
|
+
emit(`${p.label} ${chalk6.dim(dur)}${failed}`);
|
|
1807
2373
|
pending.delete(block2.tool_use_id);
|
|
1808
2374
|
}
|
|
1809
2375
|
}
|
|
@@ -1815,9 +2381,9 @@ async function renderClaudeStream(sub, cwd) {
|
|
|
1815
2381
|
}
|
|
1816
2382
|
}
|
|
1817
2383
|
}
|
|
1818
|
-
for (const p of pending.values()) emit(`${p.label} ${
|
|
2384
|
+
for (const p of pending.values()) emit(`${p.label} ${chalk6.dim("\u2026")}`);
|
|
1819
2385
|
clearInterval(timer);
|
|
1820
|
-
if (result) spinner.succeed(`${brand.accent("agent finished")} ${
|
|
2386
|
+
if (result) spinner.succeed(`${brand.accent("agent finished")} ${chalk6.dim(result)}`);
|
|
1821
2387
|
else spinner.stop();
|
|
1822
2388
|
} catch (err) {
|
|
1823
2389
|
clearInterval(timer);
|
|
@@ -1830,22 +2396,22 @@ function toolLabel(name, input, cwd) {
|
|
|
1830
2396
|
const rel = (key) => {
|
|
1831
2397
|
const p = input?.[key];
|
|
1832
2398
|
if (typeof p !== "string") return "";
|
|
1833
|
-
return p.startsWith(cwd) ?
|
|
2399
|
+
return p.startsWith(cwd) ? path13.relative(cwd, p) : path13.basename(p);
|
|
1834
2400
|
};
|
|
1835
2401
|
if (name.includes("figma")) {
|
|
1836
2402
|
const short = name.split("__").pop() ?? name;
|
|
1837
|
-
return `${icon.figma} ${
|
|
2403
|
+
return `${icon.figma} ${chalk6.bold(short)} ${chalk6.dim("Figma MCP")}`;
|
|
1838
2404
|
}
|
|
1839
2405
|
switch (name) {
|
|
1840
2406
|
case "Write":
|
|
1841
2407
|
case "Edit":
|
|
1842
2408
|
case "NotebookEdit":
|
|
1843
|
-
return `${brand.accent("\u270E")} ${
|
|
2409
|
+
return `${brand.accent("\u270E")} ${chalk6.cyan(rel("file_path"))}`;
|
|
1844
2410
|
case "Read":
|
|
1845
|
-
return
|
|
2411
|
+
return chalk6.dim(`\xB7 read ${rel("file_path")}`);
|
|
1846
2412
|
case "Bash": {
|
|
1847
2413
|
const cmd = typeof input?.command === "string" ? input.command.split("\n")[0].slice(0, 70) : "";
|
|
1848
|
-
return
|
|
2414
|
+
return chalk6.dim(`$ ${cmd}`);
|
|
1849
2415
|
}
|
|
1850
2416
|
default:
|
|
1851
2417
|
return `${brand.blue("\u2192")} ${name}`;
|
|
@@ -1861,13 +2427,13 @@ var init_invoke = __esm({
|
|
|
1861
2427
|
});
|
|
1862
2428
|
|
|
1863
2429
|
// src/lib/config.ts
|
|
1864
|
-
import { existsSync as
|
|
1865
|
-
import { readFile as
|
|
2430
|
+
import { existsSync as existsSync11 } from "fs";
|
|
2431
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
1866
2432
|
import { homedir as homedir2 } from "os";
|
|
1867
|
-
import
|
|
2433
|
+
import path14 from "path";
|
|
1868
2434
|
async function loadJsonFile(filePath) {
|
|
1869
2435
|
try {
|
|
1870
|
-
const content = await
|
|
2436
|
+
const content = await readFile8(filePath, "utf-8");
|
|
1871
2437
|
return JSON.parse(content);
|
|
1872
2438
|
} catch {
|
|
1873
2439
|
return void 0;
|
|
@@ -1875,7 +2441,7 @@ async function loadJsonFile(filePath) {
|
|
|
1875
2441
|
}
|
|
1876
2442
|
async function loadConfig(projectRoot) {
|
|
1877
2443
|
const globalRaw = await loadJsonFile(GLOBAL_CONFIG_PATH);
|
|
1878
|
-
const projectRaw = projectRoot ? await loadJsonFile(
|
|
2444
|
+
const projectRaw = projectRoot ? await loadJsonFile(path14.join(projectRoot, PROJECT_CONFIG_NAME)) : void 0;
|
|
1879
2445
|
const merged = {
|
|
1880
2446
|
...typeof globalRaw === "object" && globalRaw !== null ? globalRaw : {},
|
|
1881
2447
|
...typeof projectRaw === "object" && projectRaw !== null ? projectRaw : {}
|
|
@@ -1894,7 +2460,7 @@ function globalConfigPath() {
|
|
|
1894
2460
|
return GLOBAL_CONFIG_PATH;
|
|
1895
2461
|
}
|
|
1896
2462
|
function globalConfigExists() {
|
|
1897
|
-
return
|
|
2463
|
+
return existsSync11(GLOBAL_CONFIG_PATH);
|
|
1898
2464
|
}
|
|
1899
2465
|
var GLOBAL_CONFIG_DIR, GLOBAL_CONFIG_PATH, PROJECT_CONFIG_NAME;
|
|
1900
2466
|
var init_config = __esm({
|
|
@@ -1903,8 +2469,8 @@ var init_config = __esm({
|
|
|
1903
2469
|
init_esm_shims();
|
|
1904
2470
|
init_logger();
|
|
1905
2471
|
init_schemas();
|
|
1906
|
-
GLOBAL_CONFIG_DIR =
|
|
1907
|
-
GLOBAL_CONFIG_PATH =
|
|
2472
|
+
GLOBAL_CONFIG_DIR = path14.join(homedir2(), ".eds");
|
|
2473
|
+
GLOBAL_CONFIG_PATH = path14.join(GLOBAL_CONFIG_DIR, "config.json");
|
|
1908
2474
|
PROJECT_CONFIG_NAME = ".edsrc.json";
|
|
1909
2475
|
}
|
|
1910
2476
|
});
|
|
@@ -1954,9 +2520,9 @@ var init_node_id = __esm({
|
|
|
1954
2520
|
});
|
|
1955
2521
|
|
|
1956
2522
|
// src/lib/figma/prompt-builder.ts
|
|
1957
|
-
import { existsSync as
|
|
1958
|
-
import { readFile as
|
|
1959
|
-
import
|
|
2523
|
+
import { existsSync as existsSync12 } from "fs";
|
|
2524
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
2525
|
+
import path15 from "path";
|
|
1960
2526
|
function contentSourceGuidance(source, cfHint, blockName) {
|
|
1961
2527
|
if (!source || source === "document" || source === "ue") return "";
|
|
1962
2528
|
const q = cfHint ? ` (model / persisted query: \`${cfHint}\`)` : "";
|
|
@@ -2168,10 +2734,10 @@ ${tokensHint}
|
|
|
2168
2734
|
`;
|
|
2169
2735
|
}
|
|
2170
2736
|
async function getTokensHint(projectRoot) {
|
|
2171
|
-
const stylesPath =
|
|
2172
|
-
if (!
|
|
2737
|
+
const stylesPath = path15.join(projectRoot, "styles", "styles.css");
|
|
2738
|
+
if (!existsSync12(stylesPath)) return "";
|
|
2173
2739
|
try {
|
|
2174
|
-
const css = await
|
|
2740
|
+
const css = await readFile9(stylesPath, "utf-8");
|
|
2175
2741
|
const rootMatch = css.match(/:root\s*\{([^}]+)\}/);
|
|
2176
2742
|
if (!rootMatch) return "";
|
|
2177
2743
|
const props = rootMatch[1].split("\n").filter((line) => line.trim().startsWith("--")).map((line) => line.trim()).join("\n ");
|
|
@@ -2201,7 +2767,8 @@ var init_prompt_builder = __esm({
|
|
|
2201
2767
|
figma: "Figma",
|
|
2202
2768
|
stitch: "Google Stitch",
|
|
2203
2769
|
canva: "Canva",
|
|
2204
|
-
sketch: "Sketch"
|
|
2770
|
+
sketch: "Sketch",
|
|
2771
|
+
framer: "Framer"
|
|
2205
2772
|
};
|
|
2206
2773
|
}
|
|
2207
2774
|
});
|
|
@@ -2311,11 +2878,11 @@ var init_harness = __esm({
|
|
|
2311
2878
|
});
|
|
2312
2879
|
|
|
2313
2880
|
// src/lib/preview/server.ts
|
|
2314
|
-
import { createReadStream, existsSync as
|
|
2881
|
+
import { createReadStream, existsSync as existsSync13, statSync } from "fs";
|
|
2315
2882
|
import http from "http";
|
|
2316
|
-
import
|
|
2883
|
+
import path16 from "path";
|
|
2317
2884
|
function startServer(root, routes, port, host = "127.0.0.1") {
|
|
2318
|
-
const resolvedRoot =
|
|
2885
|
+
const resolvedRoot = path16.resolve(root);
|
|
2319
2886
|
const clients = /* @__PURE__ */ new Set();
|
|
2320
2887
|
const server = http.createServer((req, res) => {
|
|
2321
2888
|
const urlPath = decodeURIComponent((req.url ?? "/").split("?")[0]);
|
|
@@ -2341,19 +2908,19 @@ function startServer(root, routes, port, host = "127.0.0.1") {
|
|
|
2341
2908
|
res.end(route.body);
|
|
2342
2909
|
return;
|
|
2343
2910
|
}
|
|
2344
|
-
const filePath =
|
|
2911
|
+
const filePath = path16.join(resolvedRoot, urlPath);
|
|
2345
2912
|
if (!filePath.startsWith(resolvedRoot)) {
|
|
2346
2913
|
res.writeHead(403);
|
|
2347
2914
|
res.end("Forbidden");
|
|
2348
2915
|
return;
|
|
2349
2916
|
}
|
|
2350
|
-
if (!
|
|
2917
|
+
if (!existsSync13(filePath) || !statSync(filePath).isFile()) {
|
|
2351
2918
|
res.writeHead(404, { "content-type": "text/plain" });
|
|
2352
2919
|
res.end(`Not found: ${urlPath}`);
|
|
2353
2920
|
return;
|
|
2354
2921
|
}
|
|
2355
2922
|
res.writeHead(200, {
|
|
2356
|
-
"content-type": MIME[
|
|
2923
|
+
"content-type": MIME[path16.extname(filePath).toLowerCase()] ?? "application/octet-stream"
|
|
2357
2924
|
});
|
|
2358
2925
|
createReadStream(filePath).pipe(res);
|
|
2359
2926
|
});
|
|
@@ -2417,10 +2984,10 @@ var preview_exports = {};
|
|
|
2417
2984
|
__export(preview_exports, {
|
|
2418
2985
|
previewBlock: () => previewBlock
|
|
2419
2986
|
});
|
|
2420
|
-
import { existsSync as
|
|
2421
|
-
import { readFile as
|
|
2422
|
-
import
|
|
2423
|
-
import
|
|
2987
|
+
import { existsSync as existsSync14, watch } from "fs";
|
|
2988
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
2989
|
+
import path17 from "path";
|
|
2990
|
+
import chalk7 from "chalk";
|
|
2424
2991
|
function labelFor(width) {
|
|
2425
2992
|
if (width <= 480) return `${width} mobile`;
|
|
2426
2993
|
if (width <= 1024) return `${width} tablet`;
|
|
@@ -2472,30 +3039,30 @@ async function previewBlock(name, options = {}) {
|
|
|
2472
3039
|
process.exitCode = 1;
|
|
2473
3040
|
return;
|
|
2474
3041
|
}
|
|
2475
|
-
const blockDir =
|
|
2476
|
-
if (!
|
|
3042
|
+
const blockDir = path17.join(projectRoot, "blocks", name);
|
|
3043
|
+
if (!existsSync14(blockDir)) {
|
|
2477
3044
|
logger.error(`Block "${name}" not found at blocks/${name}/.`);
|
|
2478
3045
|
process.exitCode = 1;
|
|
2479
3046
|
return;
|
|
2480
3047
|
}
|
|
2481
|
-
if (!
|
|
3048
|
+
if (!existsSync14(path17.join(blockDir, `${name}.css`)) && !existsSync14(path17.join(blockDir, `${name}.js`))) {
|
|
2482
3049
|
logger.error(`Block "${name}" has no ${name}.css / ${name}.js to preview.`);
|
|
2483
3050
|
process.exitCode = 1;
|
|
2484
3051
|
return;
|
|
2485
3052
|
}
|
|
2486
3053
|
let meta = null;
|
|
2487
|
-
const metaPath =
|
|
2488
|
-
if (
|
|
3054
|
+
const metaPath = path17.join(blockDir, ".eds-meta.json");
|
|
3055
|
+
if (existsSync14(metaPath)) {
|
|
2489
3056
|
try {
|
|
2490
|
-
meta = JSON.parse(await
|
|
3057
|
+
meta = JSON.parse(await readFile10(metaPath, "utf-8"));
|
|
2491
3058
|
} catch {
|
|
2492
3059
|
}
|
|
2493
3060
|
}
|
|
2494
|
-
const samplePath =
|
|
3061
|
+
const samplePath = path17.join(blockDir, `_${name}.preview.html`);
|
|
2495
3062
|
let sampleContent;
|
|
2496
3063
|
let placeholder = false;
|
|
2497
|
-
if (
|
|
2498
|
-
sampleContent = await
|
|
3064
|
+
if (existsSync14(samplePath)) {
|
|
3065
|
+
sampleContent = await readFile10(samplePath, "utf-8");
|
|
2499
3066
|
} else {
|
|
2500
3067
|
sampleContent = defaultSample(name);
|
|
2501
3068
|
placeholder = true;
|
|
@@ -2545,9 +3112,9 @@ async function previewBlock(name, options = {}) {
|
|
|
2545
3112
|
reloadTimer = setTimeout(() => server.reload(), 150);
|
|
2546
3113
|
});
|
|
2547
3114
|
logger.info("");
|
|
2548
|
-
logger.info(` ${
|
|
3115
|
+
logger.info(` ${chalk7.bold("\u279C")} ${chalk7.cyan(server.url)}`);
|
|
2549
3116
|
logger.info(statusLine("pass", "live reload", "edits to the block refresh the browser"));
|
|
2550
|
-
logger.info(
|
|
3117
|
+
logger.info(chalk7.dim(" Press Ctrl+C to stop.\n"));
|
|
2551
3118
|
if (options.open !== false) await openInBrowser(server.url);
|
|
2552
3119
|
const stop = async () => {
|
|
2553
3120
|
watcher.close();
|
|
@@ -2576,10 +3143,10 @@ var from_design_exports = {};
|
|
|
2576
3143
|
__export(from_design_exports, {
|
|
2577
3144
|
blockFromDesign: () => blockFromDesign
|
|
2578
3145
|
});
|
|
2579
|
-
import { existsSync as
|
|
2580
|
-
import { readFile as
|
|
2581
|
-
import
|
|
2582
|
-
import
|
|
3146
|
+
import { existsSync as existsSync15 } from "fs";
|
|
3147
|
+
import { readFile as readFile11, writeFile as writeFile9 } from "fs/promises";
|
|
3148
|
+
import path18 from "path";
|
|
3149
|
+
import chalk8 from "chalk";
|
|
2583
3150
|
import ora from "ora";
|
|
2584
3151
|
async function previewRunning(port = 8777) {
|
|
2585
3152
|
try {
|
|
@@ -2602,7 +3169,8 @@ async function runWizard(figmaUrl) {
|
|
|
2602
3169
|
{ name: "Figma", value: "figma" },
|
|
2603
3170
|
{ name: "Google Stitch", value: "stitch" },
|
|
2604
3171
|
{ name: "Canva", value: "canva" },
|
|
2605
|
-
{ name: "Sketch", value: "sketch" }
|
|
3172
|
+
{ name: "Sketch", value: "sketch" },
|
|
3173
|
+
{ name: "Framer", value: "framer" }
|
|
2606
3174
|
],
|
|
2607
3175
|
default: "figma"
|
|
2608
3176
|
});
|
|
@@ -2728,7 +3296,7 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
2728
3296
|
try {
|
|
2729
3297
|
figma2 = parseFigmaUrl(designUrl);
|
|
2730
3298
|
spinner.succeed(
|
|
2731
|
-
`Parsed Figma URL ${
|
|
3299
|
+
`Parsed Figma URL ${chalk8.dim(figma2.nodeId ? `node ${figma2.nodeId}` : `file ${figma2.fileKey}`)}`
|
|
2732
3300
|
);
|
|
2733
3301
|
} catch (err) {
|
|
2734
3302
|
spinner.fail(err.message);
|
|
@@ -2736,7 +3304,7 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
2736
3304
|
return;
|
|
2737
3305
|
}
|
|
2738
3306
|
} else {
|
|
2739
|
-
logger.info(` ${icon.pass} ${providerName} design ${
|
|
3307
|
+
logger.info(` ${icon.pass} ${providerName} design ${chalk8.dim(designRef)}`);
|
|
2740
3308
|
}
|
|
2741
3309
|
const blockName = options.name || (figma2 ? inferBlockName(figma2.fileName) : "");
|
|
2742
3310
|
if (!kebabCaseRegex.test(blockName)) {
|
|
@@ -2759,20 +3327,20 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
2759
3327
|
screenshot: options.screenshot !== false
|
|
2760
3328
|
});
|
|
2761
3329
|
const promptFile = await savePromptToFile(prompt, projectRoot);
|
|
2762
|
-
const hasTokens =
|
|
3330
|
+
const hasTokens = existsSync15(path18.join(projectRoot, "styles", "styles.css"));
|
|
2763
3331
|
promptSpinner.succeed(
|
|
2764
|
-
hasTokens ? `Loaded design tokens ${
|
|
3332
|
+
hasTokens ? `Loaded design tokens ${chalk8.dim("styles/styles.css")}` : `Built prompt ${chalk8.dim("no project tokens found")}`
|
|
2765
3333
|
);
|
|
2766
|
-
logger.info(
|
|
3334
|
+
logger.info(chalk8.dim(` prompt \u2192 ${promptFile}`));
|
|
2767
3335
|
if (options.dryRun) {
|
|
2768
3336
|
logger.info("");
|
|
2769
|
-
logger.info(
|
|
2770
|
-
logger.info(`Prompt file: ${
|
|
2771
|
-
logger.info(`Block name: ${
|
|
3337
|
+
logger.info(chalk8.yellow("Dry run mode \u2014 prompt generated but agent not invoked."));
|
|
3338
|
+
logger.info(`Prompt file: ${chalk8.cyan(promptFile)}`);
|
|
3339
|
+
logger.info(`Block name: ${chalk8.cyan(blockName)}`);
|
|
2772
3340
|
logger.info(`Prompt version: ${PROMPT_VERSION}`);
|
|
2773
3341
|
logger.info("");
|
|
2774
3342
|
logger.info("Preview:");
|
|
2775
|
-
logger.info(
|
|
3343
|
+
logger.info(chalk8.dim("\u2500".repeat(60)));
|
|
2776
3344
|
console.log(prompt);
|
|
2777
3345
|
return;
|
|
2778
3346
|
}
|
|
@@ -2780,22 +3348,22 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
2780
3348
|
const preferredAgent = options.agent || config.agent;
|
|
2781
3349
|
if (options.agent === "none") {
|
|
2782
3350
|
logger.info("Agent skipped (--agent none). Use the prompt file manually:");
|
|
2783
|
-
logger.info(` ${
|
|
3351
|
+
logger.info(` ${chalk8.cyan(promptFile)}`);
|
|
2784
3352
|
return;
|
|
2785
3353
|
}
|
|
2786
3354
|
const agentSpinner = ora("Detecting AI agent...").start();
|
|
2787
3355
|
const agent = await detectAgent(preferredAgent);
|
|
2788
3356
|
if (!agent) {
|
|
2789
3357
|
agentSpinner.fail("No AI agent found (claude, cursor, or codex).");
|
|
2790
|
-
logger.info(`Install Claude Code: ${
|
|
2791
|
-
logger.info(`Or use the prompt file manually: ${
|
|
3358
|
+
logger.info(`Install Claude Code: ${chalk8.cyan("npm install -g @anthropic-ai/claude-code")}`);
|
|
3359
|
+
logger.info(`Or use the prompt file manually: ${chalk8.cyan(promptFile)}`);
|
|
2792
3360
|
process.exitCode = 1;
|
|
2793
3361
|
return;
|
|
2794
3362
|
}
|
|
2795
|
-
agentSpinner.succeed(`Agent ready ${
|
|
3363
|
+
agentSpinner.succeed(`Agent ready ${chalk8.cyan(agent.type)} ${chalk8.dim(agent.path)}`);
|
|
2796
3364
|
if (provider === "figma" && !agent.hasFigmaMcp) {
|
|
2797
3365
|
logger.warn(`Figma MCP may not be configured for ${agent.type}.`);
|
|
2798
|
-
logger.info(`Run ${
|
|
3366
|
+
logger.info(`Run ${chalk8.cyan("eds figma setup")} to configure it.`);
|
|
2799
3367
|
}
|
|
2800
3368
|
if (!options.yes) {
|
|
2801
3369
|
const { confirm } = await import("@inquirer/prompts");
|
|
@@ -2804,24 +3372,24 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
2804
3372
|
default: true
|
|
2805
3373
|
});
|
|
2806
3374
|
if (!proceed) {
|
|
2807
|
-
logger.info(`Prompt saved to: ${
|
|
3375
|
+
logger.info(`Prompt saved to: ${chalk8.cyan(promptFile)}`);
|
|
2808
3376
|
return;
|
|
2809
3377
|
}
|
|
2810
3378
|
}
|
|
2811
3379
|
logger.info(
|
|
2812
|
-
` ${
|
|
3380
|
+
` ${chalk8.dim("\u2192")} Handing off to ${chalk8.cyan(agent.type)} ${chalk8.dim(`via ${providerName} MCP`)}`
|
|
2813
3381
|
);
|
|
2814
3382
|
if (provider === "figma") {
|
|
2815
|
-
logger.info(
|
|
3383
|
+
logger.info(chalk8.dim(" get_design_context \xB7 get_variable_defs \xB7 get_screenshot"));
|
|
2816
3384
|
}
|
|
2817
3385
|
const success = await invokeAgent(agent, promptFile, projectRoot);
|
|
2818
3386
|
if (!success) {
|
|
2819
3387
|
process.exitCode = 1;
|
|
2820
3388
|
return;
|
|
2821
3389
|
}
|
|
2822
|
-
const blockDir =
|
|
3390
|
+
const blockDir = path18.join(projectRoot, "blocks", blockName);
|
|
2823
3391
|
const ok = await verifyGeneratedBlock(blockDir, blockName, options.ueModel !== false);
|
|
2824
|
-
if (
|
|
3392
|
+
if (existsSync15(blockDir)) {
|
|
2825
3393
|
const meta = {
|
|
2826
3394
|
provider,
|
|
2827
3395
|
designRef,
|
|
@@ -2831,7 +3399,7 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
2831
3399
|
promptVersion: PROMPT_VERSION,
|
|
2832
3400
|
agentUsed: agent.type
|
|
2833
3401
|
};
|
|
2834
|
-
await
|
|
3402
|
+
await writeFile9(path18.join(blockDir, ".eds-meta.json"), `${JSON.stringify(meta, null, 2)}
|
|
2835
3403
|
`);
|
|
2836
3404
|
}
|
|
2837
3405
|
if (!ok) {
|
|
@@ -2842,10 +3410,21 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
2842
3410
|
return;
|
|
2843
3411
|
}
|
|
2844
3412
|
logger.success(`Block "${blockName}" generation complete.`);
|
|
3413
|
+
if (!options.yes) {
|
|
3414
|
+
const { confirm } = await import("@inquirer/prompts");
|
|
3415
|
+
const addTracking = await confirm({
|
|
3416
|
+
message: "Instrument this block for analytics (dataLayer)?",
|
|
3417
|
+
default: false
|
|
3418
|
+
});
|
|
3419
|
+
if (addTracking) {
|
|
3420
|
+
const { trackBlock: trackBlock2 } = await Promise.resolve().then(() => (init_track2(), track_exports));
|
|
3421
|
+
await trackBlock2(blockName, {});
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
2845
3424
|
if (options.serve !== false) {
|
|
2846
3425
|
if (await previewRunning()) {
|
|
2847
3426
|
logger.info(
|
|
2848
|
-
|
|
3427
|
+
chalk8.dim(
|
|
2849
3428
|
" A preview server is already running at http://127.0.0.1:8777 \u2014 your change will refresh there."
|
|
2850
3429
|
)
|
|
2851
3430
|
);
|
|
@@ -2855,15 +3434,15 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
2855
3434
|
await previewBlock2(blockName, {});
|
|
2856
3435
|
}
|
|
2857
3436
|
} else {
|
|
2858
|
-
logger.info(` ${
|
|
3437
|
+
logger.info(` ${chalk8.dim("\u2192")} Next: ${chalk8.cyan(`eds block preview ${blockName}`)}`);
|
|
2859
3438
|
}
|
|
2860
3439
|
}
|
|
2861
3440
|
async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
2862
3441
|
const checks = [];
|
|
2863
|
-
const jsPath =
|
|
2864
|
-
const cssPath =
|
|
2865
|
-
const hasJs =
|
|
2866
|
-
const hasCss =
|
|
3442
|
+
const jsPath = path18.join(blockDir, `${blockName}.js`);
|
|
3443
|
+
const cssPath = path18.join(blockDir, `${blockName}.css`);
|
|
3444
|
+
const hasJs = existsSync15(jsPath);
|
|
3445
|
+
const hasCss = existsSync15(cssPath);
|
|
2867
3446
|
checks.push({
|
|
2868
3447
|
status: hasJs ? "pass" : "fail",
|
|
2869
3448
|
name: `${blockName}.js`,
|
|
@@ -2875,7 +3454,7 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
|
2875
3454
|
message: hasCss ? "created" : "missing \u2014 agent did not write the CSS file"
|
|
2876
3455
|
});
|
|
2877
3456
|
if (hasJs) {
|
|
2878
|
-
const js = await
|
|
3457
|
+
const js = await readFile11(jsPath, "utf-8");
|
|
2879
3458
|
checks.push({
|
|
2880
3459
|
status: /export\s+default/.test(js) ? "pass" : "fail",
|
|
2881
3460
|
name: "decorate export",
|
|
@@ -2890,7 +3469,7 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
|
2890
3469
|
}
|
|
2891
3470
|
}
|
|
2892
3471
|
if (hasCss) {
|
|
2893
|
-
const css = await
|
|
3472
|
+
const css = await readFile11(cssPath, "utf-8");
|
|
2894
3473
|
checks.push({
|
|
2895
3474
|
status: css.includes(`.${blockName}`) ? "pass" : "warn",
|
|
2896
3475
|
name: "scoped CSS",
|
|
@@ -2898,12 +3477,12 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
|
2898
3477
|
});
|
|
2899
3478
|
}
|
|
2900
3479
|
if (withUeModel) {
|
|
2901
|
-
const hasPerBlock =
|
|
2902
|
-
const modelsFile =
|
|
3480
|
+
const hasPerBlock = existsSync15(path18.join(blockDir, `_${blockName}.json`));
|
|
3481
|
+
const modelsFile = path18.join(blockDir, "..", "..", "component-models.json");
|
|
2903
3482
|
let hasAggregated = false;
|
|
2904
|
-
if (!hasPerBlock &&
|
|
3483
|
+
if (!hasPerBlock && existsSync15(modelsFile)) {
|
|
2905
3484
|
try {
|
|
2906
|
-
hasAggregated = (await
|
|
3485
|
+
hasAggregated = (await readFile11(modelsFile, "utf-8")).includes(`"${blockName}"`);
|
|
2907
3486
|
} catch {
|
|
2908
3487
|
}
|
|
2909
3488
|
}
|
|
@@ -2915,8 +3494,8 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
|
2915
3494
|
});
|
|
2916
3495
|
if (hasPerBlock && hasJs) {
|
|
2917
3496
|
try {
|
|
2918
|
-
const modelRaw = await
|
|
2919
|
-
const js = await
|
|
3497
|
+
const modelRaw = await readFile11(path18.join(blockDir, `_${blockName}.json`), "utf-8");
|
|
3498
|
+
const js = await readFile11(jsPath, "utf-8");
|
|
2920
3499
|
const isRepeatable = /"resourceType"\s*:\s*"[^"]*\/item"/.test(modelRaw) || /"filter"\s*:/.test(modelRaw) || /"multi"\s*:\s*true/.test(modelRaw);
|
|
2921
3500
|
const iterates = /\.children/.test(js) || /\bfor\s*\(/.test(js) || /\.forEach\s*\(/.test(js);
|
|
2922
3501
|
if (isRepeatable) {
|
|
@@ -2959,10 +3538,10 @@ var list_exports = {};
|
|
|
2959
3538
|
__export(list_exports, {
|
|
2960
3539
|
listBlocks: () => listBlocks
|
|
2961
3540
|
});
|
|
2962
|
-
import { existsSync as
|
|
2963
|
-
import { readFile as
|
|
2964
|
-
import
|
|
2965
|
-
import
|
|
3541
|
+
import { existsSync as existsSync16 } from "fs";
|
|
3542
|
+
import { readFile as readFile12, readdir as readdir2 } from "fs/promises";
|
|
3543
|
+
import path19 from "path";
|
|
3544
|
+
import chalk9 from "chalk";
|
|
2966
3545
|
async function listBlocks(options = {}) {
|
|
2967
3546
|
const projectRoot = findProjectRoot();
|
|
2968
3547
|
if (!projectRoot) {
|
|
@@ -2970,27 +3549,27 @@ async function listBlocks(options = {}) {
|
|
|
2970
3549
|
process.exitCode = 1;
|
|
2971
3550
|
return;
|
|
2972
3551
|
}
|
|
2973
|
-
const blocksDir =
|
|
2974
|
-
if (!
|
|
3552
|
+
const blocksDir = path19.join(projectRoot, "blocks");
|
|
3553
|
+
if (!existsSync16(blocksDir)) {
|
|
2975
3554
|
logger.info("No blocks/ directory found.");
|
|
2976
3555
|
return;
|
|
2977
3556
|
}
|
|
2978
|
-
const entries = await
|
|
3557
|
+
const entries = await readdir2(blocksDir, { withFileTypes: true });
|
|
2979
3558
|
const blocks = [];
|
|
2980
3559
|
for (const entry of entries) {
|
|
2981
3560
|
if (!entry.isDirectory()) continue;
|
|
2982
3561
|
const name = entry.name;
|
|
2983
|
-
const blockDir =
|
|
3562
|
+
const blockDir = path19.join(blocksDir, name);
|
|
2984
3563
|
const info = {
|
|
2985
3564
|
name,
|
|
2986
|
-
hasJs:
|
|
2987
|
-
hasCss:
|
|
2988
|
-
hasUeModel:
|
|
3565
|
+
hasJs: existsSync16(path19.join(blockDir, `${name}.js`)),
|
|
3566
|
+
hasCss: existsSync16(path19.join(blockDir, `${name}.css`)),
|
|
3567
|
+
hasUeModel: existsSync16(path19.join(blockDir, `_${name}.json`))
|
|
2989
3568
|
};
|
|
2990
|
-
const metaFile =
|
|
2991
|
-
if (
|
|
3569
|
+
const metaFile = path19.join(blockDir, ".eds-meta.json");
|
|
3570
|
+
if (existsSync16(metaFile)) {
|
|
2992
3571
|
try {
|
|
2993
|
-
const meta = JSON.parse(await
|
|
3572
|
+
const meta = JSON.parse(await readFile12(metaFile, "utf-8"));
|
|
2994
3573
|
info.figmaSource = {
|
|
2995
3574
|
fileKey: meta.figmaFileKey,
|
|
2996
3575
|
nodeId: meta.figmaNodeId,
|
|
@@ -3013,17 +3592,17 @@ async function listBlocks(options = {}) {
|
|
|
3013
3592
|
const nameWidth = columnWidth(blocks.map((b) => b.name));
|
|
3014
3593
|
for (const b of blocks) {
|
|
3015
3594
|
const badges = [
|
|
3016
|
-
b.hasJs ? brand.ok("js") :
|
|
3017
|
-
b.hasCss ? brand.ok("css") :
|
|
3595
|
+
b.hasJs ? brand.ok("js") : chalk9.dim.strikethrough("js"),
|
|
3596
|
+
b.hasCss ? brand.ok("css") : chalk9.dim.strikethrough("css")
|
|
3018
3597
|
];
|
|
3019
3598
|
if (b.hasUeModel) badges.push(brand.blue("ue-model"));
|
|
3020
3599
|
if (b.figmaSource) badges.push(`${icon.figma} ${brand.dotPurple("figma")}`);
|
|
3021
|
-
const name = padEndVisible(
|
|
3022
|
-
logger.info(` ${name} ${
|
|
3600
|
+
const name = padEndVisible(chalk9.bold(b.name), nameWidth);
|
|
3601
|
+
logger.info(` ${name} ${chalk9.dim("[")}${badges.join(" ")}${chalk9.dim("]")}`);
|
|
3023
3602
|
if (b.figmaSource) {
|
|
3024
3603
|
const synced = new Date(b.figmaSource.lastSyncedAt).toLocaleDateString();
|
|
3025
3604
|
logger.info(
|
|
3026
|
-
` ${" ".repeat(nameWidth)} ${icon.arrow} ${
|
|
3605
|
+
` ${" ".repeat(nameWidth)} ${icon.arrow} ${chalk9.dim(
|
|
3027
3606
|
`figma:${b.figmaSource.fileKey} \xB7 synced ${synced}`
|
|
3028
3607
|
)}`
|
|
3029
3608
|
);
|
|
@@ -3063,22 +3642,85 @@ var init_integrations = __esm({
|
|
|
3063
3642
|
id: "gtm",
|
|
3064
3643
|
name: "Google Tag Manager",
|
|
3065
3644
|
fields: [{ name: "id", message: "GTM container ID", placeholder: "GTM-XXXXXXX" }],
|
|
3066
|
-
|
|
3067
|
-
(
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3645
|
+
// delayed.js imports the loader + page-meta, pushes page metadata, then GTM.
|
|
3646
|
+
code: () => `// Google Tag Manager - loaded from ./analytics.js (delayed phase, after LCP).
|
|
3647
|
+
import { loadGTM } from './analytics.js';
|
|
3648
|
+
import pageMetaPush from './page-meta.js';
|
|
3649
|
+
|
|
3650
|
+
const pageMetaReady = pageMetaPush();
|
|
3651
|
+
pageMetaReady.then(loadGTM);`,
|
|
3652
|
+
module: (v) => [
|
|
3653
|
+
{
|
|
3654
|
+
file: "scripts/analytics.js",
|
|
3655
|
+
content: `// scripts/analytics.js - Google Tag Manager (generated by \`eds integrate gtm\`).
|
|
3656
|
+
// The container ID comes from the \`gtm-id\` metadata, falling back to the one
|
|
3657
|
+
// configured here. Import { trackEvent } from './analytics.js' to push events.
|
|
3658
|
+
|
|
3659
|
+
const FALLBACK_ID = ${JSON.stringify(v.id || "")};
|
|
3660
|
+
|
|
3661
|
+
function getMetadata(name) {
|
|
3662
|
+
const meta = document.querySelector(\`meta[name="\${name}"]\`);
|
|
3663
|
+
return meta ? meta.content : '';
|
|
3664
|
+
}
|
|
3665
|
+
|
|
3666
|
+
function isDev() {
|
|
3667
|
+
const h = window.location.hostname;
|
|
3668
|
+
return h === 'localhost' || h === '127.0.0.1' || h.endsWith('.aem.page') || h.endsWith('.hlx.page');
|
|
3669
|
+
}
|
|
3670
|
+
|
|
3671
|
+
// dataLayer must exist before anything pushes to it (trackEvent may run before loadGTM).
|
|
3672
|
+
window.dataLayer = window.dataLayer || [];
|
|
3673
|
+
|
|
3674
|
+
export function trackEvent(event) {
|
|
3675
|
+
if (isDev()) {
|
|
3676
|
+
// eslint-disable-next-line no-console
|
|
3677
|
+
console.log('[gtm]', JSON.stringify(event, null, 2));
|
|
3678
|
+
}
|
|
3679
|
+
window.dataLayer.push(event);
|
|
3680
|
+
}
|
|
3681
|
+
|
|
3682
|
+
let loaded = false;
|
|
3683
|
+
|
|
3684
|
+
export function loadGTM() {
|
|
3685
|
+
if (loaded) return;
|
|
3686
|
+
const raw = getMetadata('gtm-id') || FALLBACK_ID;
|
|
3687
|
+
if (!raw) return;
|
|
3688
|
+
loaded = true;
|
|
3689
|
+
|
|
3690
|
+
const id = raw.startsWith('GTM-') ? raw : \`GTM-\${raw}\`;
|
|
3691
|
+
window.dataLayer.push({ 'gtm.start': Date.now(), event: 'gtm.js' });
|
|
3692
|
+
|
|
3693
|
+
const script = document.createElement('script');
|
|
3694
|
+
script.async = true;
|
|
3695
|
+
script.src = \`https://www.googletagmanager.com/gtm.js?id=\${id}\`;
|
|
3696
|
+
document.head.append(script);
|
|
3697
|
+
}
|
|
3698
|
+
`
|
|
3699
|
+
},
|
|
3700
|
+
{
|
|
3701
|
+
file: "scripts/page-meta.js",
|
|
3702
|
+
content: `// scripts/page-meta.js - pushes page-level metadata to the dataLayer before
|
|
3703
|
+
// GTM initialises. Extend \`pageMeta\` with the fields your container expects.
|
|
3704
|
+
import { trackEvent } from './analytics.js';
|
|
3705
|
+
|
|
3706
|
+
export default async function pageMetaPush() {
|
|
3707
|
+
const pageMeta = {
|
|
3708
|
+
event: 'page_meta',
|
|
3709
|
+
};
|
|
3710
|
+
|
|
3711
|
+
trackEvent(pageMeta);
|
|
3712
|
+
|
|
3713
|
+
return pageMeta;
|
|
3714
|
+
}
|
|
3715
|
+
`
|
|
3716
|
+
}
|
|
3717
|
+
]
|
|
3076
3718
|
},
|
|
3077
3719
|
{
|
|
3078
3720
|
id: "ga4",
|
|
3079
3721
|
name: "Google Analytics 4",
|
|
3080
3722
|
fields: [{ name: "id", message: "Measurement ID", placeholder: "G-XXXXXXXXXX" }],
|
|
3081
|
-
code: (v) => `// Google Analytics 4 (${v.id})
|
|
3723
|
+
code: (v) => `// Google Analytics 4 (${v.id}) - delayed phase.
|
|
3082
3724
|
const gaScript = document.createElement('script');
|
|
3083
3725
|
gaScript.async = true;
|
|
3084
3726
|
gaScript.src = 'https://www.googletagmanager.com/gtag/js?id=${v.id}';
|
|
@@ -3095,7 +3737,7 @@ gtag('config', '${v.id}');`
|
|
|
3095
3737
|
{ name: "label", message: "Button label", placeholder: "Chat with us" },
|
|
3096
3738
|
{ name: "src", message: "Chat widget script URL" }
|
|
3097
3739
|
],
|
|
3098
|
-
code: (v) => `// Chat widget
|
|
3740
|
+
code: (v) => `// Chat widget - facade pattern: load the heavy script only on first interaction.
|
|
3099
3741
|
const chatBtn = document.createElement('button');
|
|
3100
3742
|
chatBtn.type = 'button';
|
|
3101
3743
|
chatBtn.className = 'chat-facade';
|
|
@@ -3113,7 +3755,7 @@ document.body.appendChild(chatBtn);`
|
|
|
3113
3755
|
id: "cookiebot",
|
|
3114
3756
|
name: "Cookie consent (Cookiebot)",
|
|
3115
3757
|
fields: [{ name: "id", message: "Cookiebot domain group ID (CBID)" }],
|
|
3116
|
-
code: (v) => `// Cookiebot consent
|
|
3758
|
+
code: (v) => `// Cookiebot consent - load early in delayed; configure GTM consent mode to respect it.
|
|
3117
3759
|
const cb = document.createElement('script');
|
|
3118
3760
|
cb.id = 'Cookiebot';
|
|
3119
3761
|
cb.src = 'https://consent.cookiebot.com/uc.js';
|
|
@@ -3125,7 +3767,7 @@ document.head.appendChild(cb);`
|
|
|
3125
3767
|
id: "custom",
|
|
3126
3768
|
name: "Custom third-party script",
|
|
3127
3769
|
fields: [{ name: "url", message: "Script URL" }],
|
|
3128
|
-
code: (v) => `// Custom third-party script
|
|
3770
|
+
code: (v) => `// Custom third-party script - delayed phase.
|
|
3129
3771
|
const s = document.createElement('script');
|
|
3130
3772
|
s.async = true;
|
|
3131
3773
|
s.src = ${JSON.stringify(v.url || "")};
|
|
@@ -3140,10 +3782,10 @@ var integrate_exports = {};
|
|
|
3140
3782
|
__export(integrate_exports, {
|
|
3141
3783
|
integrate: () => integrate
|
|
3142
3784
|
});
|
|
3143
|
-
import { existsSync as
|
|
3144
|
-
import { mkdir as
|
|
3145
|
-
import
|
|
3146
|
-
import
|
|
3785
|
+
import { existsSync as existsSync17 } from "fs";
|
|
3786
|
+
import { mkdir as mkdir6, readFile as readFile13, writeFile as writeFile10 } from "fs/promises";
|
|
3787
|
+
import path20 from "path";
|
|
3788
|
+
import chalk10 from "chalk";
|
|
3147
3789
|
async function integrate(type) {
|
|
3148
3790
|
const projectRoot = findProjectRoot();
|
|
3149
3791
|
if (!projectRoot) {
|
|
@@ -3151,7 +3793,7 @@ async function integrate(type) {
|
|
|
3151
3793
|
process.exitCode = 1;
|
|
3152
3794
|
return;
|
|
3153
3795
|
}
|
|
3154
|
-
logger.logoOnce(logo("Integrate
|
|
3796
|
+
logger.logoOnce(logo("Integrate - third-party service"));
|
|
3155
3797
|
const { select, input } = await import("@inquirer/prompts");
|
|
3156
3798
|
const chosenId = type || await select({
|
|
3157
3799
|
message: "Which service do you want to add?",
|
|
@@ -3170,26 +3812,39 @@ async function integrate(type) {
|
|
|
3170
3812
|
const answer = await input({ message: ` ${f.message}`, default: f.placeholder });
|
|
3171
3813
|
values[f.name] = answer.trim();
|
|
3172
3814
|
}
|
|
3173
|
-
const
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3815
|
+
const modulePaths = [];
|
|
3816
|
+
if (integration.module) {
|
|
3817
|
+
for (const mod of integration.module(values)) {
|
|
3818
|
+
const abs = path20.join(projectRoot, mod.file);
|
|
3819
|
+
await mkdir6(path20.dirname(abs), { recursive: true });
|
|
3820
|
+
await writeFile10(abs, mod.content);
|
|
3821
|
+
modulePaths.push(mod.file);
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
const block2 = integration.module ? `
|
|
3825
|
+
// ${integration.name} (eds integrate)
|
|
3826
|
+
${integration.code(values)}
|
|
3827
|
+
` : wrapBlock(integration.name, integration.code(values));
|
|
3828
|
+
const delayedPath = path20.join(projectRoot, "scripts", "delayed.js");
|
|
3829
|
+
await mkdir6(path20.dirname(delayedPath), { recursive: true });
|
|
3830
|
+
const existing = existsSync17(delayedPath) ? await readFile13(delayedPath, "utf-8") : DELAYED_HEADER;
|
|
3831
|
+
await writeFile10(delayedPath, `${existing.replace(/\s*$/, "")}
|
|
3178
3832
|
${block2}`);
|
|
3179
3833
|
logger.info(heading("Added"));
|
|
3180
|
-
logger.info(accentLine(integration.name,
|
|
3834
|
+
for (const p of modulePaths) logger.info(accentLine(integration.name, `wrote ${p}`));
|
|
3835
|
+
logger.info(accentLine(integration.name, "wired into scripts/delayed.js"));
|
|
3181
3836
|
logger.info("");
|
|
3182
3837
|
logger.info(
|
|
3183
|
-
|
|
3838
|
+
chalk10.dim(" Loads after LCP (delayed phase) - zero impact on your Core Web Vitals.")
|
|
3184
3839
|
);
|
|
3185
3840
|
if (integration.id === "gtm" || integration.id === "ga4") {
|
|
3186
3841
|
logger.info(
|
|
3187
|
-
|
|
3842
|
+
chalk10.dim(" Tip: gate analytics behind user consent (add the Cookiebot integration).")
|
|
3188
3843
|
);
|
|
3189
3844
|
}
|
|
3190
3845
|
if (integration.id === "chat") {
|
|
3191
3846
|
logger.info(
|
|
3192
|
-
|
|
3847
|
+
chalk10.dim(" The widget loads only when the visitor clicks the button (facade pattern).")
|
|
3193
3848
|
);
|
|
3194
3849
|
}
|
|
3195
3850
|
logger.info("");
|
|
@@ -3204,7 +3859,7 @@ var init_integrate = __esm({
|
|
|
3204
3859
|
init_logger();
|
|
3205
3860
|
init_project();
|
|
3206
3861
|
init_ui();
|
|
3207
|
-
DELAYED_HEADER = `// delayed.js
|
|
3862
|
+
DELAYED_HEADER = `// delayed.js - third-party / non-critical scripts.
|
|
3208
3863
|
// Loaded in the EDS delayed phase (after LCP), so nothing here blocks the page.
|
|
3209
3864
|
`;
|
|
3210
3865
|
}
|
|
@@ -3215,9 +3870,9 @@ var setup_exports = {};
|
|
|
3215
3870
|
__export(setup_exports, {
|
|
3216
3871
|
figmaSetup: () => figmaSetup
|
|
3217
3872
|
});
|
|
3218
|
-
import { existsSync as
|
|
3219
|
-
import { mkdir as
|
|
3220
|
-
import
|
|
3873
|
+
import { existsSync as existsSync18 } from "fs";
|
|
3874
|
+
import { mkdir as mkdir7, writeFile as writeFile11 } from "fs/promises";
|
|
3875
|
+
import chalk11 from "chalk";
|
|
3221
3876
|
async function figmaSetup() {
|
|
3222
3877
|
const { select, confirm } = await import("@inquirer/prompts");
|
|
3223
3878
|
logger.logoOnce(logo());
|
|
@@ -3227,15 +3882,15 @@ async function figmaSetup() {
|
|
|
3227
3882
|
let selectedAgent;
|
|
3228
3883
|
if (agents.length === 0) {
|
|
3229
3884
|
logger.warn("No AI agents found (claude, cursor, codex).");
|
|
3230
|
-
logger.info(`Install Claude Code: ${
|
|
3885
|
+
logger.info(`Install Claude Code: ${chalk11.cyan("npm install -g @anthropic-ai/claude-code")}`);
|
|
3231
3886
|
selectedAgent = "none";
|
|
3232
3887
|
} else {
|
|
3233
|
-
logger.success(`Found: ${agents.map((a) =>
|
|
3888
|
+
logger.success(`Found: ${agents.map((a) => chalk11.cyan(a.type)).join(", ")}`);
|
|
3234
3889
|
selectedAgent = await select({
|
|
3235
3890
|
message: "Which agent do you want to use with eds-cli?",
|
|
3236
3891
|
choices: [
|
|
3237
3892
|
...agents.map((a) => ({
|
|
3238
|
-
name: `${a.type}${a.hasFigmaMcp ?
|
|
3893
|
+
name: `${a.type}${a.hasFigmaMcp ? chalk11.green(" (Figma MCP detected)") : ""}`,
|
|
3239
3894
|
value: a.type
|
|
3240
3895
|
})),
|
|
3241
3896
|
{ name: "None (manual mode)", value: "none" }
|
|
@@ -3251,17 +3906,17 @@ async function figmaSetup() {
|
|
|
3251
3906
|
switch (selectedAgent) {
|
|
3252
3907
|
case "claude":
|
|
3253
3908
|
logger.info(
|
|
3254
|
-
` ${
|
|
3909
|
+
` ${chalk11.cyan("claude mcp add --transport sse figma-sse https://mcp.figma.com/sse")}`
|
|
3255
3910
|
);
|
|
3256
3911
|
logger.info(" or for Figma Desktop app:");
|
|
3257
3912
|
logger.info(
|
|
3258
|
-
` ${
|
|
3913
|
+
` ${chalk11.cyan("claude mcp add --transport http figma-desktop http://127.0.0.1:3845/mcp")}`
|
|
3259
3914
|
);
|
|
3260
3915
|
break;
|
|
3261
3916
|
case "cursor":
|
|
3262
|
-
logger.info(` Add to ${
|
|
3917
|
+
logger.info(` Add to ${chalk11.cyan("~/.cursor/mcp.json")}:`);
|
|
3263
3918
|
logger.info(
|
|
3264
|
-
|
|
3919
|
+
chalk11.dim(
|
|
3265
3920
|
JSON.stringify(
|
|
3266
3921
|
{
|
|
3267
3922
|
mcpServers: {
|
|
@@ -3286,7 +3941,7 @@ async function figmaSetup() {
|
|
|
3286
3941
|
}
|
|
3287
3942
|
}
|
|
3288
3943
|
logger.info(
|
|
3289
|
-
|
|
3944
|
+
chalk11.dim(
|
|
3290
3945
|
"\nNote: Figma MCP requires a Figma seat (Dev or Full on paid plans for desktop server)."
|
|
3291
3946
|
)
|
|
3292
3947
|
);
|
|
@@ -3296,22 +3951,22 @@ async function figmaSetup() {
|
|
|
3296
3951
|
});
|
|
3297
3952
|
if (shouldSave) {
|
|
3298
3953
|
const configDir = globalConfigDir();
|
|
3299
|
-
if (!
|
|
3300
|
-
await
|
|
3954
|
+
if (!existsSync18(configDir)) {
|
|
3955
|
+
await mkdir7(configDir, { recursive: true });
|
|
3301
3956
|
}
|
|
3302
3957
|
const config = {};
|
|
3303
3958
|
if (selectedAgent !== "none") {
|
|
3304
3959
|
config.agent = selectedAgent;
|
|
3305
3960
|
}
|
|
3306
|
-
await
|
|
3961
|
+
await writeFile11(globalConfigPath(), `${JSON.stringify(config, null, 2)}
|
|
3307
3962
|
`, {
|
|
3308
3963
|
mode: 384
|
|
3309
3964
|
});
|
|
3310
|
-
logger.success(`Config saved to ${
|
|
3965
|
+
logger.success(`Config saved to ${chalk11.cyan(globalConfigPath())}`);
|
|
3311
3966
|
}
|
|
3312
|
-
logger.info(
|
|
3967
|
+
logger.info(chalk11.bold("\nSetup complete!"));
|
|
3313
3968
|
if (selectedAgent !== "none") {
|
|
3314
|
-
logger.info(`Try: ${
|
|
3969
|
+
logger.info(`Try: ${chalk11.cyan('eds block from-design "<figma-url>" --dry-run')}`);
|
|
3315
3970
|
}
|
|
3316
3971
|
}
|
|
3317
3972
|
var init_setup = __esm({
|
|
@@ -3330,9 +3985,9 @@ var doctor_exports = {};
|
|
|
3330
3985
|
__export(doctor_exports, {
|
|
3331
3986
|
doctor: () => doctor
|
|
3332
3987
|
});
|
|
3333
|
-
import { existsSync as
|
|
3334
|
-
import { readFile as
|
|
3335
|
-
import
|
|
3988
|
+
import { existsSync as existsSync19 } from "fs";
|
|
3989
|
+
import { readFile as readFile14, readdir as readdir3 } from "fs/promises";
|
|
3990
|
+
import path21 from "path";
|
|
3336
3991
|
import { parse as parseYaml } from "yaml";
|
|
3337
3992
|
async function doctor(options = {}) {
|
|
3338
3993
|
const projectRoot = findProjectRoot();
|
|
@@ -3354,7 +4009,7 @@ async function doctor(options = {}) {
|
|
|
3354
4009
|
});
|
|
3355
4010
|
if (project.hasFstab) {
|
|
3356
4011
|
try {
|
|
3357
|
-
const content = await
|
|
4012
|
+
const content = await readFile14(path21.join(projectRoot, "fstab.yaml"), "utf-8");
|
|
3358
4013
|
parseYaml(content);
|
|
3359
4014
|
results.push({ name: "fstab.yaml valid YAML", status: "pass", message: "Valid" });
|
|
3360
4015
|
} catch {
|
|
@@ -3376,28 +4031,28 @@ async function doctor(options = {}) {
|
|
|
3376
4031
|
message: project.hasBlocks ? "Found" : "Missing - no blocks yet"
|
|
3377
4032
|
});
|
|
3378
4033
|
if (project.hasBlocks) {
|
|
3379
|
-
const blocksDir =
|
|
3380
|
-
const entries = await
|
|
4034
|
+
const blocksDir = path21.join(projectRoot, "blocks");
|
|
4035
|
+
const entries = await readdir3(blocksDir, { withFileTypes: true });
|
|
3381
4036
|
let aggregatedModels = "";
|
|
3382
|
-
const aggregatedPath =
|
|
3383
|
-
if (
|
|
4037
|
+
const aggregatedPath = path21.join(projectRoot, "component-models.json");
|
|
4038
|
+
if (existsSync19(aggregatedPath)) {
|
|
3384
4039
|
try {
|
|
3385
|
-
aggregatedModels = await
|
|
4040
|
+
aggregatedModels = await readFile14(aggregatedPath, "utf-8");
|
|
3386
4041
|
} catch {
|
|
3387
4042
|
}
|
|
3388
4043
|
}
|
|
3389
4044
|
for (const entry of entries) {
|
|
3390
4045
|
if (!entry.isDirectory()) continue;
|
|
3391
4046
|
const blockName = entry.name;
|
|
3392
|
-
const jsFile =
|
|
3393
|
-
const hasJs =
|
|
4047
|
+
const jsFile = path21.join(blocksDir, blockName, `${blockName}.js`);
|
|
4048
|
+
const hasJs = existsSync19(jsFile);
|
|
3394
4049
|
results.push({
|
|
3395
4050
|
name: `blocks/${blockName} has JS`,
|
|
3396
4051
|
status: hasJs ? "pass" : "warn",
|
|
3397
4052
|
message: hasJs ? "Found" : `Missing ${blockName}.js`
|
|
3398
4053
|
});
|
|
3399
4054
|
if (hasJs) {
|
|
3400
|
-
const jsContent = await
|
|
4055
|
+
const jsContent = await readFile14(jsFile, "utf-8");
|
|
3401
4056
|
if (jsContent.includes("console.log")) {
|
|
3402
4057
|
results.push({
|
|
3403
4058
|
name: `blocks/${blockName} no console.log`,
|
|
@@ -3406,17 +4061,17 @@ async function doctor(options = {}) {
|
|
|
3406
4061
|
});
|
|
3407
4062
|
}
|
|
3408
4063
|
}
|
|
3409
|
-
const hasPerBlockModel =
|
|
4064
|
+
const hasPerBlockModel = existsSync19(path21.join(blocksDir, blockName, `_${blockName}.json`));
|
|
3410
4065
|
const hasAggregatedModel = aggregatedModels.includes(`"${blockName}"`);
|
|
3411
4066
|
results.push({
|
|
3412
4067
|
name: `blocks/${blockName} UE model`,
|
|
3413
4068
|
status: hasPerBlockModel || hasAggregatedModel ? "pass" : "warn",
|
|
3414
4069
|
message: hasPerBlockModel || hasAggregatedModel ? hasPerBlockModel ? `_${blockName}.json` : "in component-models.json" : "no Universal Editor model - authors can't edit this block in UE"
|
|
3415
4070
|
});
|
|
3416
|
-
const metaFile =
|
|
3417
|
-
if (
|
|
4071
|
+
const metaFile = path21.join(blocksDir, blockName, ".eds-meta.json");
|
|
4072
|
+
if (existsSync19(metaFile)) {
|
|
3418
4073
|
try {
|
|
3419
|
-
const meta = JSON.parse(await
|
|
4074
|
+
const meta = JSON.parse(await readFile14(metaFile, "utf-8"));
|
|
3420
4075
|
const syncDate = new Date(meta.lastSyncedAt);
|
|
3421
4076
|
const daysSinceSync = Math.floor(
|
|
3422
4077
|
(Date.now() - syncDate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
@@ -3436,10 +4091,10 @@ async function doctor(options = {}) {
|
|
|
3436
4091
|
}
|
|
3437
4092
|
}
|
|
3438
4093
|
}
|
|
3439
|
-
const queryFile =
|
|
3440
|
-
if (
|
|
4094
|
+
const queryFile = path21.join(projectRoot, "helix-query.yaml");
|
|
4095
|
+
if (existsSync19(queryFile)) {
|
|
3441
4096
|
try {
|
|
3442
|
-
const content = await
|
|
4097
|
+
const content = await readFile14(queryFile, "utf-8");
|
|
3443
4098
|
parseYaml(content);
|
|
3444
4099
|
results.push({ name: "helix-query.yaml valid", status: "pass", message: "Valid YAML" });
|
|
3445
4100
|
} catch {
|
|
@@ -3496,10 +4151,10 @@ var audit_loading_exports = {};
|
|
|
3496
4151
|
__export(audit_loading_exports, {
|
|
3497
4152
|
auditLoading: () => auditLoading
|
|
3498
4153
|
});
|
|
3499
|
-
import { existsSync as
|
|
3500
|
-
import { readFile as
|
|
3501
|
-
import
|
|
3502
|
-
import
|
|
4154
|
+
import { existsSync as existsSync20 } from "fs";
|
|
4155
|
+
import { readFile as readFile15, readdir as readdir4, stat as stat3 } from "fs/promises";
|
|
4156
|
+
import path22 from "path";
|
|
4157
|
+
import chalk12 from "chalk";
|
|
3503
4158
|
async function auditLoading(options = {}) {
|
|
3504
4159
|
const projectRoot = findProjectRoot();
|
|
3505
4160
|
if (!projectRoot) {
|
|
@@ -3532,8 +4187,8 @@ async function auditLoading(options = {}) {
|
|
|
3532
4187
|
printResults(results);
|
|
3533
4188
|
}
|
|
3534
4189
|
async function checkHeadHtml(root, results) {
|
|
3535
|
-
const filePath =
|
|
3536
|
-
if (!
|
|
4190
|
+
const filePath = path22.join(root, "head.html");
|
|
4191
|
+
if (!existsSync20(filePath)) {
|
|
3537
4192
|
results.push({
|
|
3538
4193
|
rule: "head.html exists",
|
|
3539
4194
|
status: "warn",
|
|
@@ -3541,7 +4196,7 @@ async function checkHeadHtml(root, results) {
|
|
|
3541
4196
|
});
|
|
3542
4197
|
return;
|
|
3543
4198
|
}
|
|
3544
|
-
const content = await
|
|
4199
|
+
const content = await readFile15(filePath, "utf-8");
|
|
3545
4200
|
const lines = content.split("\n");
|
|
3546
4201
|
for (let i = 0; i < lines.length; i++) {
|
|
3547
4202
|
const line = lines[i];
|
|
@@ -3613,8 +4268,8 @@ async function checkHeadHtml(root, results) {
|
|
|
3613
4268
|
}
|
|
3614
4269
|
}
|
|
3615
4270
|
async function checkScriptsJs(root, results) {
|
|
3616
|
-
const filePath =
|
|
3617
|
-
if (!
|
|
4271
|
+
const filePath = path22.join(root, "scripts", "scripts.js");
|
|
4272
|
+
if (!existsSync20(filePath)) {
|
|
3618
4273
|
results.push({
|
|
3619
4274
|
rule: "scripts/scripts.js exists",
|
|
3620
4275
|
status: "warn",
|
|
@@ -3622,7 +4277,7 @@ async function checkScriptsJs(root, results) {
|
|
|
3622
4277
|
});
|
|
3623
4278
|
return;
|
|
3624
4279
|
}
|
|
3625
|
-
const content = await
|
|
4280
|
+
const content = await readFile15(filePath, "utf-8");
|
|
3626
4281
|
const lines = content.split("\n");
|
|
3627
4282
|
const hasLoadEager = /function\s+loadEager|loadEager\s*=/i.test(content);
|
|
3628
4283
|
const hasLoadLazy = /function\s+loadLazy|loadLazy\s*=/i.test(content);
|
|
@@ -3700,8 +4355,8 @@ async function checkScriptsJs(root, results) {
|
|
|
3700
4355
|
}
|
|
3701
4356
|
}
|
|
3702
4357
|
async function checkStylesCss(root, results) {
|
|
3703
|
-
const filePath =
|
|
3704
|
-
if (!
|
|
4358
|
+
const filePath = path22.join(root, "styles", "styles.css");
|
|
4359
|
+
if (!existsSync20(filePath)) return;
|
|
3705
4360
|
const stats = await stat3(filePath);
|
|
3706
4361
|
const sizeKb = stats.size / 1024;
|
|
3707
4362
|
results.push({
|
|
@@ -3710,7 +4365,7 @@ async function checkStylesCss(root, results) {
|
|
|
3710
4365
|
message: `${sizeKb.toFixed(1)} KB${sizeKb > 40 ? " \u2014 large CSS competes with 100KB LCP budget" : ""}`,
|
|
3711
4366
|
file: "styles/styles.css"
|
|
3712
4367
|
});
|
|
3713
|
-
const content = await
|
|
4368
|
+
const content = await readFile15(filePath, "utf-8");
|
|
3714
4369
|
if (/@import\s/.test(content)) {
|
|
3715
4370
|
results.push({
|
|
3716
4371
|
rule: "No @import in styles.css",
|
|
@@ -3721,8 +4376,8 @@ async function checkStylesCss(root, results) {
|
|
|
3721
4376
|
}
|
|
3722
4377
|
}
|
|
3723
4378
|
async function checkDelayedJs(root, results) {
|
|
3724
|
-
const filePath =
|
|
3725
|
-
if (!
|
|
4379
|
+
const filePath = path22.join(root, "scripts", "delayed.js");
|
|
4380
|
+
if (!existsSync20(filePath)) {
|
|
3726
4381
|
results.push({
|
|
3727
4382
|
rule: "scripts/delayed.js exists",
|
|
3728
4383
|
status: "info",
|
|
@@ -3730,7 +4385,7 @@ async function checkDelayedJs(root, results) {
|
|
|
3730
4385
|
});
|
|
3731
4386
|
return;
|
|
3732
4387
|
}
|
|
3733
|
-
const content = await
|
|
4388
|
+
const content = await readFile15(filePath, "utf-8");
|
|
3734
4389
|
const stripped = content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").trim();
|
|
3735
4390
|
if (!stripped) {
|
|
3736
4391
|
results.push({
|
|
@@ -3742,15 +4397,15 @@ async function checkDelayedJs(root, results) {
|
|
|
3742
4397
|
}
|
|
3743
4398
|
}
|
|
3744
4399
|
async function checkBlocks(root, results) {
|
|
3745
|
-
const blocksDir =
|
|
3746
|
-
if (!
|
|
3747
|
-
const entries = await
|
|
4400
|
+
const blocksDir = path22.join(root, "blocks");
|
|
4401
|
+
if (!existsSync20(blocksDir)) return;
|
|
4402
|
+
const entries = await readdir4(blocksDir, { withFileTypes: true });
|
|
3748
4403
|
for (const entry of entries) {
|
|
3749
4404
|
if (!entry.isDirectory()) continue;
|
|
3750
4405
|
const blockName = entry.name;
|
|
3751
|
-
const jsFile =
|
|
3752
|
-
if (!
|
|
3753
|
-
const content = await
|
|
4406
|
+
const jsFile = path22.join(blocksDir, blockName, `${blockName}.js`);
|
|
4407
|
+
if (!existsSync20(jsFile)) continue;
|
|
4408
|
+
const content = await readFile15(jsFile, "utf-8");
|
|
3754
4409
|
const lines = content.split("\n");
|
|
3755
4410
|
const heavyLibs = [
|
|
3756
4411
|
"jquery",
|
|
@@ -3800,9 +4455,9 @@ async function checkBlocks(root, results) {
|
|
|
3800
4455
|
for (const entry of entries) {
|
|
3801
4456
|
if (!entry.isDirectory()) continue;
|
|
3802
4457
|
const blockName = entry.name;
|
|
3803
|
-
const cssFile =
|
|
3804
|
-
if (!
|
|
3805
|
-
const content = await
|
|
4458
|
+
const cssFile = path22.join(blocksDir, blockName, `${blockName}.css`);
|
|
4459
|
+
if (!existsSync20(cssFile)) continue;
|
|
4460
|
+
const content = await readFile15(cssFile, "utf-8");
|
|
3806
4461
|
const selectorPattern = /^([a-z][a-z0-9-]*)\s*\{/gm;
|
|
3807
4462
|
for (const selectorMatch of content.matchAll(selectorPattern)) {
|
|
3808
4463
|
const selector = selectorMatch[1];
|
|
@@ -3818,8 +4473,8 @@ async function checkBlocks(root, results) {
|
|
|
3818
4473
|
}
|
|
3819
4474
|
}
|
|
3820
4475
|
async function checkAemJs(root, results) {
|
|
3821
|
-
const filePath =
|
|
3822
|
-
if (!
|
|
4476
|
+
const filePath = path22.join(root, "scripts", "aem.js");
|
|
4477
|
+
if (!existsSync20(filePath)) {
|
|
3823
4478
|
results.push({
|
|
3824
4479
|
rule: "scripts/aem.js exists",
|
|
3825
4480
|
status: "warn",
|
|
@@ -3857,12 +4512,12 @@ function printResults(results) {
|
|
|
3857
4512
|
grouped.set(key, bucket);
|
|
3858
4513
|
}
|
|
3859
4514
|
for (const [file, fileResults] of grouped) {
|
|
3860
|
-
logger.info(` ${icon.bullet} ${
|
|
4515
|
+
logger.info(` ${icon.bullet} ${chalk12.underline(file)}`);
|
|
3861
4516
|
for (const r of fileResults) {
|
|
3862
|
-
const loc = r.line ?
|
|
4517
|
+
const loc = r.line ? chalk12.dim(`:${r.line}`) : "";
|
|
3863
4518
|
logger.info(` ${statusIcon(r.status)} ${r.rule}${loc}`);
|
|
3864
4519
|
if (r.status !== "pass") {
|
|
3865
|
-
logger.info(
|
|
4520
|
+
logger.info(chalk12.dim(` ${r.message}`));
|
|
3866
4521
|
}
|
|
3867
4522
|
}
|
|
3868
4523
|
logger.info("");
|
|
@@ -3891,17 +4546,17 @@ var audit_security_exports = {};
|
|
|
3891
4546
|
__export(audit_security_exports, {
|
|
3892
4547
|
auditSecurity: () => auditSecurity
|
|
3893
4548
|
});
|
|
3894
|
-
import { existsSync as
|
|
3895
|
-
import { readFile as
|
|
3896
|
-
import
|
|
3897
|
-
import
|
|
3898
|
-
async function*
|
|
3899
|
-
const entries = await
|
|
4549
|
+
import { existsSync as existsSync21 } from "fs";
|
|
4550
|
+
import { readFile as readFile16, readdir as readdir5 } from "fs/promises";
|
|
4551
|
+
import path23 from "path";
|
|
4552
|
+
import chalk13 from "chalk";
|
|
4553
|
+
async function* walk2(dir) {
|
|
4554
|
+
const entries = await readdir5(dir, { withFileTypes: true });
|
|
3900
4555
|
for (const entry of entries) {
|
|
3901
|
-
const full =
|
|
4556
|
+
const full = path23.join(dir, entry.name);
|
|
3902
4557
|
if (entry.isDirectory()) {
|
|
3903
4558
|
if (IGNORE_DIRS.has(entry.name)) continue;
|
|
3904
|
-
yield*
|
|
4559
|
+
yield* walk2(full);
|
|
3905
4560
|
} else if (entry.isFile()) {
|
|
3906
4561
|
yield full;
|
|
3907
4562
|
}
|
|
@@ -3932,11 +4587,11 @@ function scanContent(content, relFile, ext) {
|
|
|
3932
4587
|
return findings;
|
|
3933
4588
|
}
|
|
3934
4589
|
async function auditDependencies(root) {
|
|
3935
|
-
if (!
|
|
4590
|
+
if (!existsSync21(path23.join(root, "package.json"))) {
|
|
3936
4591
|
return { status: "skipped", note: "no package.json" };
|
|
3937
4592
|
}
|
|
3938
|
-
const hasLock =
|
|
3939
|
-
if (!hasLock && !
|
|
4593
|
+
const hasLock = existsSync21(path23.join(root, "package-lock.json")) || existsSync21(path23.join(root, "npm-shrinkwrap.json"));
|
|
4594
|
+
if (!hasLock && !existsSync21(path23.join(root, "node_modules"))) {
|
|
3940
4595
|
return { status: "skipped", note: "no package-lock.json / node_modules \u2014 run npm install" };
|
|
3941
4596
|
}
|
|
3942
4597
|
try {
|
|
@@ -3967,12 +4622,12 @@ async function auditSecurity(options = {}) {
|
|
|
3967
4622
|
return;
|
|
3968
4623
|
}
|
|
3969
4624
|
const findings = [];
|
|
3970
|
-
for await (const file of
|
|
3971
|
-
const ext =
|
|
4625
|
+
for await (const file of walk2(projectRoot)) {
|
|
4626
|
+
const ext = path23.extname(file).slice(1).toLowerCase();
|
|
3972
4627
|
if (!SCAN_EXT.has(ext)) continue;
|
|
3973
|
-
const rel =
|
|
4628
|
+
const rel = path23.relative(projectRoot, file);
|
|
3974
4629
|
if (rel.endsWith("aem.js") || rel.endsWith("lib-franklin.js")) continue;
|
|
3975
|
-
const content = await
|
|
4630
|
+
const content = await readFile16(file, "utf-8");
|
|
3976
4631
|
findings.push(...scanContent(content, rel, ext));
|
|
3977
4632
|
}
|
|
3978
4633
|
const deps = await auditDependencies(projectRoot);
|
|
@@ -4020,9 +4675,9 @@ async function auditSecurity(options = {}) {
|
|
|
4020
4675
|
);
|
|
4021
4676
|
for (const f of sorted) {
|
|
4022
4677
|
const meta = SEVERITY_META[f.severity];
|
|
4023
|
-
const loc =
|
|
4024
|
-
logger.info(` ${statusIcon(meta.status)} ${
|
|
4025
|
-
logger.info(
|
|
4678
|
+
const loc = chalk13.dim(`${f.file}:${f.line}`);
|
|
4679
|
+
logger.info(` ${statusIcon(meta.status)} ${chalk13.bold(meta.label)} ${f.message} ${loc}`);
|
|
4680
|
+
logger.info(chalk13.dim(` ${f.snippet}`));
|
|
4026
4681
|
}
|
|
4027
4682
|
}
|
|
4028
4683
|
const summaryLine = summary({
|
|
@@ -4229,7 +4884,7 @@ __export(preview_exports2, {
|
|
|
4229
4884
|
preview: () => preview,
|
|
4230
4885
|
resolveAdminConfig: () => resolveAdminConfig
|
|
4231
4886
|
});
|
|
4232
|
-
import
|
|
4887
|
+
import chalk14 from "chalk";
|
|
4233
4888
|
import ora2 from "ora";
|
|
4234
4889
|
async function preview(paths, options) {
|
|
4235
4890
|
const projectRoot = findProjectRoot();
|
|
@@ -4242,7 +4897,7 @@ async function preview(paths, options) {
|
|
|
4242
4897
|
let hasErrors = false;
|
|
4243
4898
|
for (const r of results) {
|
|
4244
4899
|
if (r.ok) {
|
|
4245
|
-
logger.success(`${r.path} \u2192 ${
|
|
4900
|
+
logger.success(`${r.path} \u2192 ${chalk14.cyan(r.previewUrl || "preview updated")}`);
|
|
4246
4901
|
} else {
|
|
4247
4902
|
logger.error(`${r.path} \u2014 ${r.message}`);
|
|
4248
4903
|
hasErrors = true;
|
|
@@ -4256,7 +4911,7 @@ function resolveAdminConfig(config, options) {
|
|
|
4256
4911
|
const ref = options.ref || config.admin?.ref || "main";
|
|
4257
4912
|
if (!org || !site) {
|
|
4258
4913
|
logger.error("Missing --org and --site. Set them via flags or in .edsrc.json:");
|
|
4259
|
-
logger.info(
|
|
4914
|
+
logger.info(chalk14.dim(' { "admin": { "org": "my-org", "site": "my-site" } }'));
|
|
4260
4915
|
process.exitCode = 1;
|
|
4261
4916
|
return void 0;
|
|
4262
4917
|
}
|
|
@@ -4278,7 +4933,7 @@ var publish_exports = {};
|
|
|
4278
4933
|
__export(publish_exports, {
|
|
4279
4934
|
publish: () => publish
|
|
4280
4935
|
});
|
|
4281
|
-
import
|
|
4936
|
+
import chalk15 from "chalk";
|
|
4282
4937
|
import ora3 from "ora";
|
|
4283
4938
|
async function publish(paths, options) {
|
|
4284
4939
|
const projectRoot = findProjectRoot();
|
|
@@ -4291,7 +4946,7 @@ async function publish(paths, options) {
|
|
|
4291
4946
|
let hasErrors = false;
|
|
4292
4947
|
for (const r of results) {
|
|
4293
4948
|
if (r.ok) {
|
|
4294
|
-
logger.success(`${r.path} \u2192 ${
|
|
4949
|
+
logger.success(`${r.path} \u2192 ${chalk15.cyan(r.liveUrl || "published")}`);
|
|
4295
4950
|
} else {
|
|
4296
4951
|
logger.error(`${r.path} \u2014 ${r.message}`);
|
|
4297
4952
|
hasErrors = true;
|
|
@@ -4390,13 +5045,13 @@ var menu_exports = {};
|
|
|
4390
5045
|
__export(menu_exports, {
|
|
4391
5046
|
mainMenu: () => mainMenu
|
|
4392
5047
|
});
|
|
4393
|
-
import { existsSync as
|
|
4394
|
-
import { readdir as
|
|
4395
|
-
import
|
|
5048
|
+
import { existsSync as existsSync22 } from "fs";
|
|
5049
|
+
import { readdir as readdir6 } from "fs/promises";
|
|
5050
|
+
import path24 from "path";
|
|
4396
5051
|
async function listBlockNames(projectRoot) {
|
|
4397
|
-
const dir =
|
|
4398
|
-
if (!
|
|
4399
|
-
const entries = await
|
|
5052
|
+
const dir = path24.join(projectRoot, "blocks");
|
|
5053
|
+
if (!existsSync22(dir)) return [];
|
|
5054
|
+
const entries = await readdir6(dir, { withFileTypes: true });
|
|
4400
5055
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
4401
5056
|
}
|
|
4402
5057
|
async function mainMenu() {
|
|
@@ -4406,10 +5061,15 @@ async function mainMenu() {
|
|
|
4406
5061
|
message: "Choose an action",
|
|
4407
5062
|
choices: [
|
|
4408
5063
|
{ name: "Set up this project (authoring model, CI, naming)", value: "init" },
|
|
4409
|
-
{
|
|
5064
|
+
{
|
|
5065
|
+
name: "Generate a block from a design (Figma/Stitch/Canva/Sketch/Framer)",
|
|
5066
|
+
value: "from-design"
|
|
5067
|
+
},
|
|
5068
|
+
{ name: "Migrate a classic AEM component to EDS", value: "migrate" },
|
|
4410
5069
|
{ name: "Scaffold Universal Editor components", value: "scaffold" },
|
|
4411
5070
|
{ name: "Create an empty block", value: "create" },
|
|
4412
5071
|
{ name: "Add a third-party integration (GTM, chat, consent\u2026)", value: "integrate" },
|
|
5072
|
+
{ name: "Instrument a block for analytics (dataLayer)", value: "track" },
|
|
4413
5073
|
{ name: "Preview a block in the browser", value: "preview-block" },
|
|
4414
5074
|
{ name: "Preview / publish pages (Admin API)", value: "admin" },
|
|
4415
5075
|
{ name: "Audit the project", value: "audit" },
|
|
@@ -4427,6 +5087,11 @@ async function mainMenu() {
|
|
|
4427
5087
|
await blockFromDesign2(void 0, {});
|
|
4428
5088
|
return;
|
|
4429
5089
|
}
|
|
5090
|
+
case "migrate": {
|
|
5091
|
+
const { migrateComponent: migrateComponent2 } = await Promise.resolve().then(() => (init_migrate(), migrate_exports));
|
|
5092
|
+
await migrateComponent2(void 0, {});
|
|
5093
|
+
return;
|
|
5094
|
+
}
|
|
4430
5095
|
case "scaffold": {
|
|
4431
5096
|
const { scaffoldInteractive: scaffoldInteractive2 } = await Promise.resolve().then(() => (init_scaffold(), scaffold_exports));
|
|
4432
5097
|
await scaffoldInteractive2();
|
|
@@ -4465,6 +5130,25 @@ async function mainMenu() {
|
|
|
4465
5130
|
await previewBlock2(name, {});
|
|
4466
5131
|
return;
|
|
4467
5132
|
}
|
|
5133
|
+
case "track": {
|
|
5134
|
+
const projectRoot = findProjectRoot();
|
|
5135
|
+
if (!projectRoot) {
|
|
5136
|
+
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
5137
|
+
return;
|
|
5138
|
+
}
|
|
5139
|
+
const blocks = await listBlockNames(projectRoot);
|
|
5140
|
+
if (blocks.length === 0) {
|
|
5141
|
+
logger.warn("No blocks found in blocks/.");
|
|
5142
|
+
return;
|
|
5143
|
+
}
|
|
5144
|
+
const name = await select({
|
|
5145
|
+
message: "Which block to instrument?",
|
|
5146
|
+
choices: blocks.map((b) => ({ name: b, value: b }))
|
|
5147
|
+
});
|
|
5148
|
+
const { trackBlock: trackBlock2 } = await Promise.resolve().then(() => (init_track2(), track_exports));
|
|
5149
|
+
await trackBlock2(name, {});
|
|
5150
|
+
return;
|
|
5151
|
+
}
|
|
4468
5152
|
case "admin": {
|
|
4469
5153
|
const { adminWizard: adminWizard2 } = await Promise.resolve().then(() => (init_admin(), admin_exports));
|
|
4470
5154
|
await adminWizard2();
|
|
@@ -4510,13 +5194,13 @@ var init_menu = __esm({
|
|
|
4510
5194
|
|
|
4511
5195
|
// src/bin/eds.ts
|
|
4512
5196
|
init_esm_shims();
|
|
4513
|
-
import
|
|
5197
|
+
import chalk16 from "chalk";
|
|
4514
5198
|
import { Command } from "commander";
|
|
4515
5199
|
|
|
4516
5200
|
// package.json
|
|
4517
5201
|
var package_default = {
|
|
4518
5202
|
name: "aem-eds-kit",
|
|
4519
|
-
version: "0.4.
|
|
5203
|
+
version: "0.4.12",
|
|
4520
5204
|
description: "CLI for AEM Edge Delivery Services with Figma MCP integration",
|
|
4521
5205
|
type: "module",
|
|
4522
5206
|
engines: {
|
|
@@ -4566,6 +5250,7 @@ var package_default = {
|
|
|
4566
5250
|
chalk: "^5.4.0",
|
|
4567
5251
|
commander: "^13.0.0",
|
|
4568
5252
|
execa: "^9.5.0",
|
|
5253
|
+
"fast-xml-parser": "^5.11.2",
|
|
4569
5254
|
ora: "^8.1.0",
|
|
4570
5255
|
yaml: "^2.6.0",
|
|
4571
5256
|
zod: "^3.24.0"
|
|
@@ -4591,13 +5276,23 @@ program.name("eds").description("CLI for AEM Edge Delivery Services with Figma i
|
|
|
4591
5276
|
const opts = thisCommand.opts();
|
|
4592
5277
|
if (opts.verbose) logger.setLevel("debug");
|
|
4593
5278
|
if (opts.quiet) logger.setQuiet(true);
|
|
4594
|
-
if (opts.color === false)
|
|
5279
|
+
if (opts.color === false) chalk16.level = 0;
|
|
4595
5280
|
});
|
|
4596
5281
|
program.addHelpText("beforeAll", () => logo());
|
|
4597
5282
|
program.command("init").description("Set up a freshly cloned EDS boilerplate for your authoring model (interactive)").option("--name <name>", "Project name (kebab-case)").option("--authoring <model>", "Authoring model: ue | da | gdrive | sharepoint").option("--mountpoint <url>", "Content source URL (document models)").option("--org <org>", "DA org (authoring=da)").option("--site <site>", "DA site (authoring=da)").option("--no-ci", "Skip the GitHub Actions CI workflow").option("--yes", "Skip prompts; apply with flags/defaults").action(async (options) => {
|
|
4598
5283
|
const { initProject: initProject2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
4599
5284
|
await initProject2(options);
|
|
4600
5285
|
});
|
|
5286
|
+
var migrate = program.command("migrate").description("Migrate from classic AEM to EDS");
|
|
5287
|
+
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) => {
|
|
5288
|
+
const { migrateComponent: migrateComponent2 } = await Promise.resolve().then(() => (init_migrate(), migrate_exports));
|
|
5289
|
+
await migrateComponent2(dir, options);
|
|
5290
|
+
});
|
|
5291
|
+
var track = program.command("track").description("Instrument blocks for analytics");
|
|
5292
|
+
track.command("block <name>").description("Add dataLayer tracking (click + form submit) to a block's decorate()").option("--no-click", "Skip click tracking").option("--no-submit", "Skip form-submit tracking").action(async (name, options) => {
|
|
5293
|
+
const { trackBlock: trackBlock2 } = await Promise.resolve().then(() => (init_track2(), track_exports));
|
|
5294
|
+
await trackBlock2(name, options);
|
|
5295
|
+
});
|
|
4601
5296
|
var block = program.command("block").description("Manage EDS blocks");
|
|
4602
5297
|
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
5298
|
const { createBlock: createBlock2 } = await Promise.resolve().then(() => (init_create(), create_exports));
|