aem-eds-kit 0.4.9 → 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 +33 -2
- package/README.md +39 -4
- package/dist/bin/eds.js +519 -272
- package/dist/bin/eds.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/eds.js
CHANGED
|
@@ -1708,14 +1708,134 @@ var init_component = __esm({
|
|
|
1708
1708
|
}
|
|
1709
1709
|
});
|
|
1710
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
|
+
|
|
1711
1831
|
// src/commands/migrate.ts
|
|
1712
1832
|
var migrate_exports = {};
|
|
1713
1833
|
__export(migrate_exports, {
|
|
1714
1834
|
migrateComponent: () => migrateComponent
|
|
1715
1835
|
});
|
|
1716
|
-
import { existsSync as
|
|
1717
|
-
import { mkdir as mkdir3, writeFile as
|
|
1718
|
-
import
|
|
1836
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1837
|
+
import { mkdir as mkdir3, writeFile as writeFile6 } from "fs/promises";
|
|
1838
|
+
import path10 from "path";
|
|
1719
1839
|
function clientlibBlockCss(blockName, css, files) {
|
|
1720
1840
|
return `/* Block: ${blockName} */
|
|
1721
1841
|
/* Migrated from the component clientlib: ${files.join(", ")}. */
|
|
@@ -1763,7 +1883,7 @@ async function migrateComponent(dirArg, options = {}) {
|
|
|
1763
1883
|
if (!componentDir && interactive) {
|
|
1764
1884
|
componentDir = await input({
|
|
1765
1885
|
message: "Path to the classic component folder",
|
|
1766
|
-
validate: (v) =>
|
|
1886
|
+
validate: (v) => existsSync8(v.trim()) || "Folder not found"
|
|
1767
1887
|
});
|
|
1768
1888
|
}
|
|
1769
1889
|
if (!componentDir) {
|
|
@@ -1771,7 +1891,7 @@ async function migrateComponent(dirArg, options = {}) {
|
|
|
1771
1891
|
process.exitCode = 1;
|
|
1772
1892
|
return;
|
|
1773
1893
|
}
|
|
1774
|
-
componentDir =
|
|
1894
|
+
componentDir = path10.resolve(componentDir.trim());
|
|
1775
1895
|
if (!findDialogFile(componentDir)) {
|
|
1776
1896
|
logger.error(`No Touch UI dialog (_cq_dialog/.content.xml) found in ${componentDir}.`);
|
|
1777
1897
|
process.exitCode = 1;
|
|
@@ -1785,7 +1905,7 @@ async function migrateComponent(dirArg, options = {}) {
|
|
|
1785
1905
|
process.exitCode = 1;
|
|
1786
1906
|
return;
|
|
1787
1907
|
}
|
|
1788
|
-
const title = await readComponentTitle(componentDir) ??
|
|
1908
|
+
const title = await readComponentTitle(componentDir) ?? path10.basename(componentDir);
|
|
1789
1909
|
const blockName = options.name && slug2(options.name) || (interactive ? slug2(await input({ message: "Block name", default: slug2(title) })) : slug2(title));
|
|
1790
1910
|
logger.info(heading("Mapped fields", `${countLeaves(parsed.fields)} from dialog`));
|
|
1791
1911
|
for (const line of previewFields(parsed.fields)) logger.info(` ${line}`);
|
|
@@ -1801,19 +1921,19 @@ async function migrateComponent(dirArg, options = {}) {
|
|
|
1801
1921
|
return;
|
|
1802
1922
|
}
|
|
1803
1923
|
}
|
|
1804
|
-
const blockDir =
|
|
1805
|
-
if (
|
|
1924
|
+
const blockDir = path10.join(projectRoot, "blocks", blockName);
|
|
1925
|
+
if (existsSync8(blockDir)) {
|
|
1806
1926
|
logger.error(
|
|
1807
1927
|
`Block "${blockName}" already exists at blocks/${blockName}. Use --name to pick another.`
|
|
1808
1928
|
);
|
|
1809
1929
|
process.exitCode = 1;
|
|
1810
1930
|
return;
|
|
1811
1931
|
}
|
|
1812
|
-
const clibDir = options.clientlib ?
|
|
1932
|
+
const clibDir = options.clientlib ? path10.resolve(options.clientlib.trim()) : await findClientlibDir(componentDir);
|
|
1813
1933
|
let cssContent = migratedBlockCss(blockName);
|
|
1814
1934
|
let cssMessage = "empty - add styles from clientlib / rendered CSS";
|
|
1815
1935
|
let preprocessedNote = [];
|
|
1816
|
-
if (clibDir &&
|
|
1936
|
+
if (clibDir && existsSync8(clibDir)) {
|
|
1817
1937
|
const lib = await readClientlibCss(clibDir);
|
|
1818
1938
|
if (lib.css.trim()) {
|
|
1819
1939
|
cssContent = clientlibBlockCss(blockName, lib.css, lib.files);
|
|
@@ -1822,13 +1942,13 @@ async function migrateComponent(dirArg, options = {}) {
|
|
|
1822
1942
|
preprocessedNote = lib.preprocessed;
|
|
1823
1943
|
}
|
|
1824
1944
|
await mkdir3(blockDir, { recursive: true });
|
|
1825
|
-
await
|
|
1826
|
-
|
|
1945
|
+
await writeFile6(
|
|
1946
|
+
path10.join(blockDir, `${blockName}.js`),
|
|
1827
1947
|
migratedBlockJs(blockName, parsed.fields)
|
|
1828
1948
|
);
|
|
1829
|
-
await
|
|
1830
|
-
await
|
|
1831
|
-
|
|
1949
|
+
await writeFile6(path10.join(blockDir, `${blockName}.css`), cssContent);
|
|
1950
|
+
await writeFile6(
|
|
1951
|
+
path10.join(blockDir, `_${blockName}.json`),
|
|
1832
1952
|
migratedModelFile(blockName, title, parsed.fields)
|
|
1833
1953
|
);
|
|
1834
1954
|
logger.info(heading("Created"));
|
|
@@ -1842,6 +1962,16 @@ async function migrateComponent(dirArg, options = {}) {
|
|
|
1842
1962
|
logger.warn(`LESS/SCSS found (needs a build, not copied): ${preprocessedNote.join(", ")}`);
|
|
1843
1963
|
}
|
|
1844
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
|
+
}
|
|
1845
1975
|
logger.info(
|
|
1846
1976
|
box([`"${blockName}" migrated. Next: add CSS, then \`eds block preview ${blockName}\``])
|
|
1847
1977
|
);
|
|
@@ -1866,10 +1996,10 @@ __export(create_exports, {
|
|
|
1866
1996
|
createBlock: () => createBlock,
|
|
1867
1997
|
ueModelFor: () => ueModelFor
|
|
1868
1998
|
});
|
|
1869
|
-
import { existsSync as
|
|
1870
|
-
import { mkdir as mkdir4, writeFile as
|
|
1871
|
-
import
|
|
1872
|
-
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";
|
|
1873
2003
|
function titleCase(name) {
|
|
1874
2004
|
return name.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
1875
2005
|
}
|
|
@@ -1947,8 +2077,8 @@ async function createBlock(name, options = {}) {
|
|
|
1947
2077
|
process.exitCode = 1;
|
|
1948
2078
|
return;
|
|
1949
2079
|
}
|
|
1950
|
-
const blocksDir =
|
|
1951
|
-
if (
|
|
2080
|
+
const blocksDir = path11.join(projectRoot, "blocks", name);
|
|
2081
|
+
if (existsSync9(blocksDir)) {
|
|
1952
2082
|
logger.error(`Block "${name}" already exists at ${blocksDir}`);
|
|
1953
2083
|
process.exitCode = 1;
|
|
1954
2084
|
return;
|
|
@@ -1962,14 +2092,14 @@ async function createBlock(name, options = {}) {
|
|
|
1962
2092
|
/* TODO: style ${name} block */
|
|
1963
2093
|
}
|
|
1964
2094
|
`;
|
|
1965
|
-
await
|
|
1966
|
-
await
|
|
2095
|
+
await writeFile7(path11.join(blocksDir, `${name}.js`), jsContent);
|
|
2096
|
+
await writeFile7(path11.join(blocksDir, `${name}.css`), cssContent);
|
|
1967
2097
|
const withUe = options.ueModel !== false;
|
|
1968
2098
|
if (withUe) {
|
|
1969
|
-
await
|
|
2099
|
+
await writeFile7(path11.join(blocksDir, `_${name}.json`), ueModelFor(name));
|
|
1970
2100
|
}
|
|
1971
2101
|
logger.success(
|
|
1972
|
-
`Block "${name}" created at ${
|
|
2102
|
+
`Block "${name}" created at ${chalk5.cyan(path11.relative(projectRoot, blocksDir))}${withUe ? chalk5.dim(" (+ Universal Editor model)") : ""}`
|
|
1973
2103
|
);
|
|
1974
2104
|
}
|
|
1975
2105
|
var init_create = __esm({
|
|
@@ -1983,10 +2113,10 @@ var init_create = __esm({
|
|
|
1983
2113
|
});
|
|
1984
2114
|
|
|
1985
2115
|
// src/lib/agents/detect.ts
|
|
1986
|
-
import { existsSync as
|
|
1987
|
-
import { readFile as
|
|
2116
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2117
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
1988
2118
|
import { homedir } from "os";
|
|
1989
|
-
import
|
|
2119
|
+
import path12 from "path";
|
|
1990
2120
|
async function whichBinary(name) {
|
|
1991
2121
|
const { execaCommand } = await import("execa");
|
|
1992
2122
|
try {
|
|
@@ -2014,8 +2144,8 @@ function findFigmaServerKey(node) {
|
|
|
2014
2144
|
async function findFigmaMcpServer(configPaths) {
|
|
2015
2145
|
for (const configPath of configPaths) {
|
|
2016
2146
|
try {
|
|
2017
|
-
if (!
|
|
2018
|
-
const content = await
|
|
2147
|
+
if (!existsSync10(configPath)) continue;
|
|
2148
|
+
const content = await readFile6(configPath, "utf-8");
|
|
2019
2149
|
try {
|
|
2020
2150
|
const key = findFigmaServerKey(JSON.parse(content));
|
|
2021
2151
|
if (key) return key;
|
|
@@ -2037,7 +2167,7 @@ async function detectAgent(preferred) {
|
|
|
2037
2167
|
return {
|
|
2038
2168
|
type: def.type,
|
|
2039
2169
|
path: binPath,
|
|
2040
|
-
configDir:
|
|
2170
|
+
configDir: existsSync10(def.configDir) ? def.configDir : void 0,
|
|
2041
2171
|
hasFigmaMcp: !!figmaMcpServerName,
|
|
2042
2172
|
figmaMcpServerName
|
|
2043
2173
|
};
|
|
@@ -2052,7 +2182,7 @@ async function detectAgent(preferred) {
|
|
|
2052
2182
|
return {
|
|
2053
2183
|
type: def.type,
|
|
2054
2184
|
path: binPath,
|
|
2055
|
-
configDir:
|
|
2185
|
+
configDir: existsSync10(def.configDir) ? def.configDir : void 0,
|
|
2056
2186
|
hasFigmaMcp: !!figmaMcpServerName,
|
|
2057
2187
|
figmaMcpServerName
|
|
2058
2188
|
};
|
|
@@ -2069,7 +2199,7 @@ async function detectAllAgents() {
|
|
|
2069
2199
|
agents.push({
|
|
2070
2200
|
type: def.type,
|
|
2071
2201
|
path: binPath,
|
|
2072
|
-
configDir:
|
|
2202
|
+
configDir: existsSync10(def.configDir) ? def.configDir : void 0,
|
|
2073
2203
|
hasFigmaMcp: !!figmaMcpServerName,
|
|
2074
2204
|
figmaMcpServerName
|
|
2075
2205
|
});
|
|
@@ -2087,22 +2217,22 @@ var init_detect = __esm({
|
|
|
2087
2217
|
{
|
|
2088
2218
|
type: "claude",
|
|
2089
2219
|
binary: "claude",
|
|
2090
|
-
configDir:
|
|
2220
|
+
configDir: path12.join(homedir(), ".claude"),
|
|
2091
2221
|
mcpConfigPaths: [
|
|
2092
|
-
|
|
2093
|
-
|
|
2222
|
+
path12.join(homedir(), ".claude", "plugins.json"),
|
|
2223
|
+
path12.join(homedir(), ".claude.json")
|
|
2094
2224
|
]
|
|
2095
2225
|
},
|
|
2096
2226
|
{
|
|
2097
2227
|
type: "cursor",
|
|
2098
2228
|
binary: "cursor",
|
|
2099
|
-
configDir:
|
|
2100
|
-
mcpConfigPaths: [
|
|
2229
|
+
configDir: path12.join(homedir(), ".cursor"),
|
|
2230
|
+
mcpConfigPaths: [path12.join(homedir(), ".cursor", "mcp.json")]
|
|
2101
2231
|
},
|
|
2102
2232
|
{
|
|
2103
2233
|
type: "codex",
|
|
2104
2234
|
binary: "codex",
|
|
2105
|
-
configDir:
|
|
2235
|
+
configDir: path12.join(homedir(), ".codex"),
|
|
2106
2236
|
mcpConfigPaths: []
|
|
2107
2237
|
}
|
|
2108
2238
|
];
|
|
@@ -2110,27 +2240,27 @@ var init_detect = __esm({
|
|
|
2110
2240
|
});
|
|
2111
2241
|
|
|
2112
2242
|
// src/lib/agents/invoke.ts
|
|
2113
|
-
import { mkdir as mkdir5, readFile as
|
|
2243
|
+
import { mkdir as mkdir5, readFile as readFile7, writeFile as writeFile8 } from "fs/promises";
|
|
2114
2244
|
import { tmpdir } from "os";
|
|
2115
|
-
import
|
|
2245
|
+
import path13 from "path";
|
|
2116
2246
|
import readline from "readline";
|
|
2117
|
-
import
|
|
2247
|
+
import chalk6 from "chalk";
|
|
2118
2248
|
async function savePromptToFile(prompt, projectRoot) {
|
|
2119
|
-
const base = projectRoot ?
|
|
2120
|
-
const dir =
|
|
2249
|
+
const base = projectRoot ? path13.join(projectRoot, "tmp") : tmpdir();
|
|
2250
|
+
const dir = path13.join(base, "eds-cli");
|
|
2121
2251
|
await mkdir5(dir, { recursive: true });
|
|
2122
2252
|
const filename = `from-design-${Date.now()}.md`;
|
|
2123
|
-
const filePath =
|
|
2124
|
-
await
|
|
2253
|
+
const filePath = path13.join(dir, filename);
|
|
2254
|
+
await writeFile8(filePath, prompt, "utf-8");
|
|
2125
2255
|
return filePath;
|
|
2126
2256
|
}
|
|
2127
2257
|
async function invokeAgent(agent, promptFile, cwd) {
|
|
2128
2258
|
const { execa } = await import("execa");
|
|
2129
|
-
const prompt = await
|
|
2259
|
+
const prompt = await readFile7(promptFile, "utf-8");
|
|
2130
2260
|
try {
|
|
2131
2261
|
switch (agent.type) {
|
|
2132
2262
|
case "claude": {
|
|
2133
|
-
logger.info(`Running Claude Code ${
|
|
2263
|
+
logger.info(`Running Claude Code ${chalk6.dim("- live log:")}`);
|
|
2134
2264
|
const figmaServers = [
|
|
2135
2265
|
...agent.figmaMcpServerName ? [agent.figmaMcpServerName] : [],
|
|
2136
2266
|
"figma-sse",
|
|
@@ -2193,10 +2323,10 @@ async function renderClaudeStream(sub, cwd) {
|
|
|
2193
2323
|
const ora4 = (await import("ora")).default;
|
|
2194
2324
|
const start = Date.now();
|
|
2195
2325
|
const elapsed = () => `${Math.round((Date.now() - start) / 1e3)}s`;
|
|
2196
|
-
const spinner = ora4({ text: `thinking ${
|
|
2326
|
+
const spinner = ora4({ text: `thinking ${chalk6.dim(elapsed())}`, color: "yellow" }).start();
|
|
2197
2327
|
let activity = "thinking";
|
|
2198
2328
|
const timer = setInterval(() => {
|
|
2199
|
-
spinner.text = `${activity} ${
|
|
2329
|
+
spinner.text = `${activity} ${chalk6.dim(elapsed())}`;
|
|
2200
2330
|
}, 300);
|
|
2201
2331
|
const pending = /* @__PURE__ */ new Map();
|
|
2202
2332
|
const emit = (line) => {
|
|
@@ -2222,7 +2352,7 @@ async function renderClaudeStream(sub, cwd) {
|
|
|
2222
2352
|
for (const block2 of content) {
|
|
2223
2353
|
if (block2.type === "text" && "text" in block2) {
|
|
2224
2354
|
const first = block2.text.trim().split("\n")[0].slice(0, 100);
|
|
2225
|
-
if (first) emit(
|
|
2355
|
+
if (first) emit(chalk6.dim(first));
|
|
2226
2356
|
} else if (block2.type === "tool_use" && "name" in block2) {
|
|
2227
2357
|
const id = "id" in block2 && block2.id ? block2.id : `${Math.random()}`;
|
|
2228
2358
|
pending.set(id, {
|
|
@@ -2238,8 +2368,8 @@ async function renderClaudeStream(sub, cwd) {
|
|
|
2238
2368
|
const p = pending.get(block2.tool_use_id);
|
|
2239
2369
|
if (p) {
|
|
2240
2370
|
const dur = `${((Date.now() - p.start) / 1e3).toFixed(1)}s`;
|
|
2241
|
-
const failed = "is_error" in block2 && block2.is_error ? ` ${
|
|
2242
|
-
emit(`${p.label} ${
|
|
2371
|
+
const failed = "is_error" in block2 && block2.is_error ? ` ${chalk6.red("failed")}` : "";
|
|
2372
|
+
emit(`${p.label} ${chalk6.dim(dur)}${failed}`);
|
|
2243
2373
|
pending.delete(block2.tool_use_id);
|
|
2244
2374
|
}
|
|
2245
2375
|
}
|
|
@@ -2251,9 +2381,9 @@ async function renderClaudeStream(sub, cwd) {
|
|
|
2251
2381
|
}
|
|
2252
2382
|
}
|
|
2253
2383
|
}
|
|
2254
|
-
for (const p of pending.values()) emit(`${p.label} ${
|
|
2384
|
+
for (const p of pending.values()) emit(`${p.label} ${chalk6.dim("\u2026")}`);
|
|
2255
2385
|
clearInterval(timer);
|
|
2256
|
-
if (result) spinner.succeed(`${brand.accent("agent finished")} ${
|
|
2386
|
+
if (result) spinner.succeed(`${brand.accent("agent finished")} ${chalk6.dim(result)}`);
|
|
2257
2387
|
else spinner.stop();
|
|
2258
2388
|
} catch (err) {
|
|
2259
2389
|
clearInterval(timer);
|
|
@@ -2266,22 +2396,22 @@ function toolLabel(name, input, cwd) {
|
|
|
2266
2396
|
const rel = (key) => {
|
|
2267
2397
|
const p = input?.[key];
|
|
2268
2398
|
if (typeof p !== "string") return "";
|
|
2269
|
-
return p.startsWith(cwd) ?
|
|
2399
|
+
return p.startsWith(cwd) ? path13.relative(cwd, p) : path13.basename(p);
|
|
2270
2400
|
};
|
|
2271
2401
|
if (name.includes("figma")) {
|
|
2272
2402
|
const short = name.split("__").pop() ?? name;
|
|
2273
|
-
return `${icon.figma} ${
|
|
2403
|
+
return `${icon.figma} ${chalk6.bold(short)} ${chalk6.dim("Figma MCP")}`;
|
|
2274
2404
|
}
|
|
2275
2405
|
switch (name) {
|
|
2276
2406
|
case "Write":
|
|
2277
2407
|
case "Edit":
|
|
2278
2408
|
case "NotebookEdit":
|
|
2279
|
-
return `${brand.accent("\u270E")} ${
|
|
2409
|
+
return `${brand.accent("\u270E")} ${chalk6.cyan(rel("file_path"))}`;
|
|
2280
2410
|
case "Read":
|
|
2281
|
-
return
|
|
2411
|
+
return chalk6.dim(`\xB7 read ${rel("file_path")}`);
|
|
2282
2412
|
case "Bash": {
|
|
2283
2413
|
const cmd = typeof input?.command === "string" ? input.command.split("\n")[0].slice(0, 70) : "";
|
|
2284
|
-
return
|
|
2414
|
+
return chalk6.dim(`$ ${cmd}`);
|
|
2285
2415
|
}
|
|
2286
2416
|
default:
|
|
2287
2417
|
return `${brand.blue("\u2192")} ${name}`;
|
|
@@ -2297,13 +2427,13 @@ var init_invoke = __esm({
|
|
|
2297
2427
|
});
|
|
2298
2428
|
|
|
2299
2429
|
// src/lib/config.ts
|
|
2300
|
-
import { existsSync as
|
|
2301
|
-
import { readFile as
|
|
2430
|
+
import { existsSync as existsSync11 } from "fs";
|
|
2431
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
2302
2432
|
import { homedir as homedir2 } from "os";
|
|
2303
|
-
import
|
|
2433
|
+
import path14 from "path";
|
|
2304
2434
|
async function loadJsonFile(filePath) {
|
|
2305
2435
|
try {
|
|
2306
|
-
const content = await
|
|
2436
|
+
const content = await readFile8(filePath, "utf-8");
|
|
2307
2437
|
return JSON.parse(content);
|
|
2308
2438
|
} catch {
|
|
2309
2439
|
return void 0;
|
|
@@ -2311,7 +2441,7 @@ async function loadJsonFile(filePath) {
|
|
|
2311
2441
|
}
|
|
2312
2442
|
async function loadConfig(projectRoot) {
|
|
2313
2443
|
const globalRaw = await loadJsonFile(GLOBAL_CONFIG_PATH);
|
|
2314
|
-
const projectRaw = projectRoot ? await loadJsonFile(
|
|
2444
|
+
const projectRaw = projectRoot ? await loadJsonFile(path14.join(projectRoot, PROJECT_CONFIG_NAME)) : void 0;
|
|
2315
2445
|
const merged = {
|
|
2316
2446
|
...typeof globalRaw === "object" && globalRaw !== null ? globalRaw : {},
|
|
2317
2447
|
...typeof projectRaw === "object" && projectRaw !== null ? projectRaw : {}
|
|
@@ -2330,7 +2460,7 @@ function globalConfigPath() {
|
|
|
2330
2460
|
return GLOBAL_CONFIG_PATH;
|
|
2331
2461
|
}
|
|
2332
2462
|
function globalConfigExists() {
|
|
2333
|
-
return
|
|
2463
|
+
return existsSync11(GLOBAL_CONFIG_PATH);
|
|
2334
2464
|
}
|
|
2335
2465
|
var GLOBAL_CONFIG_DIR, GLOBAL_CONFIG_PATH, PROJECT_CONFIG_NAME;
|
|
2336
2466
|
var init_config = __esm({
|
|
@@ -2339,8 +2469,8 @@ var init_config = __esm({
|
|
|
2339
2469
|
init_esm_shims();
|
|
2340
2470
|
init_logger();
|
|
2341
2471
|
init_schemas();
|
|
2342
|
-
GLOBAL_CONFIG_DIR =
|
|
2343
|
-
GLOBAL_CONFIG_PATH =
|
|
2472
|
+
GLOBAL_CONFIG_DIR = path14.join(homedir2(), ".eds");
|
|
2473
|
+
GLOBAL_CONFIG_PATH = path14.join(GLOBAL_CONFIG_DIR, "config.json");
|
|
2344
2474
|
PROJECT_CONFIG_NAME = ".edsrc.json";
|
|
2345
2475
|
}
|
|
2346
2476
|
});
|
|
@@ -2390,9 +2520,9 @@ var init_node_id = __esm({
|
|
|
2390
2520
|
});
|
|
2391
2521
|
|
|
2392
2522
|
// src/lib/figma/prompt-builder.ts
|
|
2393
|
-
import { existsSync as
|
|
2394
|
-
import { readFile as
|
|
2395
|
-
import
|
|
2523
|
+
import { existsSync as existsSync12 } from "fs";
|
|
2524
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
2525
|
+
import path15 from "path";
|
|
2396
2526
|
function contentSourceGuidance(source, cfHint, blockName) {
|
|
2397
2527
|
if (!source || source === "document" || source === "ue") return "";
|
|
2398
2528
|
const q = cfHint ? ` (model / persisted query: \`${cfHint}\`)` : "";
|
|
@@ -2604,10 +2734,10 @@ ${tokensHint}
|
|
|
2604
2734
|
`;
|
|
2605
2735
|
}
|
|
2606
2736
|
async function getTokensHint(projectRoot) {
|
|
2607
|
-
const stylesPath =
|
|
2608
|
-
if (!
|
|
2737
|
+
const stylesPath = path15.join(projectRoot, "styles", "styles.css");
|
|
2738
|
+
if (!existsSync12(stylesPath)) return "";
|
|
2609
2739
|
try {
|
|
2610
|
-
const css = await
|
|
2740
|
+
const css = await readFile9(stylesPath, "utf-8");
|
|
2611
2741
|
const rootMatch = css.match(/:root\s*\{([^}]+)\}/);
|
|
2612
2742
|
if (!rootMatch) return "";
|
|
2613
2743
|
const props = rootMatch[1].split("\n").filter((line) => line.trim().startsWith("--")).map((line) => line.trim()).join("\n ");
|
|
@@ -2637,7 +2767,8 @@ var init_prompt_builder = __esm({
|
|
|
2637
2767
|
figma: "Figma",
|
|
2638
2768
|
stitch: "Google Stitch",
|
|
2639
2769
|
canva: "Canva",
|
|
2640
|
-
sketch: "Sketch"
|
|
2770
|
+
sketch: "Sketch",
|
|
2771
|
+
framer: "Framer"
|
|
2641
2772
|
};
|
|
2642
2773
|
}
|
|
2643
2774
|
});
|
|
@@ -2747,11 +2878,11 @@ var init_harness = __esm({
|
|
|
2747
2878
|
});
|
|
2748
2879
|
|
|
2749
2880
|
// src/lib/preview/server.ts
|
|
2750
|
-
import { createReadStream, existsSync as
|
|
2881
|
+
import { createReadStream, existsSync as existsSync13, statSync } from "fs";
|
|
2751
2882
|
import http from "http";
|
|
2752
|
-
import
|
|
2883
|
+
import path16 from "path";
|
|
2753
2884
|
function startServer(root, routes, port, host = "127.0.0.1") {
|
|
2754
|
-
const resolvedRoot =
|
|
2885
|
+
const resolvedRoot = path16.resolve(root);
|
|
2755
2886
|
const clients = /* @__PURE__ */ new Set();
|
|
2756
2887
|
const server = http.createServer((req, res) => {
|
|
2757
2888
|
const urlPath = decodeURIComponent((req.url ?? "/").split("?")[0]);
|
|
@@ -2777,19 +2908,19 @@ function startServer(root, routes, port, host = "127.0.0.1") {
|
|
|
2777
2908
|
res.end(route.body);
|
|
2778
2909
|
return;
|
|
2779
2910
|
}
|
|
2780
|
-
const filePath =
|
|
2911
|
+
const filePath = path16.join(resolvedRoot, urlPath);
|
|
2781
2912
|
if (!filePath.startsWith(resolvedRoot)) {
|
|
2782
2913
|
res.writeHead(403);
|
|
2783
2914
|
res.end("Forbidden");
|
|
2784
2915
|
return;
|
|
2785
2916
|
}
|
|
2786
|
-
if (!
|
|
2917
|
+
if (!existsSync13(filePath) || !statSync(filePath).isFile()) {
|
|
2787
2918
|
res.writeHead(404, { "content-type": "text/plain" });
|
|
2788
2919
|
res.end(`Not found: ${urlPath}`);
|
|
2789
2920
|
return;
|
|
2790
2921
|
}
|
|
2791
2922
|
res.writeHead(200, {
|
|
2792
|
-
"content-type": MIME[
|
|
2923
|
+
"content-type": MIME[path16.extname(filePath).toLowerCase()] ?? "application/octet-stream"
|
|
2793
2924
|
});
|
|
2794
2925
|
createReadStream(filePath).pipe(res);
|
|
2795
2926
|
});
|
|
@@ -2853,10 +2984,10 @@ var preview_exports = {};
|
|
|
2853
2984
|
__export(preview_exports, {
|
|
2854
2985
|
previewBlock: () => previewBlock
|
|
2855
2986
|
});
|
|
2856
|
-
import { existsSync as
|
|
2857
|
-
import { readFile as
|
|
2858
|
-
import
|
|
2859
|
-
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";
|
|
2860
2991
|
function labelFor(width) {
|
|
2861
2992
|
if (width <= 480) return `${width} mobile`;
|
|
2862
2993
|
if (width <= 1024) return `${width} tablet`;
|
|
@@ -2908,30 +3039,30 @@ async function previewBlock(name, options = {}) {
|
|
|
2908
3039
|
process.exitCode = 1;
|
|
2909
3040
|
return;
|
|
2910
3041
|
}
|
|
2911
|
-
const blockDir =
|
|
2912
|
-
if (!
|
|
3042
|
+
const blockDir = path17.join(projectRoot, "blocks", name);
|
|
3043
|
+
if (!existsSync14(blockDir)) {
|
|
2913
3044
|
logger.error(`Block "${name}" not found at blocks/${name}/.`);
|
|
2914
3045
|
process.exitCode = 1;
|
|
2915
3046
|
return;
|
|
2916
3047
|
}
|
|
2917
|
-
if (!
|
|
3048
|
+
if (!existsSync14(path17.join(blockDir, `${name}.css`)) && !existsSync14(path17.join(blockDir, `${name}.js`))) {
|
|
2918
3049
|
logger.error(`Block "${name}" has no ${name}.css / ${name}.js to preview.`);
|
|
2919
3050
|
process.exitCode = 1;
|
|
2920
3051
|
return;
|
|
2921
3052
|
}
|
|
2922
3053
|
let meta = null;
|
|
2923
|
-
const metaPath =
|
|
2924
|
-
if (
|
|
3054
|
+
const metaPath = path17.join(blockDir, ".eds-meta.json");
|
|
3055
|
+
if (existsSync14(metaPath)) {
|
|
2925
3056
|
try {
|
|
2926
|
-
meta = JSON.parse(await
|
|
3057
|
+
meta = JSON.parse(await readFile10(metaPath, "utf-8"));
|
|
2927
3058
|
} catch {
|
|
2928
3059
|
}
|
|
2929
3060
|
}
|
|
2930
|
-
const samplePath =
|
|
3061
|
+
const samplePath = path17.join(blockDir, `_${name}.preview.html`);
|
|
2931
3062
|
let sampleContent;
|
|
2932
3063
|
let placeholder = false;
|
|
2933
|
-
if (
|
|
2934
|
-
sampleContent = await
|
|
3064
|
+
if (existsSync14(samplePath)) {
|
|
3065
|
+
sampleContent = await readFile10(samplePath, "utf-8");
|
|
2935
3066
|
} else {
|
|
2936
3067
|
sampleContent = defaultSample(name);
|
|
2937
3068
|
placeholder = true;
|
|
@@ -2981,9 +3112,9 @@ async function previewBlock(name, options = {}) {
|
|
|
2981
3112
|
reloadTimer = setTimeout(() => server.reload(), 150);
|
|
2982
3113
|
});
|
|
2983
3114
|
logger.info("");
|
|
2984
|
-
logger.info(` ${
|
|
3115
|
+
logger.info(` ${chalk7.bold("\u279C")} ${chalk7.cyan(server.url)}`);
|
|
2985
3116
|
logger.info(statusLine("pass", "live reload", "edits to the block refresh the browser"));
|
|
2986
|
-
logger.info(
|
|
3117
|
+
logger.info(chalk7.dim(" Press Ctrl+C to stop.\n"));
|
|
2987
3118
|
if (options.open !== false) await openInBrowser(server.url);
|
|
2988
3119
|
const stop = async () => {
|
|
2989
3120
|
watcher.close();
|
|
@@ -3012,10 +3143,10 @@ var from_design_exports = {};
|
|
|
3012
3143
|
__export(from_design_exports, {
|
|
3013
3144
|
blockFromDesign: () => blockFromDesign
|
|
3014
3145
|
});
|
|
3015
|
-
import { existsSync as
|
|
3016
|
-
import { readFile as
|
|
3017
|
-
import
|
|
3018
|
-
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";
|
|
3019
3150
|
import ora from "ora";
|
|
3020
3151
|
async function previewRunning(port = 8777) {
|
|
3021
3152
|
try {
|
|
@@ -3038,7 +3169,8 @@ async function runWizard(figmaUrl) {
|
|
|
3038
3169
|
{ name: "Figma", value: "figma" },
|
|
3039
3170
|
{ name: "Google Stitch", value: "stitch" },
|
|
3040
3171
|
{ name: "Canva", value: "canva" },
|
|
3041
|
-
{ name: "Sketch", value: "sketch" }
|
|
3172
|
+
{ name: "Sketch", value: "sketch" },
|
|
3173
|
+
{ name: "Framer", value: "framer" }
|
|
3042
3174
|
],
|
|
3043
3175
|
default: "figma"
|
|
3044
3176
|
});
|
|
@@ -3164,7 +3296,7 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
3164
3296
|
try {
|
|
3165
3297
|
figma2 = parseFigmaUrl(designUrl);
|
|
3166
3298
|
spinner.succeed(
|
|
3167
|
-
`Parsed Figma URL ${
|
|
3299
|
+
`Parsed Figma URL ${chalk8.dim(figma2.nodeId ? `node ${figma2.nodeId}` : `file ${figma2.fileKey}`)}`
|
|
3168
3300
|
);
|
|
3169
3301
|
} catch (err) {
|
|
3170
3302
|
spinner.fail(err.message);
|
|
@@ -3172,7 +3304,7 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
3172
3304
|
return;
|
|
3173
3305
|
}
|
|
3174
3306
|
} else {
|
|
3175
|
-
logger.info(` ${icon.pass} ${providerName} design ${
|
|
3307
|
+
logger.info(` ${icon.pass} ${providerName} design ${chalk8.dim(designRef)}`);
|
|
3176
3308
|
}
|
|
3177
3309
|
const blockName = options.name || (figma2 ? inferBlockName(figma2.fileName) : "");
|
|
3178
3310
|
if (!kebabCaseRegex.test(blockName)) {
|
|
@@ -3195,20 +3327,20 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
3195
3327
|
screenshot: options.screenshot !== false
|
|
3196
3328
|
});
|
|
3197
3329
|
const promptFile = await savePromptToFile(prompt, projectRoot);
|
|
3198
|
-
const hasTokens =
|
|
3330
|
+
const hasTokens = existsSync15(path18.join(projectRoot, "styles", "styles.css"));
|
|
3199
3331
|
promptSpinner.succeed(
|
|
3200
|
-
hasTokens ? `Loaded design tokens ${
|
|
3332
|
+
hasTokens ? `Loaded design tokens ${chalk8.dim("styles/styles.css")}` : `Built prompt ${chalk8.dim("no project tokens found")}`
|
|
3201
3333
|
);
|
|
3202
|
-
logger.info(
|
|
3334
|
+
logger.info(chalk8.dim(` prompt \u2192 ${promptFile}`));
|
|
3203
3335
|
if (options.dryRun) {
|
|
3204
3336
|
logger.info("");
|
|
3205
|
-
logger.info(
|
|
3206
|
-
logger.info(`Prompt file: ${
|
|
3207
|
-
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)}`);
|
|
3208
3340
|
logger.info(`Prompt version: ${PROMPT_VERSION}`);
|
|
3209
3341
|
logger.info("");
|
|
3210
3342
|
logger.info("Preview:");
|
|
3211
|
-
logger.info(
|
|
3343
|
+
logger.info(chalk8.dim("\u2500".repeat(60)));
|
|
3212
3344
|
console.log(prompt);
|
|
3213
3345
|
return;
|
|
3214
3346
|
}
|
|
@@ -3216,22 +3348,22 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
3216
3348
|
const preferredAgent = options.agent || config.agent;
|
|
3217
3349
|
if (options.agent === "none") {
|
|
3218
3350
|
logger.info("Agent skipped (--agent none). Use the prompt file manually:");
|
|
3219
|
-
logger.info(` ${
|
|
3351
|
+
logger.info(` ${chalk8.cyan(promptFile)}`);
|
|
3220
3352
|
return;
|
|
3221
3353
|
}
|
|
3222
3354
|
const agentSpinner = ora("Detecting AI agent...").start();
|
|
3223
3355
|
const agent = await detectAgent(preferredAgent);
|
|
3224
3356
|
if (!agent) {
|
|
3225
3357
|
agentSpinner.fail("No AI agent found (claude, cursor, or codex).");
|
|
3226
|
-
logger.info(`Install Claude Code: ${
|
|
3227
|
-
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)}`);
|
|
3228
3360
|
process.exitCode = 1;
|
|
3229
3361
|
return;
|
|
3230
3362
|
}
|
|
3231
|
-
agentSpinner.succeed(`Agent ready ${
|
|
3363
|
+
agentSpinner.succeed(`Agent ready ${chalk8.cyan(agent.type)} ${chalk8.dim(agent.path)}`);
|
|
3232
3364
|
if (provider === "figma" && !agent.hasFigmaMcp) {
|
|
3233
3365
|
logger.warn(`Figma MCP may not be configured for ${agent.type}.`);
|
|
3234
|
-
logger.info(`Run ${
|
|
3366
|
+
logger.info(`Run ${chalk8.cyan("eds figma setup")} to configure it.`);
|
|
3235
3367
|
}
|
|
3236
3368
|
if (!options.yes) {
|
|
3237
3369
|
const { confirm } = await import("@inquirer/prompts");
|
|
@@ -3240,24 +3372,24 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
3240
3372
|
default: true
|
|
3241
3373
|
});
|
|
3242
3374
|
if (!proceed) {
|
|
3243
|
-
logger.info(`Prompt saved to: ${
|
|
3375
|
+
logger.info(`Prompt saved to: ${chalk8.cyan(promptFile)}`);
|
|
3244
3376
|
return;
|
|
3245
3377
|
}
|
|
3246
3378
|
}
|
|
3247
3379
|
logger.info(
|
|
3248
|
-
` ${
|
|
3380
|
+
` ${chalk8.dim("\u2192")} Handing off to ${chalk8.cyan(agent.type)} ${chalk8.dim(`via ${providerName} MCP`)}`
|
|
3249
3381
|
);
|
|
3250
3382
|
if (provider === "figma") {
|
|
3251
|
-
logger.info(
|
|
3383
|
+
logger.info(chalk8.dim(" get_design_context \xB7 get_variable_defs \xB7 get_screenshot"));
|
|
3252
3384
|
}
|
|
3253
3385
|
const success = await invokeAgent(agent, promptFile, projectRoot);
|
|
3254
3386
|
if (!success) {
|
|
3255
3387
|
process.exitCode = 1;
|
|
3256
3388
|
return;
|
|
3257
3389
|
}
|
|
3258
|
-
const blockDir =
|
|
3390
|
+
const blockDir = path18.join(projectRoot, "blocks", blockName);
|
|
3259
3391
|
const ok = await verifyGeneratedBlock(blockDir, blockName, options.ueModel !== false);
|
|
3260
|
-
if (
|
|
3392
|
+
if (existsSync15(blockDir)) {
|
|
3261
3393
|
const meta = {
|
|
3262
3394
|
provider,
|
|
3263
3395
|
designRef,
|
|
@@ -3267,7 +3399,7 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
3267
3399
|
promptVersion: PROMPT_VERSION,
|
|
3268
3400
|
agentUsed: agent.type
|
|
3269
3401
|
};
|
|
3270
|
-
await
|
|
3402
|
+
await writeFile9(path18.join(blockDir, ".eds-meta.json"), `${JSON.stringify(meta, null, 2)}
|
|
3271
3403
|
`);
|
|
3272
3404
|
}
|
|
3273
3405
|
if (!ok) {
|
|
@@ -3278,10 +3410,21 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
3278
3410
|
return;
|
|
3279
3411
|
}
|
|
3280
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
|
+
}
|
|
3281
3424
|
if (options.serve !== false) {
|
|
3282
3425
|
if (await previewRunning()) {
|
|
3283
3426
|
logger.info(
|
|
3284
|
-
|
|
3427
|
+
chalk8.dim(
|
|
3285
3428
|
" A preview server is already running at http://127.0.0.1:8777 \u2014 your change will refresh there."
|
|
3286
3429
|
)
|
|
3287
3430
|
);
|
|
@@ -3291,15 +3434,15 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
3291
3434
|
await previewBlock2(blockName, {});
|
|
3292
3435
|
}
|
|
3293
3436
|
} else {
|
|
3294
|
-
logger.info(` ${
|
|
3437
|
+
logger.info(` ${chalk8.dim("\u2192")} Next: ${chalk8.cyan(`eds block preview ${blockName}`)}`);
|
|
3295
3438
|
}
|
|
3296
3439
|
}
|
|
3297
3440
|
async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
3298
3441
|
const checks = [];
|
|
3299
|
-
const jsPath =
|
|
3300
|
-
const cssPath =
|
|
3301
|
-
const hasJs =
|
|
3302
|
-
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);
|
|
3303
3446
|
checks.push({
|
|
3304
3447
|
status: hasJs ? "pass" : "fail",
|
|
3305
3448
|
name: `${blockName}.js`,
|
|
@@ -3311,7 +3454,7 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
|
3311
3454
|
message: hasCss ? "created" : "missing \u2014 agent did not write the CSS file"
|
|
3312
3455
|
});
|
|
3313
3456
|
if (hasJs) {
|
|
3314
|
-
const js = await
|
|
3457
|
+
const js = await readFile11(jsPath, "utf-8");
|
|
3315
3458
|
checks.push({
|
|
3316
3459
|
status: /export\s+default/.test(js) ? "pass" : "fail",
|
|
3317
3460
|
name: "decorate export",
|
|
@@ -3326,7 +3469,7 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
|
3326
3469
|
}
|
|
3327
3470
|
}
|
|
3328
3471
|
if (hasCss) {
|
|
3329
|
-
const css = await
|
|
3472
|
+
const css = await readFile11(cssPath, "utf-8");
|
|
3330
3473
|
checks.push({
|
|
3331
3474
|
status: css.includes(`.${blockName}`) ? "pass" : "warn",
|
|
3332
3475
|
name: "scoped CSS",
|
|
@@ -3334,12 +3477,12 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
|
3334
3477
|
});
|
|
3335
3478
|
}
|
|
3336
3479
|
if (withUeModel) {
|
|
3337
|
-
const hasPerBlock =
|
|
3338
|
-
const modelsFile =
|
|
3480
|
+
const hasPerBlock = existsSync15(path18.join(blockDir, `_${blockName}.json`));
|
|
3481
|
+
const modelsFile = path18.join(blockDir, "..", "..", "component-models.json");
|
|
3339
3482
|
let hasAggregated = false;
|
|
3340
|
-
if (!hasPerBlock &&
|
|
3483
|
+
if (!hasPerBlock && existsSync15(modelsFile)) {
|
|
3341
3484
|
try {
|
|
3342
|
-
hasAggregated = (await
|
|
3485
|
+
hasAggregated = (await readFile11(modelsFile, "utf-8")).includes(`"${blockName}"`);
|
|
3343
3486
|
} catch {
|
|
3344
3487
|
}
|
|
3345
3488
|
}
|
|
@@ -3351,8 +3494,8 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
|
3351
3494
|
});
|
|
3352
3495
|
if (hasPerBlock && hasJs) {
|
|
3353
3496
|
try {
|
|
3354
|
-
const modelRaw = await
|
|
3355
|
-
const js = await
|
|
3497
|
+
const modelRaw = await readFile11(path18.join(blockDir, `_${blockName}.json`), "utf-8");
|
|
3498
|
+
const js = await readFile11(jsPath, "utf-8");
|
|
3356
3499
|
const isRepeatable = /"resourceType"\s*:\s*"[^"]*\/item"/.test(modelRaw) || /"filter"\s*:/.test(modelRaw) || /"multi"\s*:\s*true/.test(modelRaw);
|
|
3357
3500
|
const iterates = /\.children/.test(js) || /\bfor\s*\(/.test(js) || /\.forEach\s*\(/.test(js);
|
|
3358
3501
|
if (isRepeatable) {
|
|
@@ -3395,10 +3538,10 @@ var list_exports = {};
|
|
|
3395
3538
|
__export(list_exports, {
|
|
3396
3539
|
listBlocks: () => listBlocks
|
|
3397
3540
|
});
|
|
3398
|
-
import { existsSync as
|
|
3399
|
-
import { readFile as
|
|
3400
|
-
import
|
|
3401
|
-
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";
|
|
3402
3545
|
async function listBlocks(options = {}) {
|
|
3403
3546
|
const projectRoot = findProjectRoot();
|
|
3404
3547
|
if (!projectRoot) {
|
|
@@ -3406,8 +3549,8 @@ async function listBlocks(options = {}) {
|
|
|
3406
3549
|
process.exitCode = 1;
|
|
3407
3550
|
return;
|
|
3408
3551
|
}
|
|
3409
|
-
const blocksDir =
|
|
3410
|
-
if (!
|
|
3552
|
+
const blocksDir = path19.join(projectRoot, "blocks");
|
|
3553
|
+
if (!existsSync16(blocksDir)) {
|
|
3411
3554
|
logger.info("No blocks/ directory found.");
|
|
3412
3555
|
return;
|
|
3413
3556
|
}
|
|
@@ -3416,17 +3559,17 @@ async function listBlocks(options = {}) {
|
|
|
3416
3559
|
for (const entry of entries) {
|
|
3417
3560
|
if (!entry.isDirectory()) continue;
|
|
3418
3561
|
const name = entry.name;
|
|
3419
|
-
const blockDir =
|
|
3562
|
+
const blockDir = path19.join(blocksDir, name);
|
|
3420
3563
|
const info = {
|
|
3421
3564
|
name,
|
|
3422
|
-
hasJs:
|
|
3423
|
-
hasCss:
|
|
3424
|
-
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`))
|
|
3425
3568
|
};
|
|
3426
|
-
const metaFile =
|
|
3427
|
-
if (
|
|
3569
|
+
const metaFile = path19.join(blockDir, ".eds-meta.json");
|
|
3570
|
+
if (existsSync16(metaFile)) {
|
|
3428
3571
|
try {
|
|
3429
|
-
const meta = JSON.parse(await
|
|
3572
|
+
const meta = JSON.parse(await readFile12(metaFile, "utf-8"));
|
|
3430
3573
|
info.figmaSource = {
|
|
3431
3574
|
fileKey: meta.figmaFileKey,
|
|
3432
3575
|
nodeId: meta.figmaNodeId,
|
|
@@ -3449,17 +3592,17 @@ async function listBlocks(options = {}) {
|
|
|
3449
3592
|
const nameWidth = columnWidth(blocks.map((b) => b.name));
|
|
3450
3593
|
for (const b of blocks) {
|
|
3451
3594
|
const badges = [
|
|
3452
|
-
b.hasJs ? brand.ok("js") :
|
|
3453
|
-
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")
|
|
3454
3597
|
];
|
|
3455
3598
|
if (b.hasUeModel) badges.push(brand.blue("ue-model"));
|
|
3456
3599
|
if (b.figmaSource) badges.push(`${icon.figma} ${brand.dotPurple("figma")}`);
|
|
3457
|
-
const name = padEndVisible(
|
|
3458
|
-
logger.info(` ${name} ${
|
|
3600
|
+
const name = padEndVisible(chalk9.bold(b.name), nameWidth);
|
|
3601
|
+
logger.info(` ${name} ${chalk9.dim("[")}${badges.join(" ")}${chalk9.dim("]")}`);
|
|
3459
3602
|
if (b.figmaSource) {
|
|
3460
3603
|
const synced = new Date(b.figmaSource.lastSyncedAt).toLocaleDateString();
|
|
3461
3604
|
logger.info(
|
|
3462
|
-
` ${" ".repeat(nameWidth)} ${icon.arrow} ${
|
|
3605
|
+
` ${" ".repeat(nameWidth)} ${icon.arrow} ${chalk9.dim(
|
|
3463
3606
|
`figma:${b.figmaSource.fileKey} \xB7 synced ${synced}`
|
|
3464
3607
|
)}`
|
|
3465
3608
|
);
|
|
@@ -3499,22 +3642,85 @@ var init_integrations = __esm({
|
|
|
3499
3642
|
id: "gtm",
|
|
3500
3643
|
name: "Google Tag Manager",
|
|
3501
3644
|
fields: [{ name: "id", message: "GTM container ID", placeholder: "GTM-XXXXXXX" }],
|
|
3502
|
-
|
|
3503
|
-
(
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
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
|
+
]
|
|
3512
3718
|
},
|
|
3513
3719
|
{
|
|
3514
3720
|
id: "ga4",
|
|
3515
3721
|
name: "Google Analytics 4",
|
|
3516
3722
|
fields: [{ name: "id", message: "Measurement ID", placeholder: "G-XXXXXXXXXX" }],
|
|
3517
|
-
code: (v) => `// Google Analytics 4 (${v.id})
|
|
3723
|
+
code: (v) => `// Google Analytics 4 (${v.id}) - delayed phase.
|
|
3518
3724
|
const gaScript = document.createElement('script');
|
|
3519
3725
|
gaScript.async = true;
|
|
3520
3726
|
gaScript.src = 'https://www.googletagmanager.com/gtag/js?id=${v.id}';
|
|
@@ -3531,7 +3737,7 @@ gtag('config', '${v.id}');`
|
|
|
3531
3737
|
{ name: "label", message: "Button label", placeholder: "Chat with us" },
|
|
3532
3738
|
{ name: "src", message: "Chat widget script URL" }
|
|
3533
3739
|
],
|
|
3534
|
-
code: (v) => `// Chat widget
|
|
3740
|
+
code: (v) => `// Chat widget - facade pattern: load the heavy script only on first interaction.
|
|
3535
3741
|
const chatBtn = document.createElement('button');
|
|
3536
3742
|
chatBtn.type = 'button';
|
|
3537
3743
|
chatBtn.className = 'chat-facade';
|
|
@@ -3549,7 +3755,7 @@ document.body.appendChild(chatBtn);`
|
|
|
3549
3755
|
id: "cookiebot",
|
|
3550
3756
|
name: "Cookie consent (Cookiebot)",
|
|
3551
3757
|
fields: [{ name: "id", message: "Cookiebot domain group ID (CBID)" }],
|
|
3552
|
-
code: (v) => `// Cookiebot consent
|
|
3758
|
+
code: (v) => `// Cookiebot consent - load early in delayed; configure GTM consent mode to respect it.
|
|
3553
3759
|
const cb = document.createElement('script');
|
|
3554
3760
|
cb.id = 'Cookiebot';
|
|
3555
3761
|
cb.src = 'https://consent.cookiebot.com/uc.js';
|
|
@@ -3561,7 +3767,7 @@ document.head.appendChild(cb);`
|
|
|
3561
3767
|
id: "custom",
|
|
3562
3768
|
name: "Custom third-party script",
|
|
3563
3769
|
fields: [{ name: "url", message: "Script URL" }],
|
|
3564
|
-
code: (v) => `// Custom third-party script
|
|
3770
|
+
code: (v) => `// Custom third-party script - delayed phase.
|
|
3565
3771
|
const s = document.createElement('script');
|
|
3566
3772
|
s.async = true;
|
|
3567
3773
|
s.src = ${JSON.stringify(v.url || "")};
|
|
@@ -3576,10 +3782,10 @@ var integrate_exports = {};
|
|
|
3576
3782
|
__export(integrate_exports, {
|
|
3577
3783
|
integrate: () => integrate
|
|
3578
3784
|
});
|
|
3579
|
-
import { existsSync as
|
|
3580
|
-
import { mkdir as mkdir6, readFile as
|
|
3581
|
-
import
|
|
3582
|
-
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";
|
|
3583
3789
|
async function integrate(type) {
|
|
3584
3790
|
const projectRoot = findProjectRoot();
|
|
3585
3791
|
if (!projectRoot) {
|
|
@@ -3587,7 +3793,7 @@ async function integrate(type) {
|
|
|
3587
3793
|
process.exitCode = 1;
|
|
3588
3794
|
return;
|
|
3589
3795
|
}
|
|
3590
|
-
logger.logoOnce(logo("Integrate
|
|
3796
|
+
logger.logoOnce(logo("Integrate - third-party service"));
|
|
3591
3797
|
const { select, input } = await import("@inquirer/prompts");
|
|
3592
3798
|
const chosenId = type || await select({
|
|
3593
3799
|
message: "Which service do you want to add?",
|
|
@@ -3606,26 +3812,39 @@ async function integrate(type) {
|
|
|
3606
3812
|
const answer = await input({ message: ` ${f.message}`, default: f.placeholder });
|
|
3607
3813
|
values[f.name] = answer.trim();
|
|
3608
3814
|
}
|
|
3609
|
-
const
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
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*$/, "")}
|
|
3614
3832
|
${block2}`);
|
|
3615
3833
|
logger.info(heading("Added"));
|
|
3616
|
-
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"));
|
|
3617
3836
|
logger.info("");
|
|
3618
3837
|
logger.info(
|
|
3619
|
-
|
|
3838
|
+
chalk10.dim(" Loads after LCP (delayed phase) - zero impact on your Core Web Vitals.")
|
|
3620
3839
|
);
|
|
3621
3840
|
if (integration.id === "gtm" || integration.id === "ga4") {
|
|
3622
3841
|
logger.info(
|
|
3623
|
-
|
|
3842
|
+
chalk10.dim(" Tip: gate analytics behind user consent (add the Cookiebot integration).")
|
|
3624
3843
|
);
|
|
3625
3844
|
}
|
|
3626
3845
|
if (integration.id === "chat") {
|
|
3627
3846
|
logger.info(
|
|
3628
|
-
|
|
3847
|
+
chalk10.dim(" The widget loads only when the visitor clicks the button (facade pattern).")
|
|
3629
3848
|
);
|
|
3630
3849
|
}
|
|
3631
3850
|
logger.info("");
|
|
@@ -3640,7 +3859,7 @@ var init_integrate = __esm({
|
|
|
3640
3859
|
init_logger();
|
|
3641
3860
|
init_project();
|
|
3642
3861
|
init_ui();
|
|
3643
|
-
DELAYED_HEADER = `// delayed.js
|
|
3862
|
+
DELAYED_HEADER = `// delayed.js - third-party / non-critical scripts.
|
|
3644
3863
|
// Loaded in the EDS delayed phase (after LCP), so nothing here blocks the page.
|
|
3645
3864
|
`;
|
|
3646
3865
|
}
|
|
@@ -3651,9 +3870,9 @@ var setup_exports = {};
|
|
|
3651
3870
|
__export(setup_exports, {
|
|
3652
3871
|
figmaSetup: () => figmaSetup
|
|
3653
3872
|
});
|
|
3654
|
-
import { existsSync as
|
|
3655
|
-
import { mkdir as mkdir7, writeFile as
|
|
3656
|
-
import
|
|
3873
|
+
import { existsSync as existsSync18 } from "fs";
|
|
3874
|
+
import { mkdir as mkdir7, writeFile as writeFile11 } from "fs/promises";
|
|
3875
|
+
import chalk11 from "chalk";
|
|
3657
3876
|
async function figmaSetup() {
|
|
3658
3877
|
const { select, confirm } = await import("@inquirer/prompts");
|
|
3659
3878
|
logger.logoOnce(logo());
|
|
@@ -3663,15 +3882,15 @@ async function figmaSetup() {
|
|
|
3663
3882
|
let selectedAgent;
|
|
3664
3883
|
if (agents.length === 0) {
|
|
3665
3884
|
logger.warn("No AI agents found (claude, cursor, codex).");
|
|
3666
|
-
logger.info(`Install Claude Code: ${
|
|
3885
|
+
logger.info(`Install Claude Code: ${chalk11.cyan("npm install -g @anthropic-ai/claude-code")}`);
|
|
3667
3886
|
selectedAgent = "none";
|
|
3668
3887
|
} else {
|
|
3669
|
-
logger.success(`Found: ${agents.map((a) =>
|
|
3888
|
+
logger.success(`Found: ${agents.map((a) => chalk11.cyan(a.type)).join(", ")}`);
|
|
3670
3889
|
selectedAgent = await select({
|
|
3671
3890
|
message: "Which agent do you want to use with eds-cli?",
|
|
3672
3891
|
choices: [
|
|
3673
3892
|
...agents.map((a) => ({
|
|
3674
|
-
name: `${a.type}${a.hasFigmaMcp ?
|
|
3893
|
+
name: `${a.type}${a.hasFigmaMcp ? chalk11.green(" (Figma MCP detected)") : ""}`,
|
|
3675
3894
|
value: a.type
|
|
3676
3895
|
})),
|
|
3677
3896
|
{ name: "None (manual mode)", value: "none" }
|
|
@@ -3687,17 +3906,17 @@ async function figmaSetup() {
|
|
|
3687
3906
|
switch (selectedAgent) {
|
|
3688
3907
|
case "claude":
|
|
3689
3908
|
logger.info(
|
|
3690
|
-
` ${
|
|
3909
|
+
` ${chalk11.cyan("claude mcp add --transport sse figma-sse https://mcp.figma.com/sse")}`
|
|
3691
3910
|
);
|
|
3692
3911
|
logger.info(" or for Figma Desktop app:");
|
|
3693
3912
|
logger.info(
|
|
3694
|
-
` ${
|
|
3913
|
+
` ${chalk11.cyan("claude mcp add --transport http figma-desktop http://127.0.0.1:3845/mcp")}`
|
|
3695
3914
|
);
|
|
3696
3915
|
break;
|
|
3697
3916
|
case "cursor":
|
|
3698
|
-
logger.info(` Add to ${
|
|
3917
|
+
logger.info(` Add to ${chalk11.cyan("~/.cursor/mcp.json")}:`);
|
|
3699
3918
|
logger.info(
|
|
3700
|
-
|
|
3919
|
+
chalk11.dim(
|
|
3701
3920
|
JSON.stringify(
|
|
3702
3921
|
{
|
|
3703
3922
|
mcpServers: {
|
|
@@ -3722,7 +3941,7 @@ async function figmaSetup() {
|
|
|
3722
3941
|
}
|
|
3723
3942
|
}
|
|
3724
3943
|
logger.info(
|
|
3725
|
-
|
|
3944
|
+
chalk11.dim(
|
|
3726
3945
|
"\nNote: Figma MCP requires a Figma seat (Dev or Full on paid plans for desktop server)."
|
|
3727
3946
|
)
|
|
3728
3947
|
);
|
|
@@ -3732,22 +3951,22 @@ async function figmaSetup() {
|
|
|
3732
3951
|
});
|
|
3733
3952
|
if (shouldSave) {
|
|
3734
3953
|
const configDir = globalConfigDir();
|
|
3735
|
-
if (!
|
|
3954
|
+
if (!existsSync18(configDir)) {
|
|
3736
3955
|
await mkdir7(configDir, { recursive: true });
|
|
3737
3956
|
}
|
|
3738
3957
|
const config = {};
|
|
3739
3958
|
if (selectedAgent !== "none") {
|
|
3740
3959
|
config.agent = selectedAgent;
|
|
3741
3960
|
}
|
|
3742
|
-
await
|
|
3961
|
+
await writeFile11(globalConfigPath(), `${JSON.stringify(config, null, 2)}
|
|
3743
3962
|
`, {
|
|
3744
3963
|
mode: 384
|
|
3745
3964
|
});
|
|
3746
|
-
logger.success(`Config saved to ${
|
|
3965
|
+
logger.success(`Config saved to ${chalk11.cyan(globalConfigPath())}`);
|
|
3747
3966
|
}
|
|
3748
|
-
logger.info(
|
|
3967
|
+
logger.info(chalk11.bold("\nSetup complete!"));
|
|
3749
3968
|
if (selectedAgent !== "none") {
|
|
3750
|
-
logger.info(`Try: ${
|
|
3969
|
+
logger.info(`Try: ${chalk11.cyan('eds block from-design "<figma-url>" --dry-run')}`);
|
|
3751
3970
|
}
|
|
3752
3971
|
}
|
|
3753
3972
|
var init_setup = __esm({
|
|
@@ -3766,9 +3985,9 @@ var doctor_exports = {};
|
|
|
3766
3985
|
__export(doctor_exports, {
|
|
3767
3986
|
doctor: () => doctor
|
|
3768
3987
|
});
|
|
3769
|
-
import { existsSync as
|
|
3770
|
-
import { readFile as
|
|
3771
|
-
import
|
|
3988
|
+
import { existsSync as existsSync19 } from "fs";
|
|
3989
|
+
import { readFile as readFile14, readdir as readdir3 } from "fs/promises";
|
|
3990
|
+
import path21 from "path";
|
|
3772
3991
|
import { parse as parseYaml } from "yaml";
|
|
3773
3992
|
async function doctor(options = {}) {
|
|
3774
3993
|
const projectRoot = findProjectRoot();
|
|
@@ -3790,7 +4009,7 @@ async function doctor(options = {}) {
|
|
|
3790
4009
|
});
|
|
3791
4010
|
if (project.hasFstab) {
|
|
3792
4011
|
try {
|
|
3793
|
-
const content = await
|
|
4012
|
+
const content = await readFile14(path21.join(projectRoot, "fstab.yaml"), "utf-8");
|
|
3794
4013
|
parseYaml(content);
|
|
3795
4014
|
results.push({ name: "fstab.yaml valid YAML", status: "pass", message: "Valid" });
|
|
3796
4015
|
} catch {
|
|
@@ -3812,28 +4031,28 @@ async function doctor(options = {}) {
|
|
|
3812
4031
|
message: project.hasBlocks ? "Found" : "Missing - no blocks yet"
|
|
3813
4032
|
});
|
|
3814
4033
|
if (project.hasBlocks) {
|
|
3815
|
-
const blocksDir =
|
|
4034
|
+
const blocksDir = path21.join(projectRoot, "blocks");
|
|
3816
4035
|
const entries = await readdir3(blocksDir, { withFileTypes: true });
|
|
3817
4036
|
let aggregatedModels = "";
|
|
3818
|
-
const aggregatedPath =
|
|
3819
|
-
if (
|
|
4037
|
+
const aggregatedPath = path21.join(projectRoot, "component-models.json");
|
|
4038
|
+
if (existsSync19(aggregatedPath)) {
|
|
3820
4039
|
try {
|
|
3821
|
-
aggregatedModels = await
|
|
4040
|
+
aggregatedModels = await readFile14(aggregatedPath, "utf-8");
|
|
3822
4041
|
} catch {
|
|
3823
4042
|
}
|
|
3824
4043
|
}
|
|
3825
4044
|
for (const entry of entries) {
|
|
3826
4045
|
if (!entry.isDirectory()) continue;
|
|
3827
4046
|
const blockName = entry.name;
|
|
3828
|
-
const jsFile =
|
|
3829
|
-
const hasJs =
|
|
4047
|
+
const jsFile = path21.join(blocksDir, blockName, `${blockName}.js`);
|
|
4048
|
+
const hasJs = existsSync19(jsFile);
|
|
3830
4049
|
results.push({
|
|
3831
4050
|
name: `blocks/${blockName} has JS`,
|
|
3832
4051
|
status: hasJs ? "pass" : "warn",
|
|
3833
4052
|
message: hasJs ? "Found" : `Missing ${blockName}.js`
|
|
3834
4053
|
});
|
|
3835
4054
|
if (hasJs) {
|
|
3836
|
-
const jsContent = await
|
|
4055
|
+
const jsContent = await readFile14(jsFile, "utf-8");
|
|
3837
4056
|
if (jsContent.includes("console.log")) {
|
|
3838
4057
|
results.push({
|
|
3839
4058
|
name: `blocks/${blockName} no console.log`,
|
|
@@ -3842,17 +4061,17 @@ async function doctor(options = {}) {
|
|
|
3842
4061
|
});
|
|
3843
4062
|
}
|
|
3844
4063
|
}
|
|
3845
|
-
const hasPerBlockModel =
|
|
4064
|
+
const hasPerBlockModel = existsSync19(path21.join(blocksDir, blockName, `_${blockName}.json`));
|
|
3846
4065
|
const hasAggregatedModel = aggregatedModels.includes(`"${blockName}"`);
|
|
3847
4066
|
results.push({
|
|
3848
4067
|
name: `blocks/${blockName} UE model`,
|
|
3849
4068
|
status: hasPerBlockModel || hasAggregatedModel ? "pass" : "warn",
|
|
3850
4069
|
message: hasPerBlockModel || hasAggregatedModel ? hasPerBlockModel ? `_${blockName}.json` : "in component-models.json" : "no Universal Editor model - authors can't edit this block in UE"
|
|
3851
4070
|
});
|
|
3852
|
-
const metaFile =
|
|
3853
|
-
if (
|
|
4071
|
+
const metaFile = path21.join(blocksDir, blockName, ".eds-meta.json");
|
|
4072
|
+
if (existsSync19(metaFile)) {
|
|
3854
4073
|
try {
|
|
3855
|
-
const meta = JSON.parse(await
|
|
4074
|
+
const meta = JSON.parse(await readFile14(metaFile, "utf-8"));
|
|
3856
4075
|
const syncDate = new Date(meta.lastSyncedAt);
|
|
3857
4076
|
const daysSinceSync = Math.floor(
|
|
3858
4077
|
(Date.now() - syncDate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
@@ -3872,10 +4091,10 @@ async function doctor(options = {}) {
|
|
|
3872
4091
|
}
|
|
3873
4092
|
}
|
|
3874
4093
|
}
|
|
3875
|
-
const queryFile =
|
|
3876
|
-
if (
|
|
4094
|
+
const queryFile = path21.join(projectRoot, "helix-query.yaml");
|
|
4095
|
+
if (existsSync19(queryFile)) {
|
|
3877
4096
|
try {
|
|
3878
|
-
const content = await
|
|
4097
|
+
const content = await readFile14(queryFile, "utf-8");
|
|
3879
4098
|
parseYaml(content);
|
|
3880
4099
|
results.push({ name: "helix-query.yaml valid", status: "pass", message: "Valid YAML" });
|
|
3881
4100
|
} catch {
|
|
@@ -3932,10 +4151,10 @@ var audit_loading_exports = {};
|
|
|
3932
4151
|
__export(audit_loading_exports, {
|
|
3933
4152
|
auditLoading: () => auditLoading
|
|
3934
4153
|
});
|
|
3935
|
-
import { existsSync as
|
|
3936
|
-
import { readFile as
|
|
3937
|
-
import
|
|
3938
|
-
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";
|
|
3939
4158
|
async function auditLoading(options = {}) {
|
|
3940
4159
|
const projectRoot = findProjectRoot();
|
|
3941
4160
|
if (!projectRoot) {
|
|
@@ -3968,8 +4187,8 @@ async function auditLoading(options = {}) {
|
|
|
3968
4187
|
printResults(results);
|
|
3969
4188
|
}
|
|
3970
4189
|
async function checkHeadHtml(root, results) {
|
|
3971
|
-
const filePath =
|
|
3972
|
-
if (!
|
|
4190
|
+
const filePath = path22.join(root, "head.html");
|
|
4191
|
+
if (!existsSync20(filePath)) {
|
|
3973
4192
|
results.push({
|
|
3974
4193
|
rule: "head.html exists",
|
|
3975
4194
|
status: "warn",
|
|
@@ -3977,7 +4196,7 @@ async function checkHeadHtml(root, results) {
|
|
|
3977
4196
|
});
|
|
3978
4197
|
return;
|
|
3979
4198
|
}
|
|
3980
|
-
const content = await
|
|
4199
|
+
const content = await readFile15(filePath, "utf-8");
|
|
3981
4200
|
const lines = content.split("\n");
|
|
3982
4201
|
for (let i = 0; i < lines.length; i++) {
|
|
3983
4202
|
const line = lines[i];
|
|
@@ -4049,8 +4268,8 @@ async function checkHeadHtml(root, results) {
|
|
|
4049
4268
|
}
|
|
4050
4269
|
}
|
|
4051
4270
|
async function checkScriptsJs(root, results) {
|
|
4052
|
-
const filePath =
|
|
4053
|
-
if (!
|
|
4271
|
+
const filePath = path22.join(root, "scripts", "scripts.js");
|
|
4272
|
+
if (!existsSync20(filePath)) {
|
|
4054
4273
|
results.push({
|
|
4055
4274
|
rule: "scripts/scripts.js exists",
|
|
4056
4275
|
status: "warn",
|
|
@@ -4058,7 +4277,7 @@ async function checkScriptsJs(root, results) {
|
|
|
4058
4277
|
});
|
|
4059
4278
|
return;
|
|
4060
4279
|
}
|
|
4061
|
-
const content = await
|
|
4280
|
+
const content = await readFile15(filePath, "utf-8");
|
|
4062
4281
|
const lines = content.split("\n");
|
|
4063
4282
|
const hasLoadEager = /function\s+loadEager|loadEager\s*=/i.test(content);
|
|
4064
4283
|
const hasLoadLazy = /function\s+loadLazy|loadLazy\s*=/i.test(content);
|
|
@@ -4136,8 +4355,8 @@ async function checkScriptsJs(root, results) {
|
|
|
4136
4355
|
}
|
|
4137
4356
|
}
|
|
4138
4357
|
async function checkStylesCss(root, results) {
|
|
4139
|
-
const filePath =
|
|
4140
|
-
if (!
|
|
4358
|
+
const filePath = path22.join(root, "styles", "styles.css");
|
|
4359
|
+
if (!existsSync20(filePath)) return;
|
|
4141
4360
|
const stats = await stat3(filePath);
|
|
4142
4361
|
const sizeKb = stats.size / 1024;
|
|
4143
4362
|
results.push({
|
|
@@ -4146,7 +4365,7 @@ async function checkStylesCss(root, results) {
|
|
|
4146
4365
|
message: `${sizeKb.toFixed(1)} KB${sizeKb > 40 ? " \u2014 large CSS competes with 100KB LCP budget" : ""}`,
|
|
4147
4366
|
file: "styles/styles.css"
|
|
4148
4367
|
});
|
|
4149
|
-
const content = await
|
|
4368
|
+
const content = await readFile15(filePath, "utf-8");
|
|
4150
4369
|
if (/@import\s/.test(content)) {
|
|
4151
4370
|
results.push({
|
|
4152
4371
|
rule: "No @import in styles.css",
|
|
@@ -4157,8 +4376,8 @@ async function checkStylesCss(root, results) {
|
|
|
4157
4376
|
}
|
|
4158
4377
|
}
|
|
4159
4378
|
async function checkDelayedJs(root, results) {
|
|
4160
|
-
const filePath =
|
|
4161
|
-
if (!
|
|
4379
|
+
const filePath = path22.join(root, "scripts", "delayed.js");
|
|
4380
|
+
if (!existsSync20(filePath)) {
|
|
4162
4381
|
results.push({
|
|
4163
4382
|
rule: "scripts/delayed.js exists",
|
|
4164
4383
|
status: "info",
|
|
@@ -4166,7 +4385,7 @@ async function checkDelayedJs(root, results) {
|
|
|
4166
4385
|
});
|
|
4167
4386
|
return;
|
|
4168
4387
|
}
|
|
4169
|
-
const content = await
|
|
4388
|
+
const content = await readFile15(filePath, "utf-8");
|
|
4170
4389
|
const stripped = content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").trim();
|
|
4171
4390
|
if (!stripped) {
|
|
4172
4391
|
results.push({
|
|
@@ -4178,15 +4397,15 @@ async function checkDelayedJs(root, results) {
|
|
|
4178
4397
|
}
|
|
4179
4398
|
}
|
|
4180
4399
|
async function checkBlocks(root, results) {
|
|
4181
|
-
const blocksDir =
|
|
4182
|
-
if (!
|
|
4400
|
+
const blocksDir = path22.join(root, "blocks");
|
|
4401
|
+
if (!existsSync20(blocksDir)) return;
|
|
4183
4402
|
const entries = await readdir4(blocksDir, { withFileTypes: true });
|
|
4184
4403
|
for (const entry of entries) {
|
|
4185
4404
|
if (!entry.isDirectory()) continue;
|
|
4186
4405
|
const blockName = entry.name;
|
|
4187
|
-
const jsFile =
|
|
4188
|
-
if (!
|
|
4189
|
-
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");
|
|
4190
4409
|
const lines = content.split("\n");
|
|
4191
4410
|
const heavyLibs = [
|
|
4192
4411
|
"jquery",
|
|
@@ -4236,9 +4455,9 @@ async function checkBlocks(root, results) {
|
|
|
4236
4455
|
for (const entry of entries) {
|
|
4237
4456
|
if (!entry.isDirectory()) continue;
|
|
4238
4457
|
const blockName = entry.name;
|
|
4239
|
-
const cssFile =
|
|
4240
|
-
if (!
|
|
4241
|
-
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");
|
|
4242
4461
|
const selectorPattern = /^([a-z][a-z0-9-]*)\s*\{/gm;
|
|
4243
4462
|
for (const selectorMatch of content.matchAll(selectorPattern)) {
|
|
4244
4463
|
const selector = selectorMatch[1];
|
|
@@ -4254,8 +4473,8 @@ async function checkBlocks(root, results) {
|
|
|
4254
4473
|
}
|
|
4255
4474
|
}
|
|
4256
4475
|
async function checkAemJs(root, results) {
|
|
4257
|
-
const filePath =
|
|
4258
|
-
if (!
|
|
4476
|
+
const filePath = path22.join(root, "scripts", "aem.js");
|
|
4477
|
+
if (!existsSync20(filePath)) {
|
|
4259
4478
|
results.push({
|
|
4260
4479
|
rule: "scripts/aem.js exists",
|
|
4261
4480
|
status: "warn",
|
|
@@ -4293,12 +4512,12 @@ function printResults(results) {
|
|
|
4293
4512
|
grouped.set(key, bucket);
|
|
4294
4513
|
}
|
|
4295
4514
|
for (const [file, fileResults] of grouped) {
|
|
4296
|
-
logger.info(` ${icon.bullet} ${
|
|
4515
|
+
logger.info(` ${icon.bullet} ${chalk12.underline(file)}`);
|
|
4297
4516
|
for (const r of fileResults) {
|
|
4298
|
-
const loc = r.line ?
|
|
4517
|
+
const loc = r.line ? chalk12.dim(`:${r.line}`) : "";
|
|
4299
4518
|
logger.info(` ${statusIcon(r.status)} ${r.rule}${loc}`);
|
|
4300
4519
|
if (r.status !== "pass") {
|
|
4301
|
-
logger.info(
|
|
4520
|
+
logger.info(chalk12.dim(` ${r.message}`));
|
|
4302
4521
|
}
|
|
4303
4522
|
}
|
|
4304
4523
|
logger.info("");
|
|
@@ -4327,14 +4546,14 @@ var audit_security_exports = {};
|
|
|
4327
4546
|
__export(audit_security_exports, {
|
|
4328
4547
|
auditSecurity: () => auditSecurity
|
|
4329
4548
|
});
|
|
4330
|
-
import { existsSync as
|
|
4331
|
-
import { readFile as
|
|
4332
|
-
import
|
|
4333
|
-
import
|
|
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";
|
|
4334
4553
|
async function* walk2(dir) {
|
|
4335
4554
|
const entries = await readdir5(dir, { withFileTypes: true });
|
|
4336
4555
|
for (const entry of entries) {
|
|
4337
|
-
const full =
|
|
4556
|
+
const full = path23.join(dir, entry.name);
|
|
4338
4557
|
if (entry.isDirectory()) {
|
|
4339
4558
|
if (IGNORE_DIRS.has(entry.name)) continue;
|
|
4340
4559
|
yield* walk2(full);
|
|
@@ -4368,11 +4587,11 @@ function scanContent(content, relFile, ext) {
|
|
|
4368
4587
|
return findings;
|
|
4369
4588
|
}
|
|
4370
4589
|
async function auditDependencies(root) {
|
|
4371
|
-
if (!
|
|
4590
|
+
if (!existsSync21(path23.join(root, "package.json"))) {
|
|
4372
4591
|
return { status: "skipped", note: "no package.json" };
|
|
4373
4592
|
}
|
|
4374
|
-
const hasLock =
|
|
4375
|
-
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"))) {
|
|
4376
4595
|
return { status: "skipped", note: "no package-lock.json / node_modules \u2014 run npm install" };
|
|
4377
4596
|
}
|
|
4378
4597
|
try {
|
|
@@ -4404,11 +4623,11 @@ async function auditSecurity(options = {}) {
|
|
|
4404
4623
|
}
|
|
4405
4624
|
const findings = [];
|
|
4406
4625
|
for await (const file of walk2(projectRoot)) {
|
|
4407
|
-
const ext =
|
|
4626
|
+
const ext = path23.extname(file).slice(1).toLowerCase();
|
|
4408
4627
|
if (!SCAN_EXT.has(ext)) continue;
|
|
4409
|
-
const rel =
|
|
4628
|
+
const rel = path23.relative(projectRoot, file);
|
|
4410
4629
|
if (rel.endsWith("aem.js") || rel.endsWith("lib-franklin.js")) continue;
|
|
4411
|
-
const content = await
|
|
4630
|
+
const content = await readFile16(file, "utf-8");
|
|
4412
4631
|
findings.push(...scanContent(content, rel, ext));
|
|
4413
4632
|
}
|
|
4414
4633
|
const deps = await auditDependencies(projectRoot);
|
|
@@ -4456,9 +4675,9 @@ async function auditSecurity(options = {}) {
|
|
|
4456
4675
|
);
|
|
4457
4676
|
for (const f of sorted) {
|
|
4458
4677
|
const meta = SEVERITY_META[f.severity];
|
|
4459
|
-
const loc =
|
|
4460
|
-
logger.info(` ${statusIcon(meta.status)} ${
|
|
4461
|
-
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}`));
|
|
4462
4681
|
}
|
|
4463
4682
|
}
|
|
4464
4683
|
const summaryLine = summary({
|
|
@@ -4665,7 +4884,7 @@ __export(preview_exports2, {
|
|
|
4665
4884
|
preview: () => preview,
|
|
4666
4885
|
resolveAdminConfig: () => resolveAdminConfig
|
|
4667
4886
|
});
|
|
4668
|
-
import
|
|
4887
|
+
import chalk14 from "chalk";
|
|
4669
4888
|
import ora2 from "ora";
|
|
4670
4889
|
async function preview(paths, options) {
|
|
4671
4890
|
const projectRoot = findProjectRoot();
|
|
@@ -4678,7 +4897,7 @@ async function preview(paths, options) {
|
|
|
4678
4897
|
let hasErrors = false;
|
|
4679
4898
|
for (const r of results) {
|
|
4680
4899
|
if (r.ok) {
|
|
4681
|
-
logger.success(`${r.path} \u2192 ${
|
|
4900
|
+
logger.success(`${r.path} \u2192 ${chalk14.cyan(r.previewUrl || "preview updated")}`);
|
|
4682
4901
|
} else {
|
|
4683
4902
|
logger.error(`${r.path} \u2014 ${r.message}`);
|
|
4684
4903
|
hasErrors = true;
|
|
@@ -4692,7 +4911,7 @@ function resolveAdminConfig(config, options) {
|
|
|
4692
4911
|
const ref = options.ref || config.admin?.ref || "main";
|
|
4693
4912
|
if (!org || !site) {
|
|
4694
4913
|
logger.error("Missing --org and --site. Set them via flags or in .edsrc.json:");
|
|
4695
|
-
logger.info(
|
|
4914
|
+
logger.info(chalk14.dim(' { "admin": { "org": "my-org", "site": "my-site" } }'));
|
|
4696
4915
|
process.exitCode = 1;
|
|
4697
4916
|
return void 0;
|
|
4698
4917
|
}
|
|
@@ -4714,7 +4933,7 @@ var publish_exports = {};
|
|
|
4714
4933
|
__export(publish_exports, {
|
|
4715
4934
|
publish: () => publish
|
|
4716
4935
|
});
|
|
4717
|
-
import
|
|
4936
|
+
import chalk15 from "chalk";
|
|
4718
4937
|
import ora3 from "ora";
|
|
4719
4938
|
async function publish(paths, options) {
|
|
4720
4939
|
const projectRoot = findProjectRoot();
|
|
@@ -4727,7 +4946,7 @@ async function publish(paths, options) {
|
|
|
4727
4946
|
let hasErrors = false;
|
|
4728
4947
|
for (const r of results) {
|
|
4729
4948
|
if (r.ok) {
|
|
4730
|
-
logger.success(`${r.path} \u2192 ${
|
|
4949
|
+
logger.success(`${r.path} \u2192 ${chalk15.cyan(r.liveUrl || "published")}`);
|
|
4731
4950
|
} else {
|
|
4732
4951
|
logger.error(`${r.path} \u2014 ${r.message}`);
|
|
4733
4952
|
hasErrors = true;
|
|
@@ -4826,12 +5045,12 @@ var menu_exports = {};
|
|
|
4826
5045
|
__export(menu_exports, {
|
|
4827
5046
|
mainMenu: () => mainMenu
|
|
4828
5047
|
});
|
|
4829
|
-
import { existsSync as
|
|
5048
|
+
import { existsSync as existsSync22 } from "fs";
|
|
4830
5049
|
import { readdir as readdir6 } from "fs/promises";
|
|
4831
|
-
import
|
|
5050
|
+
import path24 from "path";
|
|
4832
5051
|
async function listBlockNames(projectRoot) {
|
|
4833
|
-
const dir =
|
|
4834
|
-
if (!
|
|
5052
|
+
const dir = path24.join(projectRoot, "blocks");
|
|
5053
|
+
if (!existsSync22(dir)) return [];
|
|
4835
5054
|
const entries = await readdir6(dir, { withFileTypes: true });
|
|
4836
5055
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
4837
5056
|
}
|
|
@@ -4842,11 +5061,15 @@ async function mainMenu() {
|
|
|
4842
5061
|
message: "Choose an action",
|
|
4843
5062
|
choices: [
|
|
4844
5063
|
{ name: "Set up this project (authoring model, CI, naming)", value: "init" },
|
|
4845
|
-
{
|
|
5064
|
+
{
|
|
5065
|
+
name: "Generate a block from a design (Figma/Stitch/Canva/Sketch/Framer)",
|
|
5066
|
+
value: "from-design"
|
|
5067
|
+
},
|
|
4846
5068
|
{ name: "Migrate a classic AEM component to EDS", value: "migrate" },
|
|
4847
5069
|
{ name: "Scaffold Universal Editor components", value: "scaffold" },
|
|
4848
5070
|
{ name: "Create an empty block", value: "create" },
|
|
4849
5071
|
{ name: "Add a third-party integration (GTM, chat, consent\u2026)", value: "integrate" },
|
|
5072
|
+
{ name: "Instrument a block for analytics (dataLayer)", value: "track" },
|
|
4850
5073
|
{ name: "Preview a block in the browser", value: "preview-block" },
|
|
4851
5074
|
{ name: "Preview / publish pages (Admin API)", value: "admin" },
|
|
4852
5075
|
{ name: "Audit the project", value: "audit" },
|
|
@@ -4907,6 +5130,25 @@ async function mainMenu() {
|
|
|
4907
5130
|
await previewBlock2(name, {});
|
|
4908
5131
|
return;
|
|
4909
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
|
+
}
|
|
4910
5152
|
case "admin": {
|
|
4911
5153
|
const { adminWizard: adminWizard2 } = await Promise.resolve().then(() => (init_admin(), admin_exports));
|
|
4912
5154
|
await adminWizard2();
|
|
@@ -4952,13 +5194,13 @@ var init_menu = __esm({
|
|
|
4952
5194
|
|
|
4953
5195
|
// src/bin/eds.ts
|
|
4954
5196
|
init_esm_shims();
|
|
4955
|
-
import
|
|
5197
|
+
import chalk16 from "chalk";
|
|
4956
5198
|
import { Command } from "commander";
|
|
4957
5199
|
|
|
4958
5200
|
// package.json
|
|
4959
5201
|
var package_default = {
|
|
4960
5202
|
name: "aem-eds-kit",
|
|
4961
|
-
version: "0.4.
|
|
5203
|
+
version: "0.4.12",
|
|
4962
5204
|
description: "CLI for AEM Edge Delivery Services with Figma MCP integration",
|
|
4963
5205
|
type: "module",
|
|
4964
5206
|
engines: {
|
|
@@ -5034,7 +5276,7 @@ program.name("eds").description("CLI for AEM Edge Delivery Services with Figma i
|
|
|
5034
5276
|
const opts = thisCommand.opts();
|
|
5035
5277
|
if (opts.verbose) logger.setLevel("debug");
|
|
5036
5278
|
if (opts.quiet) logger.setQuiet(true);
|
|
5037
|
-
if (opts.color === false)
|
|
5279
|
+
if (opts.color === false) chalk16.level = 0;
|
|
5038
5280
|
});
|
|
5039
5281
|
program.addHelpText("beforeAll", () => logo());
|
|
5040
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) => {
|
|
@@ -5046,6 +5288,11 @@ migrate.command("component [dir]").description("Migrate a classic AEM component'
|
|
|
5046
5288
|
const { migrateComponent: migrateComponent2 } = await Promise.resolve().then(() => (init_migrate(), migrate_exports));
|
|
5047
5289
|
await migrateComponent2(dir, options);
|
|
5048
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
|
+
});
|
|
5049
5296
|
var block = program.command("block").description("Manage EDS blocks");
|
|
5050
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) => {
|
|
5051
5298
|
const { createBlock: createBlock2 } = await Promise.resolve().then(() => (init_create(), create_exports));
|