aem-eds-kit 0.4.12 → 0.4.16
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 +49 -2
- package/README.md +83 -0
- package/dist/bin/eds.js +1816 -724
- package/dist/bin/eds.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/eds.js
CHANGED
|
@@ -67,7 +67,7 @@ function mix(a, b, t) {
|
|
|
67
67
|
const r = Math.round(ar + (br - ar) * t);
|
|
68
68
|
const g = Math.round(ag + (bg - ag) * t);
|
|
69
69
|
const bl = Math.round(ab + (bb - ab) * t);
|
|
70
|
-
return `#${[r, g, bl].map((
|
|
70
|
+
return `#${[r, g, bl].map((n2) => n2.toString(16).padStart(2, "0")).join("")}`;
|
|
71
71
|
}
|
|
72
72
|
function gradientLine(line, stops, width) {
|
|
73
73
|
const chars = [...line];
|
|
@@ -127,8 +127,8 @@ function summary(counts) {
|
|
|
127
127
|
if (counts.info) parts.push(`${icon.info} ${counts.info} info`);
|
|
128
128
|
return parts.join(chalk.dim(" "));
|
|
129
129
|
}
|
|
130
|
-
function plural(
|
|
131
|
-
return
|
|
130
|
+
function plural(n2, word) {
|
|
131
|
+
return n2 === 1 ? word : `${word}s`;
|
|
132
132
|
}
|
|
133
133
|
var brand, icon, ANSI, DOTS_N, LOGO_ART, LOGO_STOPS;
|
|
134
134
|
var init_ui = __esm({
|
|
@@ -355,117 +355,6 @@ var init_project = __esm({
|
|
|
355
355
|
}
|
|
356
356
|
});
|
|
357
357
|
|
|
358
|
-
// src/lib/scaffold/ci.ts
|
|
359
|
-
import { existsSync as existsSync2 } from "fs";
|
|
360
|
-
import { mkdir, writeFile } from "fs/promises";
|
|
361
|
-
import path3 from "path";
|
|
362
|
-
async function writeCiWorkflow(projectRoot) {
|
|
363
|
-
const file = path3.join(projectRoot, ".github", "workflows", "eds.yml");
|
|
364
|
-
if (existsSync2(file)) return { path: CI_WORKFLOW_PATH, created: false };
|
|
365
|
-
await mkdir(path3.dirname(file), { recursive: true });
|
|
366
|
-
await writeFile(file, CI_WORKFLOW);
|
|
367
|
-
return { path: CI_WORKFLOW_PATH, created: true };
|
|
368
|
-
}
|
|
369
|
-
var CI_WORKFLOW_PATH, CI_WORKFLOW;
|
|
370
|
-
var init_ci = __esm({
|
|
371
|
-
"src/lib/scaffold/ci.ts"() {
|
|
372
|
-
"use strict";
|
|
373
|
-
init_esm_shims();
|
|
374
|
-
CI_WORKFLOW_PATH = ".github/workflows/eds.yml";
|
|
375
|
-
CI_WORKFLOW = `name: EDS checks
|
|
376
|
-
|
|
377
|
-
# Generated by aem-eds-kit (\`eds scaffold ci\`). Runs the project's own
|
|
378
|
-
# lint/build plus the eds audits as a gatekeeper on every PR and push to main.
|
|
379
|
-
on:
|
|
380
|
-
pull_request:
|
|
381
|
-
push:
|
|
382
|
-
branches: [main]
|
|
383
|
-
|
|
384
|
-
jobs:
|
|
385
|
-
checks:
|
|
386
|
-
runs-on: ubuntu-latest
|
|
387
|
-
steps:
|
|
388
|
-
- uses: actions/checkout@v4
|
|
389
|
-
- uses: actions/setup-node@v4
|
|
390
|
-
with:
|
|
391
|
-
node-version: 20
|
|
392
|
-
|
|
393
|
-
# Install project dependencies when the repo has them.
|
|
394
|
-
- name: Install dependencies
|
|
395
|
-
run: |
|
|
396
|
-
if [ -f package-lock.json ]; then
|
|
397
|
-
npm ci
|
|
398
|
-
elif [ -f package.json ]; then
|
|
399
|
-
npm install
|
|
400
|
-
fi
|
|
401
|
-
|
|
402
|
-
# The project's own scripts, if defined (skipped otherwise).
|
|
403
|
-
- name: Lint
|
|
404
|
-
if: hashFiles('package.json') != ''
|
|
405
|
-
run: npm run lint --if-present
|
|
406
|
-
- name: Build
|
|
407
|
-
if: hashFiles('package.json') != ''
|
|
408
|
-
run: npm run build --if-present
|
|
409
|
-
|
|
410
|
-
# aem-eds-kit audits - run via npx, no install needed. Each exits
|
|
411
|
-
# non-zero on failures, so a bad PR fails the check.
|
|
412
|
-
- name: Project health (doctor)
|
|
413
|
-
run: npx --yes aem-eds-kit@latest doctor
|
|
414
|
-
- name: Loading & LCP budget
|
|
415
|
-
run: npx --yes aem-eds-kit@latest audit loading
|
|
416
|
-
- name: Security & exploit scan
|
|
417
|
-
run: npx --yes aem-eds-kit@latest audit security
|
|
418
|
-
`;
|
|
419
|
-
}
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
// src/lib/scaffold/fstab.ts
|
|
423
|
-
import { writeFile as writeFile2 } from "fs/promises";
|
|
424
|
-
import path4 from "path";
|
|
425
|
-
function needsFstab(a) {
|
|
426
|
-
return a !== "ue";
|
|
427
|
-
}
|
|
428
|
-
function daMountpoint(org, site) {
|
|
429
|
-
return `https://content.da.live/${org}/${site}/`;
|
|
430
|
-
}
|
|
431
|
-
function fstabYaml(mountpoint) {
|
|
432
|
-
return `mountpoints:
|
|
433
|
-
/: ${mountpoint}
|
|
434
|
-
`;
|
|
435
|
-
}
|
|
436
|
-
async function writeFstab(projectRoot, mountpoint) {
|
|
437
|
-
await writeFile2(path4.join(projectRoot, "fstab.yaml"), fstabYaml(mountpoint));
|
|
438
|
-
return "fstab.yaml";
|
|
439
|
-
}
|
|
440
|
-
function pathsJson(site) {
|
|
441
|
-
return `${JSON.stringify(
|
|
442
|
-
{
|
|
443
|
-
mappings: [`/content/${site}/:/`],
|
|
444
|
-
includes: [`/content/${site}/`]
|
|
445
|
-
},
|
|
446
|
-
null,
|
|
447
|
-
2
|
|
448
|
-
)}
|
|
449
|
-
`;
|
|
450
|
-
}
|
|
451
|
-
async function writePaths(projectRoot, site) {
|
|
452
|
-
await writeFile2(path4.join(projectRoot, "paths.json"), pathsJson(site));
|
|
453
|
-
return "paths.json";
|
|
454
|
-
}
|
|
455
|
-
var AUTHORING_NAMES;
|
|
456
|
-
var init_fstab = __esm({
|
|
457
|
-
"src/lib/scaffold/fstab.ts"() {
|
|
458
|
-
"use strict";
|
|
459
|
-
init_esm_shims();
|
|
460
|
-
AUTHORING_NAMES = {
|
|
461
|
-
ue: "Universal Editor (crosswalk)",
|
|
462
|
-
da: "Document Authoring (da.live)",
|
|
463
|
-
gdrive: "Document Authoring (Google Drive)",
|
|
464
|
-
sharepoint: "Document Authoring (SharePoint)"
|
|
465
|
-
};
|
|
466
|
-
}
|
|
467
|
-
});
|
|
468
|
-
|
|
469
358
|
// src/lib/scaffold/blocks.ts
|
|
470
359
|
function json(value) {
|
|
471
360
|
return `${JSON.stringify(value, null, 2)}
|
|
@@ -479,14 +368,14 @@ var init_blocks = __esm({
|
|
|
479
368
|
"src/lib/scaffold/blocks.ts"() {
|
|
480
369
|
"use strict";
|
|
481
370
|
init_esm_shims();
|
|
482
|
-
blockDef = (title, id,
|
|
371
|
+
blockDef = (title, id, model2) => ({
|
|
483
372
|
title,
|
|
484
373
|
id,
|
|
485
374
|
plugins: {
|
|
486
375
|
xwalk: {
|
|
487
376
|
page: {
|
|
488
377
|
resourceType: "core/franklin/components/block/v1/block",
|
|
489
|
-
template: { name: title, model }
|
|
378
|
+
template: { name: title, model: model2 }
|
|
490
379
|
}
|
|
491
380
|
}
|
|
492
381
|
}
|
|
@@ -503,14 +392,14 @@ var init_blocks = __esm({
|
|
|
503
392
|
}
|
|
504
393
|
}
|
|
505
394
|
});
|
|
506
|
-
itemDef = (title, id,
|
|
395
|
+
itemDef = (title, id, model2) => ({
|
|
507
396
|
title,
|
|
508
397
|
id,
|
|
509
398
|
plugins: {
|
|
510
399
|
xwalk: {
|
|
511
400
|
page: {
|
|
512
401
|
resourceType: "core/franklin/components/block/v1/block/item",
|
|
513
|
-
template: { name: title, model }
|
|
402
|
+
template: { name: title, model: model2 }
|
|
514
403
|
}
|
|
515
404
|
}
|
|
516
405
|
}
|
|
@@ -872,7 +761,7 @@ function fieldReferenceFields() {
|
|
|
872
761
|
];
|
|
873
762
|
}
|
|
874
763
|
function fieldReferenceModelFile() {
|
|
875
|
-
const
|
|
764
|
+
const model2 = {
|
|
876
765
|
definitions: [
|
|
877
766
|
{
|
|
878
767
|
title: "Field Reference",
|
|
@@ -890,7 +779,7 @@ function fieldReferenceModelFile() {
|
|
|
890
779
|
models: [{ id: "field-reference", fields: fieldReferenceFields() }],
|
|
891
780
|
filters: []
|
|
892
781
|
};
|
|
893
|
-
return `${JSON.stringify(
|
|
782
|
+
return `${JSON.stringify(model2, null, 2)}
|
|
894
783
|
`;
|
|
895
784
|
}
|
|
896
785
|
function mergeById(existing, additions) {
|
|
@@ -913,7 +802,7 @@ function mergeDefinitions(existing, defaultContent, blockDefs) {
|
|
|
913
802
|
}
|
|
914
803
|
function mergeSectionFilter(existing, ids) {
|
|
915
804
|
const filters = existing ? [...existing] : [];
|
|
916
|
-
let section = filters.find((
|
|
805
|
+
let section = filters.find((f2) => f2.id === "section");
|
|
917
806
|
if (!section) {
|
|
918
807
|
section = { id: "section", components: ["text", "image", "button", "title"] };
|
|
919
808
|
filters.push(section);
|
|
@@ -1044,152 +933,740 @@ export default function decorate(block) {
|
|
|
1044
933
|
}
|
|
1045
934
|
});
|
|
1046
935
|
|
|
1047
|
-
// src/
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
936
|
+
// src/lib/template/authoring.ts
|
|
937
|
+
import { existsSync as existsSync2 } from "fs";
|
|
938
|
+
import { readFile } from "fs/promises";
|
|
939
|
+
import path3 from "path";
|
|
940
|
+
import { parse as parseYaml } from "yaml";
|
|
941
|
+
async function detectAuthoring(projectRoot) {
|
|
942
|
+
const fstab = path3.join(projectRoot, "fstab.yaml");
|
|
943
|
+
if (existsSync2(fstab)) {
|
|
944
|
+
try {
|
|
945
|
+
const parsed = parseYaml(await readFile(fstab, "utf-8")) ?? {};
|
|
946
|
+
const mp = parsed.mountpoints?.["/"];
|
|
947
|
+
const url = typeof mp === "string" ? mp : mp?.url ?? "";
|
|
948
|
+
const da = url.match(/content\.da\.live\/([^/]+)\/([^/]+)/);
|
|
949
|
+
if (da) return { model: "da", org: da[1], site: da[2], mountpoint: url };
|
|
950
|
+
if (/drive\.google\.com/.test(url)) return { model: "gdrive", mountpoint: url };
|
|
951
|
+
if (/sharepoint\.com|-my\.sharepoint/.test(url))
|
|
952
|
+
return { model: "sharepoint", mountpoint: url };
|
|
953
|
+
return { model: "document", mountpoint: url };
|
|
954
|
+
} catch {
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
return { model: "ue" };
|
|
958
|
+
}
|
|
959
|
+
var init_authoring = __esm({
|
|
960
|
+
"src/lib/template/authoring.ts"() {
|
|
961
|
+
"use strict";
|
|
962
|
+
init_esm_shims();
|
|
963
|
+
}
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
// src/lib/template/da.ts
|
|
967
|
+
function daSourceUrl(org, site, path30) {
|
|
968
|
+
const clean = path30.replace(/^\/+/, "").replace(/\.html$/i, "");
|
|
969
|
+
return `https://admin.da.live/source/${org}/${site}/${clean}.html`;
|
|
970
|
+
}
|
|
971
|
+
function daEditUrl(org, site, path30) {
|
|
972
|
+
const clean = path30.replace(/^\/+/, "").replace(/\.html$/i, "");
|
|
973
|
+
return `https://da.live/edit#/${org}/${site}/${clean}`;
|
|
974
|
+
}
|
|
975
|
+
async function pushToDa(org, site, path30, html, token) {
|
|
976
|
+
const url = daSourceUrl(org, site, path30);
|
|
977
|
+
const form = new FormData();
|
|
978
|
+
form.append("data", new Blob([html], { type: "text/html" }), "content.html");
|
|
979
|
+
const headers = {};
|
|
980
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
981
|
+
const res = await fetch(url, { method: "POST", body: form, headers });
|
|
982
|
+
if (!res.ok) {
|
|
983
|
+
throw new Error(`DA push failed: ${res.status} ${res.statusText}`);
|
|
984
|
+
}
|
|
985
|
+
return url;
|
|
986
|
+
}
|
|
987
|
+
var init_da = __esm({
|
|
988
|
+
"src/lib/template/da.ts"() {
|
|
989
|
+
"use strict";
|
|
990
|
+
init_esm_shims();
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
// src/lib/template/page.ts
|
|
995
|
+
function blockDiv(name) {
|
|
996
|
+
return ` <div class="${name}">
|
|
997
|
+
<div>
|
|
998
|
+
<div>${name} content</div>
|
|
999
|
+
</div>
|
|
1000
|
+
</div>`;
|
|
1001
|
+
}
|
|
1002
|
+
function metadataBlock(opts) {
|
|
1003
|
+
const rows = [
|
|
1004
|
+
` <div><div>Title</div><div>${opts.title}</div></div>`,
|
|
1005
|
+
` <div><div>Description</div><div>${opts.description ?? ""}</div></div>`
|
|
1006
|
+
];
|
|
1007
|
+
if (opts.area) rows.push(` <div><div>Template</div><div>${opts.area}</div></div>`);
|
|
1008
|
+
return ` <div class="metadata">
|
|
1009
|
+
${rows.join("\n")}
|
|
1010
|
+
</div>`;
|
|
1011
|
+
}
|
|
1012
|
+
function buildPageHtml(opts) {
|
|
1013
|
+
const blocks = opts.blocks ?? [];
|
|
1014
|
+
const intro = ` <div>
|
|
1015
|
+
<h1>${opts.title}</h1>
|
|
1016
|
+
<p>${opts.description ?? "Intro copy goes here."}</p>
|
|
1017
|
+
</div>`;
|
|
1018
|
+
const blockSections = blocks.map((b) => ` <div>
|
|
1019
|
+
${blockDiv(b)}
|
|
1020
|
+
</div>`);
|
|
1021
|
+
const metaSection = ` <div>
|
|
1022
|
+
${metadataBlock(opts)}
|
|
1023
|
+
</div>`;
|
|
1024
|
+
return `<body>
|
|
1025
|
+
<header></header>
|
|
1026
|
+
<main>
|
|
1027
|
+
${[intro, ...blockSections, metaSection].join("\n")}
|
|
1028
|
+
</main>
|
|
1029
|
+
<footer></footer>
|
|
1030
|
+
</body>
|
|
1031
|
+
`;
|
|
1032
|
+
}
|
|
1033
|
+
var init_page = __esm({
|
|
1034
|
+
"src/lib/template/page.ts"() {
|
|
1035
|
+
"use strict";
|
|
1036
|
+
init_esm_shims();
|
|
1037
|
+
}
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
// src/commands/template.ts
|
|
1041
|
+
var template_exports = {};
|
|
1042
|
+
__export(template_exports, {
|
|
1043
|
+
templateNew: () => templateNew
|
|
1054
1044
|
});
|
|
1055
1045
|
import { existsSync as existsSync3 } from "fs";
|
|
1056
|
-
import { mkdir as
|
|
1057
|
-
import
|
|
1058
|
-
|
|
1059
|
-
|
|
1046
|
+
import { mkdir, readFile as readFile2, readdir, writeFile } from "fs/promises";
|
|
1047
|
+
import path4 from "path";
|
|
1048
|
+
import chalk3 from "chalk";
|
|
1049
|
+
async function projectBlocks(projectRoot) {
|
|
1050
|
+
const dir = path4.join(projectRoot, "blocks");
|
|
1051
|
+
if (!existsSync3(dir)) return [];
|
|
1052
|
+
return (await readdir(dir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
1053
|
+
}
|
|
1054
|
+
async function readEdsrcAdmin(projectRoot) {
|
|
1055
|
+
const rc = path4.join(projectRoot, ".edsrc.json");
|
|
1056
|
+
if (!existsSync3(rc)) return {};
|
|
1060
1057
|
try {
|
|
1061
|
-
return JSON.parse(await
|
|
1058
|
+
return JSON.parse(await readFile2(rc, "utf-8")).admin ?? {};
|
|
1062
1059
|
} catch {
|
|
1063
|
-
return
|
|
1060
|
+
return {};
|
|
1064
1061
|
}
|
|
1065
1062
|
}
|
|
1066
|
-
async function
|
|
1067
|
-
|
|
1063
|
+
async function registerBlocks(projectRoot, blocks) {
|
|
1064
|
+
if (!blocks.length) return false;
|
|
1065
|
+
const file = path4.join(projectRoot, "component-filters.json");
|
|
1066
|
+
let existing = null;
|
|
1067
|
+
if (existsSync3(file)) {
|
|
1068
|
+
try {
|
|
1069
|
+
existing = JSON.parse(await readFile2(file, "utf-8"));
|
|
1070
|
+
} catch {
|
|
1071
|
+
existing = null;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
const filters = mergeSectionFilter(existing, blocks);
|
|
1075
|
+
await writeFile(file, `${JSON.stringify(filters, null, 2)}
|
|
1068
1076
|
`);
|
|
1077
|
+
return true;
|
|
1069
1078
|
}
|
|
1070
|
-
async function
|
|
1079
|
+
async function templateNew(nameArg, options = {}) {
|
|
1071
1080
|
const projectRoot = findProjectRoot();
|
|
1072
1081
|
if (!projectRoot) {
|
|
1073
1082
|
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
1074
1083
|
process.exitCode = 1;
|
|
1075
1084
|
return;
|
|
1076
1085
|
}
|
|
1077
|
-
logger.
|
|
1078
|
-
const
|
|
1079
|
-
const
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
const
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
{
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1086
|
+
await logger.logoOnceAnimated("Page template");
|
|
1087
|
+
const { input, checkbox, confirm } = await import("@inquirer/prompts");
|
|
1088
|
+
const interactive = !options.yes;
|
|
1089
|
+
const detected = await detectAuthoring(projectRoot);
|
|
1090
|
+
const model2 = options.authoring ?? detected.model;
|
|
1091
|
+
const name = slug(nameArg);
|
|
1092
|
+
const title = options.title ?? (interactive ? await input({ message: "Page title", default: nameArg }) : nameArg);
|
|
1093
|
+
const description = options.description ?? (interactive ? await input({ message: "Description (optional)" }) : "");
|
|
1094
|
+
const pagePath = options.path ?? `/templates/${name}`;
|
|
1095
|
+
let blocks = options.blocks;
|
|
1096
|
+
if (!blocks && interactive) {
|
|
1097
|
+
const available = await projectBlocks(projectRoot);
|
|
1098
|
+
const choices = (available.length ? available : STANDARD_BLOCKS.map((b) => b.id)).map((b) => ({
|
|
1099
|
+
name: b,
|
|
1100
|
+
value: b
|
|
1101
|
+
}));
|
|
1102
|
+
blocks = await checkbox({ message: "Which blocks to seed into the page?", choices });
|
|
1103
|
+
}
|
|
1104
|
+
const seeded = blocks ?? [];
|
|
1105
|
+
const html = buildPageHtml({
|
|
1106
|
+
title,
|
|
1107
|
+
description: description || void 0,
|
|
1108
|
+
blocks: seeded,
|
|
1109
|
+
area: options.area
|
|
1110
|
+
});
|
|
1111
|
+
const localRel = path4.join("templates", `${name}.html`);
|
|
1112
|
+
await mkdir(path4.join(projectRoot, "templates"), { recursive: true });
|
|
1113
|
+
await writeFile(path4.join(projectRoot, localRel), html);
|
|
1114
|
+
logger.info(heading("Page template", `${pagePath} \xB7 ${model2}`));
|
|
1115
|
+
logger.info(accentLine(localRel, `${seeded.length} block(s) + metadata`));
|
|
1116
|
+
if (model2 === "da") {
|
|
1117
|
+
const admin = await readEdsrcAdmin(projectRoot);
|
|
1118
|
+
const org = options.org ?? admin.org ?? detected.org;
|
|
1119
|
+
const site = options.site ?? admin.site ?? detected.site;
|
|
1120
|
+
const wantPush = options.push ?? (interactive ? await confirm({ message: "Push to DA (da.live)?", default: true }) : true);
|
|
1121
|
+
if (wantPush) {
|
|
1122
|
+
if (!org || !site) {
|
|
1123
|
+
logger.warn("No DA org/site. Pass --org/--site or set admin in .edsrc.json.");
|
|
1124
|
+
} else {
|
|
1125
|
+
try {
|
|
1126
|
+
await pushToDa(org, site, pagePath, html, process.env.DA_TOKEN);
|
|
1127
|
+
logger.info(accentLine("pushed to DA", daEditUrl(org, site, pagePath)));
|
|
1128
|
+
} catch (err) {
|
|
1129
|
+
logger.error(`DA push failed: ${err.message}`);
|
|
1130
|
+
logger.info(chalk3.dim(" Open projects allow anonymous writes; others need DA_TOKEN."));
|
|
1098
1131
|
}
|
|
1099
1132
|
}
|
|
1100
1133
|
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
await writeJson(filtersFile, filters);
|
|
1116
|
-
created.push("component-filters.json (section filter)");
|
|
1117
|
-
logger.info(heading("Created / merged"));
|
|
1118
|
-
const names = created.map((c) => c.split(" ")[0]);
|
|
1119
|
-
const w = columnWidth(names);
|
|
1120
|
-
for (const c of created) {
|
|
1121
|
-
const [name, ...rest] = c.split(" ");
|
|
1122
|
-
logger.info(accentLine(name, rest.join(" ").replace(/^\((.*)\)$/, "$1"), w));
|
|
1123
|
-
}
|
|
1124
|
-
logger.info("");
|
|
1125
|
-
logger.info(box(["Universal Editor scaffold ready"]));
|
|
1126
|
-
}
|
|
1127
|
-
async function scaffoldBlocks(names) {
|
|
1128
|
-
const projectRoot = findProjectRoot();
|
|
1129
|
-
if (!projectRoot) {
|
|
1130
|
-
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
1131
|
-
process.exitCode = 1;
|
|
1132
|
-
return;
|
|
1133
|
-
}
|
|
1134
|
-
logger.logoOnce(logo("Scaffold \u2014 standard blocks"));
|
|
1135
|
-
let selected;
|
|
1136
|
-
if (names?.length) {
|
|
1137
|
-
selected = names.map((n) => blockById(n.trim())).filter((b) => !!b);
|
|
1138
|
-
const unknown = names.filter((n) => !blockById(n.trim()));
|
|
1139
|
-
if (unknown.length) {
|
|
1140
|
-
logger.warn(
|
|
1141
|
-
`Unknown block(s): ${unknown.join(", ")}. Available: ${STANDARD_BLOCKS.map((b) => b.id).join(", ")}`
|
|
1134
|
+
} else if (model2 === "gdrive" || model2 === "sharepoint") {
|
|
1135
|
+
const where = model2 === "gdrive" ? "a Google Doc" : "a Word doc";
|
|
1136
|
+
logger.info("");
|
|
1137
|
+
logger.info(
|
|
1138
|
+
chalk3.dim(` Document model: import ${localRel} into ${where} in your mounted folder,`)
|
|
1139
|
+
);
|
|
1140
|
+
logger.info(
|
|
1141
|
+
chalk3.dim(" then preview/publish. (Share the folder + install aem-code-sync first.)")
|
|
1142
|
+
);
|
|
1143
|
+
} else if (model2 === "ue") {
|
|
1144
|
+
const registered = await registerBlocks(projectRoot, seeded);
|
|
1145
|
+
if (registered) {
|
|
1146
|
+
logger.info(
|
|
1147
|
+
accentLine("component-filters.json", "seeded blocks allowed in Universal Editor")
|
|
1142
1148
|
);
|
|
1143
1149
|
}
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
for (const b of selected) {
|
|
1150
|
-
const dir = path5.join(projectRoot, "blocks", b.id);
|
|
1151
|
-
if (existsSync3(dir)) {
|
|
1152
|
-
skipped.push(b.id);
|
|
1153
|
-
continue;
|
|
1154
|
-
}
|
|
1155
|
-
await mkdir2(dir, { recursive: true });
|
|
1156
|
-
await writeFile3(path5.join(dir, `${b.id}.js`), b.js);
|
|
1157
|
-
await writeFile3(path5.join(dir, `${b.id}.css`), b.css);
|
|
1158
|
-
await writeFile3(path5.join(dir, `_${b.id}.json`), b.model);
|
|
1159
|
-
createdIds.push(b.id);
|
|
1150
|
+
logger.info("");
|
|
1151
|
+
logger.info(
|
|
1152
|
+
chalk3.dim(" Universal Editor: create the page's editable template in AEM and author it")
|
|
1153
|
+
);
|
|
1154
|
+
logger.info(chalk3.dim(` in the Universal Editor; ${localRel} is the reference structure.`));
|
|
1160
1155
|
}
|
|
1161
|
-
const filtersFile = path5.join(projectRoot, "component-filters.json");
|
|
1162
|
-
const filters = mergeSectionFilter(
|
|
1163
|
-
await readJson(filtersFile),
|
|
1164
|
-
selected.map((b) => b.id)
|
|
1165
|
-
);
|
|
1166
|
-
await writeJson(filtersFile, filters);
|
|
1167
|
-
logger.info(heading("Blocks"));
|
|
1168
|
-
const w = columnWidth([...createdIds, ...skipped, "component-filters"]);
|
|
1169
|
-
for (const id of createdIds) logger.info(accentLine(id, "created", w));
|
|
1170
|
-
for (const id of skipped) logger.info(statusLine("warn", id, "already exists \u2014 skipped", w));
|
|
1171
1156
|
logger.info("");
|
|
1172
|
-
logger.info(box([
|
|
1157
|
+
logger.info(box([`"${name}" template (${model2}) - preview with \`eds preview ${pagePath}\``]));
|
|
1173
1158
|
}
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1159
|
+
var slug;
|
|
1160
|
+
var init_template = __esm({
|
|
1161
|
+
"src/commands/template.ts"() {
|
|
1162
|
+
"use strict";
|
|
1163
|
+
init_esm_shims();
|
|
1164
|
+
init_logger();
|
|
1165
|
+
init_project();
|
|
1166
|
+
init_blocks();
|
|
1167
|
+
init_ue();
|
|
1168
|
+
init_authoring();
|
|
1169
|
+
init_da();
|
|
1170
|
+
init_page();
|
|
1171
|
+
init_ui();
|
|
1172
|
+
slug = (s) => s.trim().toLowerCase().replace(/[^a-z0-9-/]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1188
1173
|
}
|
|
1189
|
-
|
|
1174
|
+
});
|
|
1175
|
+
|
|
1176
|
+
// src/lib/scaffold/ci.ts
|
|
1177
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1178
|
+
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
1179
|
+
import path5 from "path";
|
|
1180
|
+
async function writeCiWorkflow(projectRoot) {
|
|
1181
|
+
const file = path5.join(projectRoot, ".github", "workflows", "eds.yml");
|
|
1182
|
+
if (existsSync4(file)) return { path: CI_WORKFLOW_PATH, created: false };
|
|
1183
|
+
await mkdir2(path5.dirname(file), { recursive: true });
|
|
1184
|
+
await writeFile2(file, CI_WORKFLOW);
|
|
1185
|
+
return { path: CI_WORKFLOW_PATH, created: true };
|
|
1186
|
+
}
|
|
1187
|
+
var CI_WORKFLOW_PATH, CI_WORKFLOW;
|
|
1188
|
+
var init_ci = __esm({
|
|
1189
|
+
"src/lib/scaffold/ci.ts"() {
|
|
1190
|
+
"use strict";
|
|
1191
|
+
init_esm_shims();
|
|
1192
|
+
CI_WORKFLOW_PATH = ".github/workflows/eds.yml";
|
|
1193
|
+
CI_WORKFLOW = `name: EDS checks
|
|
1194
|
+
|
|
1195
|
+
# Generated by aem-eds-kit (\`eds scaffold ci\`). Runs the project's own
|
|
1196
|
+
# lint/build plus the eds audits as a gatekeeper on every PR and push to main.
|
|
1197
|
+
on:
|
|
1198
|
+
pull_request:
|
|
1199
|
+
push:
|
|
1200
|
+
branches: [main]
|
|
1201
|
+
|
|
1202
|
+
jobs:
|
|
1203
|
+
checks:
|
|
1204
|
+
runs-on: ubuntu-latest
|
|
1205
|
+
steps:
|
|
1206
|
+
- uses: actions/checkout@v4
|
|
1207
|
+
- uses: actions/setup-node@v4
|
|
1208
|
+
with:
|
|
1209
|
+
node-version: 20
|
|
1210
|
+
|
|
1211
|
+
# Install project dependencies when the repo has them.
|
|
1212
|
+
- name: Install dependencies
|
|
1213
|
+
run: |
|
|
1214
|
+
if [ -f package-lock.json ]; then
|
|
1215
|
+
npm ci
|
|
1216
|
+
elif [ -f package.json ]; then
|
|
1217
|
+
npm install
|
|
1218
|
+
fi
|
|
1219
|
+
|
|
1220
|
+
# The project's own scripts, if defined (skipped otherwise).
|
|
1221
|
+
- name: Lint
|
|
1222
|
+
if: hashFiles('package.json') != ''
|
|
1223
|
+
run: npm run lint --if-present
|
|
1224
|
+
- name: Build
|
|
1225
|
+
if: hashFiles('package.json') != ''
|
|
1226
|
+
run: npm run build --if-present
|
|
1227
|
+
|
|
1228
|
+
# aem-eds-kit audits - run via npx, no install needed. Each exits
|
|
1229
|
+
# non-zero on failures, so a bad PR fails the check.
|
|
1230
|
+
- name: Project health (doctor)
|
|
1231
|
+
run: npx --yes aem-eds-kit@latest doctor
|
|
1232
|
+
- name: Loading & LCP budget
|
|
1233
|
+
run: npx --yes aem-eds-kit@latest audit loading
|
|
1234
|
+
- name: Security & exploit scan
|
|
1235
|
+
run: npx --yes aem-eds-kit@latest audit security
|
|
1236
|
+
`;
|
|
1237
|
+
}
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
// src/lib/scaffold/fstab.ts
|
|
1241
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
1242
|
+
import path6 from "path";
|
|
1243
|
+
function needsFstab(a) {
|
|
1244
|
+
return a !== "ue";
|
|
1245
|
+
}
|
|
1246
|
+
function daMountpoint(org, site) {
|
|
1247
|
+
return `https://content.da.live/${org}/${site}/`;
|
|
1248
|
+
}
|
|
1249
|
+
function fstabYaml(mountpoint) {
|
|
1250
|
+
return `mountpoints:
|
|
1251
|
+
/: ${mountpoint}
|
|
1252
|
+
`;
|
|
1253
|
+
}
|
|
1254
|
+
async function writeFstab(projectRoot, mountpoint) {
|
|
1255
|
+
await writeFile3(path6.join(projectRoot, "fstab.yaml"), fstabYaml(mountpoint));
|
|
1256
|
+
return "fstab.yaml";
|
|
1257
|
+
}
|
|
1258
|
+
function pathsJson(site) {
|
|
1259
|
+
return `${JSON.stringify(
|
|
1260
|
+
{
|
|
1261
|
+
mappings: [`/content/${site}/:/`],
|
|
1262
|
+
includes: [`/content/${site}/`]
|
|
1263
|
+
},
|
|
1264
|
+
null,
|
|
1265
|
+
2
|
|
1266
|
+
)}
|
|
1267
|
+
`;
|
|
1268
|
+
}
|
|
1269
|
+
async function writePaths(projectRoot, site) {
|
|
1270
|
+
await writeFile3(path6.join(projectRoot, "paths.json"), pathsJson(site));
|
|
1271
|
+
return "paths.json";
|
|
1272
|
+
}
|
|
1273
|
+
var AUTHORING_NAMES;
|
|
1274
|
+
var init_fstab = __esm({
|
|
1275
|
+
"src/lib/scaffold/fstab.ts"() {
|
|
1276
|
+
"use strict";
|
|
1277
|
+
init_esm_shims();
|
|
1278
|
+
AUTHORING_NAMES = {
|
|
1279
|
+
ue: "Universal Editor (crosswalk)",
|
|
1280
|
+
da: "Document Authoring (da.live)",
|
|
1281
|
+
gdrive: "Document Authoring (Google Drive)",
|
|
1282
|
+
sharepoint: "Document Authoring (SharePoint)"
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
});
|
|
1286
|
+
|
|
1287
|
+
// src/lib/scaffold/helpers.ts
|
|
1288
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1289
|
+
import { mkdir as mkdir3, writeFile as writeFile4 } from "fs/promises";
|
|
1290
|
+
import path7 from "path";
|
|
1291
|
+
function helperById(id) {
|
|
1292
|
+
return HELPERS.find((h) => h.id === id);
|
|
1293
|
+
}
|
|
1294
|
+
function resolveHelpers(ids) {
|
|
1295
|
+
const want = /* @__PURE__ */ new Set();
|
|
1296
|
+
const add = (id) => {
|
|
1297
|
+
if (want.has(id)) return;
|
|
1298
|
+
const h = helperById(id);
|
|
1299
|
+
if (!h) return;
|
|
1300
|
+
for (const dep of h.deps ?? []) add(dep);
|
|
1301
|
+
want.add(id);
|
|
1302
|
+
};
|
|
1303
|
+
for (const id of ids) add(id);
|
|
1304
|
+
return HELPERS.filter((h) => want.has(h.id)).map((h) => h.id);
|
|
1305
|
+
}
|
|
1306
|
+
function buildHelpers(ids) {
|
|
1307
|
+
const resolved = resolveHelpers(ids);
|
|
1308
|
+
const body = resolved.map((id) => helperById(id)).filter((h) => !!h).map((h) => h.code).join("\n\n");
|
|
1309
|
+
return `// scripts/utils.js - common EDS helpers (generated by \`eds scaffold helpers\`).
|
|
1310
|
+
|
|
1311
|
+
${body}
|
|
1312
|
+
`;
|
|
1313
|
+
}
|
|
1314
|
+
async function writeHelpers(projectRoot, ids = ALL_HELPER_IDS) {
|
|
1315
|
+
const resolved = resolveHelpers(ids);
|
|
1316
|
+
const file = path7.join(projectRoot, "scripts", "utils.js");
|
|
1317
|
+
if (existsSync5(file)) return { path: HELPERS_PATH, created: false, helpers: resolved };
|
|
1318
|
+
await mkdir3(path7.dirname(file), { recursive: true });
|
|
1319
|
+
await writeFile4(file, buildHelpers(ids));
|
|
1320
|
+
return { path: HELPERS_PATH, created: true, helpers: resolved };
|
|
1321
|
+
}
|
|
1322
|
+
var HELPERS_PATH, HELPERS, ALL_HELPER_IDS;
|
|
1323
|
+
var init_helpers = __esm({
|
|
1324
|
+
"src/lib/scaffold/helpers.ts"() {
|
|
1325
|
+
"use strict";
|
|
1326
|
+
init_esm_shims();
|
|
1327
|
+
HELPERS_PATH = "scripts/utils.js";
|
|
1328
|
+
HELPERS = [
|
|
1329
|
+
{
|
|
1330
|
+
id: "getMetadata",
|
|
1331
|
+
label: "getMetadata (read a page meta value)",
|
|
1332
|
+
code: `/**
|
|
1333
|
+
* Reads a page metadata value by name (meta[name] or meta[property]).
|
|
1334
|
+
* @param {string} name The metadata name.
|
|
1335
|
+
* @returns {string} The value, or an empty string.
|
|
1336
|
+
*/
|
|
1337
|
+
export function getMetadata(name) {
|
|
1338
|
+
const meta =
|
|
1339
|
+
document.querySelector(\`meta[name="\${name}"]\`) ||
|
|
1340
|
+
document.querySelector(\`meta[property="\${name}"]\`);
|
|
1341
|
+
return meta ? meta.content : '';
|
|
1342
|
+
}`
|
|
1343
|
+
},
|
|
1344
|
+
{
|
|
1345
|
+
id: "toCamelCase",
|
|
1346
|
+
label: "toCamelCase (dash/underscore/space -> camelCase)",
|
|
1347
|
+
code: `/**
|
|
1348
|
+
* Converts a string to camelCase, handling dashes, underscores and spaces.
|
|
1349
|
+
* @param {string} value The string to convert.
|
|
1350
|
+
* @returns {string} The camelCased string.
|
|
1351
|
+
*/
|
|
1352
|
+
export function toCamelCase(value) {
|
|
1353
|
+
return String(value || '')
|
|
1354
|
+
.trim()
|
|
1355
|
+
.toLowerCase()
|
|
1356
|
+
.replace(/[-_\\s]+(.)/g, (_, char) => char.toUpperCase());
|
|
1357
|
+
}`
|
|
1358
|
+
},
|
|
1359
|
+
{
|
|
1360
|
+
id: "getLanguageRootPath",
|
|
1361
|
+
label: "getLanguageRootPath (e.g. /en, /en-us)",
|
|
1362
|
+
code: `/**
|
|
1363
|
+
* Returns the language root of a path, e.g. '/en' or '/en-us' ('' if none).
|
|
1364
|
+
* @param {string} path The page path.
|
|
1365
|
+
* @returns {string} The language root prefix.
|
|
1366
|
+
*/
|
|
1367
|
+
export function getLanguageRootPath(path) {
|
|
1368
|
+
const match = String(path || '').match(/^\\/[a-z]{2}(?:-[a-z]{2})?(?=\\/|$)/i);
|
|
1369
|
+
return match ? match[0] : '';
|
|
1370
|
+
}`
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
id: "getPagePath",
|
|
1374
|
+
label: "getPagePath (path without content root)",
|
|
1375
|
+
deps: ["getLanguageRootPath"],
|
|
1376
|
+
code: `// Content root stripped from page paths. Set this if your site is mounted under
|
|
1377
|
+
// a sub-path (e.g. '/content/my-site'); leave empty for a root-mounted site.
|
|
1378
|
+
const ROOT_PATH = '';
|
|
1379
|
+
|
|
1380
|
+
/**
|
|
1381
|
+
* Returns the page path without the content root; \`clean\` drops the language root.
|
|
1382
|
+
* @param {boolean} [clean=false] Also strip the language root.
|
|
1383
|
+
* @returns {string} The page path or 'NA' for the language root.
|
|
1384
|
+
*/
|
|
1385
|
+
export function getPagePath({ clean = false } = {}) {
|
|
1386
|
+
const path = window.location.pathname.replace(ROOT_PATH, '').replace(/\\.html$/i, '');
|
|
1387
|
+
|
|
1388
|
+
if (clean) {
|
|
1389
|
+
const langRoot = getLanguageRootPath(path);
|
|
1390
|
+
const cleaned = path.startsWith(langRoot) ? path.slice(langRoot.length) : path;
|
|
1391
|
+
return cleaned || 'NA';
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
return path || 'NA';
|
|
1395
|
+
}`
|
|
1396
|
+
},
|
|
1397
|
+
{
|
|
1398
|
+
id: "getSiteArea",
|
|
1399
|
+
label: "getSiteArea (blog / products / home)",
|
|
1400
|
+
deps: ["getPagePath"],
|
|
1401
|
+
code: `/**
|
|
1402
|
+
* Returns the top-level area of the site from the current path - e.g. 'blog' or
|
|
1403
|
+
* 'products', and 'home' at the site root. Uses the language-cleaned path, so you
|
|
1404
|
+
* can tell whether you're on the blog or somewhere else.
|
|
1405
|
+
* @returns {string} The site area.
|
|
1406
|
+
*/
|
|
1407
|
+
export function getSiteArea() {
|
|
1408
|
+
const path = getPagePath({ clean: true });
|
|
1409
|
+
if (!path || path === 'NA') return 'home';
|
|
1410
|
+
return path.split('/').filter(Boolean)[0] || 'home';
|
|
1411
|
+
}`
|
|
1412
|
+
},
|
|
1413
|
+
{
|
|
1414
|
+
id: "getEnvironment",
|
|
1415
|
+
label: "getEnvironment (dev / stage / prod)",
|
|
1416
|
+
code: `/**
|
|
1417
|
+
* Detects the current environment from the hostname.
|
|
1418
|
+
* @returns {'NA'|'dev'|'stage'|'prod'} The environment name.
|
|
1419
|
+
*/
|
|
1420
|
+
export function getEnvironment() {
|
|
1421
|
+
const { hostname } = window.location;
|
|
1422
|
+
if (hostname.includes('author')) return 'NA';
|
|
1423
|
+
if (hostname === 'localhost') return 'dev';
|
|
1424
|
+
const repo = hostname.match(/^[^.]+--([^.]+)--[^.]+\\.aem\\.(?:page|live)$/)?.[1] || '';
|
|
1425
|
+
if (repo.includes('dev')) return 'dev';
|
|
1426
|
+
if (repo.includes('stage')) return 'stage';
|
|
1427
|
+
return 'prod';
|
|
1428
|
+
}`
|
|
1429
|
+
},
|
|
1430
|
+
{
|
|
1431
|
+
id: "getContentTopic",
|
|
1432
|
+
label: "getContentTopic (cq-tags content-topic values)",
|
|
1433
|
+
deps: ["getMetadata"],
|
|
1434
|
+
code: `/**
|
|
1435
|
+
* Reads all content-topic values from the page's cq-tags metadata.
|
|
1436
|
+
* @returns {string[]} The topics (part after \`content-topic/\`), empty if none.
|
|
1437
|
+
*/
|
|
1438
|
+
export function getContentTopic() {
|
|
1439
|
+
return getMetadata('cq-tags')
|
|
1440
|
+
.split(',')
|
|
1441
|
+
.map((t) => t.trim())
|
|
1442
|
+
.filter((t) => t.includes('content-topic/'))
|
|
1443
|
+
.map((t) => t.split('content-topic/')[1]);
|
|
1444
|
+
}`
|
|
1445
|
+
},
|
|
1446
|
+
{
|
|
1447
|
+
id: "isUEEdit",
|
|
1448
|
+
label: "isUEEdit (Universal Editor edit mode)",
|
|
1449
|
+
code: `/**
|
|
1450
|
+
* Checks whether the current page is opened in the Universal Editor Edit mode.
|
|
1451
|
+
* @returns {boolean} True in Universal Editor Edit mode.
|
|
1452
|
+
*/
|
|
1453
|
+
export function isUEEdit() {
|
|
1454
|
+
return document.documentElement.classList.contains('adobe-ue-edit');
|
|
1455
|
+
}`
|
|
1456
|
+
},
|
|
1457
|
+
{
|
|
1458
|
+
id: "isUEPreview",
|
|
1459
|
+
label: "isUEPreview (Universal Editor preview mode)",
|
|
1460
|
+
code: `/**
|
|
1461
|
+
* Checks whether the current page is opened in the Universal Editor Preview mode.
|
|
1462
|
+
* @returns {boolean} True in Universal Editor Preview mode.
|
|
1463
|
+
*/
|
|
1464
|
+
export function isUEPreview() {
|
|
1465
|
+
return document.documentElement.classList.contains('adobe-ue-preview');
|
|
1466
|
+
}`
|
|
1467
|
+
},
|
|
1468
|
+
{
|
|
1469
|
+
id: "isUE",
|
|
1470
|
+
label: "isUE (Universal Editor, any mode)",
|
|
1471
|
+
deps: ["isUEEdit", "isUEPreview"],
|
|
1472
|
+
code: `/**
|
|
1473
|
+
* Checks whether the current page is opened in the Universal Editor (any mode).
|
|
1474
|
+
* @returns {boolean} True in the Universal Editor.
|
|
1475
|
+
*/
|
|
1476
|
+
export function isUE() {
|
|
1477
|
+
return isUEEdit() || isUEPreview();
|
|
1478
|
+
}`
|
|
1479
|
+
}
|
|
1480
|
+
];
|
|
1481
|
+
ALL_HELPER_IDS = HELPERS.map((h) => h.id);
|
|
1482
|
+
}
|
|
1483
|
+
});
|
|
1484
|
+
|
|
1485
|
+
// src/commands/scaffold.ts
|
|
1486
|
+
var scaffold_exports = {};
|
|
1487
|
+
__export(scaffold_exports, {
|
|
1488
|
+
scaffoldBlocks: () => scaffoldBlocks,
|
|
1489
|
+
scaffoldCi: () => scaffoldCi,
|
|
1490
|
+
scaffoldHelpers: () => scaffoldHelpers,
|
|
1491
|
+
scaffoldInteractive: () => scaffoldInteractive,
|
|
1492
|
+
scaffoldUe: () => scaffoldUe
|
|
1493
|
+
});
|
|
1494
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1495
|
+
import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile5 } from "fs/promises";
|
|
1496
|
+
import path8 from "path";
|
|
1497
|
+
async function readJson(file) {
|
|
1498
|
+
if (!existsSync6(file)) return null;
|
|
1499
|
+
try {
|
|
1500
|
+
return JSON.parse(await readFile3(file, "utf-8"));
|
|
1501
|
+
} catch {
|
|
1502
|
+
return null;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
async function writeJson(file, data) {
|
|
1506
|
+
await writeFile5(file, `${JSON.stringify(data, null, 2)}
|
|
1507
|
+
`);
|
|
1508
|
+
}
|
|
1509
|
+
async function scaffoldUe() {
|
|
1510
|
+
const projectRoot = findProjectRoot();
|
|
1511
|
+
if (!projectRoot) {
|
|
1512
|
+
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
1513
|
+
process.exitCode = 1;
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
1516
|
+
logger.logoOnce(logo("Scaffold - Universal Editor"));
|
|
1517
|
+
const created = [];
|
|
1518
|
+
const blockDir = path8.join(projectRoot, "blocks", "field-reference");
|
|
1519
|
+
await mkdir4(blockDir, { recursive: true });
|
|
1520
|
+
await writeFile5(path8.join(blockDir, "field-reference.js"), FIELD_REFERENCE_JS);
|
|
1521
|
+
await writeFile5(path8.join(blockDir, "field-reference.css"), FIELD_REFERENCE_CSS);
|
|
1522
|
+
await writeFile5(path8.join(blockDir, "_field-reference.json"), fieldReferenceModelFile());
|
|
1523
|
+
created.push("blocks/field-reference/ (all 17 fields + multifield)");
|
|
1524
|
+
const defFile = path8.join(projectRoot, "component-definition.json");
|
|
1525
|
+
const modelsFile = path8.join(projectRoot, "component-models.json");
|
|
1526
|
+
const filtersFile = path8.join(projectRoot, "component-filters.json");
|
|
1527
|
+
const blockDefs = [
|
|
1528
|
+
{
|
|
1529
|
+
title: "Field Reference",
|
|
1530
|
+
id: "field-reference",
|
|
1531
|
+
plugins: {
|
|
1532
|
+
xwalk: {
|
|
1533
|
+
page: {
|
|
1534
|
+
resourceType: "core/franklin/components/block/v1/block",
|
|
1535
|
+
template: { name: "Field Reference", model: "field-reference" }
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
];
|
|
1541
|
+
const definitions = mergeDefinitions(
|
|
1542
|
+
await readJson(defFile),
|
|
1543
|
+
DEFAULT_CONTENT_DEFINITIONS,
|
|
1544
|
+
blockDefs
|
|
1545
|
+
);
|
|
1546
|
+
await writeJson(defFile, definitions);
|
|
1547
|
+
created.push("component-definition.json (default content + field-reference)");
|
|
1548
|
+
const existingModels = await readJson(modelsFile) ?? [];
|
|
1549
|
+
const fieldRefModel = JSON.parse(fieldReferenceModelFile()).models[0];
|
|
1550
|
+
const models = mergeById(existingModels, [...DEFAULT_CONTENT_MODELS, fieldRefModel]);
|
|
1551
|
+
await writeJson(modelsFile, models);
|
|
1552
|
+
created.push("component-models.json (title / image / button / field-reference)");
|
|
1553
|
+
const filters = mergeSectionFilter(await readJson(filtersFile), ["field-reference"]);
|
|
1554
|
+
await writeJson(filtersFile, filters);
|
|
1555
|
+
created.push("component-filters.json (section filter)");
|
|
1556
|
+
logger.info(heading("Created / merged"));
|
|
1557
|
+
const names = created.map((c) => c.split(" ")[0]);
|
|
1558
|
+
const w = columnWidth(names);
|
|
1559
|
+
for (const c of created) {
|
|
1560
|
+
const [name, ...rest] = c.split(" ");
|
|
1561
|
+
logger.info(accentLine(name, rest.join(" ").replace(/^\((.*)\)$/, "$1"), w));
|
|
1562
|
+
}
|
|
1563
|
+
logger.info("");
|
|
1564
|
+
logger.info(box(["Universal Editor scaffold ready"]));
|
|
1565
|
+
}
|
|
1566
|
+
async function scaffoldBlocks(names) {
|
|
1567
|
+
const projectRoot = findProjectRoot();
|
|
1568
|
+
if (!projectRoot) {
|
|
1569
|
+
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
1570
|
+
process.exitCode = 1;
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
logger.logoOnce(logo("Scaffold \u2014 standard blocks"));
|
|
1574
|
+
let selected;
|
|
1575
|
+
if (names?.length) {
|
|
1576
|
+
selected = names.map((n2) => blockById(n2.trim())).filter((b) => !!b);
|
|
1577
|
+
const unknown = names.filter((n2) => !blockById(n2.trim()));
|
|
1578
|
+
if (unknown.length) {
|
|
1579
|
+
logger.warn(
|
|
1580
|
+
`Unknown block(s): ${unknown.join(", ")}. Available: ${STANDARD_BLOCKS.map((b) => b.id).join(", ")}`
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1583
|
+
} else {
|
|
1584
|
+
selected = STANDARD_BLOCKS;
|
|
1585
|
+
}
|
|
1586
|
+
const createdIds = [];
|
|
1587
|
+
const skipped = [];
|
|
1588
|
+
for (const b of selected) {
|
|
1589
|
+
const dir = path8.join(projectRoot, "blocks", b.id);
|
|
1590
|
+
if (existsSync6(dir)) {
|
|
1591
|
+
skipped.push(b.id);
|
|
1592
|
+
continue;
|
|
1593
|
+
}
|
|
1594
|
+
await mkdir4(dir, { recursive: true });
|
|
1595
|
+
await writeFile5(path8.join(dir, `${b.id}.js`), b.js);
|
|
1596
|
+
await writeFile5(path8.join(dir, `${b.id}.css`), b.css);
|
|
1597
|
+
await writeFile5(path8.join(dir, `_${b.id}.json`), b.model);
|
|
1598
|
+
createdIds.push(b.id);
|
|
1599
|
+
}
|
|
1600
|
+
const filtersFile = path8.join(projectRoot, "component-filters.json");
|
|
1601
|
+
const filters = mergeSectionFilter(
|
|
1602
|
+
await readJson(filtersFile),
|
|
1603
|
+
selected.map((b) => b.id)
|
|
1604
|
+
);
|
|
1605
|
+
await writeJson(filtersFile, filters);
|
|
1606
|
+
logger.info(heading("Blocks"));
|
|
1607
|
+
const w = columnWidth([...createdIds, ...skipped, "component-filters"]);
|
|
1608
|
+
for (const id of createdIds) logger.info(accentLine(id, "created", w));
|
|
1609
|
+
for (const id of skipped) logger.info(statusLine("warn", id, "already exists \u2014 skipped", w));
|
|
1610
|
+
logger.info("");
|
|
1611
|
+
logger.info(box([`${createdIds.length} block(s) scaffolded + registered`]));
|
|
1612
|
+
}
|
|
1613
|
+
async function scaffoldCi() {
|
|
1614
|
+
const projectRoot = findProjectRoot();
|
|
1615
|
+
if (!projectRoot) {
|
|
1616
|
+
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
1617
|
+
process.exitCode = 1;
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
logger.logoOnce(logo("Scaffold - CI workflow"));
|
|
1621
|
+
const res = await writeCiWorkflow(projectRoot);
|
|
1622
|
+
logger.info(heading("GitHub Actions"));
|
|
1623
|
+
if (res.created) {
|
|
1624
|
+
logger.info(accentLine(res.path, "doctor + audit loading + audit security + lint/build"));
|
|
1625
|
+
} else {
|
|
1626
|
+
logger.info(statusLine("warn", res.path, "already exists - skipped"));
|
|
1627
|
+
}
|
|
1628
|
+
logger.info("");
|
|
1629
|
+
logger.info(
|
|
1630
|
+
box([
|
|
1631
|
+
res.created ? "CI workflow scaffolded - commit it and it runs on every PR" : "CI workflow left untouched"
|
|
1632
|
+
])
|
|
1633
|
+
);
|
|
1634
|
+
}
|
|
1635
|
+
async function scaffoldHelpers(ids) {
|
|
1636
|
+
const projectRoot = findProjectRoot();
|
|
1637
|
+
if (!projectRoot) {
|
|
1638
|
+
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
1639
|
+
process.exitCode = 1;
|
|
1640
|
+
return;
|
|
1641
|
+
}
|
|
1642
|
+
logger.logoOnce(logo("Scaffold - helpers"));
|
|
1643
|
+
let selected = ids?.length ? ids : void 0;
|
|
1644
|
+
if (!selected) {
|
|
1645
|
+
if (process.stdout.isTTY) {
|
|
1646
|
+
const { checkbox } = await import("@inquirer/prompts");
|
|
1647
|
+
selected = await checkbox({
|
|
1648
|
+
message: "Which helpers?",
|
|
1649
|
+
choices: HELPERS.map((h) => ({ name: h.label, value: h.id, checked: true }))
|
|
1650
|
+
});
|
|
1651
|
+
} else {
|
|
1652
|
+
selected = ALL_HELPER_IDS;
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
if (!selected.length) {
|
|
1656
|
+
logger.info("Nothing selected.");
|
|
1657
|
+
return;
|
|
1658
|
+
}
|
|
1659
|
+
const res = await writeHelpers(projectRoot, selected);
|
|
1660
|
+
logger.info(heading("Helpers"));
|
|
1661
|
+
if (res.created) {
|
|
1662
|
+
logger.info(accentLine(res.path, res.helpers.join(", ")));
|
|
1663
|
+
} else {
|
|
1664
|
+
logger.info(statusLine("warn", res.path, "already exists - skipped"));
|
|
1665
|
+
}
|
|
1666
|
+
logger.info("");
|
|
1190
1667
|
logger.info(
|
|
1191
1668
|
box([
|
|
1192
|
-
res.created ?
|
|
1669
|
+
res.created ? `${res.helpers.length} helper(s) -> scripts/utils.js` : "Helpers left untouched"
|
|
1193
1670
|
])
|
|
1194
1671
|
);
|
|
1195
1672
|
}
|
|
@@ -1202,6 +1679,7 @@ async function scaffoldInteractive() {
|
|
|
1202
1679
|
{ name: "Universal Editor config + field-reference (all 17 field types)", value: "ue" },
|
|
1203
1680
|
{ name: "Standard blocks (hero, cards, columns, accordion, embed)", value: "blocks" },
|
|
1204
1681
|
{ name: "GitHub Actions CI (doctor + audits + lint/build)", value: "ci" },
|
|
1682
|
+
{ name: "Helper functions (scripts/utils.js)", value: "helpers" },
|
|
1205
1683
|
{ name: "UE config + standard blocks", value: "both" }
|
|
1206
1684
|
]
|
|
1207
1685
|
});
|
|
@@ -1214,6 +1692,7 @@ async function scaffoldInteractive() {
|
|
|
1214
1692
|
await scaffoldBlocks(picked);
|
|
1215
1693
|
}
|
|
1216
1694
|
if (what === "ci") await scaffoldCi();
|
|
1695
|
+
if (what === "helpers") await scaffoldHelpers();
|
|
1217
1696
|
}
|
|
1218
1697
|
var init_scaffold = __esm({
|
|
1219
1698
|
"src/commands/scaffold.ts"() {
|
|
@@ -1223,6 +1702,7 @@ var init_scaffold = __esm({
|
|
|
1223
1702
|
init_project();
|
|
1224
1703
|
init_blocks();
|
|
1225
1704
|
init_ci();
|
|
1705
|
+
init_helpers();
|
|
1226
1706
|
init_ue();
|
|
1227
1707
|
init_ui();
|
|
1228
1708
|
}
|
|
@@ -1233,17 +1713,17 @@ var init_exports = {};
|
|
|
1233
1713
|
__export(init_exports, {
|
|
1234
1714
|
initProject: () => initProject
|
|
1235
1715
|
});
|
|
1236
|
-
import { existsSync as
|
|
1237
|
-
import { readFile as
|
|
1238
|
-
import
|
|
1239
|
-
import
|
|
1716
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1717
|
+
import { readFile as readFile4, writeFile as writeFile6 } from "fs/promises";
|
|
1718
|
+
import path9 from "path";
|
|
1719
|
+
import chalk4 from "chalk";
|
|
1240
1720
|
async function setPackageName(root, name) {
|
|
1241
|
-
const file =
|
|
1242
|
-
if (!
|
|
1721
|
+
const file = path9.join(root, "package.json");
|
|
1722
|
+
if (!existsSync7(file)) return false;
|
|
1243
1723
|
try {
|
|
1244
|
-
const pkg = JSON.parse(await
|
|
1724
|
+
const pkg = JSON.parse(await readFile4(file, "utf-8"));
|
|
1245
1725
|
pkg.name = name;
|
|
1246
|
-
await
|
|
1726
|
+
await writeFile6(file, `${JSON.stringify(pkg, null, 2)}
|
|
1247
1727
|
`);
|
|
1248
1728
|
return true;
|
|
1249
1729
|
} catch {
|
|
@@ -1253,13 +1733,13 @@ async function setPackageName(root, name) {
|
|
|
1253
1733
|
async function replacePlaceholders(root, name) {
|
|
1254
1734
|
const touched = [];
|
|
1255
1735
|
for (const rel of ["README.md", "index.html", "head.html"]) {
|
|
1256
|
-
const file =
|
|
1257
|
-
if (!
|
|
1736
|
+
const file = path9.join(root, rel);
|
|
1737
|
+
if (!existsSync7(file)) continue;
|
|
1258
1738
|
try {
|
|
1259
|
-
const before = await
|
|
1739
|
+
const before = await readFile4(file, "utf-8");
|
|
1260
1740
|
const after = before.replace(/aem-boilerplate/gi, name);
|
|
1261
1741
|
if (after !== before) {
|
|
1262
|
-
await
|
|
1742
|
+
await writeFile6(file, after);
|
|
1263
1743
|
touched.push(rel);
|
|
1264
1744
|
}
|
|
1265
1745
|
} catch {
|
|
@@ -1314,9 +1794,9 @@ async function externalSteps(authoring) {
|
|
|
1314
1794
|
logger.info(heading("Next steps (outside the repo)"));
|
|
1315
1795
|
for (let i = 0; i < steps.length; i++) {
|
|
1316
1796
|
const s = steps[i];
|
|
1317
|
-
logger.info(` ${brand.accent(String(i + 1))} ${
|
|
1318
|
-
if (s.url) logger.info(` ${
|
|
1319
|
-
logger.info(` ${
|
|
1797
|
+
logger.info(` ${brand.accent(String(i + 1))} ${chalk4.bold(s.title)}`);
|
|
1798
|
+
if (s.url) logger.info(` ${chalk4.cyan(s.url)}`);
|
|
1799
|
+
logger.info(` ${chalk4.dim(s.note)}`);
|
|
1320
1800
|
await confirm({ message: " Done / skip?", default: true });
|
|
1321
1801
|
}
|
|
1322
1802
|
}
|
|
@@ -1332,19 +1812,19 @@ async function initProject(options = {}) {
|
|
|
1332
1812
|
await logger.logoOnceAnimated("Project setup");
|
|
1333
1813
|
const { input, select, confirm } = await import("@inquirer/prompts");
|
|
1334
1814
|
const interactive = !options.yes;
|
|
1335
|
-
let defaultName =
|
|
1336
|
-
const pkgFile =
|
|
1337
|
-
if (
|
|
1815
|
+
let defaultName = path9.basename(projectRoot);
|
|
1816
|
+
const pkgFile = path9.join(projectRoot, "package.json");
|
|
1817
|
+
if (existsSync7(pkgFile)) {
|
|
1338
1818
|
try {
|
|
1339
|
-
defaultName = JSON.parse(await
|
|
1819
|
+
defaultName = JSON.parse(await readFile4(pkgFile, "utf-8")).name || defaultName;
|
|
1340
1820
|
} catch {
|
|
1341
1821
|
}
|
|
1342
1822
|
}
|
|
1343
|
-
const name = options.name &&
|
|
1823
|
+
const name = options.name && slug2(options.name) || slug2(
|
|
1344
1824
|
await input({
|
|
1345
1825
|
message: "1/4 Project name",
|
|
1346
|
-
default:
|
|
1347
|
-
validate: (v) => !!
|
|
1826
|
+
default: slug2(defaultName),
|
|
1827
|
+
validate: (v) => !!slug2(v) || "Use letters, numbers and dashes"
|
|
1348
1828
|
})
|
|
1349
1829
|
);
|
|
1350
1830
|
const authoring = options.authoring ?? await select({
|
|
@@ -1361,7 +1841,7 @@ async function initProject(options = {}) {
|
|
|
1361
1841
|
if (authoring === "da" && (options.org || interactive)) {
|
|
1362
1842
|
const org = options.org ?? await input({ message: " DA org", validate: (v) => !!v.trim() || "Required" });
|
|
1363
1843
|
const site = options.site ?? (interactive ? await input({ message: " DA site", default: name }) : name);
|
|
1364
|
-
mountpoint = daMountpoint(org.trim(),
|
|
1844
|
+
mountpoint = daMountpoint(org.trim(), slug2(site));
|
|
1365
1845
|
} else if (authoring !== "da" && interactive) {
|
|
1366
1846
|
const label = authoring === "gdrive" ? "Google Drive folder URL" : "SharePoint folder URL";
|
|
1367
1847
|
mountpoint = (await input({
|
|
@@ -1402,14 +1882,14 @@ async function initProject(options = {}) {
|
|
|
1402
1882
|
logger.info(heading("Configured"));
|
|
1403
1883
|
const dw = columnWidth(done.map((d) => d.split(" ")[0]));
|
|
1404
1884
|
for (const d of done) {
|
|
1405
|
-
const [
|
|
1406
|
-
logger.info(accentLine(
|
|
1885
|
+
const [n2, ...rest] = d.split(" ");
|
|
1886
|
+
logger.info(accentLine(n2, rest.join(" ").replace(/^\((.*)\)$/, "$1"), dw));
|
|
1407
1887
|
}
|
|
1408
1888
|
logger.info("");
|
|
1409
1889
|
if (interactive) await externalSteps(authoring);
|
|
1410
1890
|
logger.info(box([`"${name}" set up for ${AUTHORING_NAMES[authoring]}`]));
|
|
1411
1891
|
}
|
|
1412
|
-
var
|
|
1892
|
+
var slug2;
|
|
1413
1893
|
var init_init = __esm({
|
|
1414
1894
|
"src/commands/init.ts"() {
|
|
1415
1895
|
"use strict";
|
|
@@ -1420,58 +1900,58 @@ var init_init = __esm({
|
|
|
1420
1900
|
init_fstab();
|
|
1421
1901
|
init_ui();
|
|
1422
1902
|
init_scaffold();
|
|
1423
|
-
|
|
1903
|
+
slug2 = (s) => s.trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1424
1904
|
}
|
|
1425
1905
|
});
|
|
1426
1906
|
|
|
1427
1907
|
// src/lib/migrate/clientlib.ts
|
|
1428
|
-
import { existsSync as
|
|
1429
|
-
import { readFile as
|
|
1430
|
-
import
|
|
1908
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1909
|
+
import { readFile as readFile5, readdir as readdir2 } from "fs/promises";
|
|
1910
|
+
import path10 from "path";
|
|
1431
1911
|
async function listFiles(dir) {
|
|
1432
1912
|
const out = [];
|
|
1433
1913
|
const walk3 = async (d) => {
|
|
1434
|
-
for (const e of await
|
|
1435
|
-
const p =
|
|
1914
|
+
for (const e of await readdir2(d, { withFileTypes: true })) {
|
|
1915
|
+
const p = path10.join(d, e.name);
|
|
1436
1916
|
if (e.isDirectory()) await walk3(p);
|
|
1437
1917
|
else out.push(p);
|
|
1438
1918
|
}
|
|
1439
1919
|
};
|
|
1440
|
-
if (
|
|
1920
|
+
if (existsSync8(dir)) await walk3(dir);
|
|
1441
1921
|
return out;
|
|
1442
1922
|
}
|
|
1443
1923
|
async function findClientlibDir(componentDir) {
|
|
1444
|
-
if (!
|
|
1445
|
-
const entries = await
|
|
1924
|
+
if (!existsSync8(componentDir)) return null;
|
|
1925
|
+
const entries = await readdir2(componentDir, { withFileTypes: true });
|
|
1446
1926
|
const match = entries.find((e) => e.isDirectory() && /clientlib/i.test(e.name));
|
|
1447
|
-
if (match) return
|
|
1927
|
+
if (match) return path10.join(componentDir, match.name);
|
|
1448
1928
|
const hasCss = entries.some((e) => e.isFile() && e.name.endsWith(".css"));
|
|
1449
1929
|
return hasCss ? componentDir : null;
|
|
1450
1930
|
}
|
|
1451
1931
|
async function readClientlibCss(clientlibDir) {
|
|
1452
1932
|
const all = await listFiles(clientlibDir);
|
|
1453
|
-
const preprocessed = all.filter((
|
|
1933
|
+
const preprocessed = all.filter((f2) => /\.(less|scss|sass)$/i.test(f2)).map((f2) => path10.relative(clientlibDir, f2));
|
|
1454
1934
|
let cssFiles;
|
|
1455
|
-
const cssTxt =
|
|
1456
|
-
if (
|
|
1457
|
-
const lines = (await
|
|
1935
|
+
const cssTxt = path10.join(clientlibDir, "css.txt");
|
|
1936
|
+
if (existsSync8(cssTxt)) {
|
|
1937
|
+
const lines = (await readFile5(cssTxt, "utf-8")).split("\n").map((l) => l.trim());
|
|
1458
1938
|
let base = clientlibDir;
|
|
1459
1939
|
const listed = [];
|
|
1460
1940
|
for (const l of lines) {
|
|
1461
1941
|
if (!l) continue;
|
|
1462
1942
|
const baseMatch = l.match(/^#base\s*=\s*(.+)$/);
|
|
1463
|
-
if (baseMatch) base =
|
|
1943
|
+
if (baseMatch) base = path10.join(clientlibDir, baseMatch[1].trim());
|
|
1464
1944
|
else if (!l.startsWith("#")) listed.push(l);
|
|
1465
1945
|
}
|
|
1466
|
-
cssFiles = listed.map((rel) =>
|
|
1946
|
+
cssFiles = listed.map((rel) => path10.join(base, rel)).filter((f2) => existsSync8(f2) && f2.endsWith(".css"));
|
|
1467
1947
|
} else {
|
|
1468
|
-
cssFiles = all.filter((
|
|
1948
|
+
cssFiles = all.filter((f2) => f2.endsWith(".css"));
|
|
1469
1949
|
}
|
|
1470
1950
|
const parts = [];
|
|
1471
|
-
for (const
|
|
1951
|
+
for (const f2 of cssFiles) parts.push(await readFile5(f2, "utf-8"));
|
|
1472
1952
|
return {
|
|
1473
1953
|
css: parts.join("\n\n"),
|
|
1474
|
-
files: cssFiles.map((
|
|
1954
|
+
files: cssFiles.map((f2) => path10.relative(clientlibDir, f2)),
|
|
1475
1955
|
preprocessed
|
|
1476
1956
|
};
|
|
1477
1957
|
}
|
|
@@ -1484,25 +1964,25 @@ var init_clientlib = __esm({
|
|
|
1484
1964
|
|
|
1485
1965
|
// src/lib/migrate/dialog.ts
|
|
1486
1966
|
import { XMLParser } from "fast-xml-parser";
|
|
1487
|
-
function tagOf(
|
|
1488
|
-
return Object.keys(
|
|
1967
|
+
function tagOf(n2) {
|
|
1968
|
+
return Object.keys(n2).find((k) => k !== ":@") ?? "";
|
|
1489
1969
|
}
|
|
1490
|
-
function childrenOf(
|
|
1491
|
-
const t = tagOf(
|
|
1492
|
-
const v =
|
|
1970
|
+
function childrenOf(n2) {
|
|
1971
|
+
const t = tagOf(n2);
|
|
1972
|
+
const v = n2[t];
|
|
1493
1973
|
return Array.isArray(v) ? v : [];
|
|
1494
1974
|
}
|
|
1495
|
-
function attrsOf(
|
|
1496
|
-
return
|
|
1975
|
+
function attrsOf(n2) {
|
|
1976
|
+
return n2[":@"] ?? {};
|
|
1497
1977
|
}
|
|
1498
1978
|
function stripType(v) {
|
|
1499
1979
|
return typeof v === "string" ? v.replace(/^\{[^}]+\}/, "") : v;
|
|
1500
1980
|
}
|
|
1501
|
-
function attr(
|
|
1502
|
-
return stripType(attrsOf(
|
|
1981
|
+
function attr(n2, name) {
|
|
1982
|
+
return stripType(attrsOf(n2)[`@_${name}`]);
|
|
1503
1983
|
}
|
|
1504
|
-
function leafComponent(rt,
|
|
1505
|
-
const multiple = attr(
|
|
1984
|
+
function leafComponent(rt, n2) {
|
|
1985
|
+
const multiple = attr(n2, "multiple") === "true" || attr(n2, "multiple") === "{Boolean}true";
|
|
1506
1986
|
if (rt.endsWith("/form/textfield")) return "text";
|
|
1507
1987
|
if (rt.endsWith("/form/textarea")) return "textarea";
|
|
1508
1988
|
if (rt.includes("/richtext")) return "richtext";
|
|
@@ -1541,8 +2021,8 @@ function valueTypeFor(c) {
|
|
|
1541
2021
|
}
|
|
1542
2022
|
}
|
|
1543
2023
|
function cleanName(raw, fallback) {
|
|
1544
|
-
const
|
|
1545
|
-
return
|
|
2024
|
+
const n2 = (raw ?? "").replace(/^\.\//, "").trim();
|
|
2025
|
+
return n2 || fallback;
|
|
1546
2026
|
}
|
|
1547
2027
|
function readOptions(fieldNode) {
|
|
1548
2028
|
const items = childrenOf(fieldNode).find((c) => tagOf(c) === "items");
|
|
@@ -1609,7 +2089,7 @@ function parseDialog(xml) {
|
|
|
1609
2089
|
trimValues: true
|
|
1610
2090
|
});
|
|
1611
2091
|
const tree = parser.parse(xml);
|
|
1612
|
-
const root = tree.find((
|
|
2092
|
+
const root = tree.find((n2) => tagOf(n2) === "jcr:root");
|
|
1613
2093
|
const fields = [];
|
|
1614
2094
|
const unmapped = /* @__PURE__ */ new Set();
|
|
1615
2095
|
if (root) walk(childrenOf(root), fields, unmapped, { i: 1 });
|
|
@@ -1623,24 +2103,24 @@ var init_dialog = __esm({
|
|
|
1623
2103
|
});
|
|
1624
2104
|
|
|
1625
2105
|
// src/lib/migrate/component.ts
|
|
1626
|
-
import { existsSync as
|
|
1627
|
-
import { readFile as
|
|
1628
|
-
import
|
|
2106
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2107
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
2108
|
+
import path11 from "path";
|
|
1629
2109
|
import { XMLParser as XMLParser2 } from "fast-xml-parser";
|
|
1630
2110
|
function findDialogFile(componentDir) {
|
|
1631
2111
|
const candidates = [
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
2112
|
+
path11.join(componentDir, "_cq_dialog", ".content.xml"),
|
|
2113
|
+
path11.join(componentDir, "_cq_dialog.xml"),
|
|
2114
|
+
path11.join(componentDir, "cq:dialog", ".content.xml")
|
|
1635
2115
|
];
|
|
1636
|
-
return candidates.find((c) =>
|
|
2116
|
+
return candidates.find((c) => existsSync9(c)) ?? null;
|
|
1637
2117
|
}
|
|
1638
2118
|
async function readComponentTitle(componentDir) {
|
|
1639
|
-
const file =
|
|
1640
|
-
if (!
|
|
2119
|
+
const file = path11.join(componentDir, ".content.xml");
|
|
2120
|
+
if (!existsSync9(file)) return void 0;
|
|
1641
2121
|
try {
|
|
1642
2122
|
const parser = new XMLParser2({ ignoreAttributes: false, attributeNamePrefix: "@_" });
|
|
1643
|
-
const parsed = parser.parse(await
|
|
2123
|
+
const parsed = parser.parse(await readFile6(file, "utf-8"));
|
|
1644
2124
|
const root = parsed["jcr:root"] ?? {};
|
|
1645
2125
|
const title = root["@_jcr:title"];
|
|
1646
2126
|
return typeof title === "string" ? title.replace(/^\{[^}]+\}/, "") : void 0;
|
|
@@ -1651,10 +2131,10 @@ async function readComponentTitle(componentDir) {
|
|
|
1651
2131
|
async function readDialogFields(componentDir) {
|
|
1652
2132
|
const dialog = findDialogFile(componentDir);
|
|
1653
2133
|
if (!dialog) return null;
|
|
1654
|
-
return parseDialog(await
|
|
2134
|
+
return parseDialog(await readFile6(dialog, "utf-8"));
|
|
1655
2135
|
}
|
|
1656
2136
|
function migratedModelFile(blockName, title, fields) {
|
|
1657
|
-
const
|
|
2137
|
+
const model2 = {
|
|
1658
2138
|
definitions: [
|
|
1659
2139
|
{
|
|
1660
2140
|
title,
|
|
@@ -1672,11 +2152,11 @@ function migratedModelFile(blockName, title, fields) {
|
|
|
1672
2152
|
models: [{ id: blockName, fields }],
|
|
1673
2153
|
filters: []
|
|
1674
2154
|
};
|
|
1675
|
-
return `${JSON.stringify(
|
|
2155
|
+
return `${JSON.stringify(model2, null, 2)}
|
|
1676
2156
|
`;
|
|
1677
2157
|
}
|
|
1678
2158
|
function migratedBlockJs(blockName, fields) {
|
|
1679
|
-
const hasContainer = fields.some((
|
|
2159
|
+
const hasContainer = fields.some((f2) => f2.component === "container" && f2.multi);
|
|
1680
2160
|
const body = hasContainer ? ` // This block was migrated from a classic AEM component with a multifield.
|
|
1681
2161
|
// Each child row is one repeated item - enhance them here.
|
|
1682
2162
|
[...block.children].forEach((row) => {
|
|
@@ -1780,10 +2260,10 @@ var track_exports = {};
|
|
|
1780
2260
|
__export(track_exports, {
|
|
1781
2261
|
trackBlock: () => trackBlock
|
|
1782
2262
|
});
|
|
1783
|
-
import { existsSync as
|
|
1784
|
-
import { readFile as
|
|
1785
|
-
import
|
|
1786
|
-
import
|
|
2263
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2264
|
+
import { readFile as readFile7, writeFile as writeFile7 } from "fs/promises";
|
|
2265
|
+
import path12 from "path";
|
|
2266
|
+
import chalk5 from "chalk";
|
|
1787
2267
|
async function trackBlock(blockName, options = {}) {
|
|
1788
2268
|
const projectRoot = findProjectRoot();
|
|
1789
2269
|
if (!projectRoot) {
|
|
@@ -1792,201 +2272,750 @@ async function trackBlock(blockName, options = {}) {
|
|
|
1792
2272
|
return;
|
|
1793
2273
|
}
|
|
1794
2274
|
logger.logoOnce(logo("Track block"));
|
|
1795
|
-
const jsPath =
|
|
1796
|
-
if (!
|
|
2275
|
+
const jsPath = path12.join(projectRoot, "blocks", blockName, `${blockName}.js`);
|
|
2276
|
+
if (!existsSync10(jsPath)) {
|
|
1797
2277
|
logger.error(`Block "${blockName}" not found (no blocks/${blockName}/${blockName}.js).`);
|
|
1798
2278
|
process.exitCode = 1;
|
|
1799
2279
|
return;
|
|
1800
2280
|
}
|
|
1801
2281
|
const events = { click: options.click !== false, submit: options.submit !== false };
|
|
1802
|
-
const res = instrumentJs(await
|
|
2282
|
+
const res = instrumentJs(await readFile7(jsPath, "utf-8"), blockName, events);
|
|
1803
2283
|
if (!res.changed) {
|
|
1804
2284
|
logger.warn(`Not instrumented: ${res.reason}.`);
|
|
1805
2285
|
return;
|
|
1806
2286
|
}
|
|
1807
|
-
await
|
|
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 (!
|
|
1814
|
-
logger.info("");
|
|
1815
|
-
logger.warn("scripts/analytics.js is missing - run `eds integrate gtm` to create it.");
|
|
2287
|
+
await writeFile7(jsPath, res.js);
|
|
2288
|
+
logger.info(heading("Instrumented"));
|
|
2289
|
+
const picked = [events.click ? "block_click" : null, events.submit ? "form_submit" : null].filter(
|
|
2290
|
+
Boolean
|
|
2291
|
+
);
|
|
2292
|
+
logger.info(accentLine(`blocks/${blockName}/${blockName}.js`, picked.join(" + ")));
|
|
2293
|
+
if (!existsSync10(path12.join(projectRoot, "scripts", "analytics.js"))) {
|
|
2294
|
+
logger.info("");
|
|
2295
|
+
logger.warn("scripts/analytics.js is missing - run `eds integrate gtm` to create it.");
|
|
2296
|
+
}
|
|
2297
|
+
logger.info("");
|
|
2298
|
+
logger.info(chalk5.dim(" Events push to window.dataLayer via trackEvent - GTM picks them up."));
|
|
2299
|
+
}
|
|
2300
|
+
var init_track2 = __esm({
|
|
2301
|
+
"src/commands/track.ts"() {
|
|
2302
|
+
"use strict";
|
|
2303
|
+
init_esm_shims();
|
|
2304
|
+
init_logger();
|
|
2305
|
+
init_project();
|
|
2306
|
+
init_track();
|
|
2307
|
+
init_ui();
|
|
2308
|
+
}
|
|
2309
|
+
});
|
|
2310
|
+
|
|
2311
|
+
// src/commands/migrate.ts
|
|
2312
|
+
var migrate_exports = {};
|
|
2313
|
+
__export(migrate_exports, {
|
|
2314
|
+
migrateComponent: () => migrateComponent
|
|
2315
|
+
});
|
|
2316
|
+
import { existsSync as existsSync11 } from "fs";
|
|
2317
|
+
import { mkdir as mkdir5, writeFile as writeFile8 } from "fs/promises";
|
|
2318
|
+
import path13 from "path";
|
|
2319
|
+
function clientlibBlockCss(blockName, css, files) {
|
|
2320
|
+
return `/* Block: ${blockName} */
|
|
2321
|
+
/* Migrated from the component clientlib: ${files.join(", ")}. */
|
|
2322
|
+
/* TODO: review selectors and scope them under .${blockName} (and port any LESS/SCSS). */
|
|
2323
|
+
|
|
2324
|
+
${css.trim()}
|
|
2325
|
+
`;
|
|
2326
|
+
}
|
|
2327
|
+
function previewFields(fields, depth = 0) {
|
|
2328
|
+
const lines = [];
|
|
2329
|
+
for (const f2 of fields) {
|
|
2330
|
+
const indent = " ".repeat(depth);
|
|
2331
|
+
if (f2.component === "tab") {
|
|
2332
|
+
lines.push(`${indent}${brand.blue("\u25B8")} ${f2.label ?? f2.name}`);
|
|
2333
|
+
} else {
|
|
2334
|
+
const opts = f2.options?.length ? ` (${f2.options.length} options)` : "";
|
|
2335
|
+
const multi = f2.multi ? " \xB7multi" : "";
|
|
2336
|
+
const label = f2.label ? `- ${f2.label}` : "";
|
|
2337
|
+
lines.push(`${indent}${brand.accent(f2.component)}${multi} ${f2.name}${opts} ${label}`);
|
|
2338
|
+
if (f2.fields?.length) lines.push(...previewFields(f2.fields, depth + 1));
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
return lines;
|
|
2342
|
+
}
|
|
2343
|
+
function countLeaves(fields) {
|
|
2344
|
+
let n2 = 0;
|
|
2345
|
+
for (const f2 of fields) {
|
|
2346
|
+
if (f2.component === "tab") continue;
|
|
2347
|
+
n2 += 1;
|
|
2348
|
+
if (f2.fields) n2 += countLeaves(f2.fields);
|
|
2349
|
+
}
|
|
2350
|
+
return n2;
|
|
2351
|
+
}
|
|
2352
|
+
async function migrateComponent(dirArg, options = {}) {
|
|
2353
|
+
const projectRoot = findProjectRoot();
|
|
2354
|
+
if (!projectRoot) {
|
|
2355
|
+
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
2356
|
+
process.exitCode = 1;
|
|
2357
|
+
return;
|
|
2358
|
+
}
|
|
2359
|
+
await logger.logoOnceAnimated("Migrate component");
|
|
2360
|
+
const { input, confirm } = await import("@inquirer/prompts");
|
|
2361
|
+
const interactive = !options.yes;
|
|
2362
|
+
let componentDir = dirArg;
|
|
2363
|
+
if (!componentDir && interactive) {
|
|
2364
|
+
componentDir = await input({
|
|
2365
|
+
message: "Path to the classic component folder",
|
|
2366
|
+
validate: (v) => existsSync11(v.trim()) || "Folder not found"
|
|
2367
|
+
});
|
|
2368
|
+
}
|
|
2369
|
+
if (!componentDir) {
|
|
2370
|
+
logger.error("Provide the path to the classic component folder.");
|
|
2371
|
+
process.exitCode = 1;
|
|
2372
|
+
return;
|
|
2373
|
+
}
|
|
2374
|
+
componentDir = path13.resolve(componentDir.trim());
|
|
2375
|
+
if (!findDialogFile(componentDir)) {
|
|
2376
|
+
logger.error(`No Touch UI dialog (_cq_dialog/.content.xml) found in ${componentDir}.`);
|
|
2377
|
+
process.exitCode = 1;
|
|
2378
|
+
return;
|
|
2379
|
+
}
|
|
2380
|
+
const parsed = await readDialogFields(componentDir);
|
|
2381
|
+
if (!parsed || parsed.fields.length === 0) {
|
|
2382
|
+
logger.warn("Dialog found but no mappable fields were detected.");
|
|
2383
|
+
if (parsed?.unmapped.length)
|
|
2384
|
+
logger.info(`Unmapped resourceTypes: ${parsed.unmapped.join(", ")}`);
|
|
2385
|
+
process.exitCode = 1;
|
|
2386
|
+
return;
|
|
2387
|
+
}
|
|
2388
|
+
const title = await readComponentTitle(componentDir) ?? path13.basename(componentDir);
|
|
2389
|
+
const blockName = options.name && slug3(options.name) || (interactive ? slug3(await input({ message: "Block name", default: slug3(title) })) : slug3(title));
|
|
2390
|
+
logger.info(heading("Mapped fields", `${countLeaves(parsed.fields)} from dialog`));
|
|
2391
|
+
for (const line of previewFields(parsed.fields)) logger.info(` ${line}`);
|
|
2392
|
+
if (parsed.unmapped.length) {
|
|
2393
|
+
logger.info("");
|
|
2394
|
+
logger.warn(`Skipped (unrecognised): ${parsed.unmapped.join(", ")}`);
|
|
2395
|
+
}
|
|
2396
|
+
logger.info("");
|
|
2397
|
+
if (interactive) {
|
|
2398
|
+
const go = await confirm({ message: `Generate block "${blockName}"?`, default: true });
|
|
2399
|
+
if (!go) {
|
|
2400
|
+
logger.info("Cancelled.");
|
|
2401
|
+
return;
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
const blockDir = path13.join(projectRoot, "blocks", blockName);
|
|
2405
|
+
if (existsSync11(blockDir)) {
|
|
2406
|
+
logger.error(
|
|
2407
|
+
`Block "${blockName}" already exists at blocks/${blockName}. Use --name to pick another.`
|
|
2408
|
+
);
|
|
2409
|
+
process.exitCode = 1;
|
|
2410
|
+
return;
|
|
2411
|
+
}
|
|
2412
|
+
const clibDir = options.clientlib ? path13.resolve(options.clientlib.trim()) : await findClientlibDir(componentDir);
|
|
2413
|
+
let cssContent = migratedBlockCss(blockName);
|
|
2414
|
+
let cssMessage = "empty - add styles from clientlib / rendered CSS";
|
|
2415
|
+
let preprocessedNote = [];
|
|
2416
|
+
if (clibDir && existsSync11(clibDir)) {
|
|
2417
|
+
const lib = await readClientlibCss(clibDir);
|
|
2418
|
+
if (lib.css.trim()) {
|
|
2419
|
+
cssContent = clientlibBlockCss(blockName, lib.css, lib.files);
|
|
2420
|
+
cssMessage = `from clientlib (${lib.files.length} file(s)) - review & scope`;
|
|
2421
|
+
}
|
|
2422
|
+
preprocessedNote = lib.preprocessed;
|
|
2423
|
+
}
|
|
2424
|
+
await mkdir5(blockDir, { recursive: true });
|
|
2425
|
+
await writeFile8(
|
|
2426
|
+
path13.join(blockDir, `${blockName}.js`),
|
|
2427
|
+
migratedBlockJs(blockName, parsed.fields)
|
|
2428
|
+
);
|
|
2429
|
+
await writeFile8(path13.join(blockDir, `${blockName}.css`), cssContent);
|
|
2430
|
+
await writeFile8(
|
|
2431
|
+
path13.join(blockDir, `_${blockName}.json`),
|
|
2432
|
+
migratedModelFile(blockName, title, parsed.fields)
|
|
2433
|
+
);
|
|
2434
|
+
logger.info(heading("Created"));
|
|
2435
|
+
const files = [`${blockName}.js`, `${blockName}.css`, `_${blockName}.json`];
|
|
2436
|
+
const w = columnWidth(files);
|
|
2437
|
+
logger.info(accentLine(`${blockName}.js`, "decorator stub (port HTL logic)", w));
|
|
2438
|
+
logger.info(accentLine(`${blockName}.css`, cssMessage, w));
|
|
2439
|
+
logger.info(accentLine(`_${blockName}.json`, "Universal Editor model", w));
|
|
2440
|
+
if (preprocessedNote.length) {
|
|
2441
|
+
logger.info("");
|
|
2442
|
+
logger.warn(`LESS/SCSS found (needs a build, not copied): ${preprocessedNote.join(", ")}`);
|
|
2443
|
+
}
|
|
2444
|
+
logger.info("");
|
|
2445
|
+
if (interactive) {
|
|
2446
|
+
const addTracking = await confirm({
|
|
2447
|
+
message: "Instrument this block for analytics (dataLayer)?",
|
|
2448
|
+
default: false
|
|
2449
|
+
});
|
|
2450
|
+
if (addTracking) {
|
|
2451
|
+
const { trackBlock: trackBlock2 } = await Promise.resolve().then(() => (init_track2(), track_exports));
|
|
2452
|
+
await trackBlock2(blockName, {});
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
logger.info(
|
|
2456
|
+
box([`"${blockName}" migrated. Next: add CSS, then \`eds block preview ${blockName}\``])
|
|
2457
|
+
);
|
|
2458
|
+
}
|
|
2459
|
+
var slug3;
|
|
2460
|
+
var init_migrate = __esm({
|
|
2461
|
+
"src/commands/migrate.ts"() {
|
|
2462
|
+
"use strict";
|
|
2463
|
+
init_esm_shims();
|
|
2464
|
+
init_logger();
|
|
2465
|
+
init_clientlib();
|
|
2466
|
+
init_component();
|
|
2467
|
+
init_project();
|
|
2468
|
+
init_ui();
|
|
2469
|
+
slug3 = (s) => s.trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2470
|
+
}
|
|
2471
|
+
});
|
|
2472
|
+
|
|
2473
|
+
// src/lib/model/helpers.ts
|
|
2474
|
+
function n(prefix, base) {
|
|
2475
|
+
if (!prefix) return base;
|
|
2476
|
+
return prefix + base.charAt(0).toUpperCase() + base.slice(1);
|
|
2477
|
+
}
|
|
2478
|
+
var f;
|
|
2479
|
+
var init_helpers2 = __esm({
|
|
2480
|
+
"src/lib/model/helpers.ts"() {
|
|
2481
|
+
"use strict";
|
|
2482
|
+
init_esm_shims();
|
|
2483
|
+
f = {
|
|
2484
|
+
text: (name, label) => ({
|
|
2485
|
+
component: "text",
|
|
2486
|
+
valueType: "string",
|
|
2487
|
+
name,
|
|
2488
|
+
label
|
|
2489
|
+
}),
|
|
2490
|
+
textarea: (name, label) => ({
|
|
2491
|
+
component: "textarea",
|
|
2492
|
+
valueType: "string",
|
|
2493
|
+
name,
|
|
2494
|
+
label
|
|
2495
|
+
}),
|
|
2496
|
+
richtext: (name, label) => ({
|
|
2497
|
+
component: "richtext",
|
|
2498
|
+
valueType: "string",
|
|
2499
|
+
name,
|
|
2500
|
+
value: "",
|
|
2501
|
+
label
|
|
2502
|
+
}),
|
|
2503
|
+
number: (name, label) => ({
|
|
2504
|
+
component: "number",
|
|
2505
|
+
valueType: "number",
|
|
2506
|
+
name,
|
|
2507
|
+
label
|
|
2508
|
+
}),
|
|
2509
|
+
boolean: (name, label, value = false) => ({
|
|
2510
|
+
component: "boolean",
|
|
2511
|
+
valueType: "boolean",
|
|
2512
|
+
name,
|
|
2513
|
+
value,
|
|
2514
|
+
label
|
|
2515
|
+
}),
|
|
2516
|
+
dateTime: (name, label) => ({
|
|
2517
|
+
component: "date-time",
|
|
2518
|
+
valueType: "date",
|
|
2519
|
+
name,
|
|
2520
|
+
label
|
|
2521
|
+
}),
|
|
2522
|
+
select: (name, label, options, value) => ({
|
|
2523
|
+
component: "select",
|
|
2524
|
+
valueType: "string",
|
|
2525
|
+
name,
|
|
2526
|
+
label,
|
|
2527
|
+
options,
|
|
2528
|
+
...value !== void 0 ? { value } : {}
|
|
2529
|
+
}),
|
|
2530
|
+
multiselect: (name, label, options) => ({
|
|
2531
|
+
component: "multiselect",
|
|
2532
|
+
valueType: "string",
|
|
2533
|
+
name,
|
|
2534
|
+
label,
|
|
2535
|
+
options
|
|
2536
|
+
}),
|
|
2537
|
+
radioGroup: (name, label, options, value) => ({
|
|
2538
|
+
component: "radio-group",
|
|
2539
|
+
valueType: "string",
|
|
2540
|
+
name,
|
|
2541
|
+
label,
|
|
2542
|
+
options,
|
|
2543
|
+
...value !== void 0 ? { value } : {}
|
|
2544
|
+
}),
|
|
2545
|
+
checkboxGroup: (name, label, options) => ({
|
|
2546
|
+
component: "checkbox-group",
|
|
2547
|
+
valueType: "string[]",
|
|
2548
|
+
name,
|
|
2549
|
+
label,
|
|
2550
|
+
options
|
|
2551
|
+
}),
|
|
2552
|
+
reference: (name, label) => ({
|
|
2553
|
+
component: "reference",
|
|
2554
|
+
valueType: "string",
|
|
2555
|
+
name,
|
|
2556
|
+
label
|
|
2557
|
+
}),
|
|
2558
|
+
aemContent: (name, label) => ({ component: "aem-content", name, label }),
|
|
2559
|
+
aemContentFragment: (name, label) => ({
|
|
2560
|
+
component: "aem-content-fragment",
|
|
2561
|
+
name,
|
|
2562
|
+
label
|
|
2563
|
+
}),
|
|
2564
|
+
aemExperienceFragment: (name, label) => ({
|
|
2565
|
+
component: "aem-experience-fragment",
|
|
2566
|
+
name,
|
|
2567
|
+
label
|
|
2568
|
+
}),
|
|
2569
|
+
aemTag: (name, label) => ({
|
|
2570
|
+
component: "aem-tag",
|
|
2571
|
+
valueType: "string",
|
|
2572
|
+
name,
|
|
2573
|
+
label
|
|
2574
|
+
}),
|
|
2575
|
+
tab: (name, label) => ({ component: "tab", name, label }),
|
|
2576
|
+
container: (name, label, fields, collapsible = true) => ({
|
|
2577
|
+
component: "container",
|
|
2578
|
+
name,
|
|
2579
|
+
label,
|
|
2580
|
+
multi: true,
|
|
2581
|
+
collapsible,
|
|
2582
|
+
fields
|
|
2583
|
+
})
|
|
2584
|
+
};
|
|
2585
|
+
}
|
|
2586
|
+
});
|
|
2587
|
+
|
|
2588
|
+
// src/lib/model/partials.ts
|
|
2589
|
+
function partialById(id) {
|
|
2590
|
+
return PARTIALS.find((p) => p.id === id);
|
|
2591
|
+
}
|
|
2592
|
+
var HEADING_LEVELS, LINK_TARGETS, BUTTON_STYLES, SECTION_STYLES, heading2, eyebrow, image, richtext, link, cta, ctaGroup, classes, embed, teaser, card, PARTIALS;
|
|
2593
|
+
var init_partials = __esm({
|
|
2594
|
+
"src/lib/model/partials.ts"() {
|
|
2595
|
+
"use strict";
|
|
2596
|
+
init_esm_shims();
|
|
2597
|
+
init_helpers2();
|
|
2598
|
+
HEADING_LEVELS = [
|
|
2599
|
+
{ name: "H1", value: "h1" },
|
|
2600
|
+
{ name: "H2", value: "h2" },
|
|
2601
|
+
{ name: "H3", value: "h3" },
|
|
2602
|
+
{ name: "H4", value: "h4" },
|
|
2603
|
+
{ name: "H5", value: "h5" },
|
|
2604
|
+
{ name: "H6", value: "h6" }
|
|
2605
|
+
];
|
|
2606
|
+
LINK_TARGETS = [
|
|
2607
|
+
{ name: "Same tab", value: "_self" },
|
|
2608
|
+
{ name: "New tab", value: "_blank" }
|
|
2609
|
+
];
|
|
2610
|
+
BUTTON_STYLES = [
|
|
2611
|
+
{ name: "Default", value: "" },
|
|
2612
|
+
{ name: "Primary", value: "primary" },
|
|
2613
|
+
{ name: "Secondary", value: "secondary" }
|
|
2614
|
+
];
|
|
2615
|
+
SECTION_STYLES = [
|
|
2616
|
+
{ name: "Highlight", value: "highlight" },
|
|
2617
|
+
{ name: "Dark", value: "dark" },
|
|
2618
|
+
{ name: "Centered", value: "centered" },
|
|
2619
|
+
{ name: "Full width", value: "full-width" }
|
|
2620
|
+
];
|
|
2621
|
+
heading2 = (p = "") => [
|
|
2622
|
+
f.text(n(p, "title"), "Title"),
|
|
2623
|
+
f.select(n(p, "titleType"), "Heading level", HEADING_LEVELS, "h2")
|
|
2624
|
+
];
|
|
2625
|
+
eyebrow = (p = "") => [f.text(n(p, "eyebrow"), "Eyebrow")];
|
|
2626
|
+
image = (p = "") => [
|
|
2627
|
+
f.reference(n(p, "image"), "Image"),
|
|
2628
|
+
f.text(n(p, "imageAlt"), "Alt text")
|
|
2629
|
+
];
|
|
2630
|
+
richtext = (p = "") => [f.richtext(n(p, "text"), "Text")];
|
|
2631
|
+
link = (p = "") => [
|
|
2632
|
+
f.text(n(p, "linkText"), "Link text"),
|
|
2633
|
+
f.aemContent(n(p, "link"), "Link"),
|
|
2634
|
+
f.select(n(p, "linkTarget"), "Open in", LINK_TARGETS, "_self")
|
|
2635
|
+
];
|
|
2636
|
+
cta = (p = "") => [
|
|
2637
|
+
...link(p),
|
|
2638
|
+
f.select(n(p, "ctaStyle"), "Style", BUTTON_STYLES, "")
|
|
2639
|
+
];
|
|
2640
|
+
ctaGroup = (p = "") => [
|
|
2641
|
+
f.container(n(p, "ctas") || "ctas", "Buttons", cta())
|
|
2642
|
+
];
|
|
2643
|
+
classes = (p = "") => [
|
|
2644
|
+
f.multiselect(n(p, "classes"), "Styles", SECTION_STYLES)
|
|
2645
|
+
];
|
|
2646
|
+
embed = (p = "") => [f.aemContent(n(p, "url"), "URL (video / embed)")];
|
|
2647
|
+
teaser = (p = "") => [
|
|
2648
|
+
...image(p),
|
|
2649
|
+
...eyebrow(p),
|
|
2650
|
+
...heading2(p),
|
|
2651
|
+
...richtext(p),
|
|
2652
|
+
...cta(p)
|
|
2653
|
+
];
|
|
2654
|
+
card = (p = "") => [
|
|
2655
|
+
...image(p),
|
|
2656
|
+
f.text(n(p, "title"), "Title"),
|
|
2657
|
+
f.richtext(n(p, "description"), "Description"),
|
|
2658
|
+
...link(p)
|
|
2659
|
+
];
|
|
2660
|
+
PARTIALS = [
|
|
2661
|
+
{ id: "heading", label: "Heading (title + level)", build: heading2 },
|
|
2662
|
+
{ id: "eyebrow", label: "Eyebrow (kicker line)", build: eyebrow },
|
|
2663
|
+
{ id: "image", label: "Image (reference + alt)", build: image },
|
|
2664
|
+
{ id: "richtext", label: "Rich text body", build: richtext },
|
|
2665
|
+
{ id: "link", label: "Link (text + url + target)", build: link },
|
|
2666
|
+
{ id: "cta", label: "CTA (link + style)", build: cta },
|
|
2667
|
+
{ id: "cta-group", label: "CTA group (repeatable buttons)", build: ctaGroup },
|
|
2668
|
+
{ id: "classes", label: "Section styles (CSS modifiers)", build: classes },
|
|
2669
|
+
{ id: "embed", label: "Embed / video (URL)", build: embed },
|
|
2670
|
+
{ id: "teaser", label: "Teaser (image + eyebrow + title + text + CTA)", build: teaser },
|
|
2671
|
+
{ id: "card", label: "Card (image + title + description + link)", build: card }
|
|
2672
|
+
];
|
|
2673
|
+
}
|
|
2674
|
+
});
|
|
2675
|
+
|
|
2676
|
+
// src/commands/model.ts
|
|
2677
|
+
var model_exports = {};
|
|
2678
|
+
__export(model_exports, {
|
|
2679
|
+
modelAdd: () => modelAdd
|
|
2680
|
+
});
|
|
2681
|
+
import { existsSync as existsSync12 } from "fs";
|
|
2682
|
+
import { readFile as readFile8, writeFile as writeFile9 } from "fs/promises";
|
|
2683
|
+
import path14 from "path";
|
|
2684
|
+
async function modelAdd(blockArg, partialArgs, options = {}) {
|
|
2685
|
+
const projectRoot = findProjectRoot();
|
|
2686
|
+
if (!projectRoot) {
|
|
2687
|
+
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
2688
|
+
process.exitCode = 1;
|
|
2689
|
+
return;
|
|
2690
|
+
}
|
|
2691
|
+
logger.logoOnce(logo("Model - add partials"));
|
|
2692
|
+
const { select, checkbox } = await import("@inquirer/prompts");
|
|
2693
|
+
const interactive = !options.yes;
|
|
2694
|
+
let blockName = blockArg;
|
|
2695
|
+
if (!blockName && interactive) {
|
|
2696
|
+
const { readdir: readdir8 } = await import("fs/promises");
|
|
2697
|
+
const blocksDir = path14.join(projectRoot, "blocks");
|
|
2698
|
+
const dirs = existsSync12(blocksDir) ? (await readdir8(blocksDir, { withFileTypes: true })).filter(
|
|
2699
|
+
(e) => e.isDirectory() && existsSync12(path14.join(blocksDir, e.name, `_${e.name}.json`))
|
|
2700
|
+
).map((e) => e.name) : [];
|
|
2701
|
+
if (dirs.length === 0) {
|
|
2702
|
+
logger.error("No block with a _<name>.json model found. Create one with `eds block create`.");
|
|
2703
|
+
process.exitCode = 1;
|
|
2704
|
+
return;
|
|
2705
|
+
}
|
|
2706
|
+
blockName = await select({
|
|
2707
|
+
message: "Which block's model?",
|
|
2708
|
+
choices: dirs.map((d) => ({ name: d, value: d }))
|
|
2709
|
+
});
|
|
2710
|
+
}
|
|
2711
|
+
if (!blockName) {
|
|
2712
|
+
logger.error("Provide a block name.");
|
|
2713
|
+
process.exitCode = 1;
|
|
2714
|
+
return;
|
|
2715
|
+
}
|
|
2716
|
+
const modelFile = path14.join(projectRoot, "blocks", blockName, `_${blockName}.json`);
|
|
2717
|
+
if (!existsSync12(modelFile)) {
|
|
2718
|
+
logger.error(`No model at blocks/${blockName}/_${blockName}.json. Create the block first.`);
|
|
2719
|
+
process.exitCode = 1;
|
|
2720
|
+
return;
|
|
2721
|
+
}
|
|
2722
|
+
let ids = partialArgs;
|
|
2723
|
+
if (ids.length === 0 && interactive) {
|
|
2724
|
+
ids = await checkbox({
|
|
2725
|
+
message: "Which partials to add?",
|
|
2726
|
+
choices: PARTIALS.map((p) => ({ name: p.label, value: p.id }))
|
|
2727
|
+
});
|
|
2728
|
+
}
|
|
2729
|
+
const partials = ids.map((id) => partialById(id.trim())).filter((p) => !!p);
|
|
2730
|
+
const unknown = ids.filter((id) => !partialById(id.trim()));
|
|
2731
|
+
if (unknown.length) {
|
|
2732
|
+
logger.warn(
|
|
2733
|
+
`Unknown partial(s): ${unknown.join(", ")}. Available: ${PARTIALS.map((p) => p.id).join(", ")}`
|
|
2734
|
+
);
|
|
2735
|
+
}
|
|
2736
|
+
if (partials.length === 0) {
|
|
2737
|
+
logger.info("Nothing to add.");
|
|
2738
|
+
return;
|
|
2739
|
+
}
|
|
2740
|
+
let model2;
|
|
2741
|
+
try {
|
|
2742
|
+
model2 = JSON.parse(await readFile8(modelFile, "utf-8"));
|
|
2743
|
+
} catch {
|
|
2744
|
+
logger.error(`Could not parse ${modelFile}.`);
|
|
2745
|
+
process.exitCode = 1;
|
|
2746
|
+
return;
|
|
2747
|
+
}
|
|
2748
|
+
const models = Array.isArray(model2.models) ? model2.models : [];
|
|
2749
|
+
const target = models.find((m) => m.id === blockName) ?? models[0];
|
|
2750
|
+
if (!target) {
|
|
2751
|
+
logger.error("Model has no `models[]` entry to add fields to.");
|
|
2752
|
+
process.exitCode = 1;
|
|
2753
|
+
return;
|
|
2754
|
+
}
|
|
2755
|
+
target.fields = Array.isArray(target.fields) ? target.fields : [];
|
|
2756
|
+
const existing = new Set(target.fields.map((x) => x.name));
|
|
2757
|
+
const added = [];
|
|
2758
|
+
const skipped = [];
|
|
2759
|
+
for (const p of partials) {
|
|
2760
|
+
for (const field of p.build()) {
|
|
2761
|
+
if (existing.has(field.name)) {
|
|
2762
|
+
skipped.push(field.name);
|
|
2763
|
+
continue;
|
|
2764
|
+
}
|
|
2765
|
+
existing.add(field.name);
|
|
2766
|
+
target.fields.push(field);
|
|
2767
|
+
added.push(field.name);
|
|
2768
|
+
}
|
|
1816
2769
|
}
|
|
2770
|
+
await writeFile9(modelFile, `${JSON.stringify(model2, null, 2)}
|
|
2771
|
+
`);
|
|
2772
|
+
logger.info(heading("Model updated", `blocks/${blockName}/_${blockName}.json`));
|
|
2773
|
+
logger.info(accentLine("partials", partials.map((p) => p.id).join(", ")));
|
|
2774
|
+
logger.info(accentLine("fields added", added.length ? added.join(", ") : "(none)"));
|
|
2775
|
+
if (skipped.length) logger.info(statusLine("warn", "skipped (exists)", skipped.join(", ")));
|
|
1817
2776
|
logger.info("");
|
|
1818
|
-
logger.info(
|
|
2777
|
+
logger.info(box([`${added.length} field(s) added to "${blockName}"`]));
|
|
1819
2778
|
}
|
|
1820
|
-
var
|
|
1821
|
-
"src/commands/
|
|
2779
|
+
var init_model = __esm({
|
|
2780
|
+
"src/commands/model.ts"() {
|
|
1822
2781
|
"use strict";
|
|
1823
2782
|
init_esm_shims();
|
|
1824
2783
|
init_logger();
|
|
2784
|
+
init_partials();
|
|
1825
2785
|
init_project();
|
|
1826
|
-
init_track();
|
|
1827
2786
|
init_ui();
|
|
1828
2787
|
}
|
|
1829
2788
|
});
|
|
1830
2789
|
|
|
1831
|
-
// src/
|
|
1832
|
-
|
|
1833
|
-
|
|
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
|
-
`;
|
|
2790
|
+
// src/lib/schema/jsonld.ts
|
|
2791
|
+
function schemaTypeById(id) {
|
|
2792
|
+
return SCHEMA_TYPES.find((t) => t.id.toLowerCase() === id.toLowerCase());
|
|
1846
2793
|
}
|
|
1847
|
-
function
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
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
|
-
}
|
|
2794
|
+
function injectJsonLd(js, type) {
|
|
2795
|
+
if (js.includes(SCHEMA_MARKER)) return { js, changed: false, reason: "already has JSON-LD" };
|
|
2796
|
+
const decorate = js.match(
|
|
2797
|
+
/export default (?:async )?function decorate\s*\(\s*([A-Za-z_$][\w$]*)?[^)]*\)\s*\{/
|
|
2798
|
+
);
|
|
2799
|
+
if (!decorate || decorate.index === void 0) {
|
|
2800
|
+
return { js, changed: false, reason: "no `export default function decorate(...)` found" };
|
|
1860
2801
|
}
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
2802
|
+
const param = decorate[1] || "block";
|
|
2803
|
+
const at = decorate.index + decorate[0].length;
|
|
2804
|
+
const snippet = `
|
|
2805
|
+
// --- ${SCHEMA_MARKER}: ${type.id}) ---
|
|
2806
|
+
{
|
|
2807
|
+
const root = ${param};
|
|
2808
|
+
const ld = ${type.object};
|
|
2809
|
+
const ldScript = document.createElement('script');
|
|
2810
|
+
ldScript.type = 'application/ld+json';
|
|
2811
|
+
ldScript.textContent = JSON.stringify(ld);
|
|
2812
|
+
document.head.append(ldScript);
|
|
1869
2813
|
}
|
|
1870
|
-
|
|
2814
|
+
`;
|
|
2815
|
+
return { js: `${js.slice(0, at)}${snippet}${js.slice(at)}`, changed: true };
|
|
1871
2816
|
}
|
|
1872
|
-
|
|
2817
|
+
var SCHEMA_MARKER, SCHEMA_TYPES;
|
|
2818
|
+
var init_jsonld = __esm({
|
|
2819
|
+
"src/lib/schema/jsonld.ts"() {
|
|
2820
|
+
"use strict";
|
|
2821
|
+
init_esm_shims();
|
|
2822
|
+
SCHEMA_MARKER = "json-ld (eds schema";
|
|
2823
|
+
SCHEMA_TYPES = [
|
|
2824
|
+
{
|
|
2825
|
+
id: "Article",
|
|
2826
|
+
label: "Article / BlogPosting",
|
|
2827
|
+
object: `{
|
|
2828
|
+
'@context': 'https://schema.org',
|
|
2829
|
+
'@type': 'Article',
|
|
2830
|
+
headline: root.querySelector('h1, h2, h3')?.textContent?.trim() || document.title,
|
|
2831
|
+
image: [...root.querySelectorAll('img')].map((img) => img.src),
|
|
2832
|
+
description: root.querySelector('p')?.textContent?.trim(),
|
|
2833
|
+
url: window.location.href,
|
|
2834
|
+
// TODO: author, datePublished, dateModified
|
|
2835
|
+
}`
|
|
2836
|
+
},
|
|
2837
|
+
{
|
|
2838
|
+
id: "FAQPage",
|
|
2839
|
+
label: "FAQ page",
|
|
2840
|
+
object: `{
|
|
2841
|
+
'@context': 'https://schema.org',
|
|
2842
|
+
'@type': 'FAQPage',
|
|
2843
|
+
mainEntity: [...root.children].map((row) => {
|
|
2844
|
+
const [q, a] = row.children;
|
|
2845
|
+
return {
|
|
2846
|
+
'@type': 'Question',
|
|
2847
|
+
name: q?.textContent?.trim(),
|
|
2848
|
+
acceptedAnswer: { '@type': 'Answer', text: a?.innerHTML?.trim() },
|
|
2849
|
+
};
|
|
2850
|
+
}),
|
|
2851
|
+
}`
|
|
2852
|
+
},
|
|
2853
|
+
{
|
|
2854
|
+
id: "BreadcrumbList",
|
|
2855
|
+
label: "Breadcrumb list",
|
|
2856
|
+
object: `{
|
|
2857
|
+
'@context': 'https://schema.org',
|
|
2858
|
+
'@type': 'BreadcrumbList',
|
|
2859
|
+
itemListElement: [...root.querySelectorAll('a')].map((a, i) => ({
|
|
2860
|
+
'@type': 'ListItem',
|
|
2861
|
+
position: i + 1,
|
|
2862
|
+
name: a.textContent?.trim(),
|
|
2863
|
+
item: a.href,
|
|
2864
|
+
})),
|
|
2865
|
+
}`
|
|
2866
|
+
},
|
|
2867
|
+
{
|
|
2868
|
+
id: "Product",
|
|
2869
|
+
label: "Product",
|
|
2870
|
+
object: `{
|
|
2871
|
+
'@context': 'https://schema.org',
|
|
2872
|
+
'@type': 'Product',
|
|
2873
|
+
name: root.querySelector('h1, h2, h3')?.textContent?.trim(),
|
|
2874
|
+
image: [...root.querySelectorAll('img')].map((img) => img.src),
|
|
2875
|
+
description: root.querySelector('p')?.textContent?.trim(),
|
|
2876
|
+
// TODO: brand, sku, offers { '@type': 'Offer', price, priceCurrency, availability }
|
|
2877
|
+
}`
|
|
2878
|
+
},
|
|
2879
|
+
{
|
|
2880
|
+
id: "Organization",
|
|
2881
|
+
label: "Organization",
|
|
2882
|
+
object: `{
|
|
2883
|
+
'@context': 'https://schema.org',
|
|
2884
|
+
'@type': 'Organization',
|
|
2885
|
+
name: document.title,
|
|
2886
|
+
url: window.location.origin,
|
|
2887
|
+
logo: root.querySelector('img')?.src,
|
|
2888
|
+
// TODO: sameAs: [social profile URLs], contactPoint
|
|
2889
|
+
}`
|
|
2890
|
+
},
|
|
2891
|
+
{
|
|
2892
|
+
id: "LocalBusiness",
|
|
2893
|
+
label: "Local business",
|
|
2894
|
+
object: `{
|
|
2895
|
+
'@context': 'https://schema.org',
|
|
2896
|
+
'@type': 'LocalBusiness',
|
|
2897
|
+
name: root.querySelector('h1, h2, h3')?.textContent?.trim() || document.title,
|
|
2898
|
+
image: root.querySelector('img')?.src,
|
|
2899
|
+
url: window.location.origin,
|
|
2900
|
+
// TODO: address (PostalAddress), telephone, openingHours, geo
|
|
2901
|
+
}`
|
|
2902
|
+
},
|
|
2903
|
+
{
|
|
2904
|
+
id: "Event",
|
|
2905
|
+
label: "Event",
|
|
2906
|
+
object: `{
|
|
2907
|
+
'@context': 'https://schema.org',
|
|
2908
|
+
'@type': 'Event',
|
|
2909
|
+
name: root.querySelector('h1, h2, h3')?.textContent?.trim(),
|
|
2910
|
+
image: [...root.querySelectorAll('img')].map((img) => img.src),
|
|
2911
|
+
url: window.location.href,
|
|
2912
|
+
// TODO: startDate, endDate, location (Place), offers
|
|
2913
|
+
}`
|
|
2914
|
+
},
|
|
2915
|
+
{
|
|
2916
|
+
id: "VideoObject",
|
|
2917
|
+
label: "Video",
|
|
2918
|
+
object: `{
|
|
2919
|
+
'@context': 'https://schema.org',
|
|
2920
|
+
'@type': 'VideoObject',
|
|
2921
|
+
name: root.querySelector('h1, h2, h3')?.textContent?.trim() || document.title,
|
|
2922
|
+
thumbnailUrl: root.querySelector('img')?.src,
|
|
2923
|
+
contentUrl: root.querySelector('a[href], video source[src]')?.href,
|
|
2924
|
+
// TODO: description, uploadDate, duration (ISO 8601)
|
|
2925
|
+
}`
|
|
2926
|
+
},
|
|
2927
|
+
{
|
|
2928
|
+
id: "Recipe",
|
|
2929
|
+
label: "Recipe",
|
|
2930
|
+
object: `{
|
|
2931
|
+
'@context': 'https://schema.org',
|
|
2932
|
+
'@type': 'Recipe',
|
|
2933
|
+
name: root.querySelector('h1, h2, h3')?.textContent?.trim(),
|
|
2934
|
+
image: [...root.querySelectorAll('img')].map((img) => img.src),
|
|
2935
|
+
recipeIngredient: [...root.querySelectorAll('li')].map((li) => li.textContent?.trim()),
|
|
2936
|
+
// TODO: recipeInstructions, prepTime, cookTime, nutrition
|
|
2937
|
+
}`
|
|
2938
|
+
},
|
|
2939
|
+
{
|
|
2940
|
+
id: "HowTo",
|
|
2941
|
+
label: "How-to",
|
|
2942
|
+
object: `{
|
|
2943
|
+
'@context': 'https://schema.org',
|
|
2944
|
+
'@type': 'HowTo',
|
|
2945
|
+
name: root.querySelector('h1, h2, h3')?.textContent?.trim(),
|
|
2946
|
+
step: [...root.querySelectorAll('li')].map((li, i) => ({
|
|
2947
|
+
'@type': 'HowToStep',
|
|
2948
|
+
position: i + 1,
|
|
2949
|
+
text: li.textContent?.trim(),
|
|
2950
|
+
})),
|
|
2951
|
+
}`
|
|
2952
|
+
}
|
|
2953
|
+
];
|
|
2954
|
+
}
|
|
2955
|
+
});
|
|
2956
|
+
|
|
2957
|
+
// src/commands/schema.ts
|
|
2958
|
+
var schema_exports = {};
|
|
2959
|
+
__export(schema_exports, {
|
|
2960
|
+
schemaBlock: () => schemaBlock
|
|
2961
|
+
});
|
|
2962
|
+
import { existsSync as existsSync13 } from "fs";
|
|
2963
|
+
import { readFile as readFile9, writeFile as writeFile10 } from "fs/promises";
|
|
2964
|
+
import path15 from "path";
|
|
2965
|
+
import chalk6 from "chalk";
|
|
2966
|
+
async function schemaBlock(blockName, options = {}) {
|
|
1873
2967
|
const projectRoot = findProjectRoot();
|
|
1874
2968
|
if (!projectRoot) {
|
|
1875
2969
|
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
1876
2970
|
process.exitCode = 1;
|
|
1877
2971
|
return;
|
|
1878
2972
|
}
|
|
1879
|
-
|
|
1880
|
-
const
|
|
1881
|
-
|
|
1882
|
-
|
|
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(", ")}`);
|
|
2973
|
+
logger.logoOnce(logo("Schema - JSON-LD"));
|
|
2974
|
+
const jsPath = path15.join(projectRoot, "blocks", blockName, `${blockName}.js`);
|
|
2975
|
+
if (!existsSync13(jsPath)) {
|
|
2976
|
+
logger.error(`Block "${blockName}" not found (no blocks/${blockName}/${blockName}.js).`);
|
|
1905
2977
|
process.exitCode = 1;
|
|
1906
2978
|
return;
|
|
1907
2979
|
}
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
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
|
-
}
|
|
2980
|
+
let typeId = options.type;
|
|
2981
|
+
if (!typeId && !options.yes) {
|
|
2982
|
+
const { select } = await import("@inquirer/prompts");
|
|
2983
|
+
typeId = await select({
|
|
2984
|
+
message: "Which schema.org type?",
|
|
2985
|
+
choices: SCHEMA_TYPES.map((t) => ({ name: `${t.id} - ${t.label}`, value: t.id }))
|
|
2986
|
+
});
|
|
1923
2987
|
}
|
|
1924
|
-
const
|
|
1925
|
-
if (
|
|
2988
|
+
const type = typeId ? schemaTypeById(typeId) : void 0;
|
|
2989
|
+
if (!type) {
|
|
1926
2990
|
logger.error(
|
|
1927
|
-
`
|
|
2991
|
+
`Unknown schema type "${typeId}". Options: ${SCHEMA_TYPES.map((t) => t.id).join(", ")}`
|
|
1928
2992
|
);
|
|
1929
2993
|
process.exitCode = 1;
|
|
1930
2994
|
return;
|
|
1931
2995
|
}
|
|
1932
|
-
const
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
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(", ")}`);
|
|
2996
|
+
const res = injectJsonLd(await readFile9(jsPath, "utf-8"), type);
|
|
2997
|
+
if (!res.changed) {
|
|
2998
|
+
logger.warn(`Not added: ${res.reason}.`);
|
|
2999
|
+
return;
|
|
1963
3000
|
}
|
|
3001
|
+
await writeFile10(jsPath, res.js);
|
|
3002
|
+
logger.info(heading("JSON-LD added"));
|
|
3003
|
+
logger.info(accentLine(`blocks/${blockName}/${blockName}.js`, `${type.id} (${type.label})`));
|
|
1964
3004
|
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
3005
|
logger.info(
|
|
1976
|
-
|
|
3006
|
+
chalk6.dim(
|
|
3007
|
+
" Review the generated object - some fields are heuristic or TODO. Validate at search.google.com/test/rich-results."
|
|
3008
|
+
)
|
|
1977
3009
|
);
|
|
1978
3010
|
}
|
|
1979
|
-
var
|
|
1980
|
-
|
|
1981
|
-
"src/commands/migrate.ts"() {
|
|
3011
|
+
var init_schema = __esm({
|
|
3012
|
+
"src/commands/schema.ts"() {
|
|
1982
3013
|
"use strict";
|
|
1983
3014
|
init_esm_shims();
|
|
1984
3015
|
init_logger();
|
|
1985
|
-
init_clientlib();
|
|
1986
|
-
init_component();
|
|
1987
3016
|
init_project();
|
|
3017
|
+
init_jsonld();
|
|
1988
3018
|
init_ui();
|
|
1989
|
-
slug2 = (s) => s.trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1990
3019
|
}
|
|
1991
3020
|
});
|
|
1992
3021
|
|
|
@@ -1996,16 +3025,16 @@ __export(create_exports, {
|
|
|
1996
3025
|
createBlock: () => createBlock,
|
|
1997
3026
|
ueModelFor: () => ueModelFor
|
|
1998
3027
|
});
|
|
1999
|
-
import { existsSync as
|
|
2000
|
-
import { mkdir as
|
|
2001
|
-
import
|
|
2002
|
-
import
|
|
3028
|
+
import { existsSync as existsSync14 } from "fs";
|
|
3029
|
+
import { mkdir as mkdir6, writeFile as writeFile11 } from "fs/promises";
|
|
3030
|
+
import path16 from "path";
|
|
3031
|
+
import chalk7 from "chalk";
|
|
2003
3032
|
function titleCase(name) {
|
|
2004
3033
|
return name.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
2005
3034
|
}
|
|
2006
3035
|
function ueModelFor(name) {
|
|
2007
3036
|
const title = titleCase(name);
|
|
2008
|
-
const
|
|
3037
|
+
const model2 = {
|
|
2009
3038
|
definitions: [
|
|
2010
3039
|
{
|
|
2011
3040
|
title,
|
|
@@ -2058,7 +3087,7 @@ function ueModelFor(name) {
|
|
|
2058
3087
|
],
|
|
2059
3088
|
filters: []
|
|
2060
3089
|
};
|
|
2061
|
-
return `${JSON.stringify(
|
|
3090
|
+
return `${JSON.stringify(model2, null, 2)}
|
|
2062
3091
|
`;
|
|
2063
3092
|
}
|
|
2064
3093
|
async function createBlock(name, options = {}) {
|
|
@@ -2077,13 +3106,13 @@ async function createBlock(name, options = {}) {
|
|
|
2077
3106
|
process.exitCode = 1;
|
|
2078
3107
|
return;
|
|
2079
3108
|
}
|
|
2080
|
-
const blocksDir =
|
|
2081
|
-
if (
|
|
3109
|
+
const blocksDir = path16.join(projectRoot, "blocks", name);
|
|
3110
|
+
if (existsSync14(blocksDir)) {
|
|
2082
3111
|
logger.error(`Block "${name}" already exists at ${blocksDir}`);
|
|
2083
3112
|
process.exitCode = 1;
|
|
2084
3113
|
return;
|
|
2085
3114
|
}
|
|
2086
|
-
await
|
|
3115
|
+
await mkdir6(blocksDir, { recursive: true });
|
|
2087
3116
|
const jsContent = `export default function decorate(block) {
|
|
2088
3117
|
// TODO: implement ${name} block
|
|
2089
3118
|
}
|
|
@@ -2092,14 +3121,14 @@ async function createBlock(name, options = {}) {
|
|
|
2092
3121
|
/* TODO: style ${name} block */
|
|
2093
3122
|
}
|
|
2094
3123
|
`;
|
|
2095
|
-
await
|
|
2096
|
-
await
|
|
3124
|
+
await writeFile11(path16.join(blocksDir, `${name}.js`), jsContent);
|
|
3125
|
+
await writeFile11(path16.join(blocksDir, `${name}.css`), cssContent);
|
|
2097
3126
|
const withUe = options.ueModel !== false;
|
|
2098
3127
|
if (withUe) {
|
|
2099
|
-
await
|
|
3128
|
+
await writeFile11(path16.join(blocksDir, `_${name}.json`), ueModelFor(name));
|
|
2100
3129
|
}
|
|
2101
3130
|
logger.success(
|
|
2102
|
-
`Block "${name}" created at ${
|
|
3131
|
+
`Block "${name}" created at ${chalk7.cyan(path16.relative(projectRoot, blocksDir))}${withUe ? chalk7.dim(" (+ Universal Editor model)") : ""}`
|
|
2103
3132
|
);
|
|
2104
3133
|
}
|
|
2105
3134
|
var init_create = __esm({
|
|
@@ -2113,10 +3142,10 @@ var init_create = __esm({
|
|
|
2113
3142
|
});
|
|
2114
3143
|
|
|
2115
3144
|
// src/lib/agents/detect.ts
|
|
2116
|
-
import { existsSync as
|
|
2117
|
-
import { readFile as
|
|
3145
|
+
import { existsSync as existsSync15 } from "fs";
|
|
3146
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
2118
3147
|
import { homedir } from "os";
|
|
2119
|
-
import
|
|
3148
|
+
import path17 from "path";
|
|
2120
3149
|
async function whichBinary(name) {
|
|
2121
3150
|
const { execaCommand } = await import("execa");
|
|
2122
3151
|
try {
|
|
@@ -2144,8 +3173,8 @@ function findFigmaServerKey(node) {
|
|
|
2144
3173
|
async function findFigmaMcpServer(configPaths) {
|
|
2145
3174
|
for (const configPath of configPaths) {
|
|
2146
3175
|
try {
|
|
2147
|
-
if (!
|
|
2148
|
-
const content = await
|
|
3176
|
+
if (!existsSync15(configPath)) continue;
|
|
3177
|
+
const content = await readFile10(configPath, "utf-8");
|
|
2149
3178
|
try {
|
|
2150
3179
|
const key = findFigmaServerKey(JSON.parse(content));
|
|
2151
3180
|
if (key) return key;
|
|
@@ -2167,7 +3196,7 @@ async function detectAgent(preferred) {
|
|
|
2167
3196
|
return {
|
|
2168
3197
|
type: def.type,
|
|
2169
3198
|
path: binPath,
|
|
2170
|
-
configDir:
|
|
3199
|
+
configDir: existsSync15(def.configDir) ? def.configDir : void 0,
|
|
2171
3200
|
hasFigmaMcp: !!figmaMcpServerName,
|
|
2172
3201
|
figmaMcpServerName
|
|
2173
3202
|
};
|
|
@@ -2182,7 +3211,7 @@ async function detectAgent(preferred) {
|
|
|
2182
3211
|
return {
|
|
2183
3212
|
type: def.type,
|
|
2184
3213
|
path: binPath,
|
|
2185
|
-
configDir:
|
|
3214
|
+
configDir: existsSync15(def.configDir) ? def.configDir : void 0,
|
|
2186
3215
|
hasFigmaMcp: !!figmaMcpServerName,
|
|
2187
3216
|
figmaMcpServerName
|
|
2188
3217
|
};
|
|
@@ -2199,7 +3228,7 @@ async function detectAllAgents() {
|
|
|
2199
3228
|
agents.push({
|
|
2200
3229
|
type: def.type,
|
|
2201
3230
|
path: binPath,
|
|
2202
|
-
configDir:
|
|
3231
|
+
configDir: existsSync15(def.configDir) ? def.configDir : void 0,
|
|
2203
3232
|
hasFigmaMcp: !!figmaMcpServerName,
|
|
2204
3233
|
figmaMcpServerName
|
|
2205
3234
|
});
|
|
@@ -2217,22 +3246,22 @@ var init_detect = __esm({
|
|
|
2217
3246
|
{
|
|
2218
3247
|
type: "claude",
|
|
2219
3248
|
binary: "claude",
|
|
2220
|
-
configDir:
|
|
3249
|
+
configDir: path17.join(homedir(), ".claude"),
|
|
2221
3250
|
mcpConfigPaths: [
|
|
2222
|
-
|
|
2223
|
-
|
|
3251
|
+
path17.join(homedir(), ".claude", "plugins.json"),
|
|
3252
|
+
path17.join(homedir(), ".claude.json")
|
|
2224
3253
|
]
|
|
2225
3254
|
},
|
|
2226
3255
|
{
|
|
2227
3256
|
type: "cursor",
|
|
2228
3257
|
binary: "cursor",
|
|
2229
|
-
configDir:
|
|
2230
|
-
mcpConfigPaths: [
|
|
3258
|
+
configDir: path17.join(homedir(), ".cursor"),
|
|
3259
|
+
mcpConfigPaths: [path17.join(homedir(), ".cursor", "mcp.json")]
|
|
2231
3260
|
},
|
|
2232
3261
|
{
|
|
2233
3262
|
type: "codex",
|
|
2234
3263
|
binary: "codex",
|
|
2235
|
-
configDir:
|
|
3264
|
+
configDir: path17.join(homedir(), ".codex"),
|
|
2236
3265
|
mcpConfigPaths: []
|
|
2237
3266
|
}
|
|
2238
3267
|
];
|
|
@@ -2240,27 +3269,27 @@ var init_detect = __esm({
|
|
|
2240
3269
|
});
|
|
2241
3270
|
|
|
2242
3271
|
// src/lib/agents/invoke.ts
|
|
2243
|
-
import { mkdir as
|
|
3272
|
+
import { mkdir as mkdir7, readFile as readFile11, writeFile as writeFile12 } from "fs/promises";
|
|
2244
3273
|
import { tmpdir } from "os";
|
|
2245
|
-
import
|
|
3274
|
+
import path18 from "path";
|
|
2246
3275
|
import readline from "readline";
|
|
2247
|
-
import
|
|
3276
|
+
import chalk8 from "chalk";
|
|
2248
3277
|
async function savePromptToFile(prompt, projectRoot) {
|
|
2249
|
-
const base = projectRoot ?
|
|
2250
|
-
const dir =
|
|
2251
|
-
await
|
|
3278
|
+
const base = projectRoot ? path18.join(projectRoot, "tmp") : tmpdir();
|
|
3279
|
+
const dir = path18.join(base, "eds-cli");
|
|
3280
|
+
await mkdir7(dir, { recursive: true });
|
|
2252
3281
|
const filename = `from-design-${Date.now()}.md`;
|
|
2253
|
-
const filePath =
|
|
2254
|
-
await
|
|
3282
|
+
const filePath = path18.join(dir, filename);
|
|
3283
|
+
await writeFile12(filePath, prompt, "utf-8");
|
|
2255
3284
|
return filePath;
|
|
2256
3285
|
}
|
|
2257
3286
|
async function invokeAgent(agent, promptFile, cwd) {
|
|
2258
3287
|
const { execa } = await import("execa");
|
|
2259
|
-
const prompt = await
|
|
3288
|
+
const prompt = await readFile11(promptFile, "utf-8");
|
|
2260
3289
|
try {
|
|
2261
3290
|
switch (agent.type) {
|
|
2262
3291
|
case "claude": {
|
|
2263
|
-
logger.info(`Running Claude Code ${
|
|
3292
|
+
logger.info(`Running Claude Code ${chalk8.dim("- live log:")}`);
|
|
2264
3293
|
const figmaServers = [
|
|
2265
3294
|
...agent.figmaMcpServerName ? [agent.figmaMcpServerName] : [],
|
|
2266
3295
|
"figma-sse",
|
|
@@ -2323,10 +3352,10 @@ async function renderClaudeStream(sub, cwd) {
|
|
|
2323
3352
|
const ora4 = (await import("ora")).default;
|
|
2324
3353
|
const start = Date.now();
|
|
2325
3354
|
const elapsed = () => `${Math.round((Date.now() - start) / 1e3)}s`;
|
|
2326
|
-
const spinner = ora4({ text: `thinking ${
|
|
3355
|
+
const spinner = ora4({ text: `thinking ${chalk8.dim(elapsed())}`, color: "yellow" }).start();
|
|
2327
3356
|
let activity = "thinking";
|
|
2328
3357
|
const timer = setInterval(() => {
|
|
2329
|
-
spinner.text = `${activity} ${
|
|
3358
|
+
spinner.text = `${activity} ${chalk8.dim(elapsed())}`;
|
|
2330
3359
|
}, 300);
|
|
2331
3360
|
const pending = /* @__PURE__ */ new Map();
|
|
2332
3361
|
const emit = (line) => {
|
|
@@ -2352,7 +3381,7 @@ async function renderClaudeStream(sub, cwd) {
|
|
|
2352
3381
|
for (const block2 of content) {
|
|
2353
3382
|
if (block2.type === "text" && "text" in block2) {
|
|
2354
3383
|
const first = block2.text.trim().split("\n")[0].slice(0, 100);
|
|
2355
|
-
if (first) emit(
|
|
3384
|
+
if (first) emit(chalk8.dim(first));
|
|
2356
3385
|
} else if (block2.type === "tool_use" && "name" in block2) {
|
|
2357
3386
|
const id = "id" in block2 && block2.id ? block2.id : `${Math.random()}`;
|
|
2358
3387
|
pending.set(id, {
|
|
@@ -2368,8 +3397,8 @@ async function renderClaudeStream(sub, cwd) {
|
|
|
2368
3397
|
const p = pending.get(block2.tool_use_id);
|
|
2369
3398
|
if (p) {
|
|
2370
3399
|
const dur = `${((Date.now() - p.start) / 1e3).toFixed(1)}s`;
|
|
2371
|
-
const failed = "is_error" in block2 && block2.is_error ? ` ${
|
|
2372
|
-
emit(`${p.label} ${
|
|
3400
|
+
const failed = "is_error" in block2 && block2.is_error ? ` ${chalk8.red("failed")}` : "";
|
|
3401
|
+
emit(`${p.label} ${chalk8.dim(dur)}${failed}`);
|
|
2373
3402
|
pending.delete(block2.tool_use_id);
|
|
2374
3403
|
}
|
|
2375
3404
|
}
|
|
@@ -2381,9 +3410,9 @@ async function renderClaudeStream(sub, cwd) {
|
|
|
2381
3410
|
}
|
|
2382
3411
|
}
|
|
2383
3412
|
}
|
|
2384
|
-
for (const p of pending.values()) emit(`${p.label} ${
|
|
3413
|
+
for (const p of pending.values()) emit(`${p.label} ${chalk8.dim("\u2026")}`);
|
|
2385
3414
|
clearInterval(timer);
|
|
2386
|
-
if (result) spinner.succeed(`${brand.accent("agent finished")} ${
|
|
3415
|
+
if (result) spinner.succeed(`${brand.accent("agent finished")} ${chalk8.dim(result)}`);
|
|
2387
3416
|
else spinner.stop();
|
|
2388
3417
|
} catch (err) {
|
|
2389
3418
|
clearInterval(timer);
|
|
@@ -2396,22 +3425,22 @@ function toolLabel(name, input, cwd) {
|
|
|
2396
3425
|
const rel = (key) => {
|
|
2397
3426
|
const p = input?.[key];
|
|
2398
3427
|
if (typeof p !== "string") return "";
|
|
2399
|
-
return p.startsWith(cwd) ?
|
|
3428
|
+
return p.startsWith(cwd) ? path18.relative(cwd, p) : path18.basename(p);
|
|
2400
3429
|
};
|
|
2401
3430
|
if (name.includes("figma")) {
|
|
2402
3431
|
const short = name.split("__").pop() ?? name;
|
|
2403
|
-
return `${icon.figma} ${
|
|
3432
|
+
return `${icon.figma} ${chalk8.bold(short)} ${chalk8.dim("Figma MCP")}`;
|
|
2404
3433
|
}
|
|
2405
3434
|
switch (name) {
|
|
2406
3435
|
case "Write":
|
|
2407
3436
|
case "Edit":
|
|
2408
3437
|
case "NotebookEdit":
|
|
2409
|
-
return `${brand.accent("\u270E")} ${
|
|
3438
|
+
return `${brand.accent("\u270E")} ${chalk8.cyan(rel("file_path"))}`;
|
|
2410
3439
|
case "Read":
|
|
2411
|
-
return
|
|
3440
|
+
return chalk8.dim(`\xB7 read ${rel("file_path")}`);
|
|
2412
3441
|
case "Bash": {
|
|
2413
3442
|
const cmd = typeof input?.command === "string" ? input.command.split("\n")[0].slice(0, 70) : "";
|
|
2414
|
-
return
|
|
3443
|
+
return chalk8.dim(`$ ${cmd}`);
|
|
2415
3444
|
}
|
|
2416
3445
|
default:
|
|
2417
3446
|
return `${brand.blue("\u2192")} ${name}`;
|
|
@@ -2427,13 +3456,13 @@ var init_invoke = __esm({
|
|
|
2427
3456
|
});
|
|
2428
3457
|
|
|
2429
3458
|
// src/lib/config.ts
|
|
2430
|
-
import { existsSync as
|
|
2431
|
-
import { readFile as
|
|
3459
|
+
import { existsSync as existsSync16 } from "fs";
|
|
3460
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
2432
3461
|
import { homedir as homedir2 } from "os";
|
|
2433
|
-
import
|
|
3462
|
+
import path19 from "path";
|
|
2434
3463
|
async function loadJsonFile(filePath) {
|
|
2435
3464
|
try {
|
|
2436
|
-
const content = await
|
|
3465
|
+
const content = await readFile12(filePath, "utf-8");
|
|
2437
3466
|
return JSON.parse(content);
|
|
2438
3467
|
} catch {
|
|
2439
3468
|
return void 0;
|
|
@@ -2441,7 +3470,7 @@ async function loadJsonFile(filePath) {
|
|
|
2441
3470
|
}
|
|
2442
3471
|
async function loadConfig(projectRoot) {
|
|
2443
3472
|
const globalRaw = await loadJsonFile(GLOBAL_CONFIG_PATH);
|
|
2444
|
-
const projectRaw = projectRoot ? await loadJsonFile(
|
|
3473
|
+
const projectRaw = projectRoot ? await loadJsonFile(path19.join(projectRoot, PROJECT_CONFIG_NAME)) : void 0;
|
|
2445
3474
|
const merged = {
|
|
2446
3475
|
...typeof globalRaw === "object" && globalRaw !== null ? globalRaw : {},
|
|
2447
3476
|
...typeof projectRaw === "object" && projectRaw !== null ? projectRaw : {}
|
|
@@ -2460,7 +3489,7 @@ function globalConfigPath() {
|
|
|
2460
3489
|
return GLOBAL_CONFIG_PATH;
|
|
2461
3490
|
}
|
|
2462
3491
|
function globalConfigExists() {
|
|
2463
|
-
return
|
|
3492
|
+
return existsSync16(GLOBAL_CONFIG_PATH);
|
|
2464
3493
|
}
|
|
2465
3494
|
var GLOBAL_CONFIG_DIR, GLOBAL_CONFIG_PATH, PROJECT_CONFIG_NAME;
|
|
2466
3495
|
var init_config = __esm({
|
|
@@ -2469,8 +3498,8 @@ var init_config = __esm({
|
|
|
2469
3498
|
init_esm_shims();
|
|
2470
3499
|
init_logger();
|
|
2471
3500
|
init_schemas();
|
|
2472
|
-
GLOBAL_CONFIG_DIR =
|
|
2473
|
-
GLOBAL_CONFIG_PATH =
|
|
3501
|
+
GLOBAL_CONFIG_DIR = path19.join(homedir2(), ".eds");
|
|
3502
|
+
GLOBAL_CONFIG_PATH = path19.join(GLOBAL_CONFIG_DIR, "config.json");
|
|
2474
3503
|
PROJECT_CONFIG_NAME = ".edsrc.json";
|
|
2475
3504
|
}
|
|
2476
3505
|
});
|
|
@@ -2520,9 +3549,9 @@ var init_node_id = __esm({
|
|
|
2520
3549
|
});
|
|
2521
3550
|
|
|
2522
3551
|
// src/lib/figma/prompt-builder.ts
|
|
2523
|
-
import { existsSync as
|
|
2524
|
-
import { readFile as
|
|
2525
|
-
import
|
|
3552
|
+
import { existsSync as existsSync17 } from "fs";
|
|
3553
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
3554
|
+
import path20 from "path";
|
|
2526
3555
|
function contentSourceGuidance(source, cfHint, blockName) {
|
|
2527
3556
|
if (!source || source === "document" || source === "ue") return "";
|
|
2528
3557
|
const q = cfHint ? ` (model / persisted query: \`${cfHint}\`)` : "";
|
|
@@ -2651,6 +3680,9 @@ Rules:
|
|
|
2651
3680
|
- **Self-check before finishing:** every field \`name\` in the model must be read by \`decorate()\` (or applied via \`classes\`). List each field and where the JS/CSS consumes it. Unconsumed fields = broken authoring.
|
|
2652
3681
|
|
|
2653
3682
|
If the project uses a **single aggregated model file** (\`component-definition.json\` + \`component-models.json\` + \`component-filters.json\`) instead of per-block \`_${ctx.blockName}.json\`, detect that convention and add the block's definition/model/filter to those files instead (and register the block in the \`section\` filter). Prefer the per-block \`_${ctx.blockName}.json\` when neither exists. Full field reference and a repeatable-cards round-trip example: \`docs/universal-editor-fields.md\`.
|
|
3683
|
+
|
|
3684
|
+
**Reusable field groups (partials):** when the design shows one of these common shapes, reuse the standard group instead of inventing fields \u2014 same groups \`eds model add\` uses, so the model stays consistent across the project:
|
|
3685
|
+
${PARTIALS.map((p) => `- \`${p.id}\` \u2014 ${p.label}`).join("\n")}
|
|
2654
3686
|
` : "";
|
|
2655
3687
|
return `# Pixel-Perfect EDS Block from ${providerName}
|
|
2656
3688
|
|
|
@@ -2734,10 +3766,10 @@ ${tokensHint}
|
|
|
2734
3766
|
`;
|
|
2735
3767
|
}
|
|
2736
3768
|
async function getTokensHint(projectRoot) {
|
|
2737
|
-
const stylesPath =
|
|
2738
|
-
if (!
|
|
3769
|
+
const stylesPath = path20.join(projectRoot, "styles", "styles.css");
|
|
3770
|
+
if (!existsSync17(stylesPath)) return "";
|
|
2739
3771
|
try {
|
|
2740
|
-
const css = await
|
|
3772
|
+
const css = await readFile13(stylesPath, "utf-8");
|
|
2741
3773
|
const rootMatch = css.match(/:root\s*\{([^}]+)\}/);
|
|
2742
3774
|
if (!rootMatch) return "";
|
|
2743
3775
|
const props = rootMatch[1].split("\n").filter((line) => line.trim().startsWith("--")).map((line) => line.trim()).join("\n ");
|
|
@@ -2762,6 +3794,7 @@ var init_prompt_builder = __esm({
|
|
|
2762
3794
|
"src/lib/figma/prompt-builder.ts"() {
|
|
2763
3795
|
"use strict";
|
|
2764
3796
|
init_esm_shims();
|
|
3797
|
+
init_partials();
|
|
2765
3798
|
PROMPT_VERSION = "0.5.0";
|
|
2766
3799
|
PROVIDER_NAMES = {
|
|
2767
3800
|
figma: "Figma",
|
|
@@ -2878,11 +3911,11 @@ var init_harness = __esm({
|
|
|
2878
3911
|
});
|
|
2879
3912
|
|
|
2880
3913
|
// src/lib/preview/server.ts
|
|
2881
|
-
import { createReadStream, existsSync as
|
|
3914
|
+
import { createReadStream, existsSync as existsSync18, statSync } from "fs";
|
|
2882
3915
|
import http from "http";
|
|
2883
|
-
import
|
|
3916
|
+
import path21 from "path";
|
|
2884
3917
|
function startServer(root, routes, port, host = "127.0.0.1") {
|
|
2885
|
-
const resolvedRoot =
|
|
3918
|
+
const resolvedRoot = path21.resolve(root);
|
|
2886
3919
|
const clients = /* @__PURE__ */ new Set();
|
|
2887
3920
|
const server = http.createServer((req, res) => {
|
|
2888
3921
|
const urlPath = decodeURIComponent((req.url ?? "/").split("?")[0]);
|
|
@@ -2908,19 +3941,19 @@ function startServer(root, routes, port, host = "127.0.0.1") {
|
|
|
2908
3941
|
res.end(route.body);
|
|
2909
3942
|
return;
|
|
2910
3943
|
}
|
|
2911
|
-
const filePath =
|
|
3944
|
+
const filePath = path21.join(resolvedRoot, urlPath);
|
|
2912
3945
|
if (!filePath.startsWith(resolvedRoot)) {
|
|
2913
3946
|
res.writeHead(403);
|
|
2914
3947
|
res.end("Forbidden");
|
|
2915
3948
|
return;
|
|
2916
3949
|
}
|
|
2917
|
-
if (!
|
|
3950
|
+
if (!existsSync18(filePath) || !statSync(filePath).isFile()) {
|
|
2918
3951
|
res.writeHead(404, { "content-type": "text/plain" });
|
|
2919
3952
|
res.end(`Not found: ${urlPath}`);
|
|
2920
3953
|
return;
|
|
2921
3954
|
}
|
|
2922
3955
|
res.writeHead(200, {
|
|
2923
|
-
"content-type": MIME[
|
|
3956
|
+
"content-type": MIME[path21.extname(filePath).toLowerCase()] ?? "application/octet-stream"
|
|
2924
3957
|
});
|
|
2925
3958
|
createReadStream(filePath).pipe(res);
|
|
2926
3959
|
});
|
|
@@ -2984,10 +4017,10 @@ var preview_exports = {};
|
|
|
2984
4017
|
__export(preview_exports, {
|
|
2985
4018
|
previewBlock: () => previewBlock
|
|
2986
4019
|
});
|
|
2987
|
-
import { existsSync as
|
|
2988
|
-
import { readFile as
|
|
2989
|
-
import
|
|
2990
|
-
import
|
|
4020
|
+
import { existsSync as existsSync19, watch } from "fs";
|
|
4021
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
4022
|
+
import path22 from "path";
|
|
4023
|
+
import chalk9 from "chalk";
|
|
2991
4024
|
function labelFor(width) {
|
|
2992
4025
|
if (width <= 480) return `${width} mobile`;
|
|
2993
4026
|
if (width <= 1024) return `${width} tablet`;
|
|
@@ -3039,30 +4072,30 @@ async function previewBlock(name, options = {}) {
|
|
|
3039
4072
|
process.exitCode = 1;
|
|
3040
4073
|
return;
|
|
3041
4074
|
}
|
|
3042
|
-
const blockDir =
|
|
3043
|
-
if (!
|
|
4075
|
+
const blockDir = path22.join(projectRoot, "blocks", name);
|
|
4076
|
+
if (!existsSync19(blockDir)) {
|
|
3044
4077
|
logger.error(`Block "${name}" not found at blocks/${name}/.`);
|
|
3045
4078
|
process.exitCode = 1;
|
|
3046
4079
|
return;
|
|
3047
4080
|
}
|
|
3048
|
-
if (!
|
|
4081
|
+
if (!existsSync19(path22.join(blockDir, `${name}.css`)) && !existsSync19(path22.join(blockDir, `${name}.js`))) {
|
|
3049
4082
|
logger.error(`Block "${name}" has no ${name}.css / ${name}.js to preview.`);
|
|
3050
4083
|
process.exitCode = 1;
|
|
3051
4084
|
return;
|
|
3052
4085
|
}
|
|
3053
4086
|
let meta = null;
|
|
3054
|
-
const metaPath =
|
|
3055
|
-
if (
|
|
4087
|
+
const metaPath = path22.join(blockDir, ".eds-meta.json");
|
|
4088
|
+
if (existsSync19(metaPath)) {
|
|
3056
4089
|
try {
|
|
3057
|
-
meta = JSON.parse(await
|
|
4090
|
+
meta = JSON.parse(await readFile14(metaPath, "utf-8"));
|
|
3058
4091
|
} catch {
|
|
3059
4092
|
}
|
|
3060
4093
|
}
|
|
3061
|
-
const samplePath =
|
|
4094
|
+
const samplePath = path22.join(blockDir, `_${name}.preview.html`);
|
|
3062
4095
|
let sampleContent;
|
|
3063
4096
|
let placeholder = false;
|
|
3064
|
-
if (
|
|
3065
|
-
sampleContent = await
|
|
4097
|
+
if (existsSync19(samplePath)) {
|
|
4098
|
+
sampleContent = await readFile14(samplePath, "utf-8");
|
|
3066
4099
|
} else {
|
|
3067
4100
|
sampleContent = defaultSample(name);
|
|
3068
4101
|
placeholder = true;
|
|
@@ -3112,9 +4145,9 @@ async function previewBlock(name, options = {}) {
|
|
|
3112
4145
|
reloadTimer = setTimeout(() => server.reload(), 150);
|
|
3113
4146
|
});
|
|
3114
4147
|
logger.info("");
|
|
3115
|
-
logger.info(` ${
|
|
4148
|
+
logger.info(` ${chalk9.bold("\u279C")} ${chalk9.cyan(server.url)}`);
|
|
3116
4149
|
logger.info(statusLine("pass", "live reload", "edits to the block refresh the browser"));
|
|
3117
|
-
logger.info(
|
|
4150
|
+
logger.info(chalk9.dim(" Press Ctrl+C to stop.\n"));
|
|
3118
4151
|
if (options.open !== false) await openInBrowser(server.url);
|
|
3119
4152
|
const stop = async () => {
|
|
3120
4153
|
watcher.close();
|
|
@@ -3143,10 +4176,10 @@ var from_design_exports = {};
|
|
|
3143
4176
|
__export(from_design_exports, {
|
|
3144
4177
|
blockFromDesign: () => blockFromDesign
|
|
3145
4178
|
});
|
|
3146
|
-
import { existsSync as
|
|
3147
|
-
import { readFile as
|
|
3148
|
-
import
|
|
3149
|
-
import
|
|
4179
|
+
import { existsSync as existsSync20 } from "fs";
|
|
4180
|
+
import { readFile as readFile15, writeFile as writeFile13 } from "fs/promises";
|
|
4181
|
+
import path23 from "path";
|
|
4182
|
+
import chalk10 from "chalk";
|
|
3150
4183
|
import ora from "ora";
|
|
3151
4184
|
async function previewRunning(port = 8777) {
|
|
3152
4185
|
try {
|
|
@@ -3296,7 +4329,7 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
3296
4329
|
try {
|
|
3297
4330
|
figma2 = parseFigmaUrl(designUrl);
|
|
3298
4331
|
spinner.succeed(
|
|
3299
|
-
`Parsed Figma URL ${
|
|
4332
|
+
`Parsed Figma URL ${chalk10.dim(figma2.nodeId ? `node ${figma2.nodeId}` : `file ${figma2.fileKey}`)}`
|
|
3300
4333
|
);
|
|
3301
4334
|
} catch (err) {
|
|
3302
4335
|
spinner.fail(err.message);
|
|
@@ -3304,7 +4337,7 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
3304
4337
|
return;
|
|
3305
4338
|
}
|
|
3306
4339
|
} else {
|
|
3307
|
-
logger.info(` ${icon.pass} ${providerName} design ${
|
|
4340
|
+
logger.info(` ${icon.pass} ${providerName} design ${chalk10.dim(designRef)}`);
|
|
3308
4341
|
}
|
|
3309
4342
|
const blockName = options.name || (figma2 ? inferBlockName(figma2.fileName) : "");
|
|
3310
4343
|
if (!kebabCaseRegex.test(blockName)) {
|
|
@@ -3327,20 +4360,20 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
3327
4360
|
screenshot: options.screenshot !== false
|
|
3328
4361
|
});
|
|
3329
4362
|
const promptFile = await savePromptToFile(prompt, projectRoot);
|
|
3330
|
-
const hasTokens =
|
|
4363
|
+
const hasTokens = existsSync20(path23.join(projectRoot, "styles", "styles.css"));
|
|
3331
4364
|
promptSpinner.succeed(
|
|
3332
|
-
hasTokens ? `Loaded design tokens ${
|
|
4365
|
+
hasTokens ? `Loaded design tokens ${chalk10.dim("styles/styles.css")}` : `Built prompt ${chalk10.dim("no project tokens found")}`
|
|
3333
4366
|
);
|
|
3334
|
-
logger.info(
|
|
4367
|
+
logger.info(chalk10.dim(` prompt \u2192 ${promptFile}`));
|
|
3335
4368
|
if (options.dryRun) {
|
|
3336
4369
|
logger.info("");
|
|
3337
|
-
logger.info(
|
|
3338
|
-
logger.info(`Prompt file: ${
|
|
3339
|
-
logger.info(`Block name: ${
|
|
4370
|
+
logger.info(chalk10.yellow("Dry run mode \u2014 prompt generated but agent not invoked."));
|
|
4371
|
+
logger.info(`Prompt file: ${chalk10.cyan(promptFile)}`);
|
|
4372
|
+
logger.info(`Block name: ${chalk10.cyan(blockName)}`);
|
|
3340
4373
|
logger.info(`Prompt version: ${PROMPT_VERSION}`);
|
|
3341
4374
|
logger.info("");
|
|
3342
4375
|
logger.info("Preview:");
|
|
3343
|
-
logger.info(
|
|
4376
|
+
logger.info(chalk10.dim("\u2500".repeat(60)));
|
|
3344
4377
|
console.log(prompt);
|
|
3345
4378
|
return;
|
|
3346
4379
|
}
|
|
@@ -3348,22 +4381,22 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
3348
4381
|
const preferredAgent = options.agent || config.agent;
|
|
3349
4382
|
if (options.agent === "none") {
|
|
3350
4383
|
logger.info("Agent skipped (--agent none). Use the prompt file manually:");
|
|
3351
|
-
logger.info(` ${
|
|
4384
|
+
logger.info(` ${chalk10.cyan(promptFile)}`);
|
|
3352
4385
|
return;
|
|
3353
4386
|
}
|
|
3354
4387
|
const agentSpinner = ora("Detecting AI agent...").start();
|
|
3355
4388
|
const agent = await detectAgent(preferredAgent);
|
|
3356
4389
|
if (!agent) {
|
|
3357
4390
|
agentSpinner.fail("No AI agent found (claude, cursor, or codex).");
|
|
3358
|
-
logger.info(`Install Claude Code: ${
|
|
3359
|
-
logger.info(`Or use the prompt file manually: ${
|
|
4391
|
+
logger.info(`Install Claude Code: ${chalk10.cyan("npm install -g @anthropic-ai/claude-code")}`);
|
|
4392
|
+
logger.info(`Or use the prompt file manually: ${chalk10.cyan(promptFile)}`);
|
|
3360
4393
|
process.exitCode = 1;
|
|
3361
4394
|
return;
|
|
3362
4395
|
}
|
|
3363
|
-
agentSpinner.succeed(`Agent ready ${
|
|
4396
|
+
agentSpinner.succeed(`Agent ready ${chalk10.cyan(agent.type)} ${chalk10.dim(agent.path)}`);
|
|
3364
4397
|
if (provider === "figma" && !agent.hasFigmaMcp) {
|
|
3365
4398
|
logger.warn(`Figma MCP may not be configured for ${agent.type}.`);
|
|
3366
|
-
logger.info(`Run ${
|
|
4399
|
+
logger.info(`Run ${chalk10.cyan("eds figma setup")} to configure it.`);
|
|
3367
4400
|
}
|
|
3368
4401
|
if (!options.yes) {
|
|
3369
4402
|
const { confirm } = await import("@inquirer/prompts");
|
|
@@ -3372,24 +4405,24 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
3372
4405
|
default: true
|
|
3373
4406
|
});
|
|
3374
4407
|
if (!proceed) {
|
|
3375
|
-
logger.info(`Prompt saved to: ${
|
|
4408
|
+
logger.info(`Prompt saved to: ${chalk10.cyan(promptFile)}`);
|
|
3376
4409
|
return;
|
|
3377
4410
|
}
|
|
3378
4411
|
}
|
|
3379
4412
|
logger.info(
|
|
3380
|
-
` ${
|
|
4413
|
+
` ${chalk10.dim("\u2192")} Handing off to ${chalk10.cyan(agent.type)} ${chalk10.dim(`via ${providerName} MCP`)}`
|
|
3381
4414
|
);
|
|
3382
4415
|
if (provider === "figma") {
|
|
3383
|
-
logger.info(
|
|
4416
|
+
logger.info(chalk10.dim(" get_design_context \xB7 get_variable_defs \xB7 get_screenshot"));
|
|
3384
4417
|
}
|
|
3385
4418
|
const success = await invokeAgent(agent, promptFile, projectRoot);
|
|
3386
4419
|
if (!success) {
|
|
3387
4420
|
process.exitCode = 1;
|
|
3388
4421
|
return;
|
|
3389
4422
|
}
|
|
3390
|
-
const blockDir =
|
|
4423
|
+
const blockDir = path23.join(projectRoot, "blocks", blockName);
|
|
3391
4424
|
const ok = await verifyGeneratedBlock(blockDir, blockName, options.ueModel !== false);
|
|
3392
|
-
if (
|
|
4425
|
+
if (existsSync20(blockDir)) {
|
|
3393
4426
|
const meta = {
|
|
3394
4427
|
provider,
|
|
3395
4428
|
designRef,
|
|
@@ -3399,7 +4432,7 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
3399
4432
|
promptVersion: PROMPT_VERSION,
|
|
3400
4433
|
agentUsed: agent.type
|
|
3401
4434
|
};
|
|
3402
|
-
await
|
|
4435
|
+
await writeFile13(path23.join(blockDir, ".eds-meta.json"), `${JSON.stringify(meta, null, 2)}
|
|
3403
4436
|
`);
|
|
3404
4437
|
}
|
|
3405
4438
|
if (!ok) {
|
|
@@ -3424,7 +4457,7 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
3424
4457
|
if (options.serve !== false) {
|
|
3425
4458
|
if (await previewRunning()) {
|
|
3426
4459
|
logger.info(
|
|
3427
|
-
|
|
4460
|
+
chalk10.dim(
|
|
3428
4461
|
" A preview server is already running at http://127.0.0.1:8777 \u2014 your change will refresh there."
|
|
3429
4462
|
)
|
|
3430
4463
|
);
|
|
@@ -3434,15 +4467,15 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
3434
4467
|
await previewBlock2(blockName, {});
|
|
3435
4468
|
}
|
|
3436
4469
|
} else {
|
|
3437
|
-
logger.info(` ${
|
|
4470
|
+
logger.info(` ${chalk10.dim("\u2192")} Next: ${chalk10.cyan(`eds block preview ${blockName}`)}`);
|
|
3438
4471
|
}
|
|
3439
4472
|
}
|
|
3440
4473
|
async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
3441
4474
|
const checks = [];
|
|
3442
|
-
const jsPath =
|
|
3443
|
-
const cssPath =
|
|
3444
|
-
const hasJs =
|
|
3445
|
-
const hasCss =
|
|
4475
|
+
const jsPath = path23.join(blockDir, `${blockName}.js`);
|
|
4476
|
+
const cssPath = path23.join(blockDir, `${blockName}.css`);
|
|
4477
|
+
const hasJs = existsSync20(jsPath);
|
|
4478
|
+
const hasCss = existsSync20(cssPath);
|
|
3446
4479
|
checks.push({
|
|
3447
4480
|
status: hasJs ? "pass" : "fail",
|
|
3448
4481
|
name: `${blockName}.js`,
|
|
@@ -3454,7 +4487,7 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
|
3454
4487
|
message: hasCss ? "created" : "missing \u2014 agent did not write the CSS file"
|
|
3455
4488
|
});
|
|
3456
4489
|
if (hasJs) {
|
|
3457
|
-
const js = await
|
|
4490
|
+
const js = await readFile15(jsPath, "utf-8");
|
|
3458
4491
|
checks.push({
|
|
3459
4492
|
status: /export\s+default/.test(js) ? "pass" : "fail",
|
|
3460
4493
|
name: "decorate export",
|
|
@@ -3469,7 +4502,7 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
|
3469
4502
|
}
|
|
3470
4503
|
}
|
|
3471
4504
|
if (hasCss) {
|
|
3472
|
-
const css = await
|
|
4505
|
+
const css = await readFile15(cssPath, "utf-8");
|
|
3473
4506
|
checks.push({
|
|
3474
4507
|
status: css.includes(`.${blockName}`) ? "pass" : "warn",
|
|
3475
4508
|
name: "scoped CSS",
|
|
@@ -3477,12 +4510,12 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
|
3477
4510
|
});
|
|
3478
4511
|
}
|
|
3479
4512
|
if (withUeModel) {
|
|
3480
|
-
const hasPerBlock =
|
|
3481
|
-
const modelsFile =
|
|
4513
|
+
const hasPerBlock = existsSync20(path23.join(blockDir, `_${blockName}.json`));
|
|
4514
|
+
const modelsFile = path23.join(blockDir, "..", "..", "component-models.json");
|
|
3482
4515
|
let hasAggregated = false;
|
|
3483
|
-
if (!hasPerBlock &&
|
|
4516
|
+
if (!hasPerBlock && existsSync20(modelsFile)) {
|
|
3484
4517
|
try {
|
|
3485
|
-
hasAggregated = (await
|
|
4518
|
+
hasAggregated = (await readFile15(modelsFile, "utf-8")).includes(`"${blockName}"`);
|
|
3486
4519
|
} catch {
|
|
3487
4520
|
}
|
|
3488
4521
|
}
|
|
@@ -3494,8 +4527,8 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
|
3494
4527
|
});
|
|
3495
4528
|
if (hasPerBlock && hasJs) {
|
|
3496
4529
|
try {
|
|
3497
|
-
const modelRaw = await
|
|
3498
|
-
const js = await
|
|
4530
|
+
const modelRaw = await readFile15(path23.join(blockDir, `_${blockName}.json`), "utf-8");
|
|
4531
|
+
const js = await readFile15(jsPath, "utf-8");
|
|
3499
4532
|
const isRepeatable = /"resourceType"\s*:\s*"[^"]*\/item"/.test(modelRaw) || /"filter"\s*:/.test(modelRaw) || /"multi"\s*:\s*true/.test(modelRaw);
|
|
3500
4533
|
const iterates = /\.children/.test(js) || /\bfor\s*\(/.test(js) || /\.forEach\s*\(/.test(js);
|
|
3501
4534
|
if (isRepeatable) {
|
|
@@ -3538,10 +4571,10 @@ var list_exports = {};
|
|
|
3538
4571
|
__export(list_exports, {
|
|
3539
4572
|
listBlocks: () => listBlocks
|
|
3540
4573
|
});
|
|
3541
|
-
import { existsSync as
|
|
3542
|
-
import { readFile as
|
|
3543
|
-
import
|
|
3544
|
-
import
|
|
4574
|
+
import { existsSync as existsSync21 } from "fs";
|
|
4575
|
+
import { readFile as readFile16, readdir as readdir3 } from "fs/promises";
|
|
4576
|
+
import path24 from "path";
|
|
4577
|
+
import chalk11 from "chalk";
|
|
3545
4578
|
async function listBlocks(options = {}) {
|
|
3546
4579
|
const projectRoot = findProjectRoot();
|
|
3547
4580
|
if (!projectRoot) {
|
|
@@ -3549,27 +4582,27 @@ async function listBlocks(options = {}) {
|
|
|
3549
4582
|
process.exitCode = 1;
|
|
3550
4583
|
return;
|
|
3551
4584
|
}
|
|
3552
|
-
const blocksDir =
|
|
3553
|
-
if (!
|
|
4585
|
+
const blocksDir = path24.join(projectRoot, "blocks");
|
|
4586
|
+
if (!existsSync21(blocksDir)) {
|
|
3554
4587
|
logger.info("No blocks/ directory found.");
|
|
3555
4588
|
return;
|
|
3556
4589
|
}
|
|
3557
|
-
const entries = await
|
|
4590
|
+
const entries = await readdir3(blocksDir, { withFileTypes: true });
|
|
3558
4591
|
const blocks = [];
|
|
3559
4592
|
for (const entry of entries) {
|
|
3560
4593
|
if (!entry.isDirectory()) continue;
|
|
3561
4594
|
const name = entry.name;
|
|
3562
|
-
const blockDir =
|
|
4595
|
+
const blockDir = path24.join(blocksDir, name);
|
|
3563
4596
|
const info = {
|
|
3564
4597
|
name,
|
|
3565
|
-
hasJs:
|
|
3566
|
-
hasCss:
|
|
3567
|
-
hasUeModel:
|
|
4598
|
+
hasJs: existsSync21(path24.join(blockDir, `${name}.js`)),
|
|
4599
|
+
hasCss: existsSync21(path24.join(blockDir, `${name}.css`)),
|
|
4600
|
+
hasUeModel: existsSync21(path24.join(blockDir, `_${name}.json`))
|
|
3568
4601
|
};
|
|
3569
|
-
const metaFile =
|
|
3570
|
-
if (
|
|
4602
|
+
const metaFile = path24.join(blockDir, ".eds-meta.json");
|
|
4603
|
+
if (existsSync21(metaFile)) {
|
|
3571
4604
|
try {
|
|
3572
|
-
const meta = JSON.parse(await
|
|
4605
|
+
const meta = JSON.parse(await readFile16(metaFile, "utf-8"));
|
|
3573
4606
|
info.figmaSource = {
|
|
3574
4607
|
fileKey: meta.figmaFileKey,
|
|
3575
4608
|
nodeId: meta.figmaNodeId,
|
|
@@ -3592,17 +4625,17 @@ async function listBlocks(options = {}) {
|
|
|
3592
4625
|
const nameWidth = columnWidth(blocks.map((b) => b.name));
|
|
3593
4626
|
for (const b of blocks) {
|
|
3594
4627
|
const badges = [
|
|
3595
|
-
b.hasJs ? brand.ok("js") :
|
|
3596
|
-
b.hasCss ? brand.ok("css") :
|
|
4628
|
+
b.hasJs ? brand.ok("js") : chalk11.dim.strikethrough("js"),
|
|
4629
|
+
b.hasCss ? brand.ok("css") : chalk11.dim.strikethrough("css")
|
|
3597
4630
|
];
|
|
3598
4631
|
if (b.hasUeModel) badges.push(brand.blue("ue-model"));
|
|
3599
4632
|
if (b.figmaSource) badges.push(`${icon.figma} ${brand.dotPurple("figma")}`);
|
|
3600
|
-
const name = padEndVisible(
|
|
3601
|
-
logger.info(` ${name} ${
|
|
4633
|
+
const name = padEndVisible(chalk11.bold(b.name), nameWidth);
|
|
4634
|
+
logger.info(` ${name} ${chalk11.dim("[")}${badges.join(" ")}${chalk11.dim("]")}`);
|
|
3602
4635
|
if (b.figmaSource) {
|
|
3603
4636
|
const synced = new Date(b.figmaSource.lastSyncedAt).toLocaleDateString();
|
|
3604
4637
|
logger.info(
|
|
3605
|
-
` ${" ".repeat(nameWidth)} ${icon.arrow} ${
|
|
4638
|
+
` ${" ".repeat(nameWidth)} ${icon.arrow} ${chalk11.dim(
|
|
3606
4639
|
`figma:${b.figmaSource.fileKey} \xB7 synced ${synced}`
|
|
3607
4640
|
)}`
|
|
3608
4641
|
);
|
|
@@ -3782,10 +4815,10 @@ var integrate_exports = {};
|
|
|
3782
4815
|
__export(integrate_exports, {
|
|
3783
4816
|
integrate: () => integrate
|
|
3784
4817
|
});
|
|
3785
|
-
import { existsSync as
|
|
3786
|
-
import { mkdir as
|
|
3787
|
-
import
|
|
3788
|
-
import
|
|
4818
|
+
import { existsSync as existsSync22 } from "fs";
|
|
4819
|
+
import { mkdir as mkdir8, readFile as readFile17, writeFile as writeFile14 } from "fs/promises";
|
|
4820
|
+
import path25 from "path";
|
|
4821
|
+
import chalk12 from "chalk";
|
|
3789
4822
|
async function integrate(type) {
|
|
3790
4823
|
const projectRoot = findProjectRoot();
|
|
3791
4824
|
if (!projectRoot) {
|
|
@@ -3808,16 +4841,16 @@ async function integrate(type) {
|
|
|
3808
4841
|
return;
|
|
3809
4842
|
}
|
|
3810
4843
|
const values = {};
|
|
3811
|
-
for (const
|
|
3812
|
-
const answer = await input({ message: ` ${
|
|
3813
|
-
values[
|
|
4844
|
+
for (const f2 of integration.fields) {
|
|
4845
|
+
const answer = await input({ message: ` ${f2.message}`, default: f2.placeholder });
|
|
4846
|
+
values[f2.name] = answer.trim();
|
|
3814
4847
|
}
|
|
3815
4848
|
const modulePaths = [];
|
|
3816
4849
|
if (integration.module) {
|
|
3817
4850
|
for (const mod of integration.module(values)) {
|
|
3818
|
-
const abs =
|
|
3819
|
-
await
|
|
3820
|
-
await
|
|
4851
|
+
const abs = path25.join(projectRoot, mod.file);
|
|
4852
|
+
await mkdir8(path25.dirname(abs), { recursive: true });
|
|
4853
|
+
await writeFile14(abs, mod.content);
|
|
3821
4854
|
modulePaths.push(mod.file);
|
|
3822
4855
|
}
|
|
3823
4856
|
}
|
|
@@ -3825,26 +4858,26 @@ async function integrate(type) {
|
|
|
3825
4858
|
// ${integration.name} (eds integrate)
|
|
3826
4859
|
${integration.code(values)}
|
|
3827
4860
|
` : wrapBlock(integration.name, integration.code(values));
|
|
3828
|
-
const delayedPath =
|
|
3829
|
-
await
|
|
3830
|
-
const existing =
|
|
3831
|
-
await
|
|
4861
|
+
const delayedPath = path25.join(projectRoot, "scripts", "delayed.js");
|
|
4862
|
+
await mkdir8(path25.dirname(delayedPath), { recursive: true });
|
|
4863
|
+
const existing = existsSync22(delayedPath) ? await readFile17(delayedPath, "utf-8") : DELAYED_HEADER;
|
|
4864
|
+
await writeFile14(delayedPath, `${existing.replace(/\s*$/, "")}
|
|
3832
4865
|
${block2}`);
|
|
3833
4866
|
logger.info(heading("Added"));
|
|
3834
4867
|
for (const p of modulePaths) logger.info(accentLine(integration.name, `wrote ${p}`));
|
|
3835
4868
|
logger.info(accentLine(integration.name, "wired into scripts/delayed.js"));
|
|
3836
4869
|
logger.info("");
|
|
3837
4870
|
logger.info(
|
|
3838
|
-
|
|
4871
|
+
chalk12.dim(" Loads after LCP (delayed phase) - zero impact on your Core Web Vitals.")
|
|
3839
4872
|
);
|
|
3840
4873
|
if (integration.id === "gtm" || integration.id === "ga4") {
|
|
3841
4874
|
logger.info(
|
|
3842
|
-
|
|
4875
|
+
chalk12.dim(" Tip: gate analytics behind user consent (add the Cookiebot integration).")
|
|
3843
4876
|
);
|
|
3844
4877
|
}
|
|
3845
4878
|
if (integration.id === "chat") {
|
|
3846
4879
|
logger.info(
|
|
3847
|
-
|
|
4880
|
+
chalk12.dim(" The widget loads only when the visitor clicks the button (facade pattern).")
|
|
3848
4881
|
);
|
|
3849
4882
|
}
|
|
3850
4883
|
logger.info("");
|
|
@@ -3870,9 +4903,9 @@ var setup_exports = {};
|
|
|
3870
4903
|
__export(setup_exports, {
|
|
3871
4904
|
figmaSetup: () => figmaSetup
|
|
3872
4905
|
});
|
|
3873
|
-
import { existsSync as
|
|
3874
|
-
import { mkdir as
|
|
3875
|
-
import
|
|
4906
|
+
import { existsSync as existsSync23 } from "fs";
|
|
4907
|
+
import { mkdir as mkdir9, writeFile as writeFile15 } from "fs/promises";
|
|
4908
|
+
import chalk13 from "chalk";
|
|
3876
4909
|
async function figmaSetup() {
|
|
3877
4910
|
const { select, confirm } = await import("@inquirer/prompts");
|
|
3878
4911
|
logger.logoOnce(logo());
|
|
@@ -3882,15 +4915,15 @@ async function figmaSetup() {
|
|
|
3882
4915
|
let selectedAgent;
|
|
3883
4916
|
if (agents.length === 0) {
|
|
3884
4917
|
logger.warn("No AI agents found (claude, cursor, codex).");
|
|
3885
|
-
logger.info(`Install Claude Code: ${
|
|
4918
|
+
logger.info(`Install Claude Code: ${chalk13.cyan("npm install -g @anthropic-ai/claude-code")}`);
|
|
3886
4919
|
selectedAgent = "none";
|
|
3887
4920
|
} else {
|
|
3888
|
-
logger.success(`Found: ${agents.map((a) =>
|
|
4921
|
+
logger.success(`Found: ${agents.map((a) => chalk13.cyan(a.type)).join(", ")}`);
|
|
3889
4922
|
selectedAgent = await select({
|
|
3890
4923
|
message: "Which agent do you want to use with eds-cli?",
|
|
3891
4924
|
choices: [
|
|
3892
4925
|
...agents.map((a) => ({
|
|
3893
|
-
name: `${a.type}${a.hasFigmaMcp ?
|
|
4926
|
+
name: `${a.type}${a.hasFigmaMcp ? chalk13.green(" (Figma MCP detected)") : ""}`,
|
|
3894
4927
|
value: a.type
|
|
3895
4928
|
})),
|
|
3896
4929
|
{ name: "None (manual mode)", value: "none" }
|
|
@@ -3906,17 +4939,17 @@ async function figmaSetup() {
|
|
|
3906
4939
|
switch (selectedAgent) {
|
|
3907
4940
|
case "claude":
|
|
3908
4941
|
logger.info(
|
|
3909
|
-
` ${
|
|
4942
|
+
` ${chalk13.cyan("claude mcp add --transport sse figma-sse https://mcp.figma.com/sse")}`
|
|
3910
4943
|
);
|
|
3911
4944
|
logger.info(" or for Figma Desktop app:");
|
|
3912
4945
|
logger.info(
|
|
3913
|
-
` ${
|
|
4946
|
+
` ${chalk13.cyan("claude mcp add --transport http figma-desktop http://127.0.0.1:3845/mcp")}`
|
|
3914
4947
|
);
|
|
3915
4948
|
break;
|
|
3916
4949
|
case "cursor":
|
|
3917
|
-
logger.info(` Add to ${
|
|
4950
|
+
logger.info(` Add to ${chalk13.cyan("~/.cursor/mcp.json")}:`);
|
|
3918
4951
|
logger.info(
|
|
3919
|
-
|
|
4952
|
+
chalk13.dim(
|
|
3920
4953
|
JSON.stringify(
|
|
3921
4954
|
{
|
|
3922
4955
|
mcpServers: {
|
|
@@ -3941,7 +4974,7 @@ async function figmaSetup() {
|
|
|
3941
4974
|
}
|
|
3942
4975
|
}
|
|
3943
4976
|
logger.info(
|
|
3944
|
-
|
|
4977
|
+
chalk13.dim(
|
|
3945
4978
|
"\nNote: Figma MCP requires a Figma seat (Dev or Full on paid plans for desktop server)."
|
|
3946
4979
|
)
|
|
3947
4980
|
);
|
|
@@ -3951,22 +4984,22 @@ async function figmaSetup() {
|
|
|
3951
4984
|
});
|
|
3952
4985
|
if (shouldSave) {
|
|
3953
4986
|
const configDir = globalConfigDir();
|
|
3954
|
-
if (!
|
|
3955
|
-
await
|
|
4987
|
+
if (!existsSync23(configDir)) {
|
|
4988
|
+
await mkdir9(configDir, { recursive: true });
|
|
3956
4989
|
}
|
|
3957
4990
|
const config = {};
|
|
3958
4991
|
if (selectedAgent !== "none") {
|
|
3959
4992
|
config.agent = selectedAgent;
|
|
3960
4993
|
}
|
|
3961
|
-
await
|
|
4994
|
+
await writeFile15(globalConfigPath(), `${JSON.stringify(config, null, 2)}
|
|
3962
4995
|
`, {
|
|
3963
4996
|
mode: 384
|
|
3964
4997
|
});
|
|
3965
|
-
logger.success(`Config saved to ${
|
|
4998
|
+
logger.success(`Config saved to ${chalk13.cyan(globalConfigPath())}`);
|
|
3966
4999
|
}
|
|
3967
|
-
logger.info(
|
|
5000
|
+
logger.info(chalk13.bold("\nSetup complete!"));
|
|
3968
5001
|
if (selectedAgent !== "none") {
|
|
3969
|
-
logger.info(`Try: ${
|
|
5002
|
+
logger.info(`Try: ${chalk13.cyan('eds block from-design "<figma-url>" --dry-run')}`);
|
|
3970
5003
|
}
|
|
3971
5004
|
}
|
|
3972
5005
|
var init_setup = __esm({
|
|
@@ -3985,10 +5018,10 @@ var doctor_exports = {};
|
|
|
3985
5018
|
__export(doctor_exports, {
|
|
3986
5019
|
doctor: () => doctor
|
|
3987
5020
|
});
|
|
3988
|
-
import { existsSync as
|
|
3989
|
-
import { readFile as
|
|
3990
|
-
import
|
|
3991
|
-
import { parse as
|
|
5021
|
+
import { existsSync as existsSync24 } from "fs";
|
|
5022
|
+
import { readFile as readFile18, readdir as readdir4 } from "fs/promises";
|
|
5023
|
+
import path26 from "path";
|
|
5024
|
+
import { parse as parseYaml2 } from "yaml";
|
|
3992
5025
|
async function doctor(options = {}) {
|
|
3993
5026
|
const projectRoot = findProjectRoot();
|
|
3994
5027
|
if (!projectRoot) {
|
|
@@ -4009,8 +5042,8 @@ async function doctor(options = {}) {
|
|
|
4009
5042
|
});
|
|
4010
5043
|
if (project.hasFstab) {
|
|
4011
5044
|
try {
|
|
4012
|
-
const content = await
|
|
4013
|
-
|
|
5045
|
+
const content = await readFile18(path26.join(projectRoot, "fstab.yaml"), "utf-8");
|
|
5046
|
+
parseYaml2(content);
|
|
4014
5047
|
results.push({ name: "fstab.yaml valid YAML", status: "pass", message: "Valid" });
|
|
4015
5048
|
} catch {
|
|
4016
5049
|
results.push({
|
|
@@ -4031,28 +5064,28 @@ async function doctor(options = {}) {
|
|
|
4031
5064
|
message: project.hasBlocks ? "Found" : "Missing - no blocks yet"
|
|
4032
5065
|
});
|
|
4033
5066
|
if (project.hasBlocks) {
|
|
4034
|
-
const blocksDir =
|
|
4035
|
-
const entries = await
|
|
5067
|
+
const blocksDir = path26.join(projectRoot, "blocks");
|
|
5068
|
+
const entries = await readdir4(blocksDir, { withFileTypes: true });
|
|
4036
5069
|
let aggregatedModels = "";
|
|
4037
|
-
const aggregatedPath =
|
|
4038
|
-
if (
|
|
5070
|
+
const aggregatedPath = path26.join(projectRoot, "component-models.json");
|
|
5071
|
+
if (existsSync24(aggregatedPath)) {
|
|
4039
5072
|
try {
|
|
4040
|
-
aggregatedModels = await
|
|
5073
|
+
aggregatedModels = await readFile18(aggregatedPath, "utf-8");
|
|
4041
5074
|
} catch {
|
|
4042
5075
|
}
|
|
4043
5076
|
}
|
|
4044
5077
|
for (const entry of entries) {
|
|
4045
5078
|
if (!entry.isDirectory()) continue;
|
|
4046
5079
|
const blockName = entry.name;
|
|
4047
|
-
const jsFile =
|
|
4048
|
-
const hasJs =
|
|
5080
|
+
const jsFile = path26.join(blocksDir, blockName, `${blockName}.js`);
|
|
5081
|
+
const hasJs = existsSync24(jsFile);
|
|
4049
5082
|
results.push({
|
|
4050
5083
|
name: `blocks/${blockName} has JS`,
|
|
4051
5084
|
status: hasJs ? "pass" : "warn",
|
|
4052
5085
|
message: hasJs ? "Found" : `Missing ${blockName}.js`
|
|
4053
5086
|
});
|
|
4054
5087
|
if (hasJs) {
|
|
4055
|
-
const jsContent = await
|
|
5088
|
+
const jsContent = await readFile18(jsFile, "utf-8");
|
|
4056
5089
|
if (jsContent.includes("console.log")) {
|
|
4057
5090
|
results.push({
|
|
4058
5091
|
name: `blocks/${blockName} no console.log`,
|
|
@@ -4061,17 +5094,17 @@ async function doctor(options = {}) {
|
|
|
4061
5094
|
});
|
|
4062
5095
|
}
|
|
4063
5096
|
}
|
|
4064
|
-
const hasPerBlockModel =
|
|
5097
|
+
const hasPerBlockModel = existsSync24(path26.join(blocksDir, blockName, `_${blockName}.json`));
|
|
4065
5098
|
const hasAggregatedModel = aggregatedModels.includes(`"${blockName}"`);
|
|
4066
5099
|
results.push({
|
|
4067
5100
|
name: `blocks/${blockName} UE model`,
|
|
4068
5101
|
status: hasPerBlockModel || hasAggregatedModel ? "pass" : "warn",
|
|
4069
5102
|
message: hasPerBlockModel || hasAggregatedModel ? hasPerBlockModel ? `_${blockName}.json` : "in component-models.json" : "no Universal Editor model - authors can't edit this block in UE"
|
|
4070
5103
|
});
|
|
4071
|
-
const metaFile =
|
|
4072
|
-
if (
|
|
5104
|
+
const metaFile = path26.join(blocksDir, blockName, ".eds-meta.json");
|
|
5105
|
+
if (existsSync24(metaFile)) {
|
|
4073
5106
|
try {
|
|
4074
|
-
const meta = JSON.parse(await
|
|
5107
|
+
const meta = JSON.parse(await readFile18(metaFile, "utf-8"));
|
|
4075
5108
|
const syncDate = new Date(meta.lastSyncedAt);
|
|
4076
5109
|
const daysSinceSync = Math.floor(
|
|
4077
5110
|
(Date.now() - syncDate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
@@ -4091,11 +5124,11 @@ async function doctor(options = {}) {
|
|
|
4091
5124
|
}
|
|
4092
5125
|
}
|
|
4093
5126
|
}
|
|
4094
|
-
const queryFile =
|
|
4095
|
-
if (
|
|
5127
|
+
const queryFile = path26.join(projectRoot, "helix-query.yaml");
|
|
5128
|
+
if (existsSync24(queryFile)) {
|
|
4096
5129
|
try {
|
|
4097
|
-
const content = await
|
|
4098
|
-
|
|
5130
|
+
const content = await readFile18(queryFile, "utf-8");
|
|
5131
|
+
parseYaml2(content);
|
|
4099
5132
|
results.push({ name: "helix-query.yaml valid", status: "pass", message: "Valid YAML" });
|
|
4100
5133
|
} catch {
|
|
4101
5134
|
results.push({
|
|
@@ -4151,10 +5184,10 @@ var audit_loading_exports = {};
|
|
|
4151
5184
|
__export(audit_loading_exports, {
|
|
4152
5185
|
auditLoading: () => auditLoading
|
|
4153
5186
|
});
|
|
4154
|
-
import { existsSync as
|
|
4155
|
-
import { readFile as
|
|
4156
|
-
import
|
|
4157
|
-
import
|
|
5187
|
+
import { existsSync as existsSync25 } from "fs";
|
|
5188
|
+
import { readFile as readFile19, readdir as readdir5, stat as stat3 } from "fs/promises";
|
|
5189
|
+
import path27 from "path";
|
|
5190
|
+
import chalk14 from "chalk";
|
|
4158
5191
|
async function auditLoading(options = {}) {
|
|
4159
5192
|
const projectRoot = findProjectRoot();
|
|
4160
5193
|
if (!projectRoot) {
|
|
@@ -4187,8 +5220,8 @@ async function auditLoading(options = {}) {
|
|
|
4187
5220
|
printResults(results);
|
|
4188
5221
|
}
|
|
4189
5222
|
async function checkHeadHtml(root, results) {
|
|
4190
|
-
const filePath =
|
|
4191
|
-
if (!
|
|
5223
|
+
const filePath = path27.join(root, "head.html");
|
|
5224
|
+
if (!existsSync25(filePath)) {
|
|
4192
5225
|
results.push({
|
|
4193
5226
|
rule: "head.html exists",
|
|
4194
5227
|
status: "warn",
|
|
@@ -4196,7 +5229,7 @@ async function checkHeadHtml(root, results) {
|
|
|
4196
5229
|
});
|
|
4197
5230
|
return;
|
|
4198
5231
|
}
|
|
4199
|
-
const content = await
|
|
5232
|
+
const content = await readFile19(filePath, "utf-8");
|
|
4200
5233
|
const lines = content.split("\n");
|
|
4201
5234
|
for (let i = 0; i < lines.length; i++) {
|
|
4202
5235
|
const line = lines[i];
|
|
@@ -4268,8 +5301,8 @@ async function checkHeadHtml(root, results) {
|
|
|
4268
5301
|
}
|
|
4269
5302
|
}
|
|
4270
5303
|
async function checkScriptsJs(root, results) {
|
|
4271
|
-
const filePath =
|
|
4272
|
-
if (!
|
|
5304
|
+
const filePath = path27.join(root, "scripts", "scripts.js");
|
|
5305
|
+
if (!existsSync25(filePath)) {
|
|
4273
5306
|
results.push({
|
|
4274
5307
|
rule: "scripts/scripts.js exists",
|
|
4275
5308
|
status: "warn",
|
|
@@ -4277,7 +5310,7 @@ async function checkScriptsJs(root, results) {
|
|
|
4277
5310
|
});
|
|
4278
5311
|
return;
|
|
4279
5312
|
}
|
|
4280
|
-
const content = await
|
|
5313
|
+
const content = await readFile19(filePath, "utf-8");
|
|
4281
5314
|
const lines = content.split("\n");
|
|
4282
5315
|
const hasLoadEager = /function\s+loadEager|loadEager\s*=/i.test(content);
|
|
4283
5316
|
const hasLoadLazy = /function\s+loadLazy|loadLazy\s*=/i.test(content);
|
|
@@ -4355,8 +5388,8 @@ async function checkScriptsJs(root, results) {
|
|
|
4355
5388
|
}
|
|
4356
5389
|
}
|
|
4357
5390
|
async function checkStylesCss(root, results) {
|
|
4358
|
-
const filePath =
|
|
4359
|
-
if (!
|
|
5391
|
+
const filePath = path27.join(root, "styles", "styles.css");
|
|
5392
|
+
if (!existsSync25(filePath)) return;
|
|
4360
5393
|
const stats = await stat3(filePath);
|
|
4361
5394
|
const sizeKb = stats.size / 1024;
|
|
4362
5395
|
results.push({
|
|
@@ -4365,7 +5398,7 @@ async function checkStylesCss(root, results) {
|
|
|
4365
5398
|
message: `${sizeKb.toFixed(1)} KB${sizeKb > 40 ? " \u2014 large CSS competes with 100KB LCP budget" : ""}`,
|
|
4366
5399
|
file: "styles/styles.css"
|
|
4367
5400
|
});
|
|
4368
|
-
const content = await
|
|
5401
|
+
const content = await readFile19(filePath, "utf-8");
|
|
4369
5402
|
if (/@import\s/.test(content)) {
|
|
4370
5403
|
results.push({
|
|
4371
5404
|
rule: "No @import in styles.css",
|
|
@@ -4376,8 +5409,8 @@ async function checkStylesCss(root, results) {
|
|
|
4376
5409
|
}
|
|
4377
5410
|
}
|
|
4378
5411
|
async function checkDelayedJs(root, results) {
|
|
4379
|
-
const filePath =
|
|
4380
|
-
if (!
|
|
5412
|
+
const filePath = path27.join(root, "scripts", "delayed.js");
|
|
5413
|
+
if (!existsSync25(filePath)) {
|
|
4381
5414
|
results.push({
|
|
4382
5415
|
rule: "scripts/delayed.js exists",
|
|
4383
5416
|
status: "info",
|
|
@@ -4385,7 +5418,7 @@ async function checkDelayedJs(root, results) {
|
|
|
4385
5418
|
});
|
|
4386
5419
|
return;
|
|
4387
5420
|
}
|
|
4388
|
-
const content = await
|
|
5421
|
+
const content = await readFile19(filePath, "utf-8");
|
|
4389
5422
|
const stripped = content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").trim();
|
|
4390
5423
|
if (!stripped) {
|
|
4391
5424
|
results.push({
|
|
@@ -4397,15 +5430,15 @@ async function checkDelayedJs(root, results) {
|
|
|
4397
5430
|
}
|
|
4398
5431
|
}
|
|
4399
5432
|
async function checkBlocks(root, results) {
|
|
4400
|
-
const blocksDir =
|
|
4401
|
-
if (!
|
|
4402
|
-
const entries = await
|
|
5433
|
+
const blocksDir = path27.join(root, "blocks");
|
|
5434
|
+
if (!existsSync25(blocksDir)) return;
|
|
5435
|
+
const entries = await readdir5(blocksDir, { withFileTypes: true });
|
|
4403
5436
|
for (const entry of entries) {
|
|
4404
5437
|
if (!entry.isDirectory()) continue;
|
|
4405
5438
|
const blockName = entry.name;
|
|
4406
|
-
const jsFile =
|
|
4407
|
-
if (!
|
|
4408
|
-
const content = await
|
|
5439
|
+
const jsFile = path27.join(blocksDir, blockName, `${blockName}.js`);
|
|
5440
|
+
if (!existsSync25(jsFile)) continue;
|
|
5441
|
+
const content = await readFile19(jsFile, "utf-8");
|
|
4409
5442
|
const lines = content.split("\n");
|
|
4410
5443
|
const heavyLibs = [
|
|
4411
5444
|
"jquery",
|
|
@@ -4455,9 +5488,9 @@ async function checkBlocks(root, results) {
|
|
|
4455
5488
|
for (const entry of entries) {
|
|
4456
5489
|
if (!entry.isDirectory()) continue;
|
|
4457
5490
|
const blockName = entry.name;
|
|
4458
|
-
const cssFile =
|
|
4459
|
-
if (!
|
|
4460
|
-
const content = await
|
|
5491
|
+
const cssFile = path27.join(blocksDir, blockName, `${blockName}.css`);
|
|
5492
|
+
if (!existsSync25(cssFile)) continue;
|
|
5493
|
+
const content = await readFile19(cssFile, "utf-8");
|
|
4461
5494
|
const selectorPattern = /^([a-z][a-z0-9-]*)\s*\{/gm;
|
|
4462
5495
|
for (const selectorMatch of content.matchAll(selectorPattern)) {
|
|
4463
5496
|
const selector = selectorMatch[1];
|
|
@@ -4473,8 +5506,8 @@ async function checkBlocks(root, results) {
|
|
|
4473
5506
|
}
|
|
4474
5507
|
}
|
|
4475
5508
|
async function checkAemJs(root, results) {
|
|
4476
|
-
const filePath =
|
|
4477
|
-
if (!
|
|
5509
|
+
const filePath = path27.join(root, "scripts", "aem.js");
|
|
5510
|
+
if (!existsSync25(filePath)) {
|
|
4478
5511
|
results.push({
|
|
4479
5512
|
rule: "scripts/aem.js exists",
|
|
4480
5513
|
status: "warn",
|
|
@@ -4512,12 +5545,12 @@ function printResults(results) {
|
|
|
4512
5545
|
grouped.set(key, bucket);
|
|
4513
5546
|
}
|
|
4514
5547
|
for (const [file, fileResults] of grouped) {
|
|
4515
|
-
logger.info(` ${icon.bullet} ${
|
|
5548
|
+
logger.info(` ${icon.bullet} ${chalk14.underline(file)}`);
|
|
4516
5549
|
for (const r of fileResults) {
|
|
4517
|
-
const loc = r.line ?
|
|
5550
|
+
const loc = r.line ? chalk14.dim(`:${r.line}`) : "";
|
|
4518
5551
|
logger.info(` ${statusIcon(r.status)} ${r.rule}${loc}`);
|
|
4519
5552
|
if (r.status !== "pass") {
|
|
4520
|
-
logger.info(
|
|
5553
|
+
logger.info(chalk14.dim(` ${r.message}`));
|
|
4521
5554
|
}
|
|
4522
5555
|
}
|
|
4523
5556
|
logger.info("");
|
|
@@ -4546,14 +5579,14 @@ var audit_security_exports = {};
|
|
|
4546
5579
|
__export(audit_security_exports, {
|
|
4547
5580
|
auditSecurity: () => auditSecurity
|
|
4548
5581
|
});
|
|
4549
|
-
import { existsSync as
|
|
4550
|
-
import { readFile as
|
|
4551
|
-
import
|
|
4552
|
-
import
|
|
5582
|
+
import { existsSync as existsSync26 } from "fs";
|
|
5583
|
+
import { readFile as readFile20, readdir as readdir6 } from "fs/promises";
|
|
5584
|
+
import path28 from "path";
|
|
5585
|
+
import chalk15 from "chalk";
|
|
4553
5586
|
async function* walk2(dir) {
|
|
4554
|
-
const entries = await
|
|
5587
|
+
const entries = await readdir6(dir, { withFileTypes: true });
|
|
4555
5588
|
for (const entry of entries) {
|
|
4556
|
-
const full =
|
|
5589
|
+
const full = path28.join(dir, entry.name);
|
|
4557
5590
|
if (entry.isDirectory()) {
|
|
4558
5591
|
if (IGNORE_DIRS.has(entry.name)) continue;
|
|
4559
5592
|
yield* walk2(full);
|
|
@@ -4587,11 +5620,11 @@ function scanContent(content, relFile, ext) {
|
|
|
4587
5620
|
return findings;
|
|
4588
5621
|
}
|
|
4589
5622
|
async function auditDependencies(root) {
|
|
4590
|
-
if (!
|
|
5623
|
+
if (!existsSync26(path28.join(root, "package.json"))) {
|
|
4591
5624
|
return { status: "skipped", note: "no package.json" };
|
|
4592
5625
|
}
|
|
4593
|
-
const hasLock =
|
|
4594
|
-
if (!hasLock && !
|
|
5626
|
+
const hasLock = existsSync26(path28.join(root, "package-lock.json")) || existsSync26(path28.join(root, "npm-shrinkwrap.json"));
|
|
5627
|
+
if (!hasLock && !existsSync26(path28.join(root, "node_modules"))) {
|
|
4595
5628
|
return { status: "skipped", note: "no package-lock.json / node_modules \u2014 run npm install" };
|
|
4596
5629
|
}
|
|
4597
5630
|
try {
|
|
@@ -4623,15 +5656,15 @@ async function auditSecurity(options = {}) {
|
|
|
4623
5656
|
}
|
|
4624
5657
|
const findings = [];
|
|
4625
5658
|
for await (const file of walk2(projectRoot)) {
|
|
4626
|
-
const ext =
|
|
5659
|
+
const ext = path28.extname(file).slice(1).toLowerCase();
|
|
4627
5660
|
if (!SCAN_EXT.has(ext)) continue;
|
|
4628
|
-
const rel =
|
|
5661
|
+
const rel = path28.relative(projectRoot, file);
|
|
4629
5662
|
if (rel.endsWith("aem.js") || rel.endsWith("lib-franklin.js")) continue;
|
|
4630
|
-
const content = await
|
|
5663
|
+
const content = await readFile20(file, "utf-8");
|
|
4631
5664
|
findings.push(...scanContent(content, rel, ext));
|
|
4632
5665
|
}
|
|
4633
5666
|
const deps = await auditDependencies(projectRoot);
|
|
4634
|
-
const bySeverity = (s) => findings.filter((
|
|
5667
|
+
const bySeverity = (s) => findings.filter((f2) => f2.severity === s);
|
|
4635
5668
|
const codeCounts = {
|
|
4636
5669
|
critical: bySeverity("critical").length,
|
|
4637
5670
|
high: bySeverity("high").length,
|
|
@@ -4673,11 +5706,11 @@ async function auditSecurity(options = {}) {
|
|
|
4673
5706
|
const sorted = [...findings].sort(
|
|
4674
5707
|
(a, b) => SEVERITY_ORDER.indexOf(a.severity) - SEVERITY_ORDER.indexOf(b.severity)
|
|
4675
5708
|
);
|
|
4676
|
-
for (const
|
|
4677
|
-
const meta = SEVERITY_META[
|
|
4678
|
-
const loc =
|
|
4679
|
-
logger.info(` ${statusIcon(meta.status)} ${
|
|
4680
|
-
logger.info(
|
|
5709
|
+
for (const f2 of sorted) {
|
|
5710
|
+
const meta = SEVERITY_META[f2.severity];
|
|
5711
|
+
const loc = chalk15.dim(`${f2.file}:${f2.line}`);
|
|
5712
|
+
logger.info(` ${statusIcon(meta.status)} ${chalk15.bold(meta.label)} ${f2.message} ${loc}`);
|
|
5713
|
+
logger.info(chalk15.dim(` ${f2.snippet}`));
|
|
4681
5714
|
}
|
|
4682
5715
|
}
|
|
4683
5716
|
const summaryLine = summary({
|
|
@@ -4884,7 +5917,7 @@ __export(preview_exports2, {
|
|
|
4884
5917
|
preview: () => preview,
|
|
4885
5918
|
resolveAdminConfig: () => resolveAdminConfig
|
|
4886
5919
|
});
|
|
4887
|
-
import
|
|
5920
|
+
import chalk16 from "chalk";
|
|
4888
5921
|
import ora2 from "ora";
|
|
4889
5922
|
async function preview(paths, options) {
|
|
4890
5923
|
const projectRoot = findProjectRoot();
|
|
@@ -4897,7 +5930,7 @@ async function preview(paths, options) {
|
|
|
4897
5930
|
let hasErrors = false;
|
|
4898
5931
|
for (const r of results) {
|
|
4899
5932
|
if (r.ok) {
|
|
4900
|
-
logger.success(`${r.path} \u2192 ${
|
|
5933
|
+
logger.success(`${r.path} \u2192 ${chalk16.cyan(r.previewUrl || "preview updated")}`);
|
|
4901
5934
|
} else {
|
|
4902
5935
|
logger.error(`${r.path} \u2014 ${r.message}`);
|
|
4903
5936
|
hasErrors = true;
|
|
@@ -4911,7 +5944,7 @@ function resolveAdminConfig(config, options) {
|
|
|
4911
5944
|
const ref = options.ref || config.admin?.ref || "main";
|
|
4912
5945
|
if (!org || !site) {
|
|
4913
5946
|
logger.error("Missing --org and --site. Set them via flags or in .edsrc.json:");
|
|
4914
|
-
logger.info(
|
|
5947
|
+
logger.info(chalk16.dim(' { "admin": { "org": "my-org", "site": "my-site" } }'));
|
|
4915
5948
|
process.exitCode = 1;
|
|
4916
5949
|
return void 0;
|
|
4917
5950
|
}
|
|
@@ -4933,7 +5966,7 @@ var publish_exports = {};
|
|
|
4933
5966
|
__export(publish_exports, {
|
|
4934
5967
|
publish: () => publish
|
|
4935
5968
|
});
|
|
4936
|
-
import
|
|
5969
|
+
import chalk17 from "chalk";
|
|
4937
5970
|
import ora3 from "ora";
|
|
4938
5971
|
async function publish(paths, options) {
|
|
4939
5972
|
const projectRoot = findProjectRoot();
|
|
@@ -4946,7 +5979,7 @@ async function publish(paths, options) {
|
|
|
4946
5979
|
let hasErrors = false;
|
|
4947
5980
|
for (const r of results) {
|
|
4948
5981
|
if (r.ok) {
|
|
4949
|
-
logger.success(`${r.path} \u2192 ${
|
|
5982
|
+
logger.success(`${r.path} \u2192 ${chalk17.cyan(r.liveUrl || "published")}`);
|
|
4950
5983
|
} else {
|
|
4951
5984
|
logger.error(`${r.path} \u2014 ${r.message}`);
|
|
4952
5985
|
hasErrors = true;
|
|
@@ -5045,13 +6078,13 @@ var menu_exports = {};
|
|
|
5045
6078
|
__export(menu_exports, {
|
|
5046
6079
|
mainMenu: () => mainMenu
|
|
5047
6080
|
});
|
|
5048
|
-
import { existsSync as
|
|
5049
|
-
import { readdir as
|
|
5050
|
-
import
|
|
6081
|
+
import { existsSync as existsSync27 } from "fs";
|
|
6082
|
+
import { readdir as readdir7 } from "fs/promises";
|
|
6083
|
+
import path29 from "path";
|
|
5051
6084
|
async function listBlockNames(projectRoot) {
|
|
5052
|
-
const dir =
|
|
5053
|
-
if (!
|
|
5054
|
-
const entries = await
|
|
6085
|
+
const dir = path29.join(projectRoot, "blocks");
|
|
6086
|
+
if (!existsSync27(dir)) return [];
|
|
6087
|
+
const entries = await readdir7(dir, { withFileTypes: true });
|
|
5055
6088
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
5056
6089
|
}
|
|
5057
6090
|
async function mainMenu() {
|
|
@@ -5061,6 +6094,7 @@ async function mainMenu() {
|
|
|
5061
6094
|
message: "Choose an action",
|
|
5062
6095
|
choices: [
|
|
5063
6096
|
{ name: "Set up this project (authoring model, CI, naming)", value: "init" },
|
|
6097
|
+
{ name: "Generate a page template (initial content)", value: "template" },
|
|
5064
6098
|
{
|
|
5065
6099
|
name: "Generate a block from a design (Figma/Stitch/Canva/Sketch/Framer)",
|
|
5066
6100
|
value: "from-design"
|
|
@@ -5069,7 +6103,9 @@ async function mainMenu() {
|
|
|
5069
6103
|
{ name: "Scaffold Universal Editor components", value: "scaffold" },
|
|
5070
6104
|
{ name: "Create an empty block", value: "create" },
|
|
5071
6105
|
{ name: "Add a third-party integration (GTM, chat, consent\u2026)", value: "integrate" },
|
|
6106
|
+
{ name: "Add fields to a block model (partials)", value: "model" },
|
|
5072
6107
|
{ name: "Instrument a block for analytics (dataLayer)", value: "track" },
|
|
6108
|
+
{ name: "Add JSON-LD structured data to a block", value: "schema" },
|
|
5073
6109
|
{ name: "Preview a block in the browser", value: "preview-block" },
|
|
5074
6110
|
{ name: "Preview / publish pages (Admin API)", value: "admin" },
|
|
5075
6111
|
{ name: "Audit the project", value: "audit" },
|
|
@@ -5082,6 +6118,15 @@ async function mainMenu() {
|
|
|
5082
6118
|
await initProject2({});
|
|
5083
6119
|
return;
|
|
5084
6120
|
}
|
|
6121
|
+
case "template": {
|
|
6122
|
+
const name = await input({
|
|
6123
|
+
message: "Template name",
|
|
6124
|
+
validate: (v) => !!v.trim() || "Required"
|
|
6125
|
+
});
|
|
6126
|
+
const { templateNew: templateNew2 } = await Promise.resolve().then(() => (init_template(), template_exports));
|
|
6127
|
+
await templateNew2(name.trim(), {});
|
|
6128
|
+
return;
|
|
6129
|
+
}
|
|
5085
6130
|
case "from-design": {
|
|
5086
6131
|
const { blockFromDesign: blockFromDesign2 } = await Promise.resolve().then(() => (init_from_design(), from_design_exports));
|
|
5087
6132
|
await blockFromDesign2(void 0, {});
|
|
@@ -5130,6 +6175,11 @@ async function mainMenu() {
|
|
|
5130
6175
|
await previewBlock2(name, {});
|
|
5131
6176
|
return;
|
|
5132
6177
|
}
|
|
6178
|
+
case "model": {
|
|
6179
|
+
const { modelAdd: modelAdd2 } = await Promise.resolve().then(() => (init_model(), model_exports));
|
|
6180
|
+
await modelAdd2(void 0, [], {});
|
|
6181
|
+
return;
|
|
6182
|
+
}
|
|
5133
6183
|
case "track": {
|
|
5134
6184
|
const projectRoot = findProjectRoot();
|
|
5135
6185
|
if (!projectRoot) {
|
|
@@ -5149,6 +6199,25 @@ async function mainMenu() {
|
|
|
5149
6199
|
await trackBlock2(name, {});
|
|
5150
6200
|
return;
|
|
5151
6201
|
}
|
|
6202
|
+
case "schema": {
|
|
6203
|
+
const projectRoot = findProjectRoot();
|
|
6204
|
+
if (!projectRoot) {
|
|
6205
|
+
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
6206
|
+
return;
|
|
6207
|
+
}
|
|
6208
|
+
const blocks = await listBlockNames(projectRoot);
|
|
6209
|
+
if (blocks.length === 0) {
|
|
6210
|
+
logger.warn("No blocks found in blocks/.");
|
|
6211
|
+
return;
|
|
6212
|
+
}
|
|
6213
|
+
const name = await select({
|
|
6214
|
+
message: "Which block?",
|
|
6215
|
+
choices: blocks.map((b) => ({ name: b, value: b }))
|
|
6216
|
+
});
|
|
6217
|
+
const { schemaBlock: schemaBlock2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
|
|
6218
|
+
await schemaBlock2(name, {});
|
|
6219
|
+
return;
|
|
6220
|
+
}
|
|
5152
6221
|
case "admin": {
|
|
5153
6222
|
const { adminWizard: adminWizard2 } = await Promise.resolve().then(() => (init_admin(), admin_exports));
|
|
5154
6223
|
await adminWizard2();
|
|
@@ -5194,13 +6263,13 @@ var init_menu = __esm({
|
|
|
5194
6263
|
|
|
5195
6264
|
// src/bin/eds.ts
|
|
5196
6265
|
init_esm_shims();
|
|
5197
|
-
import
|
|
6266
|
+
import chalk18 from "chalk";
|
|
5198
6267
|
import { Command } from "commander";
|
|
5199
6268
|
|
|
5200
6269
|
// package.json
|
|
5201
6270
|
var package_default = {
|
|
5202
6271
|
name: "aem-eds-kit",
|
|
5203
|
-
version: "0.4.
|
|
6272
|
+
version: "0.4.16",
|
|
5204
6273
|
description: "CLI for AEM Edge Delivery Services with Figma MCP integration",
|
|
5205
6274
|
type: "module",
|
|
5206
6275
|
engines: {
|
|
@@ -5276,9 +6345,18 @@ program.name("eds").description("CLI for AEM Edge Delivery Services with Figma i
|
|
|
5276
6345
|
const opts = thisCommand.opts();
|
|
5277
6346
|
if (opts.verbose) logger.setLevel("debug");
|
|
5278
6347
|
if (opts.quiet) logger.setQuiet(true);
|
|
5279
|
-
if (opts.color === false)
|
|
6348
|
+
if (opts.color === false) chalk18.level = 0;
|
|
5280
6349
|
});
|
|
5281
6350
|
program.addHelpText("beforeAll", () => logo());
|
|
6351
|
+
var template = program.command("template").description("Generate page templates / initial content");
|
|
6352
|
+
template.command("new <name>").description("Generate EDS page initial content (sections + blocks + metadata), DA push optional").option("--path <path>", "Content path (default: /templates/<name>)").option("--title <title>", "Page title").option("--description <text>", "Page description").option(
|
|
6353
|
+
"--blocks <list>",
|
|
6354
|
+
"Comma-separated block ids to seed",
|
|
6355
|
+
(v) => v.split(",").map((b) => b.trim())
|
|
6356
|
+
).option("--area <area>", "Site area / template label (stored in metadata)").option("--authoring <model>", "Force authoring model: da | gdrive | sharepoint | ue").option("--push", "Push the page to DA (da.live)").option("--org <org>", "DA org (else from .edsrc.json)").option("--site <site>", "DA site (else from .edsrc.json)").option("--yes", "Skip prompts").action(async (name, options) => {
|
|
6357
|
+
const { templateNew: templateNew2 } = await Promise.resolve().then(() => (init_template(), template_exports));
|
|
6358
|
+
await templateNew2(name, options);
|
|
6359
|
+
});
|
|
5282
6360
|
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) => {
|
|
5283
6361
|
const { initProject: initProject2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
5284
6362
|
await initProject2(options);
|
|
@@ -5288,6 +6366,16 @@ migrate.command("component [dir]").description("Migrate a classic AEM component'
|
|
|
5288
6366
|
const { migrateComponent: migrateComponent2 } = await Promise.resolve().then(() => (init_migrate(), migrate_exports));
|
|
5289
6367
|
await migrateComponent2(dir, options);
|
|
5290
6368
|
});
|
|
6369
|
+
var model = program.command("model").description("Compose Universal Editor models");
|
|
6370
|
+
model.command("add <block> [partials...]").description("Add reusable field groups (partials) to a block's UE model").option("--yes", "Skip prompts").action(async (blockName, partials, options) => {
|
|
6371
|
+
const { modelAdd: modelAdd2 } = await Promise.resolve().then(() => (init_model(), model_exports));
|
|
6372
|
+
await modelAdd2(blockName, partials, options);
|
|
6373
|
+
});
|
|
6374
|
+
var schema = program.command("schema").description("Structured data (JSON-LD) for blocks");
|
|
6375
|
+
schema.command("block <name>").description("Inject a JSON-LD (schema.org) builder into a block's decorate()").option("--type <type>", "schema.org type (Article, FAQPage, Product, ...)").option("--yes", "Skip prompts").action(async (name, options) => {
|
|
6376
|
+
const { schemaBlock: schemaBlock2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
|
|
6377
|
+
await schemaBlock2(name, options);
|
|
6378
|
+
});
|
|
5291
6379
|
var track = program.command("track").description("Instrument blocks for analytics");
|
|
5292
6380
|
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
6381
|
const { trackBlock: trackBlock2 } = await Promise.resolve().then(() => (init_track2(), track_exports));
|
|
@@ -5328,6 +6416,10 @@ scaffold.command("ci").description("Scaffold a GitHub Actions workflow (doctor +
|
|
|
5328
6416
|
const { scaffoldCi: scaffoldCi2 } = await Promise.resolve().then(() => (init_scaffold(), scaffold_exports));
|
|
5329
6417
|
await scaffoldCi2();
|
|
5330
6418
|
});
|
|
6419
|
+
scaffold.command("helpers [names...]").description("Scaffold scripts/utils.js with chosen EDS helpers (interactive picker)").action(async (names) => {
|
|
6420
|
+
const { scaffoldHelpers: scaffoldHelpers2 } = await Promise.resolve().then(() => (init_scaffold(), scaffold_exports));
|
|
6421
|
+
await scaffoldHelpers2(names);
|
|
6422
|
+
});
|
|
5331
6423
|
program.command("integrate [type]").description("Add a third-party service (GTM, GA4, chat, consent) to scripts/delayed.js").action(async (type) => {
|
|
5332
6424
|
const { integrate: integrate2 } = await Promise.resolve().then(() => (init_integrate(), integrate_exports));
|
|
5333
6425
|
await integrate2(type);
|