aem-eds-kit 0.4.16 → 0.4.22
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 +61 -2
- package/README.md +50 -1
- package/dist/bin/eds.js +2203 -1251
- package/dist/bin/eds.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/eds.js
CHANGED
|
@@ -273,6 +273,178 @@ ${logoArt()}
|
|
|
273
273
|
}
|
|
274
274
|
});
|
|
275
275
|
|
|
276
|
+
// src/lib/sandbox/api.ts
|
|
277
|
+
function headers(token) {
|
|
278
|
+
return {
|
|
279
|
+
Authorization: `Bearer ${token}`,
|
|
280
|
+
Accept: "application/vnd.github+json",
|
|
281
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
282
|
+
"User-Agent": "aem-eds-kit",
|
|
283
|
+
"Content-Type": "application/json"
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function generateEndpoint(template2) {
|
|
287
|
+
return `${API}/repos/${template2}/generate`;
|
|
288
|
+
}
|
|
289
|
+
async function getUser(token) {
|
|
290
|
+
const res = await fetch(`${API}/user`, { headers: headers(token) });
|
|
291
|
+
if (!res.ok) throw new Error(`GitHub auth failed: ${res.status}`);
|
|
292
|
+
return (await res.json()).login;
|
|
293
|
+
}
|
|
294
|
+
async function repoExists(token, owner, repo) {
|
|
295
|
+
const res = await fetch(`${API}/repos/${owner}/${repo}`, { headers: headers(token) });
|
|
296
|
+
if (res.status === 404) return false;
|
|
297
|
+
if (res.ok) return true;
|
|
298
|
+
throw new Error(`existence check failed: ${res.status} ${await res.text()}`);
|
|
299
|
+
}
|
|
300
|
+
async function createFromTemplate(token, template2, opts) {
|
|
301
|
+
const res = await fetch(generateEndpoint(template2), {
|
|
302
|
+
method: "POST",
|
|
303
|
+
headers: headers(token),
|
|
304
|
+
body: JSON.stringify({
|
|
305
|
+
owner: opts.owner,
|
|
306
|
+
name: opts.name,
|
|
307
|
+
private: !!opts.private,
|
|
308
|
+
include_all_branches: false
|
|
309
|
+
})
|
|
310
|
+
});
|
|
311
|
+
if (!res.ok) throw new Error(`create failed: ${res.status} ${await res.text()}`);
|
|
312
|
+
const r = await res.json();
|
|
313
|
+
return { fullName: r.full_name, htmlUrl: r.html_url, cloneUrl: r.clone_url };
|
|
314
|
+
}
|
|
315
|
+
async function addTopics(token, owner, repo, topics) {
|
|
316
|
+
await fetch(`${API}/repos/${owner}/${repo}/topics`, {
|
|
317
|
+
method: "PUT",
|
|
318
|
+
headers: headers(token),
|
|
319
|
+
body: JSON.stringify({ names: topics })
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
async function deleteRepo(token, owner, repo) {
|
|
323
|
+
const res = await fetch(`${API}/repos/${owner}/${repo}`, {
|
|
324
|
+
method: "DELETE",
|
|
325
|
+
headers: headers(token)
|
|
326
|
+
});
|
|
327
|
+
if (!res.ok && res.status !== 204) {
|
|
328
|
+
throw new Error(`delete failed: ${res.status} ${await res.text()}`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
async function searchByTopic(token, login, topic) {
|
|
332
|
+
const q = encodeURIComponent(`user:${login} topic:${topic} fork:true`);
|
|
333
|
+
const res = await fetch(`${API}/search/repositories?q=${q}&per_page=100`, {
|
|
334
|
+
headers: headers(token)
|
|
335
|
+
});
|
|
336
|
+
if (!res.ok) throw new Error(`search failed: ${res.status}`);
|
|
337
|
+
const data = await res.json();
|
|
338
|
+
return data.items.map((i) => i.full_name);
|
|
339
|
+
}
|
|
340
|
+
var API;
|
|
341
|
+
var init_api = __esm({
|
|
342
|
+
"src/lib/sandbox/api.ts"() {
|
|
343
|
+
"use strict";
|
|
344
|
+
init_esm_shims();
|
|
345
|
+
API = "https://api.github.com";
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// src/lib/sandbox/auth.ts
|
|
350
|
+
import { existsSync } from "fs";
|
|
351
|
+
import { mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
352
|
+
import os from "os";
|
|
353
|
+
import path2 from "path";
|
|
354
|
+
async function getToken() {
|
|
355
|
+
const env = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
356
|
+
if (env) return env;
|
|
357
|
+
if (existsSync(TOKEN_FILE)) {
|
|
358
|
+
try {
|
|
359
|
+
return JSON.parse(await readFile(TOKEN_FILE, "utf-8")).token ?? null;
|
|
360
|
+
} catch {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
async function saveToken(token) {
|
|
367
|
+
await mkdir(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
368
|
+
await writeFile(TOKEN_FILE, `${JSON.stringify({ token }, null, 2)}
|
|
369
|
+
`, { mode: 384 });
|
|
370
|
+
}
|
|
371
|
+
async function clearToken() {
|
|
372
|
+
if (existsSync(TOKEN_FILE)) await rm(TOKEN_FILE);
|
|
373
|
+
}
|
|
374
|
+
async function startDeviceFlow(scopes) {
|
|
375
|
+
const res = await fetch("https://github.com/login/device/code", {
|
|
376
|
+
method: "POST",
|
|
377
|
+
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
|
378
|
+
body: JSON.stringify({ client_id: CLIENT_ID, scope: scopes.join(" ") })
|
|
379
|
+
});
|
|
380
|
+
if (!res.ok) throw new Error(`device code request failed: ${res.status}`);
|
|
381
|
+
return await res.json();
|
|
382
|
+
}
|
|
383
|
+
async function pollDeviceToken(deviceCode, interval, expiresIn) {
|
|
384
|
+
let wait = Math.max(interval, 1);
|
|
385
|
+
const deadline = Date.now() + expiresIn * 1e3;
|
|
386
|
+
while (Date.now() < deadline) {
|
|
387
|
+
await new Promise((r) => setTimeout(r, wait * 1e3));
|
|
388
|
+
const res = await fetch("https://github.com/login/oauth/access_token", {
|
|
389
|
+
method: "POST",
|
|
390
|
+
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
|
391
|
+
body: JSON.stringify({
|
|
392
|
+
client_id: CLIENT_ID,
|
|
393
|
+
device_code: deviceCode,
|
|
394
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
395
|
+
})
|
|
396
|
+
});
|
|
397
|
+
const data = await res.json();
|
|
398
|
+
if (data.access_token) return data.access_token;
|
|
399
|
+
if (data.error === "slow_down") wait += 5;
|
|
400
|
+
else if (data.error !== "authorization_pending") {
|
|
401
|
+
throw new Error(data.error ?? "device flow failed");
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
throw new Error("device flow timed out");
|
|
405
|
+
}
|
|
406
|
+
var CONFIG_DIR, TOKEN_FILE, CLIENT_ID;
|
|
407
|
+
var init_auth = __esm({
|
|
408
|
+
"src/lib/sandbox/auth.ts"() {
|
|
409
|
+
"use strict";
|
|
410
|
+
init_esm_shims();
|
|
411
|
+
CONFIG_DIR = path2.join(os.homedir(), ".eds");
|
|
412
|
+
TOKEN_FILE = path2.join(CONFIG_DIR, "github.json");
|
|
413
|
+
CLIENT_ID = process.env.EDS_GITHUB_CLIENT_ID ?? "";
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// src/lib/sandbox/github.ts
|
|
418
|
+
function previewUrl(owner, repo, ref = "main") {
|
|
419
|
+
return `https://${ref}--${repo}--${owner}.aem.page/`;
|
|
420
|
+
}
|
|
421
|
+
function codeSyncInstallUrl() {
|
|
422
|
+
return "https://github.com/apps/aem-code-sync/installations/new";
|
|
423
|
+
}
|
|
424
|
+
function createArgs(repoRef, opts) {
|
|
425
|
+
return [
|
|
426
|
+
"repo",
|
|
427
|
+
"create",
|
|
428
|
+
repoRef,
|
|
429
|
+
"--template",
|
|
430
|
+
opts.template,
|
|
431
|
+
opts.private ? "--private" : "--public",
|
|
432
|
+
...opts.clone ? ["--clone"] : []
|
|
433
|
+
];
|
|
434
|
+
}
|
|
435
|
+
var SANDBOX_TOPIC, BOILERPLATES;
|
|
436
|
+
var init_github = __esm({
|
|
437
|
+
"src/lib/sandbox/github.ts"() {
|
|
438
|
+
"use strict";
|
|
439
|
+
init_esm_shims();
|
|
440
|
+
SANDBOX_TOPIC = "eds-sandbox";
|
|
441
|
+
BOILERPLATES = {
|
|
442
|
+
document: "adobe/aem-boilerplate",
|
|
443
|
+
ue: "adobe/aem-boilerplate-xwalk"
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
|
|
276
448
|
// src/lib/schemas.ts
|
|
277
449
|
import { z } from "zod";
|
|
278
450
|
var figmaUrlSchema, edsConfigSchema, edsProjectSchema, edsMetaSchema, kebabCaseRegex;
|
|
@@ -315,26 +487,26 @@ var init_schemas = __esm({
|
|
|
315
487
|
});
|
|
316
488
|
|
|
317
489
|
// src/lib/project.ts
|
|
318
|
-
import { existsSync } from "fs";
|
|
319
|
-
import
|
|
490
|
+
import { existsSync as existsSync2 } from "fs";
|
|
491
|
+
import path3 from "path";
|
|
320
492
|
function findProjectRoot(startDir = process.cwd()) {
|
|
321
|
-
let dir =
|
|
322
|
-
const { root } =
|
|
493
|
+
let dir = path3.resolve(startDir);
|
|
494
|
+
const { root } = path3.parse(dir);
|
|
323
495
|
while (dir !== root) {
|
|
324
|
-
if (ROOT_MARKERS.some((m) =>
|
|
496
|
+
if (ROOT_MARKERS.some((m) => existsSync2(path3.join(dir, m)))) {
|
|
325
497
|
return dir;
|
|
326
498
|
}
|
|
327
|
-
dir =
|
|
499
|
+
dir = path3.dirname(dir);
|
|
328
500
|
}
|
|
329
|
-
if (ROOT_MARKERS.some((m) =>
|
|
501
|
+
if (ROOT_MARKERS.some((m) => existsSync2(path3.join(root, m)))) return root;
|
|
330
502
|
return void 0;
|
|
331
503
|
}
|
|
332
504
|
function detectProject(rootDir) {
|
|
333
505
|
return edsProjectSchema.parse({
|
|
334
506
|
root: rootDir,
|
|
335
|
-
hasFstab:
|
|
336
|
-
hasBlocks:
|
|
337
|
-
hasHeadHtml:
|
|
507
|
+
hasFstab: existsSync2(path3.join(rootDir, "fstab.yaml")),
|
|
508
|
+
hasBlocks: existsSync2(path3.join(rootDir, "blocks")),
|
|
509
|
+
hasHeadHtml: existsSync2(path3.join(rootDir, "head.html"))
|
|
338
510
|
});
|
|
339
511
|
}
|
|
340
512
|
var ROOT_MARKERS;
|
|
@@ -355,6 +527,206 @@ var init_project = __esm({
|
|
|
355
527
|
}
|
|
356
528
|
});
|
|
357
529
|
|
|
530
|
+
// src/lib/scaffold/ci.ts
|
|
531
|
+
import { existsSync as existsSync3 } from "fs";
|
|
532
|
+
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
533
|
+
import path4 from "path";
|
|
534
|
+
async function writeCiWorkflow(projectRoot) {
|
|
535
|
+
const file = path4.join(projectRoot, ".github", "workflows", "eds.yml");
|
|
536
|
+
if (existsSync3(file)) return { path: CI_WORKFLOW_PATH, created: false };
|
|
537
|
+
await mkdir2(path4.dirname(file), { recursive: true });
|
|
538
|
+
await writeFile2(file, CI_WORKFLOW);
|
|
539
|
+
return { path: CI_WORKFLOW_PATH, created: true };
|
|
540
|
+
}
|
|
541
|
+
var CI_WORKFLOW_PATH, CI_WORKFLOW;
|
|
542
|
+
var init_ci = __esm({
|
|
543
|
+
"src/lib/scaffold/ci.ts"() {
|
|
544
|
+
"use strict";
|
|
545
|
+
init_esm_shims();
|
|
546
|
+
CI_WORKFLOW_PATH = ".github/workflows/eds.yml";
|
|
547
|
+
CI_WORKFLOW = `name: EDS checks
|
|
548
|
+
|
|
549
|
+
# Generated by aem-eds-kit (\`eds scaffold ci\`). Runs the project's own
|
|
550
|
+
# lint/build plus the eds audits as a gatekeeper on every PR and push to main.
|
|
551
|
+
on:
|
|
552
|
+
pull_request:
|
|
553
|
+
push:
|
|
554
|
+
branches: [main]
|
|
555
|
+
|
|
556
|
+
jobs:
|
|
557
|
+
checks:
|
|
558
|
+
runs-on: ubuntu-latest
|
|
559
|
+
steps:
|
|
560
|
+
- uses: actions/checkout@v4
|
|
561
|
+
- uses: actions/setup-node@v4
|
|
562
|
+
with:
|
|
563
|
+
node-version: 20
|
|
564
|
+
|
|
565
|
+
# Install project dependencies when the repo has them.
|
|
566
|
+
- name: Install dependencies
|
|
567
|
+
run: |
|
|
568
|
+
if [ -f package-lock.json ]; then
|
|
569
|
+
npm ci
|
|
570
|
+
elif [ -f package.json ]; then
|
|
571
|
+
npm install
|
|
572
|
+
fi
|
|
573
|
+
|
|
574
|
+
# The project's own scripts, if defined (skipped otherwise).
|
|
575
|
+
- name: Lint
|
|
576
|
+
if: hashFiles('package.json') != ''
|
|
577
|
+
run: npm run lint --if-present
|
|
578
|
+
- name: Build
|
|
579
|
+
if: hashFiles('package.json') != ''
|
|
580
|
+
run: npm run build --if-present
|
|
581
|
+
|
|
582
|
+
# aem-eds-kit audits - run via npx, no install needed. Each exits
|
|
583
|
+
# non-zero on failures, so a bad PR fails the check.
|
|
584
|
+
- name: Project health (doctor)
|
|
585
|
+
run: npx --yes aem-eds-kit@latest doctor
|
|
586
|
+
- name: Loading & LCP budget
|
|
587
|
+
run: npx --yes aem-eds-kit@latest audit loading
|
|
588
|
+
- name: Security & exploit scan
|
|
589
|
+
run: npx --yes aem-eds-kit@latest audit security
|
|
590
|
+
`;
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
// src/lib/scaffold/env.ts
|
|
595
|
+
import { existsSync as existsSync4 } from "fs";
|
|
596
|
+
import { readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
|
|
597
|
+
import path5 from "path";
|
|
598
|
+
function buildEnv(opts = {}) {
|
|
599
|
+
const open = opts.open ?? "/";
|
|
600
|
+
const port = opts.port ?? 3007;
|
|
601
|
+
const pagesUrl = opts.pagesUrl ?? "";
|
|
602
|
+
return `AEM_OPEN=${open}
|
|
603
|
+
AEM_PORT=${port}
|
|
604
|
+
AEM_PAGES_URL=${pagesUrl}
|
|
605
|
+
`;
|
|
606
|
+
}
|
|
607
|
+
async function ensureEnvIgnored(projectRoot) {
|
|
608
|
+
const file = path5.join(projectRoot, ".gitignore");
|
|
609
|
+
let current = "";
|
|
610
|
+
if (existsSync4(file)) current = await readFile2(file, "utf-8");
|
|
611
|
+
if (current.split(/\r?\n/).some((l) => l.trim() === ".env")) return false;
|
|
612
|
+
const prefix = current && !current.endsWith("\n") ? "\n" : "";
|
|
613
|
+
await writeFile3(file, `${current}${prefix}.env
|
|
614
|
+
`);
|
|
615
|
+
return true;
|
|
616
|
+
}
|
|
617
|
+
async function writeEnv(projectRoot, opts = {}) {
|
|
618
|
+
const file = path5.join(projectRoot, ".env");
|
|
619
|
+
const created = !existsSync4(file);
|
|
620
|
+
if (created) await writeFile3(file, buildEnv(opts));
|
|
621
|
+
const gitignoreUpdated = await ensureEnvIgnored(projectRoot);
|
|
622
|
+
return { path: ".env", created, gitignoreUpdated };
|
|
623
|
+
}
|
|
624
|
+
var init_env = __esm({
|
|
625
|
+
"src/lib/scaffold/env.ts"() {
|
|
626
|
+
"use strict";
|
|
627
|
+
init_esm_shims();
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
// src/lib/scaffold/fstab.ts
|
|
632
|
+
import { writeFile as writeFile4 } from "fs/promises";
|
|
633
|
+
import path6 from "path";
|
|
634
|
+
function needsFstab(a) {
|
|
635
|
+
return a !== "ue";
|
|
636
|
+
}
|
|
637
|
+
function daMountpoint(org, site) {
|
|
638
|
+
return `https://content.da.live/${org}/${site}/`;
|
|
639
|
+
}
|
|
640
|
+
function fstabYaml(mountpoint) {
|
|
641
|
+
return `mountpoints:
|
|
642
|
+
/: ${mountpoint}
|
|
643
|
+
`;
|
|
644
|
+
}
|
|
645
|
+
async function writeFstab(projectRoot, mountpoint) {
|
|
646
|
+
await writeFile4(path6.join(projectRoot, "fstab.yaml"), fstabYaml(mountpoint));
|
|
647
|
+
return "fstab.yaml";
|
|
648
|
+
}
|
|
649
|
+
function pathsJson(site) {
|
|
650
|
+
return `${JSON.stringify(
|
|
651
|
+
{
|
|
652
|
+
mappings: [`/content/${site}/:/`],
|
|
653
|
+
includes: [`/content/${site}/`]
|
|
654
|
+
},
|
|
655
|
+
null,
|
|
656
|
+
2
|
|
657
|
+
)}
|
|
658
|
+
`;
|
|
659
|
+
}
|
|
660
|
+
async function writePaths(projectRoot, site) {
|
|
661
|
+
await writeFile4(path6.join(projectRoot, "paths.json"), pathsJson(site));
|
|
662
|
+
return "paths.json";
|
|
663
|
+
}
|
|
664
|
+
var AUTHORING_NAMES;
|
|
665
|
+
var init_fstab = __esm({
|
|
666
|
+
"src/lib/scaffold/fstab.ts"() {
|
|
667
|
+
"use strict";
|
|
668
|
+
init_esm_shims();
|
|
669
|
+
AUTHORING_NAMES = {
|
|
670
|
+
ue: "Universal Editor (crosswalk)",
|
|
671
|
+
da: "Document Authoring (da.live)",
|
|
672
|
+
gdrive: "Document Authoring (Google Drive)",
|
|
673
|
+
sharepoint: "Document Authoring (SharePoint)"
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
// src/lib/scaffold/xwalk.ts
|
|
679
|
+
import { existsSync as existsSync5 } from "fs";
|
|
680
|
+
import { writeFile as writeFile5 } from "fs/promises";
|
|
681
|
+
import path7 from "path";
|
|
682
|
+
function buildXwalk() {
|
|
683
|
+
return `${JSON.stringify(XWALK_CONFIG, null, 2)}
|
|
684
|
+
`;
|
|
685
|
+
}
|
|
686
|
+
async function writeXwalk(projectRoot) {
|
|
687
|
+
const file = path7.join(projectRoot, "xwalk.json");
|
|
688
|
+
if (existsSync5(file)) return { path: "xwalk.json", created: false };
|
|
689
|
+
await writeFile5(file, buildXwalk());
|
|
690
|
+
return { path: "xwalk.json", created: true };
|
|
691
|
+
}
|
|
692
|
+
var XWALK_CONFIG;
|
|
693
|
+
var init_xwalk = __esm({
|
|
694
|
+
"src/lib/scaffold/xwalk.ts"() {
|
|
695
|
+
"use strict";
|
|
696
|
+
init_esm_shims();
|
|
697
|
+
XWALK_CONFIG = {
|
|
698
|
+
public: {
|
|
699
|
+
xwalk: {
|
|
700
|
+
"multi-field": {
|
|
701
|
+
enabled: true
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
// src/lib/ue/link.ts
|
|
710
|
+
function parseRemote(url) {
|
|
711
|
+
const m = url.trim().match(/github\.com[:/]([^/]+)\/(.+?)(?:\.git)?\/?$/i);
|
|
712
|
+
return m ? { owner: m[1], repo: m[2] } : null;
|
|
713
|
+
}
|
|
714
|
+
function previewHost(owner, repo, ref = "main") {
|
|
715
|
+
return `${ref}--${repo}--${owner}.aem.page`;
|
|
716
|
+
}
|
|
717
|
+
function editorLink({ host, path: path36 = "/", org }) {
|
|
718
|
+
const cleanHost = host.replace(/^https?:\/\//, "").replace(/\/+$/, "");
|
|
719
|
+
const p = path36.startsWith("/") ? path36 : `/${path36}`;
|
|
720
|
+
const orgSeg = org ? `@${org}/` : "";
|
|
721
|
+
return `https://experience.adobe.com/#/${orgSeg}aem/editor/canvas/${cleanHost}${p}`;
|
|
722
|
+
}
|
|
723
|
+
var init_link = __esm({
|
|
724
|
+
"src/lib/ue/link.ts"() {
|
|
725
|
+
"use strict";
|
|
726
|
+
init_esm_shims();
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
|
|
358
730
|
// src/lib/scaffold/blocks.ts
|
|
359
731
|
function json(value) {
|
|
360
732
|
return `${JSON.stringify(value, null, 2)}
|
|
@@ -676,65 +1048,263 @@ export default function decorate(block) {
|
|
|
676
1048
|
}
|
|
677
1049
|
});
|
|
678
1050
|
|
|
679
|
-
// src/lib/scaffold/
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
1051
|
+
// src/lib/scaffold/helpers.ts
|
|
1052
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1053
|
+
import { mkdir as mkdir3, writeFile as writeFile6 } from "fs/promises";
|
|
1054
|
+
import path8 from "path";
|
|
1055
|
+
function helperById(id) {
|
|
1056
|
+
return HELPERS.find((h) => h.id === id);
|
|
1057
|
+
}
|
|
1058
|
+
function resolveHelpers(ids) {
|
|
1059
|
+
const want = /* @__PURE__ */ new Set();
|
|
1060
|
+
const add = (id) => {
|
|
1061
|
+
if (want.has(id)) return;
|
|
1062
|
+
const h = helperById(id);
|
|
1063
|
+
if (!h) return;
|
|
1064
|
+
for (const dep of h.deps ?? []) add(dep);
|
|
1065
|
+
want.add(id);
|
|
1066
|
+
};
|
|
1067
|
+
for (const id of ids) add(id);
|
|
1068
|
+
return HELPERS.filter((h) => want.has(h.id)).map((h) => h.id);
|
|
1069
|
+
}
|
|
1070
|
+
function buildHelpers(ids) {
|
|
1071
|
+
const resolved = resolveHelpers(ids);
|
|
1072
|
+
const body = resolved.map((id) => helperById(id)).filter((h) => !!h).map((h) => h.code).join("\n\n");
|
|
1073
|
+
return `// scripts/utils.js - common EDS helpers (generated by \`eds scaffold helpers\`).
|
|
1074
|
+
|
|
1075
|
+
${body}
|
|
1076
|
+
`;
|
|
1077
|
+
}
|
|
1078
|
+
async function writeHelpers(projectRoot, ids = ALL_HELPER_IDS) {
|
|
1079
|
+
const resolved = resolveHelpers(ids);
|
|
1080
|
+
const file = path8.join(projectRoot, "scripts", "utils.js");
|
|
1081
|
+
if (existsSync6(file)) return { path: HELPERS_PATH, created: false, helpers: resolved };
|
|
1082
|
+
await mkdir3(path8.dirname(file), { recursive: true });
|
|
1083
|
+
await writeFile6(file, buildHelpers(ids));
|
|
1084
|
+
return { path: HELPERS_PATH, created: true, helpers: resolved };
|
|
1085
|
+
}
|
|
1086
|
+
var HELPERS_PATH, HELPERS, ALL_HELPER_IDS;
|
|
1087
|
+
var init_helpers = __esm({
|
|
1088
|
+
"src/lib/scaffold/helpers.ts"() {
|
|
1089
|
+
"use strict";
|
|
1090
|
+
init_esm_shims();
|
|
1091
|
+
HELPERS_PATH = "scripts/utils.js";
|
|
1092
|
+
HELPERS = [
|
|
1093
|
+
{
|
|
1094
|
+
id: "getMetadata",
|
|
1095
|
+
label: "getMetadata (read a page meta value)",
|
|
1096
|
+
code: `/**
|
|
1097
|
+
* Reads a page metadata value by name (meta[name] or meta[property]).
|
|
1098
|
+
* @param {string} name The metadata name.
|
|
1099
|
+
* @returns {string} The value, or an empty string.
|
|
1100
|
+
*/
|
|
1101
|
+
export function getMetadata(name) {
|
|
1102
|
+
const meta =
|
|
1103
|
+
document.querySelector(\`meta[name="\${name}"]\`) ||
|
|
1104
|
+
document.querySelector(\`meta[property="\${name}"]\`);
|
|
1105
|
+
return meta ? meta.content : '';
|
|
1106
|
+
}`
|
|
1107
|
+
},
|
|
1108
|
+
{
|
|
1109
|
+
id: "toCamelCase",
|
|
1110
|
+
label: "toCamelCase (dash/underscore/space -> camelCase)",
|
|
1111
|
+
code: `/**
|
|
1112
|
+
* Converts a string to camelCase, handling dashes, underscores and spaces.
|
|
1113
|
+
* @param {string} value The string to convert.
|
|
1114
|
+
* @returns {string} The camelCased string.
|
|
1115
|
+
*/
|
|
1116
|
+
export function toCamelCase(value) {
|
|
1117
|
+
return String(value || '')
|
|
1118
|
+
.trim()
|
|
1119
|
+
.toLowerCase()
|
|
1120
|
+
.replace(/[-_\\s]+(.)/g, (_, char) => char.toUpperCase());
|
|
1121
|
+
}`
|
|
1122
|
+
},
|
|
1123
|
+
{
|
|
1124
|
+
id: "getLanguageRootPath",
|
|
1125
|
+
label: "getLanguageRootPath (e.g. /en, /en-us)",
|
|
1126
|
+
code: `/**
|
|
1127
|
+
* Returns the language root of a path, e.g. '/en' or '/en-us' ('' if none).
|
|
1128
|
+
* @param {string} path The page path.
|
|
1129
|
+
* @returns {string} The language root prefix.
|
|
1130
|
+
*/
|
|
1131
|
+
export function getLanguageRootPath(path) {
|
|
1132
|
+
const match = String(path || '').match(/^\\/[a-z]{2}(?:-[a-z]{2})?(?=\\/|$)/i);
|
|
1133
|
+
return match ? match[0] : '';
|
|
1134
|
+
}`
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
id: "getPagePath",
|
|
1138
|
+
label: "getPagePath (path without content root)",
|
|
1139
|
+
deps: ["getLanguageRootPath"],
|
|
1140
|
+
code: `// Content root stripped from page paths. Set this if your site is mounted under
|
|
1141
|
+
// a sub-path (e.g. '/content/my-site'); leave empty for a root-mounted site.
|
|
1142
|
+
const ROOT_PATH = '';
|
|
1143
|
+
|
|
1144
|
+
/**
|
|
1145
|
+
* Returns the page path without the content root; \`clean\` drops the language root.
|
|
1146
|
+
* @param {boolean} [clean=false] Also strip the language root.
|
|
1147
|
+
* @returns {string} The page path or 'NA' for the language root.
|
|
1148
|
+
*/
|
|
1149
|
+
export function getPagePath({ clean = false } = {}) {
|
|
1150
|
+
const path = window.location.pathname.replace(ROOT_PATH, '').replace(/\\.html$/i, '');
|
|
1151
|
+
|
|
1152
|
+
if (clean) {
|
|
1153
|
+
const langRoot = getLanguageRootPath(path);
|
|
1154
|
+
const cleaned = path.startsWith(langRoot) ? path.slice(langRoot.length) : path;
|
|
1155
|
+
return cleaned || 'NA';
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
return path || 'NA';
|
|
1159
|
+
}`
|
|
1160
|
+
},
|
|
1161
|
+
{
|
|
1162
|
+
id: "getSiteArea",
|
|
1163
|
+
label: "getSiteArea (blog / products / home)",
|
|
1164
|
+
deps: ["getPagePath"],
|
|
1165
|
+
code: `/**
|
|
1166
|
+
* Returns the top-level area of the site from the current path - e.g. 'blog' or
|
|
1167
|
+
* 'products', and 'home' at the site root. Uses the language-cleaned path, so you
|
|
1168
|
+
* can tell whether you're on the blog or somewhere else.
|
|
1169
|
+
* @returns {string} The site area.
|
|
1170
|
+
*/
|
|
1171
|
+
export function getSiteArea() {
|
|
1172
|
+
const path = getPagePath({ clean: true });
|
|
1173
|
+
if (!path || path === 'NA') return 'home';
|
|
1174
|
+
return path.split('/').filter(Boolean)[0] || 'home';
|
|
1175
|
+
}`
|
|
1176
|
+
},
|
|
1177
|
+
{
|
|
1178
|
+
id: "getEnvironment",
|
|
1179
|
+
label: "getEnvironment (dev / stage / prod)",
|
|
1180
|
+
code: `/**
|
|
1181
|
+
* Detects the current environment from the hostname.
|
|
1182
|
+
* @returns {'NA'|'dev'|'stage'|'prod'} The environment name.
|
|
1183
|
+
*/
|
|
1184
|
+
export function getEnvironment() {
|
|
1185
|
+
const { hostname } = window.location;
|
|
1186
|
+
if (hostname.includes('author')) return 'NA';
|
|
1187
|
+
if (hostname === 'localhost') return 'dev';
|
|
1188
|
+
const repo = hostname.match(/^[^.]+--([^.]+)--[^.]+\\.aem\\.(?:page|live)$/)?.[1] || '';
|
|
1189
|
+
if (repo.includes('dev')) return 'dev';
|
|
1190
|
+
if (repo.includes('stage')) return 'stage';
|
|
1191
|
+
return 'prod';
|
|
1192
|
+
}`
|
|
1193
|
+
},
|
|
1194
|
+
{
|
|
1195
|
+
id: "getContentTopic",
|
|
1196
|
+
label: "getContentTopic (cq-tags content-topic values)",
|
|
1197
|
+
deps: ["getMetadata"],
|
|
1198
|
+
code: `/**
|
|
1199
|
+
* Reads all content-topic values from the page's cq-tags metadata.
|
|
1200
|
+
* @returns {string[]} The topics (part after \`content-topic/\`), empty if none.
|
|
1201
|
+
*/
|
|
1202
|
+
export function getContentTopic() {
|
|
1203
|
+
return getMetadata('cq-tags')
|
|
1204
|
+
.split(',')
|
|
1205
|
+
.map((t) => t.trim())
|
|
1206
|
+
.filter((t) => t.includes('content-topic/'))
|
|
1207
|
+
.map((t) => t.split('content-topic/')[1]);
|
|
1208
|
+
}`
|
|
1209
|
+
},
|
|
1210
|
+
{
|
|
1211
|
+
id: "isUEEdit",
|
|
1212
|
+
label: "isUEEdit (Universal Editor edit mode)",
|
|
1213
|
+
code: `/**
|
|
1214
|
+
* Checks whether the current page is opened in the Universal Editor Edit mode.
|
|
1215
|
+
* @returns {boolean} True in Universal Editor Edit mode.
|
|
1216
|
+
*/
|
|
1217
|
+
export function isUEEdit() {
|
|
1218
|
+
return document.documentElement.classList.contains('adobe-ue-edit');
|
|
1219
|
+
}`
|
|
1220
|
+
},
|
|
1221
|
+
{
|
|
1222
|
+
id: "isUEPreview",
|
|
1223
|
+
label: "isUEPreview (Universal Editor preview mode)",
|
|
1224
|
+
code: `/**
|
|
1225
|
+
* Checks whether the current page is opened in the Universal Editor Preview mode.
|
|
1226
|
+
* @returns {boolean} True in Universal Editor Preview mode.
|
|
1227
|
+
*/
|
|
1228
|
+
export function isUEPreview() {
|
|
1229
|
+
return document.documentElement.classList.contains('adobe-ue-preview');
|
|
1230
|
+
}`
|
|
1231
|
+
},
|
|
1232
|
+
{
|
|
1233
|
+
id: "isUE",
|
|
1234
|
+
label: "isUE (Universal Editor, any mode)",
|
|
1235
|
+
deps: ["isUEEdit", "isUEPreview"],
|
|
1236
|
+
code: `/**
|
|
1237
|
+
* Checks whether the current page is opened in the Universal Editor (any mode).
|
|
1238
|
+
* @returns {boolean} True in the Universal Editor.
|
|
1239
|
+
*/
|
|
1240
|
+
export function isUE() {
|
|
1241
|
+
return isUEEdit() || isUEPreview();
|
|
1242
|
+
}`
|
|
1243
|
+
}
|
|
1244
|
+
];
|
|
1245
|
+
ALL_HELPER_IDS = HELPERS.map((h) => h.id);
|
|
1246
|
+
}
|
|
1247
|
+
});
|
|
1248
|
+
|
|
1249
|
+
// src/lib/scaffold/ue.ts
|
|
1250
|
+
function fieldReferenceFields() {
|
|
1251
|
+
return [
|
|
1252
|
+
{ component: "tab", name: "basics", label: "Basics" },
|
|
1253
|
+
{ component: "text", valueType: "string", name: "text", label: "Text (single line)" },
|
|
1254
|
+
{
|
|
1255
|
+
component: "textarea",
|
|
1256
|
+
valueType: "string",
|
|
1257
|
+
name: "textarea",
|
|
1258
|
+
label: "Textarea (multi-line)"
|
|
1259
|
+
},
|
|
1260
|
+
{ component: "richtext", valueType: "string", name: "richtext", value: "", label: "Rich text" },
|
|
1261
|
+
{ component: "number", valueType: "number", name: "number", label: "Number" },
|
|
1262
|
+
{ component: "boolean", valueType: "boolean", name: "boolean", value: false, label: "Boolean" },
|
|
1263
|
+
{ component: "date-time", valueType: "date", name: "date", label: "Date / time" },
|
|
1264
|
+
{ component: "tab", name: "selection", label: "Selection" },
|
|
1265
|
+
{
|
|
1266
|
+
component: "select",
|
|
1267
|
+
valueType: "string",
|
|
1268
|
+
name: "select",
|
|
1269
|
+
label: "Select",
|
|
1270
|
+
value: "a",
|
|
1271
|
+
options: [
|
|
1272
|
+
{ name: "Option A", value: "a" },
|
|
1273
|
+
{ name: "Option B", value: "b" }
|
|
1274
|
+
]
|
|
1275
|
+
},
|
|
1276
|
+
{
|
|
1277
|
+
component: "radio-group",
|
|
1278
|
+
valueType: "string",
|
|
1279
|
+
name: "radio",
|
|
1280
|
+
label: "Radio group",
|
|
1281
|
+
value: "x",
|
|
1282
|
+
options: [
|
|
1283
|
+
{ name: "X", value: "x" },
|
|
1284
|
+
{ name: "Y", value: "y" }
|
|
1285
|
+
]
|
|
1286
|
+
},
|
|
1287
|
+
{
|
|
1288
|
+
component: "multiselect",
|
|
1289
|
+
valueType: "string",
|
|
1290
|
+
name: "classes",
|
|
1291
|
+
label: "Multiselect (CSS classes)",
|
|
1292
|
+
options: [
|
|
1293
|
+
{ name: "Highlight", value: "highlight" },
|
|
1294
|
+
{ name: "Dark", value: "dark" }
|
|
1295
|
+
]
|
|
1296
|
+
},
|
|
1297
|
+
{
|
|
1298
|
+
component: "checkbox-group",
|
|
1299
|
+
valueType: "string[]",
|
|
1300
|
+
name: "toggles",
|
|
1301
|
+
label: "Checkbox group",
|
|
1302
|
+
options: [
|
|
1303
|
+
{ name: "Show title", value: "show-title" },
|
|
1304
|
+
{ name: "Show image", value: "show-image" }
|
|
1305
|
+
]
|
|
1306
|
+
},
|
|
1307
|
+
{ component: "tab", name: "media", label: "Media & content" },
|
|
738
1308
|
{ component: "reference", valueType: "string", name: "image", label: "Image (DAM asset)" },
|
|
739
1309
|
{ component: "text", valueType: "string", name: "imageAlt", label: "Image alt" },
|
|
740
1310
|
{ component: "aem-content", name: "link", label: "Link (page / URL)" },
|
|
@@ -933,1025 +1503,1158 @@ export default function decorate(block) {
|
|
|
933
1503
|
}
|
|
934
1504
|
});
|
|
935
1505
|
|
|
936
|
-
// src/
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
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
|
|
1506
|
+
// src/commands/scaffold.ts
|
|
1507
|
+
var scaffold_exports = {};
|
|
1508
|
+
__export(scaffold_exports, {
|
|
1509
|
+
scaffoldBlocks: () => scaffoldBlocks,
|
|
1510
|
+
scaffoldCi: () => scaffoldCi,
|
|
1511
|
+
scaffoldEnv: () => scaffoldEnv,
|
|
1512
|
+
scaffoldHelpers: () => scaffoldHelpers,
|
|
1513
|
+
scaffoldInteractive: () => scaffoldInteractive,
|
|
1514
|
+
scaffoldUe: () => scaffoldUe,
|
|
1515
|
+
scaffoldXwalk: () => scaffoldXwalk
|
|
1044
1516
|
});
|
|
1045
|
-
import { existsSync as
|
|
1046
|
-
import { mkdir, readFile as
|
|
1047
|
-
import
|
|
1048
|
-
|
|
1049
|
-
|
|
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 {};
|
|
1517
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1518
|
+
import { mkdir as mkdir4, readFile as readFile3, writeFile as writeFile7 } from "fs/promises";
|
|
1519
|
+
import path9 from "path";
|
|
1520
|
+
async function readJson(file) {
|
|
1521
|
+
if (!existsSync7(file)) return null;
|
|
1057
1522
|
try {
|
|
1058
|
-
return JSON.parse(await
|
|
1523
|
+
return JSON.parse(await readFile3(file, "utf-8"));
|
|
1059
1524
|
} catch {
|
|
1060
|
-
return
|
|
1525
|
+
return null;
|
|
1061
1526
|
}
|
|
1062
1527
|
}
|
|
1063
|
-
async function
|
|
1064
|
-
|
|
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)}
|
|
1528
|
+
async function writeJson(file, data) {
|
|
1529
|
+
await writeFile7(file, `${JSON.stringify(data, null, 2)}
|
|
1076
1530
|
`);
|
|
1077
|
-
return true;
|
|
1078
1531
|
}
|
|
1079
|
-
async function
|
|
1532
|
+
async function scaffoldUe() {
|
|
1080
1533
|
const projectRoot = findProjectRoot();
|
|
1081
1534
|
if (!projectRoot) {
|
|
1082
1535
|
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
1083
1536
|
process.exitCode = 1;
|
|
1084
1537
|
return;
|
|
1085
1538
|
}
|
|
1086
|
-
|
|
1087
|
-
const
|
|
1088
|
-
const
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
const
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
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."));
|
|
1539
|
+
logger.logoOnce(logo("Scaffold - Universal Editor"));
|
|
1540
|
+
const created = [];
|
|
1541
|
+
const blockDir = path9.join(projectRoot, "blocks", "field-reference");
|
|
1542
|
+
await mkdir4(blockDir, { recursive: true });
|
|
1543
|
+
await writeFile7(path9.join(blockDir, "field-reference.js"), FIELD_REFERENCE_JS);
|
|
1544
|
+
await writeFile7(path9.join(blockDir, "field-reference.css"), FIELD_REFERENCE_CSS);
|
|
1545
|
+
await writeFile7(path9.join(blockDir, "_field-reference.json"), fieldReferenceModelFile());
|
|
1546
|
+
created.push("blocks/field-reference/ (all 17 fields + multifield)");
|
|
1547
|
+
const defFile = path9.join(projectRoot, "component-definition.json");
|
|
1548
|
+
const modelsFile = path9.join(projectRoot, "component-models.json");
|
|
1549
|
+
const filtersFile = path9.join(projectRoot, "component-filters.json");
|
|
1550
|
+
const blockDefs = [
|
|
1551
|
+
{
|
|
1552
|
+
title: "Field Reference",
|
|
1553
|
+
id: "field-reference",
|
|
1554
|
+
plugins: {
|
|
1555
|
+
xwalk: {
|
|
1556
|
+
page: {
|
|
1557
|
+
resourceType: "core/franklin/components/block/v1/block",
|
|
1558
|
+
template: { name: "Field Reference", model: "field-reference" }
|
|
1559
|
+
}
|
|
1131
1560
|
}
|
|
1132
1561
|
}
|
|
1133
1562
|
}
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1563
|
+
];
|
|
1564
|
+
const definitions = mergeDefinitions(
|
|
1565
|
+
await readJson(defFile),
|
|
1566
|
+
DEFAULT_CONTENT_DEFINITIONS,
|
|
1567
|
+
blockDefs
|
|
1568
|
+
);
|
|
1569
|
+
await writeJson(defFile, definitions);
|
|
1570
|
+
created.push("component-definition.json (default content + field-reference)");
|
|
1571
|
+
const existingModels = await readJson(modelsFile) ?? [];
|
|
1572
|
+
const fieldRefModel = JSON.parse(fieldReferenceModelFile()).models[0];
|
|
1573
|
+
const models = mergeById(existingModels, [...DEFAULT_CONTENT_MODELS, fieldRefModel]);
|
|
1574
|
+
await writeJson(modelsFile, models);
|
|
1575
|
+
created.push("component-models.json (title / image / button / field-reference)");
|
|
1576
|
+
const filters = mergeSectionFilter(await readJson(filtersFile), ["field-reference"]);
|
|
1577
|
+
await writeJson(filtersFile, filters);
|
|
1578
|
+
created.push("component-filters.json (section filter)");
|
|
1579
|
+
logger.info(heading("Created / merged"));
|
|
1580
|
+
const names = created.map((c) => c.split(" ")[0]);
|
|
1581
|
+
const w = columnWidth(names);
|
|
1582
|
+
for (const c of created) {
|
|
1583
|
+
const [name, ...rest] = c.split(" ");
|
|
1584
|
+
logger.info(accentLine(name, rest.join(" ").replace(/^\((.*)\)$/, "$1"), w));
|
|
1585
|
+
}
|
|
1586
|
+
logger.info("");
|
|
1587
|
+
logger.info(box(["Universal Editor scaffold ready"]));
|
|
1588
|
+
}
|
|
1589
|
+
async function scaffoldBlocks(names) {
|
|
1590
|
+
const projectRoot = findProjectRoot();
|
|
1591
|
+
if (!projectRoot) {
|
|
1592
|
+
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
1593
|
+
process.exitCode = 1;
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
logger.logoOnce(logo("Scaffold \u2014 standard blocks"));
|
|
1597
|
+
let selected;
|
|
1598
|
+
if (names?.length) {
|
|
1599
|
+
selected = names.map((n2) => blockById(n2.trim())).filter((b) => !!b);
|
|
1600
|
+
const unknown = names.filter((n2) => !blockById(n2.trim()));
|
|
1601
|
+
if (unknown.length) {
|
|
1602
|
+
logger.warn(
|
|
1603
|
+
`Unknown block(s): ${unknown.join(", ")}. Available: ${STANDARD_BLOCKS.map((b) => b.id).join(", ")}`
|
|
1148
1604
|
);
|
|
1149
1605
|
}
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1606
|
+
} else {
|
|
1607
|
+
selected = STANDARD_BLOCKS;
|
|
1608
|
+
}
|
|
1609
|
+
const createdIds = [];
|
|
1610
|
+
const skipped = [];
|
|
1611
|
+
for (const b of selected) {
|
|
1612
|
+
const dir = path9.join(projectRoot, "blocks", b.id);
|
|
1613
|
+
if (existsSync7(dir)) {
|
|
1614
|
+
skipped.push(b.id);
|
|
1615
|
+
continue;
|
|
1616
|
+
}
|
|
1617
|
+
await mkdir4(dir, { recursive: true });
|
|
1618
|
+
await writeFile7(path9.join(dir, `${b.id}.js`), b.js);
|
|
1619
|
+
await writeFile7(path9.join(dir, `${b.id}.css`), b.css);
|
|
1620
|
+
await writeFile7(path9.join(dir, `_${b.id}.json`), b.model);
|
|
1621
|
+
createdIds.push(b.id);
|
|
1155
1622
|
}
|
|
1623
|
+
const filtersFile = path9.join(projectRoot, "component-filters.json");
|
|
1624
|
+
const filters = mergeSectionFilter(
|
|
1625
|
+
await readJson(filtersFile),
|
|
1626
|
+
selected.map((b) => b.id)
|
|
1627
|
+
);
|
|
1628
|
+
await writeJson(filtersFile, filters);
|
|
1629
|
+
logger.info(heading("Blocks"));
|
|
1630
|
+
const w = columnWidth([...createdIds, ...skipped, "component-filters"]);
|
|
1631
|
+
for (const id of createdIds) logger.info(accentLine(id, "created", w));
|
|
1632
|
+
for (const id of skipped) logger.info(statusLine("warn", id, "already exists \u2014 skipped", w));
|
|
1156
1633
|
logger.info("");
|
|
1157
|
-
logger.info(box([
|
|
1634
|
+
logger.info(box([`${createdIds.length} block(s) scaffolded + registered`]));
|
|
1158
1635
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
"
|
|
1163
|
-
|
|
1164
|
-
|
|
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, "");
|
|
1636
|
+
async function scaffoldCi() {
|
|
1637
|
+
const projectRoot = findProjectRoot();
|
|
1638
|
+
if (!projectRoot) {
|
|
1639
|
+
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
1640
|
+
process.exitCode = 1;
|
|
1641
|
+
return;
|
|
1173
1642
|
}
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
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
|
-
`;
|
|
1643
|
+
logger.logoOnce(logo("Scaffold - CI workflow"));
|
|
1644
|
+
const res = await writeCiWorkflow(projectRoot);
|
|
1645
|
+
logger.info(heading("GitHub Actions"));
|
|
1646
|
+
if (res.created) {
|
|
1647
|
+
logger.info(accentLine(res.path, "doctor + audit loading + audit security + lint/build"));
|
|
1648
|
+
} else {
|
|
1649
|
+
logger.info(statusLine("warn", res.path, "already exists - skipped"));
|
|
1237
1650
|
}
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
return a !== "ue";
|
|
1245
|
-
}
|
|
1246
|
-
function daMountpoint(org, site) {
|
|
1247
|
-
return `https://content.da.live/${org}/${site}/`;
|
|
1651
|
+
logger.info("");
|
|
1652
|
+
logger.info(
|
|
1653
|
+
box([
|
|
1654
|
+
res.created ? "CI workflow scaffolded - commit it and it runs on every PR" : "CI workflow left untouched"
|
|
1655
|
+
])
|
|
1656
|
+
);
|
|
1248
1657
|
}
|
|
1249
|
-
function
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1658
|
+
async function scaffoldHelpers(ids) {
|
|
1659
|
+
const projectRoot = findProjectRoot();
|
|
1660
|
+
if (!projectRoot) {
|
|
1661
|
+
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
1662
|
+
process.exitCode = 1;
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
logger.logoOnce(logo("Scaffold - helpers"));
|
|
1666
|
+
let selected = ids?.length ? ids : void 0;
|
|
1667
|
+
if (!selected) {
|
|
1668
|
+
if (process.stdout.isTTY) {
|
|
1669
|
+
const { checkbox } = await import("@inquirer/prompts");
|
|
1670
|
+
selected = await checkbox({
|
|
1671
|
+
message: "Which helpers?",
|
|
1672
|
+
choices: HELPERS.map((h) => ({ name: h.label, value: h.id, checked: true }))
|
|
1673
|
+
});
|
|
1674
|
+
} else {
|
|
1675
|
+
selected = ALL_HELPER_IDS;
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
if (!selected.length) {
|
|
1679
|
+
logger.info("Nothing selected.");
|
|
1680
|
+
return;
|
|
1681
|
+
}
|
|
1682
|
+
const res = await writeHelpers(projectRoot, selected);
|
|
1683
|
+
logger.info(heading("Helpers"));
|
|
1684
|
+
if (res.created) {
|
|
1685
|
+
logger.info(accentLine(res.path, res.helpers.join(", ")));
|
|
1686
|
+
} else {
|
|
1687
|
+
logger.info(statusLine("warn", res.path, "already exists - skipped"));
|
|
1688
|
+
}
|
|
1689
|
+
logger.info("");
|
|
1690
|
+
logger.info(
|
|
1691
|
+
box([
|
|
1692
|
+
res.created ? `${res.helpers.length} helper(s) -> scripts/utils.js` : "Helpers left untouched"
|
|
1693
|
+
])
|
|
1694
|
+
);
|
|
1253
1695
|
}
|
|
1254
|
-
async function
|
|
1255
|
-
|
|
1256
|
-
|
|
1696
|
+
async function scaffoldEnv(options = {}) {
|
|
1697
|
+
const projectRoot = findProjectRoot();
|
|
1698
|
+
if (!projectRoot) {
|
|
1699
|
+
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
1700
|
+
process.exitCode = 1;
|
|
1701
|
+
return;
|
|
1702
|
+
}
|
|
1703
|
+
logger.logoOnce(logo("Scaffold - .env"));
|
|
1704
|
+
let pagesUrl = options.pagesUrl;
|
|
1705
|
+
if (pagesUrl === void 0) {
|
|
1706
|
+
try {
|
|
1707
|
+
const { execa } = await import("execa");
|
|
1708
|
+
const res2 = await execa("git", ["remote", "get-url", "origin"], { cwd: projectRoot });
|
|
1709
|
+
const parsed = parseRemote(res2.stdout.trim());
|
|
1710
|
+
pagesUrl = parsed ? `https://${previewHost(parsed.owner, parsed.repo)}` : "";
|
|
1711
|
+
} catch {
|
|
1712
|
+
pagesUrl = "";
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
let open = options.open ?? "/";
|
|
1716
|
+
let port = options.port ?? "3007";
|
|
1717
|
+
if (!options.yes && process.stdout.isTTY) {
|
|
1718
|
+
const { input } = await import("@inquirer/prompts");
|
|
1719
|
+
open = await input({ message: "AEM_OPEN (path opened on start)", default: open });
|
|
1720
|
+
port = await input({ message: "AEM_PORT", default: String(port) });
|
|
1721
|
+
pagesUrl = await input({ message: "AEM_PAGES_URL (content origin)", default: pagesUrl });
|
|
1722
|
+
}
|
|
1723
|
+
const res = await writeEnv(projectRoot, { open, port, pagesUrl });
|
|
1724
|
+
logger.info(heading(".env"));
|
|
1725
|
+
if (res.created) logger.info(accentLine(res.path, `AEM_OPEN=${open} AEM_PORT=${port}`));
|
|
1726
|
+
else logger.info(statusLine("warn", res.path, "already exists - left untouched"));
|
|
1727
|
+
if (res.gitignoreUpdated) logger.info(accentLine(".gitignore", "added .env"));
|
|
1728
|
+
logger.info("");
|
|
1729
|
+
logger.info(box([res.created ? ".env ready for `aem up`" : ".env left untouched"]));
|
|
1257
1730
|
}
|
|
1258
|
-
function
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
)
|
|
1267
|
-
|
|
1731
|
+
async function scaffoldXwalk() {
|
|
1732
|
+
const projectRoot = findProjectRoot();
|
|
1733
|
+
if (!projectRoot) {
|
|
1734
|
+
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
1735
|
+
process.exitCode = 1;
|
|
1736
|
+
return;
|
|
1737
|
+
}
|
|
1738
|
+
logger.logoOnce(logo("Scaffold - xwalk.json"));
|
|
1739
|
+
const res = await writeXwalk(projectRoot);
|
|
1740
|
+
logger.info(heading("xwalk.json"));
|
|
1741
|
+
if (res.created) logger.info(accentLine(res.path, "multi-field enabled"));
|
|
1742
|
+
else logger.info(statusLine("warn", res.path, "already exists - left untouched"));
|
|
1743
|
+
logger.info("");
|
|
1744
|
+
logger.info(
|
|
1745
|
+
box([res.created ? "xwalk.json ready (UE multi-field)" : "xwalk.json left untouched"])
|
|
1746
|
+
);
|
|
1268
1747
|
}
|
|
1269
|
-
async function
|
|
1270
|
-
await
|
|
1271
|
-
|
|
1748
|
+
async function scaffoldInteractive() {
|
|
1749
|
+
const { select, checkbox } = await import("@inquirer/prompts");
|
|
1750
|
+
logger.logoOnce(logo("Scaffold"));
|
|
1751
|
+
const what = await select({
|
|
1752
|
+
message: "What do you want to scaffold?",
|
|
1753
|
+
choices: [
|
|
1754
|
+
{ name: "Universal Editor config + field-reference (all 17 field types)", value: "ue" },
|
|
1755
|
+
{ name: "Standard blocks (hero, cards, columns, accordion, embed)", value: "blocks" },
|
|
1756
|
+
{ name: "GitHub Actions CI (doctor + audits + lint/build)", value: "ci" },
|
|
1757
|
+
{ name: "Helper functions (scripts/utils.js)", value: "helpers" },
|
|
1758
|
+
{ name: "Local-dev .env (aem up: port / open / pages URL)", value: "env" },
|
|
1759
|
+
{ name: "xwalk.json (enable Universal Editor multi-field)", value: "xwalk" },
|
|
1760
|
+
{ name: "UE config + standard blocks", value: "both" }
|
|
1761
|
+
]
|
|
1762
|
+
});
|
|
1763
|
+
if (what === "env") return scaffoldEnv();
|
|
1764
|
+
if (what === "xwalk") return scaffoldXwalk();
|
|
1765
|
+
if (what === "ue" || what === "both") await scaffoldUe();
|
|
1766
|
+
if (what === "blocks" || what === "both") {
|
|
1767
|
+
const picked = await checkbox({
|
|
1768
|
+
message: "Which blocks?",
|
|
1769
|
+
choices: STANDARD_BLOCKS.map((b) => ({ name: b.title, value: b.id, checked: true }))
|
|
1770
|
+
});
|
|
1771
|
+
await scaffoldBlocks(picked);
|
|
1772
|
+
}
|
|
1773
|
+
if (what === "ci") await scaffoldCi();
|
|
1774
|
+
if (what === "helpers") await scaffoldHelpers();
|
|
1272
1775
|
}
|
|
1273
|
-
var
|
|
1274
|
-
|
|
1275
|
-
"src/lib/scaffold/fstab.ts"() {
|
|
1776
|
+
var init_scaffold = __esm({
|
|
1777
|
+
"src/commands/scaffold.ts"() {
|
|
1276
1778
|
"use strict";
|
|
1277
1779
|
init_esm_shims();
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1780
|
+
init_logger();
|
|
1781
|
+
init_project();
|
|
1782
|
+
init_blocks();
|
|
1783
|
+
init_ci();
|
|
1784
|
+
init_env();
|
|
1785
|
+
init_helpers();
|
|
1786
|
+
init_ue();
|
|
1787
|
+
init_xwalk();
|
|
1788
|
+
init_link();
|
|
1789
|
+
init_ui();
|
|
1284
1790
|
}
|
|
1285
1791
|
});
|
|
1286
1792
|
|
|
1287
|
-
// src/
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1793
|
+
// src/commands/init.ts
|
|
1794
|
+
var init_exports = {};
|
|
1795
|
+
__export(init_exports, {
|
|
1796
|
+
initProject: () => initProject
|
|
1797
|
+
});
|
|
1798
|
+
import { existsSync as existsSync8 } from "fs";
|
|
1799
|
+
import { readFile as readFile4, writeFile as writeFile8 } from "fs/promises";
|
|
1800
|
+
import path10 from "path";
|
|
1801
|
+
import chalk3 from "chalk";
|
|
1802
|
+
async function derivePagesUrl(root) {
|
|
1803
|
+
try {
|
|
1804
|
+
const { execa } = await import("execa");
|
|
1805
|
+
const res = await execa("git", ["remote", "get-url", "origin"], { cwd: root });
|
|
1806
|
+
const parsed = parseRemote(res.stdout.trim());
|
|
1807
|
+
return parsed ? `https://${previewHost(parsed.owner, parsed.repo)}` : "";
|
|
1808
|
+
} catch {
|
|
1809
|
+
return "";
|
|
1810
|
+
}
|
|
1293
1811
|
}
|
|
1294
|
-
function
|
|
1295
|
-
const
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
const
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1812
|
+
async function setPackageName(root, name) {
|
|
1813
|
+
const file = path10.join(root, "package.json");
|
|
1814
|
+
if (!existsSync8(file)) return false;
|
|
1815
|
+
try {
|
|
1816
|
+
const pkg = JSON.parse(await readFile4(file, "utf-8"));
|
|
1817
|
+
pkg.name = name;
|
|
1818
|
+
await writeFile8(file, `${JSON.stringify(pkg, null, 2)}
|
|
1819
|
+
`);
|
|
1820
|
+
return true;
|
|
1821
|
+
} catch {
|
|
1822
|
+
return false;
|
|
1823
|
+
}
|
|
1305
1824
|
}
|
|
1306
|
-
function
|
|
1307
|
-
const
|
|
1308
|
-
const
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1825
|
+
async function replacePlaceholders(root, name) {
|
|
1826
|
+
const touched = [];
|
|
1827
|
+
for (const rel of ["README.md", "index.html", "head.html"]) {
|
|
1828
|
+
const file = path10.join(root, rel);
|
|
1829
|
+
if (!existsSync8(file)) continue;
|
|
1830
|
+
try {
|
|
1831
|
+
const before = await readFile4(file, "utf-8");
|
|
1832
|
+
const after = before.replace(/aem-boilerplate/gi, name);
|
|
1833
|
+
if (after !== before) {
|
|
1834
|
+
await writeFile8(file, after);
|
|
1835
|
+
touched.push(rel);
|
|
1836
|
+
}
|
|
1837
|
+
} catch {
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
return touched;
|
|
1313
1841
|
}
|
|
1314
|
-
async function
|
|
1315
|
-
const
|
|
1316
|
-
const
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1842
|
+
async function externalSteps(authoring) {
|
|
1843
|
+
const { confirm } = await import("@inquirer/prompts");
|
|
1844
|
+
const steps = [
|
|
1845
|
+
{
|
|
1846
|
+
title: "Install the AEM Code Sync GitHub app",
|
|
1847
|
+
url: "https://github.com/apps/aem-code-sync/installations/new",
|
|
1848
|
+
note: "Connects your repo to the Edge Delivery pipeline (preview/publish)."
|
|
1849
|
+
}
|
|
1850
|
+
];
|
|
1851
|
+
if (authoring === "gdrive") {
|
|
1852
|
+
steps.push({
|
|
1853
|
+
title: "Share your content folder",
|
|
1854
|
+
note: "Share the Google Drive folder with the AEM service and set it as the mountpoint."
|
|
1855
|
+
});
|
|
1856
|
+
}
|
|
1857
|
+
if (authoring === "sharepoint") {
|
|
1858
|
+
steps.push({
|
|
1859
|
+
title: "Share your content folder",
|
|
1860
|
+
note: "Grant the SharePoint/OneDrive folder to the AEM service account."
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1863
|
+
if (authoring === "da") {
|
|
1864
|
+
steps.push({
|
|
1865
|
+
title: "Author in DA",
|
|
1866
|
+
url: "https://da.live",
|
|
1867
|
+
note: "Create documents at da.live."
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
if (authoring === "ue") {
|
|
1871
|
+
steps.push({
|
|
1872
|
+
title: "Open the Universal Editor",
|
|
1873
|
+
url: "https://experience.adobe.com",
|
|
1874
|
+
note: "Author in-context once the repo is connected to AEM."
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
steps.push({
|
|
1878
|
+
title: "Install the AEM Sidekick extension",
|
|
1879
|
+
url: "https://chromewebstore.google.com/detail/aem-sidekick/igkmdomcgoebiipaifhmpfjhbjccggml",
|
|
1880
|
+
note: "Preview and publish from the browser."
|
|
1881
|
+
});
|
|
1882
|
+
steps.push({
|
|
1883
|
+
title: "Run the local dev server",
|
|
1884
|
+
note: "npm install, then `aem up` (from @adobe/aem-cli)."
|
|
1885
|
+
});
|
|
1886
|
+
logger.info(heading("Next steps (outside the repo)"));
|
|
1887
|
+
for (let i = 0; i < steps.length; i++) {
|
|
1888
|
+
const s = steps[i];
|
|
1889
|
+
logger.info(` ${brand.accent(String(i + 1))} ${chalk3.bold(s.title)}`);
|
|
1890
|
+
if (s.url) logger.info(` ${chalk3.cyan(s.url)}`);
|
|
1891
|
+
logger.info(` ${chalk3.dim(s.note)}`);
|
|
1892
|
+
await confirm({ message: " Done / skip?", default: true });
|
|
1893
|
+
}
|
|
1321
1894
|
}
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1895
|
+
async function initProject(options = {}) {
|
|
1896
|
+
const projectRoot = findProjectRoot();
|
|
1897
|
+
if (!projectRoot) {
|
|
1898
|
+
logger.error(
|
|
1899
|
+
"Not inside an EDS project (no EDS project markers found). Clone an EDS boilerplate first, then run this from its root."
|
|
1900
|
+
);
|
|
1901
|
+
process.exitCode = 1;
|
|
1902
|
+
return;
|
|
1903
|
+
}
|
|
1904
|
+
await logger.logoOnceAnimated("Project setup");
|
|
1905
|
+
const { input, select, confirm } = await import("@inquirer/prompts");
|
|
1906
|
+
const interactive = !options.yes;
|
|
1907
|
+
let defaultName = path10.basename(projectRoot);
|
|
1908
|
+
const pkgFile = path10.join(projectRoot, "package.json");
|
|
1909
|
+
if (existsSync8(pkgFile)) {
|
|
1910
|
+
try {
|
|
1911
|
+
defaultName = JSON.parse(await readFile4(pkgFile, "utf-8")).name || defaultName;
|
|
1912
|
+
} catch {
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
const name = options.name && slug(options.name) || slug(
|
|
1916
|
+
await input({
|
|
1917
|
+
message: "1/4 Project name",
|
|
1918
|
+
default: slug(defaultName),
|
|
1919
|
+
validate: (v) => !!slug(v) || "Use letters, numbers and dashes"
|
|
1920
|
+
})
|
|
1921
|
+
);
|
|
1922
|
+
const authoring = options.authoring ?? await select({
|
|
1923
|
+
message: "2/4 Authoring model",
|
|
1924
|
+
choices: [
|
|
1925
|
+
{ name: AUTHORING_NAMES.ue, value: "ue" },
|
|
1926
|
+
{ name: AUTHORING_NAMES.da, value: "da" },
|
|
1927
|
+
{ name: AUTHORING_NAMES.gdrive, value: "gdrive" },
|
|
1928
|
+
{ name: AUTHORING_NAMES.sharepoint, value: "sharepoint" }
|
|
1929
|
+
]
|
|
1930
|
+
});
|
|
1931
|
+
let mountpoint = options.mountpoint;
|
|
1932
|
+
if (needsFstab(authoring) && !mountpoint) {
|
|
1933
|
+
if (authoring === "da" && (options.org || interactive)) {
|
|
1934
|
+
const org = options.org ?? await input({ message: " DA org", validate: (v) => !!v.trim() || "Required" });
|
|
1935
|
+
const site = options.site ?? (interactive ? await input({ message: " DA site", default: name }) : name);
|
|
1936
|
+
mountpoint = daMountpoint(org.trim(), slug(site));
|
|
1937
|
+
} else if (authoring !== "da" && interactive) {
|
|
1938
|
+
const label = authoring === "gdrive" ? "Google Drive folder URL" : "SharePoint folder URL";
|
|
1939
|
+
mountpoint = (await input({
|
|
1940
|
+
message: ` ${label}`,
|
|
1941
|
+
validate: (v) => /^https?:\/\//.test(v.trim()) || "Paste the folder URL"
|
|
1942
|
+
})).trim();
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
const withCi = options.ci ?? (interactive ? await confirm({ message: "4/4 Add GitHub Actions CI (audits gatekeeper)?", default: true }) : true);
|
|
1946
|
+
logger.info(heading("Summary"));
|
|
1947
|
+
const w = columnWidth(["name", "authoring", "mountpoint", "ci"]);
|
|
1948
|
+
logger.info(accentLine("name", name, w));
|
|
1949
|
+
logger.info(accentLine("authoring", AUTHORING_NAMES[authoring], w));
|
|
1950
|
+
if (mountpoint) logger.info(accentLine("mountpoint", mountpoint, w));
|
|
1951
|
+
else if (needsFstab(authoring))
|
|
1952
|
+
logger.info(accentLine("mountpoint", "(skipped - set it in fstab.yaml later)", w));
|
|
1953
|
+
logger.info(accentLine("ci", withCi ? "yes" : "no", w));
|
|
1954
|
+
logger.info("");
|
|
1955
|
+
if (interactive) {
|
|
1956
|
+
const go = await confirm({ message: "Apply this setup?", default: true });
|
|
1957
|
+
if (!go) {
|
|
1958
|
+
logger.info("Cancelled.");
|
|
1959
|
+
return;
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
const done = [];
|
|
1963
|
+
if (await setPackageName(projectRoot, name)) done.push("package.json (name)");
|
|
1964
|
+
for (const t of await replacePlaceholders(projectRoot, name)) done.push(`${t} (renamed)`);
|
|
1965
|
+
if (needsFstab(authoring) && mountpoint) done.push(await writeFstab(projectRoot, mountpoint));
|
|
1966
|
+
if (authoring === "ue") {
|
|
1967
|
+
await scaffoldUe();
|
|
1968
|
+
done.push(await writePaths(projectRoot, name));
|
|
1969
|
+
}
|
|
1970
|
+
if (withCi) {
|
|
1971
|
+
const ci = await writeCiWorkflow(projectRoot);
|
|
1972
|
+
done.push(ci.created ? ci.path : `${ci.path} (exists, skipped)`);
|
|
1973
|
+
}
|
|
1974
|
+
if (options.env !== false) {
|
|
1975
|
+
const res = await writeEnv(projectRoot, { pagesUrl: await derivePagesUrl(projectRoot) });
|
|
1976
|
+
done.push(res.created ? ".env (aem up config)" : ".env (exists, skipped)");
|
|
1977
|
+
}
|
|
1978
|
+
if (options.xwalk !== false && authoring === "ue") {
|
|
1979
|
+
const res = await writeXwalk(projectRoot);
|
|
1980
|
+
done.push(res.created ? `${res.path} (UE multi-field)` : `${res.path} (exists, skipped)`);
|
|
1981
|
+
}
|
|
1982
|
+
logger.info(heading("Configured"));
|
|
1983
|
+
const dw = columnWidth(done.map((d) => d.split(" ")[0]));
|
|
1984
|
+
for (const d of done) {
|
|
1985
|
+
const [n2, ...rest] = d.split(" ");
|
|
1986
|
+
logger.info(accentLine(n2, rest.join(" ").replace(/^\((.*)\)$/, "$1"), dw));
|
|
1987
|
+
}
|
|
1988
|
+
logger.info("");
|
|
1989
|
+
if (interactive) await externalSteps(authoring);
|
|
1990
|
+
logger.info(box([`"${name}" set up for ${AUTHORING_NAMES[authoring]}`]));
|
|
1991
|
+
}
|
|
1992
|
+
var slug;
|
|
1993
|
+
var init_init = __esm({
|
|
1994
|
+
"src/commands/init.ts"() {
|
|
1325
1995
|
"use strict";
|
|
1326
1996
|
init_esm_shims();
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
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';
|
|
1997
|
+
init_logger();
|
|
1998
|
+
init_project();
|
|
1999
|
+
init_ci();
|
|
2000
|
+
init_env();
|
|
2001
|
+
init_fstab();
|
|
2002
|
+
init_xwalk();
|
|
2003
|
+
init_link();
|
|
2004
|
+
init_ui();
|
|
2005
|
+
init_scaffold();
|
|
2006
|
+
slug = (s) => s.trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1392
2007
|
}
|
|
2008
|
+
});
|
|
1393
2009
|
|
|
1394
|
-
|
|
1395
|
-
}
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
}
|
|
1467
|
-
}
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
2010
|
+
// src/commands/sandbox.ts
|
|
2011
|
+
var sandbox_exports = {};
|
|
2012
|
+
__export(sandbox_exports, {
|
|
2013
|
+
sandboxList: () => sandboxList,
|
|
2014
|
+
sandboxLogin: () => sandboxLogin,
|
|
2015
|
+
sandboxLogout: () => sandboxLogout,
|
|
2016
|
+
sandboxNew: () => sandboxNew,
|
|
2017
|
+
sandboxRm: () => sandboxRm
|
|
2018
|
+
});
|
|
2019
|
+
import path11 from "path";
|
|
2020
|
+
import chalk4 from "chalk";
|
|
2021
|
+
async function run(cmd, args, opts = {}) {
|
|
2022
|
+
const { execa } = await import("execa");
|
|
2023
|
+
const res = await execa(cmd, args, opts);
|
|
2024
|
+
return (res.stdout ?? "").toString().trim();
|
|
2025
|
+
}
|
|
2026
|
+
async function openUrl(url) {
|
|
2027
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
2028
|
+
try {
|
|
2029
|
+
await run(cmd, [url], { stdio: "ignore" });
|
|
2030
|
+
} catch {
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
async function ghLogin() {
|
|
2034
|
+
try {
|
|
2035
|
+
await run("gh", ["auth", "status"]);
|
|
2036
|
+
return await run("gh", ["api", "user", "-q", ".login"]);
|
|
2037
|
+
} catch {
|
|
2038
|
+
return null;
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
async function resolveAuth() {
|
|
2042
|
+
const token = await getToken();
|
|
2043
|
+
if (token) {
|
|
2044
|
+
try {
|
|
2045
|
+
return { mode: "api", token, login: await getUser(token) };
|
|
2046
|
+
} catch {
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
const login = await ghLogin();
|
|
2050
|
+
if (login) return { mode: "gh", login };
|
|
2051
|
+
return null;
|
|
2052
|
+
}
|
|
2053
|
+
async function exists(auth, owner, name) {
|
|
2054
|
+
if (auth.mode === "api") return repoExists(auth.token, owner, name);
|
|
2055
|
+
try {
|
|
2056
|
+
await run("gh", ["repo", "view", `${owner}/${name}`, "--json", "name"]);
|
|
2057
|
+
return true;
|
|
2058
|
+
} catch {
|
|
2059
|
+
return false;
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
function noAuth() {
|
|
2063
|
+
logger.error("Not authenticated with GitHub.");
|
|
2064
|
+
logger.info(" Run `eds sandbox login` to authorize GitHub, or install + `gh auth login`.");
|
|
2065
|
+
process.exitCode = 1;
|
|
2066
|
+
}
|
|
2067
|
+
async function sandboxLogin() {
|
|
2068
|
+
await logger.logoOnceAnimated("Sandbox - GitHub login");
|
|
2069
|
+
const { input } = await import("@inquirer/prompts");
|
|
2070
|
+
if (CLIENT_ID) {
|
|
2071
|
+
const code = await startDeviceFlow(["repo", "delete_repo"]);
|
|
2072
|
+
logger.info(heading("Authorize GitHub"));
|
|
2073
|
+
logger.info(accentLine("code", code.user_code));
|
|
2074
|
+
logger.info(accentLine("open", code.verification_uri));
|
|
2075
|
+
logger.info("");
|
|
2076
|
+
await openUrl(code.verification_uri);
|
|
2077
|
+
logger.info(chalk4.dim(" Waiting for you to authorize in the browser..."));
|
|
2078
|
+
try {
|
|
2079
|
+
const token2 = await pollDeviceToken(code.device_code, code.interval, code.expires_in);
|
|
2080
|
+
await saveToken(token2);
|
|
2081
|
+
logger.success(`Authorized as ${await getUser(token2)}.`);
|
|
2082
|
+
} catch (err) {
|
|
2083
|
+
logger.error(`Device flow failed: ${err.message}`);
|
|
2084
|
+
process.exitCode = 1;
|
|
2085
|
+
}
|
|
2086
|
+
return;
|
|
2087
|
+
}
|
|
2088
|
+
logger.info(heading("Authorize GitHub (least-privilege token)"));
|
|
2089
|
+
logger.info(" Create a fine-grained token limited to your sandboxes only:");
|
|
2090
|
+
logger.info(` ${chalk4.cyan("https://github.com/settings/personal-access-tokens/new")}`);
|
|
2091
|
+
logger.info(
|
|
2092
|
+
chalk4.dim(" - Resource owner: a dedicated sandbox org (recommended) or your account")
|
|
2093
|
+
);
|
|
2094
|
+
logger.info(
|
|
2095
|
+
chalk4.dim(" - Repository access: All repositories in that org (to create new sandboxes),")
|
|
2096
|
+
);
|
|
2097
|
+
logger.info(chalk4.dim(" or Only select repositories to manage existing ones"));
|
|
2098
|
+
logger.info(
|
|
2099
|
+
chalk4.dim(" - Permissions: Administration = Read and write, Contents = Read and write,")
|
|
2100
|
+
);
|
|
2101
|
+
logger.info(chalk4.dim(" Metadata = Read. Set a short expiry."));
|
|
2102
|
+
logger.info(
|
|
2103
|
+
chalk4.dim(
|
|
2104
|
+
" Tip: `eds sandbox new --org <sandbox-org>` so nothing in your personal account is touched."
|
|
2105
|
+
)
|
|
2106
|
+
);
|
|
2107
|
+
logger.info("");
|
|
2108
|
+
if (await (await import("@inquirer/prompts")).confirm({
|
|
2109
|
+
message: "Open the token page?",
|
|
2110
|
+
default: true
|
|
2111
|
+
})) {
|
|
2112
|
+
await openUrl("https://github.com/settings/personal-access-tokens/new");
|
|
1482
2113
|
}
|
|
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;
|
|
2114
|
+
const token = (await input({ message: "Paste the token", validate: (v) => !!v.trim() || "Required" })).trim();
|
|
1499
2115
|
try {
|
|
1500
|
-
|
|
2116
|
+
const login = await getUser(token);
|
|
2117
|
+
await saveToken(token);
|
|
2118
|
+
logger.success(`Authorized as ${login}.`);
|
|
1501
2119
|
} catch {
|
|
1502
|
-
|
|
2120
|
+
logger.error("That token didn't work (check the scopes).");
|
|
2121
|
+
process.exitCode = 1;
|
|
1503
2122
|
}
|
|
1504
2123
|
}
|
|
1505
|
-
async function
|
|
1506
|
-
await
|
|
1507
|
-
|
|
2124
|
+
async function sandboxLogout() {
|
|
2125
|
+
await clearToken();
|
|
2126
|
+
logger.success("Signed out (stored GitHub token removed).");
|
|
1508
2127
|
}
|
|
1509
|
-
async function
|
|
1510
|
-
const
|
|
1511
|
-
if (!
|
|
1512
|
-
|
|
2128
|
+
async function sandboxNew(name, options = {}) {
|
|
2129
|
+
const auth = await resolveAuth();
|
|
2130
|
+
if (!auth) return noAuth();
|
|
2131
|
+
await logger.logoOnceAnimated("Sandbox");
|
|
2132
|
+
const interactive = !options.yes;
|
|
2133
|
+
const { select, confirm } = await import("@inquirer/prompts");
|
|
2134
|
+
let template2 = options.boilerplate;
|
|
2135
|
+
if (!template2) {
|
|
2136
|
+
if (options.ue) template2 = BOILERPLATES.ue;
|
|
2137
|
+
else if (interactive) {
|
|
2138
|
+
template2 = await select({
|
|
2139
|
+
message: "Which boilerplate?",
|
|
2140
|
+
choices: [
|
|
2141
|
+
{ name: "Document Authoring (adobe/aem-boilerplate)", value: BOILERPLATES.document },
|
|
2142
|
+
{
|
|
2143
|
+
name: "Universal Editor / crosswalk (adobe/aem-boilerplate-xwalk)",
|
|
2144
|
+
value: BOILERPLATES.ue
|
|
2145
|
+
}
|
|
2146
|
+
]
|
|
2147
|
+
});
|
|
2148
|
+
} else template2 = BOILERPLATES.document;
|
|
2149
|
+
}
|
|
2150
|
+
let priv = options.private ?? false;
|
|
2151
|
+
if (options.private === void 0 && interactive) {
|
|
2152
|
+
priv = await select({
|
|
2153
|
+
message: "Visibility?",
|
|
2154
|
+
choices: [
|
|
2155
|
+
{ name: "Public", value: false },
|
|
2156
|
+
{ name: "Private", value: true }
|
|
2157
|
+
],
|
|
2158
|
+
default: false
|
|
2159
|
+
});
|
|
2160
|
+
}
|
|
2161
|
+
const owner = options.org ?? auth.login;
|
|
2162
|
+
if (await exists(auth, owner, name)) {
|
|
2163
|
+
logger.error(`A repository ${owner}/${name} already exists - refusing to touch it.`);
|
|
2164
|
+
logger.info(
|
|
2165
|
+
chalk4.dim(" Pick another name, or remove it first (e.g. `eds sandbox rm` for a sandbox).")
|
|
2166
|
+
);
|
|
1513
2167
|
process.exitCode = 1;
|
|
1514
2168
|
return;
|
|
1515
2169
|
}
|
|
1516
|
-
logger.
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
plugins: {
|
|
1532
|
-
xwalk: {
|
|
1533
|
-
page: {
|
|
1534
|
-
resourceType: "core/franklin/components/block/v1/block",
|
|
1535
|
-
template: { name: "Field Reference", model: "field-reference" }
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
2170
|
+
logger.info(heading("Creating sandbox", `${owner}/${name} \xB7 ${template2}`));
|
|
2171
|
+
try {
|
|
2172
|
+
if (auth.mode === "api") {
|
|
2173
|
+
const repo = await createFromTemplate(auth.token, template2, { owner, name, private: priv });
|
|
2174
|
+
await addTopics(auth.token, owner, name, [SANDBOX_TOPIC]);
|
|
2175
|
+
logger.info(chalk4.dim(" Cloning..."));
|
|
2176
|
+
await run("git", ["clone", repo.cloneUrl, name], { stdio: "inherit" });
|
|
2177
|
+
} else {
|
|
2178
|
+
const repoRef = options.org ? `${options.org}/${name}` : name;
|
|
2179
|
+
await run("gh", createArgs(repoRef, { template: template2, private: priv, clone: true }), {
|
|
2180
|
+
stdio: "inherit"
|
|
2181
|
+
});
|
|
2182
|
+
try {
|
|
2183
|
+
await run("gh", ["repo", "edit", `${owner}/${name}`, "--add-topic", SANDBOX_TOPIC]);
|
|
2184
|
+
} catch {
|
|
1538
2185
|
}
|
|
1539
2186
|
}
|
|
1540
|
-
|
|
1541
|
-
|
|
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).");
|
|
2187
|
+
} catch (err) {
|
|
2188
|
+
logger.error(`Create failed: ${err.message}`);
|
|
1570
2189
|
process.exitCode = 1;
|
|
1571
2190
|
return;
|
|
1572
2191
|
}
|
|
1573
|
-
logger.
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
2192
|
+
logger.info("");
|
|
2193
|
+
logger.info(heading("Sandbox ready"));
|
|
2194
|
+
logger.info(accentLine("repo", `https://github.com/${owner}/${name}`));
|
|
2195
|
+
logger.info(accentLine("preview", previewUrl(owner, name)));
|
|
2196
|
+
logger.info("");
|
|
2197
|
+
if (interactive) {
|
|
2198
|
+
if (await confirm({ message: "Open the AEM Code Sync install page now?", default: true })) {
|
|
2199
|
+
await openUrl(codeSyncInstallUrl());
|
|
2200
|
+
}
|
|
2201
|
+
if (await confirm({ message: `Configure authoring now (eds init in ./${name})?`, default: true })) {
|
|
2202
|
+
const prevCwd = process.cwd();
|
|
2203
|
+
try {
|
|
2204
|
+
process.chdir(path11.join(prevCwd, name));
|
|
2205
|
+
const { initProject: initProject2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
2206
|
+
await initProject2({});
|
|
2207
|
+
} finally {
|
|
2208
|
+
process.chdir(prevCwd);
|
|
2209
|
+
}
|
|
1582
2210
|
}
|
|
1583
2211
|
} else {
|
|
1584
|
-
|
|
2212
|
+
logger.info(chalk4.dim(` Install AEM Code Sync: ${codeSyncInstallUrl()}`));
|
|
2213
|
+
logger.info(chalk4.dim(` Configure authoring: cd ${name} && eds init`));
|
|
1585
2214
|
}
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
2215
|
+
logger.info("");
|
|
2216
|
+
logger.info(box([`Sandbox "${name}" ready - tear down with \`eds sandbox rm ${name}\``]));
|
|
2217
|
+
}
|
|
2218
|
+
async function sandboxList() {
|
|
2219
|
+
const auth = await resolveAuth();
|
|
2220
|
+
if (!auth) return noAuth();
|
|
2221
|
+
let names = [];
|
|
2222
|
+
try {
|
|
2223
|
+
if (auth.mode === "api") {
|
|
2224
|
+
names = await searchByTopic(auth.token, auth.login, SANDBOX_TOPIC);
|
|
2225
|
+
} else {
|
|
2226
|
+
const out = await run("gh", [
|
|
2227
|
+
"repo",
|
|
2228
|
+
"list",
|
|
2229
|
+
"--topic",
|
|
2230
|
+
SANDBOX_TOPIC,
|
|
2231
|
+
"--limit",
|
|
2232
|
+
"100",
|
|
2233
|
+
"--json",
|
|
2234
|
+
"nameWithOwner",
|
|
2235
|
+
"-q",
|
|
2236
|
+
".[].nameWithOwner"
|
|
2237
|
+
]);
|
|
2238
|
+
names = out ? out.split("\n").filter(Boolean) : [];
|
|
1593
2239
|
}
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
2240
|
+
} catch (err) {
|
|
2241
|
+
logger.error(`List failed: ${err.message}`);
|
|
2242
|
+
process.exitCode = 1;
|
|
2243
|
+
return;
|
|
2244
|
+
}
|
|
2245
|
+
logger.info(heading("Sandboxes", `${names.length} tagged ${SANDBOX_TOPIC}`));
|
|
2246
|
+
if (names.length === 0) {
|
|
2247
|
+
logger.info(chalk4.dim(" None yet. Create one with `eds sandbox new <name>`."));
|
|
2248
|
+
return;
|
|
2249
|
+
}
|
|
2250
|
+
const w = columnWidth(names);
|
|
2251
|
+
for (const full of names) {
|
|
2252
|
+
const [o, n2] = full.split("/");
|
|
2253
|
+
logger.info(accentLine(full, previewUrl(o, n2), w));
|
|
1599
2254
|
}
|
|
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
2255
|
}
|
|
1613
|
-
async function
|
|
1614
|
-
const
|
|
1615
|
-
if (!
|
|
1616
|
-
|
|
2256
|
+
async function sandboxRm(name, options = {}) {
|
|
2257
|
+
const auth = await resolveAuth();
|
|
2258
|
+
if (!auth) return noAuth();
|
|
2259
|
+
const owner = options.org ?? auth.login;
|
|
2260
|
+
const repoRef = `${owner}/${name}`;
|
|
2261
|
+
if (!options.yes) {
|
|
2262
|
+
const { confirm } = await import("@inquirer/prompts");
|
|
2263
|
+
const go = await confirm({
|
|
2264
|
+
message: `Permanently delete ${repoRef}? This cannot be undone.`,
|
|
2265
|
+
default: false
|
|
2266
|
+
});
|
|
2267
|
+
if (!go) {
|
|
2268
|
+
logger.info("Cancelled.");
|
|
2269
|
+
return;
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
try {
|
|
2273
|
+
if (auth.mode === "api") await deleteRepo(auth.token, owner, name);
|
|
2274
|
+
else await run("gh", ["repo", "delete", repoRef, "--yes"]);
|
|
2275
|
+
} catch (err) {
|
|
2276
|
+
logger.error(`Delete failed: ${err.message}`);
|
|
2277
|
+
logger.info(
|
|
2278
|
+
chalk4.dim(
|
|
2279
|
+
" Deleting needs the delete_repo scope (re-run `eds sandbox login`, or `gh auth refresh -s delete_repo`)."
|
|
2280
|
+
)
|
|
2281
|
+
);
|
|
1617
2282
|
process.exitCode = 1;
|
|
1618
2283
|
return;
|
|
1619
2284
|
}
|
|
1620
|
-
logger.
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
2285
|
+
logger.info(heading("Deleted"));
|
|
2286
|
+
logger.info(accentLine(repoRef, "removed"));
|
|
2287
|
+
}
|
|
2288
|
+
var init_sandbox = __esm({
|
|
2289
|
+
"src/commands/sandbox.ts"() {
|
|
2290
|
+
"use strict";
|
|
2291
|
+
init_esm_shims();
|
|
2292
|
+
init_logger();
|
|
2293
|
+
init_api();
|
|
2294
|
+
init_auth();
|
|
2295
|
+
init_github();
|
|
2296
|
+
init_ui();
|
|
1627
2297
|
}
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
2298
|
+
});
|
|
2299
|
+
|
|
2300
|
+
// src/lib/template/authoring.ts
|
|
2301
|
+
import { existsSync as existsSync9 } from "fs";
|
|
2302
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
2303
|
+
import path12 from "path";
|
|
2304
|
+
import { parse as parseYaml } from "yaml";
|
|
2305
|
+
async function detectAuthoring(projectRoot) {
|
|
2306
|
+
const fstab = path12.join(projectRoot, "fstab.yaml");
|
|
2307
|
+
if (existsSync9(fstab)) {
|
|
2308
|
+
try {
|
|
2309
|
+
const parsed = parseYaml(await readFile5(fstab, "utf-8")) ?? {};
|
|
2310
|
+
const mp = parsed.mountpoints?.["/"];
|
|
2311
|
+
const url = typeof mp === "string" ? mp : mp?.url ?? "";
|
|
2312
|
+
const da = url.match(/content\.da\.live\/([^/]+)\/([^/]+)/);
|
|
2313
|
+
if (da) return { model: "da", org: da[1], site: da[2], mountpoint: url };
|
|
2314
|
+
if (/drive\.google\.com/.test(url)) return { model: "gdrive", mountpoint: url };
|
|
2315
|
+
if (/sharepoint\.com|-my\.sharepoint/.test(url))
|
|
2316
|
+
return { model: "sharepoint", mountpoint: url };
|
|
2317
|
+
return { model: "document", mountpoint: url };
|
|
2318
|
+
} catch {
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
return { model: "ue" };
|
|
2322
|
+
}
|
|
2323
|
+
var init_authoring = __esm({
|
|
2324
|
+
"src/lib/template/authoring.ts"() {
|
|
2325
|
+
"use strict";
|
|
2326
|
+
init_esm_shims();
|
|
2327
|
+
}
|
|
2328
|
+
});
|
|
2329
|
+
|
|
2330
|
+
// src/lib/template/da.ts
|
|
2331
|
+
function daSourceUrl(org, site, path36) {
|
|
2332
|
+
const clean = path36.replace(/^\/+/, "").replace(/\.html$/i, "");
|
|
2333
|
+
return `https://admin.da.live/source/${org}/${site}/${clean}.html`;
|
|
2334
|
+
}
|
|
2335
|
+
function daEditUrl(org, site, path36) {
|
|
2336
|
+
const clean = path36.replace(/^\/+/, "").replace(/\.html$/i, "");
|
|
2337
|
+
return `https://da.live/edit#/${org}/${site}/${clean}`;
|
|
2338
|
+
}
|
|
2339
|
+
async function pushToDa(org, site, path36, html, token) {
|
|
2340
|
+
const url = daSourceUrl(org, site, path36);
|
|
2341
|
+
const form = new FormData();
|
|
2342
|
+
form.append("data", new Blob([html], { type: "text/html" }), "content.html");
|
|
2343
|
+
const headers2 = {};
|
|
2344
|
+
if (token) headers2.Authorization = `Bearer ${token}`;
|
|
2345
|
+
const res = await fetch(url, { method: "POST", body: form, headers: headers2 });
|
|
2346
|
+
if (!res.ok) {
|
|
2347
|
+
throw new Error(`DA push failed: ${res.status} ${res.statusText}`);
|
|
2348
|
+
}
|
|
2349
|
+
return url;
|
|
2350
|
+
}
|
|
2351
|
+
var init_da = __esm({
|
|
2352
|
+
"src/lib/template/da.ts"() {
|
|
2353
|
+
"use strict";
|
|
2354
|
+
init_esm_shims();
|
|
2355
|
+
}
|
|
2356
|
+
});
|
|
2357
|
+
|
|
2358
|
+
// src/lib/template/page.ts
|
|
2359
|
+
function blockDiv(name) {
|
|
2360
|
+
return ` <div class="${name}">
|
|
2361
|
+
<div>
|
|
2362
|
+
<div>${name} content</div>
|
|
2363
|
+
</div>
|
|
2364
|
+
</div>`;
|
|
2365
|
+
}
|
|
2366
|
+
function metadataBlock(opts) {
|
|
2367
|
+
const rows = [
|
|
2368
|
+
` <div><div>Title</div><div>${opts.title}</div></div>`,
|
|
2369
|
+
` <div><div>Description</div><div>${opts.description ?? ""}</div></div>`
|
|
2370
|
+
];
|
|
2371
|
+
if (opts.area) rows.push(` <div><div>Template</div><div>${opts.area}</div></div>`);
|
|
2372
|
+
return ` <div class="metadata">
|
|
2373
|
+
${rows.join("\n")}
|
|
2374
|
+
</div>`;
|
|
2375
|
+
}
|
|
2376
|
+
function buildPageHtml(opts) {
|
|
2377
|
+
const blocks = opts.blocks ?? [];
|
|
2378
|
+
const intro = ` <div>
|
|
2379
|
+
<h1>${opts.title}</h1>
|
|
2380
|
+
<p>${opts.description ?? "Intro copy goes here."}</p>
|
|
2381
|
+
</div>`;
|
|
2382
|
+
const blockSections = blocks.map((b) => ` <div>
|
|
2383
|
+
${blockDiv(b)}
|
|
2384
|
+
</div>`);
|
|
2385
|
+
const metaSection = ` <div>
|
|
2386
|
+
${metadataBlock(opts)}
|
|
2387
|
+
</div>`;
|
|
2388
|
+
return `<body>
|
|
2389
|
+
<header></header>
|
|
2390
|
+
<main>
|
|
2391
|
+
${[intro, ...blockSections, metaSection].join("\n")}
|
|
2392
|
+
</main>
|
|
2393
|
+
<footer></footer>
|
|
2394
|
+
</body>
|
|
2395
|
+
`;
|
|
1634
2396
|
}
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
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
|
-
}
|
|
2397
|
+
var init_page = __esm({
|
|
2398
|
+
"src/lib/template/page.ts"() {
|
|
2399
|
+
"use strict";
|
|
2400
|
+
init_esm_shims();
|
|
1654
2401
|
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
2402
|
+
});
|
|
2403
|
+
|
|
2404
|
+
// src/lib/template/ue.ts
|
|
2405
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2406
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
2407
|
+
import path13 from "path";
|
|
2408
|
+
function fieldDefault(field) {
|
|
2409
|
+
if (field.value !== void 0) return field.value;
|
|
2410
|
+
switch (field.valueType) {
|
|
2411
|
+
case "boolean":
|
|
2412
|
+
return false;
|
|
2413
|
+
case "number":
|
|
2414
|
+
return 0;
|
|
2415
|
+
case "string[]":
|
|
2416
|
+
return [];
|
|
2417
|
+
default:
|
|
2418
|
+
return "";
|
|
1658
2419
|
}
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
2420
|
+
}
|
|
2421
|
+
async function blockDefaults(projectRoot, block2) {
|
|
2422
|
+
const file = path13.join(projectRoot, "blocks", block2, `_${block2}.json`);
|
|
2423
|
+
if (!existsSync10(file)) return {};
|
|
2424
|
+
try {
|
|
2425
|
+
const parsed = JSON.parse(await readFile6(file, "utf-8"));
|
|
2426
|
+
const models = Array.isArray(parsed.models) ? parsed.models : [];
|
|
2427
|
+
const model2 = models.find((m) => m.id === block2) ?? models[0];
|
|
2428
|
+
const fields = model2?.fields ?? [];
|
|
2429
|
+
const out = {};
|
|
2430
|
+
for (const f2 of fields) {
|
|
2431
|
+
if (f2.component === "tab") continue;
|
|
2432
|
+
out[f2.name] = f2.component === "container" ? [] : fieldDefault(f2);
|
|
2433
|
+
}
|
|
2434
|
+
return out;
|
|
2435
|
+
} catch {
|
|
2436
|
+
return {};
|
|
1665
2437
|
}
|
|
1666
|
-
logger.info("");
|
|
1667
|
-
logger.info(
|
|
1668
|
-
box([
|
|
1669
|
-
res.created ? `${res.helpers.length} helper(s) -> scripts/utils.js` : "Helpers left untouched"
|
|
1670
|
-
])
|
|
1671
|
-
);
|
|
1672
2438
|
}
|
|
1673
|
-
async function
|
|
1674
|
-
const
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
message: "What do you want to scaffold?",
|
|
1678
|
-
choices: [
|
|
1679
|
-
{ name: "Universal Editor config + field-reference (all 17 field types)", value: "ue" },
|
|
1680
|
-
{ name: "Standard blocks (hero, cards, columns, accordion, embed)", value: "blocks" },
|
|
1681
|
-
{ name: "GitHub Actions CI (doctor + audits + lint/build)", value: "ci" },
|
|
1682
|
-
{ name: "Helper functions (scripts/utils.js)", value: "helpers" },
|
|
1683
|
-
{ name: "UE config + standard blocks", value: "both" }
|
|
1684
|
-
]
|
|
1685
|
-
});
|
|
1686
|
-
if (what === "ue" || what === "both") await scaffoldUe();
|
|
1687
|
-
if (what === "blocks" || what === "both") {
|
|
1688
|
-
const picked = await checkbox({
|
|
1689
|
-
message: "Which blocks?",
|
|
1690
|
-
choices: STANDARD_BLOCKS.map((b) => ({ name: b.title, value: b.id, checked: true }))
|
|
1691
|
-
});
|
|
1692
|
-
await scaffoldBlocks(picked);
|
|
2439
|
+
async function buildUeTemplate(projectRoot, opts) {
|
|
2440
|
+
const components = [];
|
|
2441
|
+
for (const b of opts.blocks) {
|
|
2442
|
+
components.push({ name: b, model: b, fields: await blockDefaults(projectRoot, b) });
|
|
1693
2443
|
}
|
|
1694
|
-
|
|
1695
|
-
|
|
2444
|
+
const template2 = { title: opts.title, template: opts.name, components };
|
|
2445
|
+
return `${JSON.stringify(template2, null, 2)}
|
|
2446
|
+
`;
|
|
1696
2447
|
}
|
|
1697
|
-
var
|
|
1698
|
-
"src/
|
|
2448
|
+
var init_ue2 = __esm({
|
|
2449
|
+
"src/lib/template/ue.ts"() {
|
|
1699
2450
|
"use strict";
|
|
1700
2451
|
init_esm_shims();
|
|
1701
|
-
init_logger();
|
|
1702
|
-
init_project();
|
|
1703
|
-
init_blocks();
|
|
1704
|
-
init_ci();
|
|
1705
|
-
init_helpers();
|
|
1706
|
-
init_ue();
|
|
1707
|
-
init_ui();
|
|
1708
2452
|
}
|
|
1709
2453
|
});
|
|
1710
2454
|
|
|
1711
|
-
// src/commands/
|
|
1712
|
-
var
|
|
1713
|
-
__export(
|
|
1714
|
-
|
|
2455
|
+
// src/commands/template.ts
|
|
2456
|
+
var template_exports = {};
|
|
2457
|
+
__export(template_exports, {
|
|
2458
|
+
templateNew: () => templateNew
|
|
1715
2459
|
});
|
|
1716
|
-
import { existsSync as
|
|
1717
|
-
import { readFile as
|
|
1718
|
-
import
|
|
1719
|
-
import
|
|
1720
|
-
async function
|
|
1721
|
-
const
|
|
1722
|
-
if (!
|
|
2460
|
+
import { existsSync as existsSync11 } from "fs";
|
|
2461
|
+
import { mkdir as mkdir5, readFile as readFile7, readdir, writeFile as writeFile9 } from "fs/promises";
|
|
2462
|
+
import path14 from "path";
|
|
2463
|
+
import chalk5 from "chalk";
|
|
2464
|
+
async function projectBlocks(projectRoot) {
|
|
2465
|
+
const dir = path14.join(projectRoot, "blocks");
|
|
2466
|
+
if (!existsSync11(dir)) return [];
|
|
2467
|
+
return (await readdir(dir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
2468
|
+
}
|
|
2469
|
+
async function readEdsrcAdmin(projectRoot) {
|
|
2470
|
+
const rc = path14.join(projectRoot, ".edsrc.json");
|
|
2471
|
+
if (!existsSync11(rc)) return {};
|
|
1723
2472
|
try {
|
|
1724
|
-
|
|
1725
|
-
pkg.name = name;
|
|
1726
|
-
await writeFile6(file, `${JSON.stringify(pkg, null, 2)}
|
|
1727
|
-
`);
|
|
1728
|
-
return true;
|
|
2473
|
+
return JSON.parse(await readFile7(rc, "utf-8")).admin ?? {};
|
|
1729
2474
|
} catch {
|
|
1730
|
-
return
|
|
2475
|
+
return {};
|
|
1731
2476
|
}
|
|
1732
2477
|
}
|
|
1733
|
-
async function
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
2478
|
+
async function registerBlocks(projectRoot, blocks) {
|
|
2479
|
+
if (!blocks.length) return false;
|
|
2480
|
+
const file = path14.join(projectRoot, "component-filters.json");
|
|
2481
|
+
let existing = null;
|
|
2482
|
+
if (existsSync11(file)) {
|
|
1738
2483
|
try {
|
|
1739
|
-
|
|
1740
|
-
const after = before.replace(/aem-boilerplate/gi, name);
|
|
1741
|
-
if (after !== before) {
|
|
1742
|
-
await writeFile6(file, after);
|
|
1743
|
-
touched.push(rel);
|
|
1744
|
-
}
|
|
2484
|
+
existing = JSON.parse(await readFile7(file, "utf-8"));
|
|
1745
2485
|
} catch {
|
|
2486
|
+
existing = null;
|
|
1746
2487
|
}
|
|
1747
2488
|
}
|
|
1748
|
-
|
|
1749
|
-
}
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
const steps = [
|
|
1753
|
-
{
|
|
1754
|
-
title: "Install the AEM Code Sync GitHub app",
|
|
1755
|
-
url: "https://github.com/apps/aem-code-sync/installations/new",
|
|
1756
|
-
note: "Connects your repo to the Edge Delivery pipeline (preview/publish)."
|
|
1757
|
-
}
|
|
1758
|
-
];
|
|
1759
|
-
if (authoring === "gdrive") {
|
|
1760
|
-
steps.push({
|
|
1761
|
-
title: "Share your content folder",
|
|
1762
|
-
note: "Share the Google Drive folder with the AEM service and set it as the mountpoint."
|
|
1763
|
-
});
|
|
1764
|
-
}
|
|
1765
|
-
if (authoring === "sharepoint") {
|
|
1766
|
-
steps.push({
|
|
1767
|
-
title: "Share your content folder",
|
|
1768
|
-
note: "Grant the SharePoint/OneDrive folder to the AEM service account."
|
|
1769
|
-
});
|
|
1770
|
-
}
|
|
1771
|
-
if (authoring === "da") {
|
|
1772
|
-
steps.push({
|
|
1773
|
-
title: "Author in DA",
|
|
1774
|
-
url: "https://da.live",
|
|
1775
|
-
note: "Create documents at da.live."
|
|
1776
|
-
});
|
|
1777
|
-
}
|
|
1778
|
-
if (authoring === "ue") {
|
|
1779
|
-
steps.push({
|
|
1780
|
-
title: "Open the Universal Editor",
|
|
1781
|
-
url: "https://experience.adobe.com",
|
|
1782
|
-
note: "Author in-context once the repo is connected to AEM."
|
|
1783
|
-
});
|
|
1784
|
-
}
|
|
1785
|
-
steps.push({
|
|
1786
|
-
title: "Install the AEM Sidekick extension",
|
|
1787
|
-
url: "https://chromewebstore.google.com/detail/aem-sidekick/igkmdomcgoebiipaifhmpfjhbjccggml",
|
|
1788
|
-
note: "Preview and publish from the browser."
|
|
1789
|
-
});
|
|
1790
|
-
steps.push({
|
|
1791
|
-
title: "Run the local dev server",
|
|
1792
|
-
note: "npm install, then `aem up` (from @adobe/aem-cli)."
|
|
1793
|
-
});
|
|
1794
|
-
logger.info(heading("Next steps (outside the repo)"));
|
|
1795
|
-
for (let i = 0; i < steps.length; i++) {
|
|
1796
|
-
const s = steps[i];
|
|
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)}`);
|
|
1800
|
-
await confirm({ message: " Done / skip?", default: true });
|
|
1801
|
-
}
|
|
2489
|
+
const filters = mergeSectionFilter(existing, blocks);
|
|
2490
|
+
await writeFile9(file, `${JSON.stringify(filters, null, 2)}
|
|
2491
|
+
`);
|
|
2492
|
+
return true;
|
|
1802
2493
|
}
|
|
1803
|
-
async function
|
|
2494
|
+
async function templateNew(nameArg, options = {}) {
|
|
1804
2495
|
const projectRoot = findProjectRoot();
|
|
1805
2496
|
if (!projectRoot) {
|
|
1806
|
-
logger.error(
|
|
1807
|
-
"Not inside an EDS project (no EDS project markers found). Clone an EDS boilerplate first, then run this from its root."
|
|
1808
|
-
);
|
|
2497
|
+
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
1809
2498
|
process.exitCode = 1;
|
|
1810
2499
|
return;
|
|
1811
2500
|
}
|
|
1812
|
-
await logger.logoOnceAnimated("
|
|
2501
|
+
await logger.logoOnceAnimated("Page template");
|
|
1813
2502
|
const { input, select, confirm } = await import("@inquirer/prompts");
|
|
1814
2503
|
const interactive = !options.yes;
|
|
1815
|
-
|
|
1816
|
-
const
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
2504
|
+
const detected = await detectAuthoring(projectRoot);
|
|
2505
|
+
const model2 = options.authoring ?? detected.model;
|
|
2506
|
+
const name = slug2(nameArg);
|
|
2507
|
+
const title = options.title ?? (interactive ? await input({ message: "Page title", default: nameArg }) : nameArg);
|
|
2508
|
+
const description = options.description ?? (interactive ? await input({ message: "Description (optional)" }) : "");
|
|
2509
|
+
const pagePath = options.path ?? `/templates/${name}`;
|
|
2510
|
+
let blocks = options.blocks;
|
|
2511
|
+
if (!blocks && interactive) {
|
|
2512
|
+
const available = await projectBlocks(projectRoot);
|
|
2513
|
+
const pool = available.length ? available : STANDARD_BLOCKS.map((b) => b.id);
|
|
2514
|
+
const ordered = [];
|
|
2515
|
+
let adding = true;
|
|
2516
|
+
while (adding) {
|
|
2517
|
+
const pick = await select({
|
|
2518
|
+
message: `Add a block in order (${ordered.length} so far)`,
|
|
2519
|
+
choices: [
|
|
2520
|
+
...pool.map((b) => ({ name: b, value: b })),
|
|
2521
|
+
{ name: ordered.length ? "- done -" : "- done (empty page) -", value: "__done" }
|
|
2522
|
+
]
|
|
2523
|
+
});
|
|
2524
|
+
if (pick === "__done") adding = false;
|
|
2525
|
+
else ordered.push(pick);
|
|
1821
2526
|
}
|
|
2527
|
+
blocks = ordered;
|
|
1822
2528
|
}
|
|
1823
|
-
const
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
);
|
|
1830
|
-
const authoring = options.authoring ?? await select({
|
|
1831
|
-
message: "2/4 Authoring model",
|
|
1832
|
-
choices: [
|
|
1833
|
-
{ name: AUTHORING_NAMES.ue, value: "ue" },
|
|
1834
|
-
{ name: AUTHORING_NAMES.da, value: "da" },
|
|
1835
|
-
{ name: AUTHORING_NAMES.gdrive, value: "gdrive" },
|
|
1836
|
-
{ name: AUTHORING_NAMES.sharepoint, value: "sharepoint" }
|
|
1837
|
-
]
|
|
2529
|
+
const seeded = blocks ?? [];
|
|
2530
|
+
const html = buildPageHtml({
|
|
2531
|
+
title,
|
|
2532
|
+
description: description || void 0,
|
|
2533
|
+
blocks: seeded,
|
|
2534
|
+
area: options.area
|
|
1838
2535
|
});
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
logger.info(accentLine("ci", withCi ? "yes" : "no", w));
|
|
1862
|
-
logger.info("");
|
|
1863
|
-
if (interactive) {
|
|
1864
|
-
const go = await confirm({ message: "Apply this setup?", default: true });
|
|
1865
|
-
if (!go) {
|
|
1866
|
-
logger.info("Cancelled.");
|
|
1867
|
-
return;
|
|
2536
|
+
const localRel = path14.join("templates", `${name}.html`);
|
|
2537
|
+
await mkdir5(path14.join(projectRoot, "templates"), { recursive: true });
|
|
2538
|
+
await writeFile9(path14.join(projectRoot, localRel), html);
|
|
2539
|
+
logger.info(heading("Page template", `${pagePath} \xB7 ${model2}`));
|
|
2540
|
+
logger.info(accentLine(localRel, `${seeded.length} block(s) + metadata`));
|
|
2541
|
+
if (model2 === "da") {
|
|
2542
|
+
const admin = await readEdsrcAdmin(projectRoot);
|
|
2543
|
+
const org = options.org ?? admin.org ?? detected.org;
|
|
2544
|
+
const site = options.site ?? admin.site ?? detected.site;
|
|
2545
|
+
const wantPush = options.push ?? (interactive ? await confirm({ message: "Push to DA (da.live)?", default: true }) : true);
|
|
2546
|
+
if (wantPush) {
|
|
2547
|
+
if (!org || !site) {
|
|
2548
|
+
logger.warn("No DA org/site. Pass --org/--site or set admin in .edsrc.json.");
|
|
2549
|
+
} else {
|
|
2550
|
+
try {
|
|
2551
|
+
await pushToDa(org, site, pagePath, html, process.env.DA_TOKEN);
|
|
2552
|
+
logger.info(accentLine("pushed to DA", daEditUrl(org, site, pagePath)));
|
|
2553
|
+
} catch (err) {
|
|
2554
|
+
logger.error(`DA push failed: ${err.message}`);
|
|
2555
|
+
logger.info(chalk5.dim(" Open projects allow anonymous writes; others need DA_TOKEN."));
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
1868
2558
|
}
|
|
1869
|
-
}
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
if (
|
|
1879
|
-
const
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
2559
|
+
} else if (model2 === "gdrive" || model2 === "sharepoint") {
|
|
2560
|
+
const where = model2 === "gdrive" ? "a Google Doc" : "a Word doc";
|
|
2561
|
+
logger.info("");
|
|
2562
|
+
logger.info(
|
|
2563
|
+
chalk5.dim(` Document model: import ${localRel} into ${where} in your mounted folder,`)
|
|
2564
|
+
);
|
|
2565
|
+
logger.info(
|
|
2566
|
+
chalk5.dim(" then preview/publish. (Share the folder + install aem-code-sync first.)")
|
|
2567
|
+
);
|
|
2568
|
+
} else if (model2 === "ue") {
|
|
2569
|
+
const jsonRel = path14.join("templates", `${name}.json`);
|
|
2570
|
+
await writeFile9(
|
|
2571
|
+
path14.join(projectRoot, jsonRel),
|
|
2572
|
+
await buildUeTemplate(projectRoot, { title, name, blocks: seeded })
|
|
2573
|
+
);
|
|
2574
|
+
logger.info(
|
|
2575
|
+
accentLine(jsonRel, `initial content (${seeded.length} component(s), in order)`)
|
|
2576
|
+
);
|
|
2577
|
+
const registered = await registerBlocks(projectRoot, seeded);
|
|
2578
|
+
if (registered) {
|
|
2579
|
+
logger.info(
|
|
2580
|
+
accentLine("component-filters.json", "seeded blocks allowed in Universal Editor")
|
|
2581
|
+
);
|
|
2582
|
+
}
|
|
2583
|
+
logger.info("");
|
|
2584
|
+
logger.info(
|
|
2585
|
+
chalk5.dim(" Universal Editor: use the template JSON as the page's initial content, then")
|
|
2586
|
+
);
|
|
2587
|
+
logger.info(chalk5.dim(" author it in the Universal Editor (wire the repo to AEM)."));
|
|
1887
2588
|
}
|
|
1888
2589
|
logger.info("");
|
|
1889
|
-
|
|
1890
|
-
logger.info(box([`"${name}" set up for ${AUTHORING_NAMES[authoring]}`]));
|
|
2590
|
+
logger.info(box([`"${name}" template (${model2}) - preview with \`eds preview ${pagePath}\``]));
|
|
1891
2591
|
}
|
|
1892
2592
|
var slug2;
|
|
1893
|
-
var
|
|
1894
|
-
"src/commands/
|
|
2593
|
+
var init_template = __esm({
|
|
2594
|
+
"src/commands/template.ts"() {
|
|
1895
2595
|
"use strict";
|
|
1896
2596
|
init_esm_shims();
|
|
1897
2597
|
init_logger();
|
|
1898
2598
|
init_project();
|
|
1899
|
-
|
|
1900
|
-
|
|
2599
|
+
init_blocks();
|
|
2600
|
+
init_ue();
|
|
2601
|
+
init_authoring();
|
|
2602
|
+
init_da();
|
|
2603
|
+
init_page();
|
|
2604
|
+
init_ue2();
|
|
1901
2605
|
init_ui();
|
|
1902
|
-
|
|
1903
|
-
slug2 = (s) => s.trim().toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2606
|
+
slug2 = (s) => s.trim().toLowerCase().replace(/[^a-z0-9-/]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1904
2607
|
}
|
|
1905
2608
|
});
|
|
1906
2609
|
|
|
1907
2610
|
// src/lib/migrate/clientlib.ts
|
|
1908
|
-
import { existsSync as
|
|
1909
|
-
import { readFile as
|
|
1910
|
-
import
|
|
2611
|
+
import { existsSync as existsSync12 } from "fs";
|
|
2612
|
+
import { readFile as readFile8, readdir as readdir2 } from "fs/promises";
|
|
2613
|
+
import path15 from "path";
|
|
1911
2614
|
async function listFiles(dir) {
|
|
1912
2615
|
const out = [];
|
|
1913
2616
|
const walk3 = async (d) => {
|
|
1914
2617
|
for (const e of await readdir2(d, { withFileTypes: true })) {
|
|
1915
|
-
const p =
|
|
2618
|
+
const p = path15.join(d, e.name);
|
|
1916
2619
|
if (e.isDirectory()) await walk3(p);
|
|
1917
2620
|
else out.push(p);
|
|
1918
2621
|
}
|
|
1919
2622
|
};
|
|
1920
|
-
if (
|
|
2623
|
+
if (existsSync12(dir)) await walk3(dir);
|
|
1921
2624
|
return out;
|
|
1922
2625
|
}
|
|
1923
2626
|
async function findClientlibDir(componentDir) {
|
|
1924
|
-
if (!
|
|
2627
|
+
if (!existsSync12(componentDir)) return null;
|
|
1925
2628
|
const entries = await readdir2(componentDir, { withFileTypes: true });
|
|
1926
2629
|
const match = entries.find((e) => e.isDirectory() && /clientlib/i.test(e.name));
|
|
1927
|
-
if (match) return
|
|
2630
|
+
if (match) return path15.join(componentDir, match.name);
|
|
1928
2631
|
const hasCss = entries.some((e) => e.isFile() && e.name.endsWith(".css"));
|
|
1929
2632
|
return hasCss ? componentDir : null;
|
|
1930
2633
|
}
|
|
1931
2634
|
async function readClientlibCss(clientlibDir) {
|
|
1932
2635
|
const all = await listFiles(clientlibDir);
|
|
1933
|
-
const preprocessed = all.filter((f2) => /\.(less|scss|sass)$/i.test(f2)).map((f2) =>
|
|
2636
|
+
const preprocessed = all.filter((f2) => /\.(less|scss|sass)$/i.test(f2)).map((f2) => path15.relative(clientlibDir, f2));
|
|
1934
2637
|
let cssFiles;
|
|
1935
|
-
const cssTxt =
|
|
1936
|
-
if (
|
|
1937
|
-
const lines = (await
|
|
2638
|
+
const cssTxt = path15.join(clientlibDir, "css.txt");
|
|
2639
|
+
if (existsSync12(cssTxt)) {
|
|
2640
|
+
const lines = (await readFile8(cssTxt, "utf-8")).split("\n").map((l) => l.trim());
|
|
1938
2641
|
let base = clientlibDir;
|
|
1939
2642
|
const listed = [];
|
|
1940
2643
|
for (const l of lines) {
|
|
1941
2644
|
if (!l) continue;
|
|
1942
2645
|
const baseMatch = l.match(/^#base\s*=\s*(.+)$/);
|
|
1943
|
-
if (baseMatch) base =
|
|
2646
|
+
if (baseMatch) base = path15.join(clientlibDir, baseMatch[1].trim());
|
|
1944
2647
|
else if (!l.startsWith("#")) listed.push(l);
|
|
1945
2648
|
}
|
|
1946
|
-
cssFiles = listed.map((rel) =>
|
|
2649
|
+
cssFiles = listed.map((rel) => path15.join(base, rel)).filter((f2) => existsSync12(f2) && f2.endsWith(".css"));
|
|
1947
2650
|
} else {
|
|
1948
2651
|
cssFiles = all.filter((f2) => f2.endsWith(".css"));
|
|
1949
2652
|
}
|
|
1950
2653
|
const parts = [];
|
|
1951
|
-
for (const f2 of cssFiles) parts.push(await
|
|
2654
|
+
for (const f2 of cssFiles) parts.push(await readFile8(f2, "utf-8"));
|
|
1952
2655
|
return {
|
|
1953
2656
|
css: parts.join("\n\n"),
|
|
1954
|
-
files: cssFiles.map((f2) =>
|
|
2657
|
+
files: cssFiles.map((f2) => path15.relative(clientlibDir, f2)),
|
|
1955
2658
|
preprocessed
|
|
1956
2659
|
};
|
|
1957
2660
|
}
|
|
@@ -2103,24 +2806,24 @@ var init_dialog = __esm({
|
|
|
2103
2806
|
});
|
|
2104
2807
|
|
|
2105
2808
|
// src/lib/migrate/component.ts
|
|
2106
|
-
import { existsSync as
|
|
2107
|
-
import { readFile as
|
|
2108
|
-
import
|
|
2809
|
+
import { existsSync as existsSync13 } from "fs";
|
|
2810
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
2811
|
+
import path16 from "path";
|
|
2109
2812
|
import { XMLParser as XMLParser2 } from "fast-xml-parser";
|
|
2110
2813
|
function findDialogFile(componentDir) {
|
|
2111
2814
|
const candidates = [
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2815
|
+
path16.join(componentDir, "_cq_dialog", ".content.xml"),
|
|
2816
|
+
path16.join(componentDir, "_cq_dialog.xml"),
|
|
2817
|
+
path16.join(componentDir, "cq:dialog", ".content.xml")
|
|
2115
2818
|
];
|
|
2116
|
-
return candidates.find((c) =>
|
|
2819
|
+
return candidates.find((c) => existsSync13(c)) ?? null;
|
|
2117
2820
|
}
|
|
2118
2821
|
async function readComponentTitle(componentDir) {
|
|
2119
|
-
const file =
|
|
2120
|
-
if (!
|
|
2822
|
+
const file = path16.join(componentDir, ".content.xml");
|
|
2823
|
+
if (!existsSync13(file)) return void 0;
|
|
2121
2824
|
try {
|
|
2122
2825
|
const parser = new XMLParser2({ ignoreAttributes: false, attributeNamePrefix: "@_" });
|
|
2123
|
-
const parsed = parser.parse(await
|
|
2826
|
+
const parsed = parser.parse(await readFile9(file, "utf-8"));
|
|
2124
2827
|
const root = parsed["jcr:root"] ?? {};
|
|
2125
2828
|
const title = root["@_jcr:title"];
|
|
2126
2829
|
return typeof title === "string" ? title.replace(/^\{[^}]+\}/, "") : void 0;
|
|
@@ -2131,7 +2834,7 @@ async function readComponentTitle(componentDir) {
|
|
|
2131
2834
|
async function readDialogFields(componentDir) {
|
|
2132
2835
|
const dialog = findDialogFile(componentDir);
|
|
2133
2836
|
if (!dialog) return null;
|
|
2134
|
-
return parseDialog(await
|
|
2837
|
+
return parseDialog(await readFile9(dialog, "utf-8"));
|
|
2135
2838
|
}
|
|
2136
2839
|
function migratedModelFile(blockName, title, fields) {
|
|
2137
2840
|
const model2 = {
|
|
@@ -2260,10 +2963,10 @@ var track_exports = {};
|
|
|
2260
2963
|
__export(track_exports, {
|
|
2261
2964
|
trackBlock: () => trackBlock
|
|
2262
2965
|
});
|
|
2263
|
-
import { existsSync as
|
|
2264
|
-
import { readFile as
|
|
2265
|
-
import
|
|
2266
|
-
import
|
|
2966
|
+
import { existsSync as existsSync14 } from "fs";
|
|
2967
|
+
import { readFile as readFile10, writeFile as writeFile10 } from "fs/promises";
|
|
2968
|
+
import path17 from "path";
|
|
2969
|
+
import chalk6 from "chalk";
|
|
2267
2970
|
async function trackBlock(blockName, options = {}) {
|
|
2268
2971
|
const projectRoot = findProjectRoot();
|
|
2269
2972
|
if (!projectRoot) {
|
|
@@ -2272,30 +2975,30 @@ async function trackBlock(blockName, options = {}) {
|
|
|
2272
2975
|
return;
|
|
2273
2976
|
}
|
|
2274
2977
|
logger.logoOnce(logo("Track block"));
|
|
2275
|
-
const jsPath =
|
|
2276
|
-
if (!
|
|
2978
|
+
const jsPath = path17.join(projectRoot, "blocks", blockName, `${blockName}.js`);
|
|
2979
|
+
if (!existsSync14(jsPath)) {
|
|
2277
2980
|
logger.error(`Block "${blockName}" not found (no blocks/${blockName}/${blockName}.js).`);
|
|
2278
2981
|
process.exitCode = 1;
|
|
2279
2982
|
return;
|
|
2280
2983
|
}
|
|
2281
2984
|
const events = { click: options.click !== false, submit: options.submit !== false };
|
|
2282
|
-
const res = instrumentJs(await
|
|
2985
|
+
const res = instrumentJs(await readFile10(jsPath, "utf-8"), blockName, events);
|
|
2283
2986
|
if (!res.changed) {
|
|
2284
2987
|
logger.warn(`Not instrumented: ${res.reason}.`);
|
|
2285
2988
|
return;
|
|
2286
2989
|
}
|
|
2287
|
-
await
|
|
2990
|
+
await writeFile10(jsPath, res.js);
|
|
2288
2991
|
logger.info(heading("Instrumented"));
|
|
2289
2992
|
const picked = [events.click ? "block_click" : null, events.submit ? "form_submit" : null].filter(
|
|
2290
2993
|
Boolean
|
|
2291
2994
|
);
|
|
2292
2995
|
logger.info(accentLine(`blocks/${blockName}/${blockName}.js`, picked.join(" + ")));
|
|
2293
|
-
if (!
|
|
2996
|
+
if (!existsSync14(path17.join(projectRoot, "scripts", "analytics.js"))) {
|
|
2294
2997
|
logger.info("");
|
|
2295
2998
|
logger.warn("scripts/analytics.js is missing - run `eds integrate gtm` to create it.");
|
|
2296
2999
|
}
|
|
2297
3000
|
logger.info("");
|
|
2298
|
-
logger.info(
|
|
3001
|
+
logger.info(chalk6.dim(" Events push to window.dataLayer via trackEvent - GTM picks them up."));
|
|
2299
3002
|
}
|
|
2300
3003
|
var init_track2 = __esm({
|
|
2301
3004
|
"src/commands/track.ts"() {
|
|
@@ -2313,9 +3016,9 @@ var migrate_exports = {};
|
|
|
2313
3016
|
__export(migrate_exports, {
|
|
2314
3017
|
migrateComponent: () => migrateComponent
|
|
2315
3018
|
});
|
|
2316
|
-
import { existsSync as
|
|
2317
|
-
import { mkdir as
|
|
2318
|
-
import
|
|
3019
|
+
import { existsSync as existsSync15 } from "fs";
|
|
3020
|
+
import { mkdir as mkdir6, writeFile as writeFile11 } from "fs/promises";
|
|
3021
|
+
import path18 from "path";
|
|
2319
3022
|
function clientlibBlockCss(blockName, css, files) {
|
|
2320
3023
|
return `/* Block: ${blockName} */
|
|
2321
3024
|
/* Migrated from the component clientlib: ${files.join(", ")}. */
|
|
@@ -2363,7 +3066,7 @@ async function migrateComponent(dirArg, options = {}) {
|
|
|
2363
3066
|
if (!componentDir && interactive) {
|
|
2364
3067
|
componentDir = await input({
|
|
2365
3068
|
message: "Path to the classic component folder",
|
|
2366
|
-
validate: (v) =>
|
|
3069
|
+
validate: (v) => existsSync15(v.trim()) || "Folder not found"
|
|
2367
3070
|
});
|
|
2368
3071
|
}
|
|
2369
3072
|
if (!componentDir) {
|
|
@@ -2371,7 +3074,7 @@ async function migrateComponent(dirArg, options = {}) {
|
|
|
2371
3074
|
process.exitCode = 1;
|
|
2372
3075
|
return;
|
|
2373
3076
|
}
|
|
2374
|
-
componentDir =
|
|
3077
|
+
componentDir = path18.resolve(componentDir.trim());
|
|
2375
3078
|
if (!findDialogFile(componentDir)) {
|
|
2376
3079
|
logger.error(`No Touch UI dialog (_cq_dialog/.content.xml) found in ${componentDir}.`);
|
|
2377
3080
|
process.exitCode = 1;
|
|
@@ -2385,7 +3088,7 @@ async function migrateComponent(dirArg, options = {}) {
|
|
|
2385
3088
|
process.exitCode = 1;
|
|
2386
3089
|
return;
|
|
2387
3090
|
}
|
|
2388
|
-
const title = await readComponentTitle(componentDir) ??
|
|
3091
|
+
const title = await readComponentTitle(componentDir) ?? path18.basename(componentDir);
|
|
2389
3092
|
const blockName = options.name && slug3(options.name) || (interactive ? slug3(await input({ message: "Block name", default: slug3(title) })) : slug3(title));
|
|
2390
3093
|
logger.info(heading("Mapped fields", `${countLeaves(parsed.fields)} from dialog`));
|
|
2391
3094
|
for (const line of previewFields(parsed.fields)) logger.info(` ${line}`);
|
|
@@ -2401,19 +3104,19 @@ async function migrateComponent(dirArg, options = {}) {
|
|
|
2401
3104
|
return;
|
|
2402
3105
|
}
|
|
2403
3106
|
}
|
|
2404
|
-
const blockDir =
|
|
2405
|
-
if (
|
|
3107
|
+
const blockDir = path18.join(projectRoot, "blocks", blockName);
|
|
3108
|
+
if (existsSync15(blockDir)) {
|
|
2406
3109
|
logger.error(
|
|
2407
3110
|
`Block "${blockName}" already exists at blocks/${blockName}. Use --name to pick another.`
|
|
2408
3111
|
);
|
|
2409
3112
|
process.exitCode = 1;
|
|
2410
3113
|
return;
|
|
2411
3114
|
}
|
|
2412
|
-
const clibDir = options.clientlib ?
|
|
3115
|
+
const clibDir = options.clientlib ? path18.resolve(options.clientlib.trim()) : await findClientlibDir(componentDir);
|
|
2413
3116
|
let cssContent = migratedBlockCss(blockName);
|
|
2414
3117
|
let cssMessage = "empty - add styles from clientlib / rendered CSS";
|
|
2415
3118
|
let preprocessedNote = [];
|
|
2416
|
-
if (clibDir &&
|
|
3119
|
+
if (clibDir && existsSync15(clibDir)) {
|
|
2417
3120
|
const lib = await readClientlibCss(clibDir);
|
|
2418
3121
|
if (lib.css.trim()) {
|
|
2419
3122
|
cssContent = clientlibBlockCss(blockName, lib.css, lib.files);
|
|
@@ -2421,14 +3124,14 @@ async function migrateComponent(dirArg, options = {}) {
|
|
|
2421
3124
|
}
|
|
2422
3125
|
preprocessedNote = lib.preprocessed;
|
|
2423
3126
|
}
|
|
2424
|
-
await
|
|
2425
|
-
await
|
|
2426
|
-
|
|
3127
|
+
await mkdir6(blockDir, { recursive: true });
|
|
3128
|
+
await writeFile11(
|
|
3129
|
+
path18.join(blockDir, `${blockName}.js`),
|
|
2427
3130
|
migratedBlockJs(blockName, parsed.fields)
|
|
2428
3131
|
);
|
|
2429
|
-
await
|
|
2430
|
-
await
|
|
2431
|
-
|
|
3132
|
+
await writeFile11(path18.join(blockDir, `${blockName}.css`), cssContent);
|
|
3133
|
+
await writeFile11(
|
|
3134
|
+
path18.join(blockDir, `_${blockName}.json`),
|
|
2432
3135
|
migratedModelFile(blockName, title, parsed.fields)
|
|
2433
3136
|
);
|
|
2434
3137
|
logger.info(heading("Created"));
|
|
@@ -2678,9 +3381,9 @@ var model_exports = {};
|
|
|
2678
3381
|
__export(model_exports, {
|
|
2679
3382
|
modelAdd: () => modelAdd
|
|
2680
3383
|
});
|
|
2681
|
-
import { existsSync as
|
|
2682
|
-
import { readFile as
|
|
2683
|
-
import
|
|
3384
|
+
import { existsSync as existsSync16 } from "fs";
|
|
3385
|
+
import { readFile as readFile11, writeFile as writeFile12 } from "fs/promises";
|
|
3386
|
+
import path19 from "path";
|
|
2684
3387
|
async function modelAdd(blockArg, partialArgs, options = {}) {
|
|
2685
3388
|
const projectRoot = findProjectRoot();
|
|
2686
3389
|
if (!projectRoot) {
|
|
@@ -2693,10 +3396,10 @@ async function modelAdd(blockArg, partialArgs, options = {}) {
|
|
|
2693
3396
|
const interactive = !options.yes;
|
|
2694
3397
|
let blockName = blockArg;
|
|
2695
3398
|
if (!blockName && interactive) {
|
|
2696
|
-
const { readdir:
|
|
2697
|
-
const blocksDir =
|
|
2698
|
-
const dirs =
|
|
2699
|
-
(e) => e.isDirectory() &&
|
|
3399
|
+
const { readdir: readdir9 } = await import("fs/promises");
|
|
3400
|
+
const blocksDir = path19.join(projectRoot, "blocks");
|
|
3401
|
+
const dirs = existsSync16(blocksDir) ? (await readdir9(blocksDir, { withFileTypes: true })).filter(
|
|
3402
|
+
(e) => e.isDirectory() && existsSync16(path19.join(blocksDir, e.name, `_${e.name}.json`))
|
|
2700
3403
|
).map((e) => e.name) : [];
|
|
2701
3404
|
if (dirs.length === 0) {
|
|
2702
3405
|
logger.error("No block with a _<name>.json model found. Create one with `eds block create`.");
|
|
@@ -2713,8 +3416,8 @@ async function modelAdd(blockArg, partialArgs, options = {}) {
|
|
|
2713
3416
|
process.exitCode = 1;
|
|
2714
3417
|
return;
|
|
2715
3418
|
}
|
|
2716
|
-
const modelFile =
|
|
2717
|
-
if (!
|
|
3419
|
+
const modelFile = path19.join(projectRoot, "blocks", blockName, `_${blockName}.json`);
|
|
3420
|
+
if (!existsSync16(modelFile)) {
|
|
2718
3421
|
logger.error(`No model at blocks/${blockName}/_${blockName}.json. Create the block first.`);
|
|
2719
3422
|
process.exitCode = 1;
|
|
2720
3423
|
return;
|
|
@@ -2739,7 +3442,7 @@ async function modelAdd(blockArg, partialArgs, options = {}) {
|
|
|
2739
3442
|
}
|
|
2740
3443
|
let model2;
|
|
2741
3444
|
try {
|
|
2742
|
-
model2 = JSON.parse(await
|
|
3445
|
+
model2 = JSON.parse(await readFile11(modelFile, "utf-8"));
|
|
2743
3446
|
} catch {
|
|
2744
3447
|
logger.error(`Could not parse ${modelFile}.`);
|
|
2745
3448
|
process.exitCode = 1;
|
|
@@ -2767,7 +3470,7 @@ async function modelAdd(blockArg, partialArgs, options = {}) {
|
|
|
2767
3470
|
added.push(field.name);
|
|
2768
3471
|
}
|
|
2769
3472
|
}
|
|
2770
|
-
await
|
|
3473
|
+
await writeFile12(modelFile, `${JSON.stringify(model2, null, 2)}
|
|
2771
3474
|
`);
|
|
2772
3475
|
logger.info(heading("Model updated", `blocks/${blockName}/_${blockName}.json`));
|
|
2773
3476
|
logger.info(accentLine("partials", partials.map((p) => p.id).join(", ")));
|
|
@@ -2959,10 +3662,10 @@ var schema_exports = {};
|
|
|
2959
3662
|
__export(schema_exports, {
|
|
2960
3663
|
schemaBlock: () => schemaBlock
|
|
2961
3664
|
});
|
|
2962
|
-
import { existsSync as
|
|
2963
|
-
import { readFile as
|
|
2964
|
-
import
|
|
2965
|
-
import
|
|
3665
|
+
import { existsSync as existsSync17 } from "fs";
|
|
3666
|
+
import { readFile as readFile12, writeFile as writeFile13 } from "fs/promises";
|
|
3667
|
+
import path20 from "path";
|
|
3668
|
+
import chalk7 from "chalk";
|
|
2966
3669
|
async function schemaBlock(blockName, options = {}) {
|
|
2967
3670
|
const projectRoot = findProjectRoot();
|
|
2968
3671
|
if (!projectRoot) {
|
|
@@ -2971,8 +3674,8 @@ async function schemaBlock(blockName, options = {}) {
|
|
|
2971
3674
|
return;
|
|
2972
3675
|
}
|
|
2973
3676
|
logger.logoOnce(logo("Schema - JSON-LD"));
|
|
2974
|
-
const jsPath =
|
|
2975
|
-
if (!
|
|
3677
|
+
const jsPath = path20.join(projectRoot, "blocks", blockName, `${blockName}.js`);
|
|
3678
|
+
if (!existsSync17(jsPath)) {
|
|
2976
3679
|
logger.error(`Block "${blockName}" not found (no blocks/${blockName}/${blockName}.js).`);
|
|
2977
3680
|
process.exitCode = 1;
|
|
2978
3681
|
return;
|
|
@@ -2993,17 +3696,17 @@ async function schemaBlock(blockName, options = {}) {
|
|
|
2993
3696
|
process.exitCode = 1;
|
|
2994
3697
|
return;
|
|
2995
3698
|
}
|
|
2996
|
-
const res = injectJsonLd(await
|
|
3699
|
+
const res = injectJsonLd(await readFile12(jsPath, "utf-8"), type);
|
|
2997
3700
|
if (!res.changed) {
|
|
2998
3701
|
logger.warn(`Not added: ${res.reason}.`);
|
|
2999
3702
|
return;
|
|
3000
3703
|
}
|
|
3001
|
-
await
|
|
3704
|
+
await writeFile13(jsPath, res.js);
|
|
3002
3705
|
logger.info(heading("JSON-LD added"));
|
|
3003
3706
|
logger.info(accentLine(`blocks/${blockName}/${blockName}.js`, `${type.id} (${type.label})`));
|
|
3004
3707
|
logger.info("");
|
|
3005
3708
|
logger.info(
|
|
3006
|
-
|
|
3709
|
+
chalk7.dim(
|
|
3007
3710
|
" Review the generated object - some fields are heuristic or TODO. Validate at search.google.com/test/rich-results."
|
|
3008
3711
|
)
|
|
3009
3712
|
);
|
|
@@ -3019,16 +3722,198 @@ var init_schema = __esm({
|
|
|
3019
3722
|
}
|
|
3020
3723
|
});
|
|
3021
3724
|
|
|
3725
|
+
// src/lib/ue/check.ts
|
|
3726
|
+
function metaContent(html, name) {
|
|
3727
|
+
for (const tag of html.match(/<meta\b[^>]*>/gi) ?? []) {
|
|
3728
|
+
if (tag.match(/\bname=["']([^"']+)["']/i)?.[1] === name) {
|
|
3729
|
+
return tag.match(/\bcontent=["']([^"']*)["']/i)?.[1] ?? "";
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
return null;
|
|
3733
|
+
}
|
|
3734
|
+
function hasAemConnection(html) {
|
|
3735
|
+
return metaContent(html, AEM_CONNECTION_META) !== null;
|
|
3736
|
+
}
|
|
3737
|
+
function modelIds(componentModels) {
|
|
3738
|
+
if (!Array.isArray(componentModels)) return [];
|
|
3739
|
+
return componentModels.map((m) => m && typeof m === "object" ? m.id : void 0).filter((id) => typeof id === "string");
|
|
3740
|
+
}
|
|
3741
|
+
function assessReadiness(input) {
|
|
3742
|
+
const isUeProject = !!input.configPresent["component-definition.json"] && !!input.configPresent["component-models.json"];
|
|
3743
|
+
const ids = new Set(input.modelIds);
|
|
3744
|
+
const blocks = input.blocks.map((b) => {
|
|
3745
|
+
const editable = b.hasModelFile || ids.has(b.name);
|
|
3746
|
+
return {
|
|
3747
|
+
name: b.name,
|
|
3748
|
+
editable,
|
|
3749
|
+
reason: editable ? void 0 : "no UE model (_<name>.json or component-models entry)"
|
|
3750
|
+
};
|
|
3751
|
+
});
|
|
3752
|
+
return { isUeProject, blocks };
|
|
3753
|
+
}
|
|
3754
|
+
var UE_CONFIG_FILES, AEM_CONNECTION_META;
|
|
3755
|
+
var init_check = __esm({
|
|
3756
|
+
"src/lib/ue/check.ts"() {
|
|
3757
|
+
"use strict";
|
|
3758
|
+
init_esm_shims();
|
|
3759
|
+
UE_CONFIG_FILES = [
|
|
3760
|
+
"component-definition.json",
|
|
3761
|
+
"component-models.json",
|
|
3762
|
+
"component-filters.json"
|
|
3763
|
+
];
|
|
3764
|
+
AEM_CONNECTION_META = "urn:adobe:aue:system:aemconnection";
|
|
3765
|
+
}
|
|
3766
|
+
});
|
|
3767
|
+
|
|
3768
|
+
// src/commands/ue.ts
|
|
3769
|
+
var ue_exports = {};
|
|
3770
|
+
__export(ue_exports, {
|
|
3771
|
+
ueCheck: () => ueCheck,
|
|
3772
|
+
ueOpen: () => ueOpen
|
|
3773
|
+
});
|
|
3774
|
+
import { existsSync as existsSync18 } from "fs";
|
|
3775
|
+
import { readFile as readFile13, readdir as readdir3 } from "fs/promises";
|
|
3776
|
+
import path21 from "path";
|
|
3777
|
+
import chalk8 from "chalk";
|
|
3778
|
+
async function readJson2(file) {
|
|
3779
|
+
if (!existsSync18(file)) return null;
|
|
3780
|
+
try {
|
|
3781
|
+
return JSON.parse(await readFile13(file, "utf-8"));
|
|
3782
|
+
} catch {
|
|
3783
|
+
return null;
|
|
3784
|
+
}
|
|
3785
|
+
}
|
|
3786
|
+
async function scanBlocks(root) {
|
|
3787
|
+
const blocksDir = path21.join(root, "blocks");
|
|
3788
|
+
if (!existsSync18(blocksDir)) return [];
|
|
3789
|
+
const entries = await readdir3(blocksDir, { withFileTypes: true });
|
|
3790
|
+
return entries.filter((e) => e.isDirectory()).map((e) => ({
|
|
3791
|
+
name: e.name,
|
|
3792
|
+
hasModelFile: existsSync18(path21.join(blocksDir, e.name, `_${e.name}.json`))
|
|
3793
|
+
}));
|
|
3794
|
+
}
|
|
3795
|
+
async function openUrl2(url) {
|
|
3796
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
3797
|
+
try {
|
|
3798
|
+
const { execa } = await import("execa");
|
|
3799
|
+
await execa(cmd, [url], { stdio: "ignore" });
|
|
3800
|
+
} catch {
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
async function gitRemote(root) {
|
|
3804
|
+
try {
|
|
3805
|
+
const { execa } = await import("execa");
|
|
3806
|
+
const res = await execa("git", ["remote", "get-url", "origin"], { cwd: root });
|
|
3807
|
+
return res.stdout.trim() || null;
|
|
3808
|
+
} catch {
|
|
3809
|
+
return null;
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3812
|
+
async function ueCheck() {
|
|
3813
|
+
const root = findProjectRoot();
|
|
3814
|
+
if (!root) {
|
|
3815
|
+
logger.error("Not inside an EDS project (no EDS project markers found).");
|
|
3816
|
+
process.exitCode = 1;
|
|
3817
|
+
return;
|
|
3818
|
+
}
|
|
3819
|
+
logger.logoOnce(logo("Universal Editor - readiness"));
|
|
3820
|
+
const configPresent = {};
|
|
3821
|
+
for (const f2 of UE_CONFIG_FILES) configPresent[f2] = existsSync18(path21.join(root, f2));
|
|
3822
|
+
const componentModels = await readJson2(path21.join(root, "component-models.json"));
|
|
3823
|
+
const blocks = await scanBlocks(root);
|
|
3824
|
+
const { isUeProject, blocks: blockReadiness } = assessReadiness({
|
|
3825
|
+
configPresent,
|
|
3826
|
+
blocks,
|
|
3827
|
+
modelIds: modelIds(componentModels)
|
|
3828
|
+
});
|
|
3829
|
+
logger.info(heading("Universal Editor config"));
|
|
3830
|
+
const names = [...UE_CONFIG_FILES];
|
|
3831
|
+
const w = columnWidth(names);
|
|
3832
|
+
for (const f2 of names) {
|
|
3833
|
+
logger.info(statusLine(configPresent[f2] ? "pass" : "fail", f2, void 0, w));
|
|
3834
|
+
}
|
|
3835
|
+
if (!isUeProject) {
|
|
3836
|
+
logger.info("");
|
|
3837
|
+
logger.warn(
|
|
3838
|
+
"This doesn't look like a Universal Editor (crosswalk) project - run `eds scaffold ue` first."
|
|
3839
|
+
);
|
|
3840
|
+
process.exitCode = 1;
|
|
3841
|
+
return;
|
|
3842
|
+
}
|
|
3843
|
+
const headFile = path21.join(root, "head.html");
|
|
3844
|
+
if (existsSync18(headFile)) {
|
|
3845
|
+
const connected = hasAemConnection(await readFile13(headFile, "utf-8"));
|
|
3846
|
+
logger.info(
|
|
3847
|
+
statusLine(
|
|
3848
|
+
connected ? "pass" : "info",
|
|
3849
|
+
"head.html",
|
|
3850
|
+
connected ? "aemconnection present" : "no aemconnection (fine for cloud author)"
|
|
3851
|
+
)
|
|
3852
|
+
);
|
|
3853
|
+
}
|
|
3854
|
+
logger.info(heading("Blocks", `${blockReadiness.length} found`));
|
|
3855
|
+
if (blockReadiness.length === 0) {
|
|
3856
|
+
logger.info(chalk8.dim(" No blocks yet."));
|
|
3857
|
+
} else {
|
|
3858
|
+
const bw = columnWidth(blockReadiness.map((b) => b.name));
|
|
3859
|
+
for (const b of blockReadiness) {
|
|
3860
|
+
logger.info(statusLine(b.editable ? "pass" : "warn", b.name, b.reason, bw));
|
|
3861
|
+
}
|
|
3862
|
+
}
|
|
3863
|
+
const editable = blockReadiness.filter((b) => b.editable).length;
|
|
3864
|
+
const notEditable = blockReadiness.length - editable;
|
|
3865
|
+
logger.info("");
|
|
3866
|
+
logger.info(summary({ pass: editable, warn: notEditable }));
|
|
3867
|
+
logger.info(chalk8.dim(" Open a page in the editor with `eds ue open <path>`."));
|
|
3868
|
+
}
|
|
3869
|
+
async function ueOpen(pagePath, options = {}) {
|
|
3870
|
+
const root = findProjectRoot() ?? process.cwd();
|
|
3871
|
+
const pPath = pagePath ?? options.path ?? "/";
|
|
3872
|
+
let host = options.url;
|
|
3873
|
+
if (!host) {
|
|
3874
|
+
const remote = await gitRemote(root);
|
|
3875
|
+
const parsed = remote ? parseRemote(remote) : null;
|
|
3876
|
+
if (!parsed) {
|
|
3877
|
+
logger.error("Couldn't determine the preview host from git.");
|
|
3878
|
+
logger.info(
|
|
3879
|
+
chalk8.dim(
|
|
3880
|
+
" Pass it explicitly: `eds ue open <path> --url main--<repo>--<owner>.aem.page`"
|
|
3881
|
+
)
|
|
3882
|
+
);
|
|
3883
|
+
process.exitCode = 1;
|
|
3884
|
+
return;
|
|
3885
|
+
}
|
|
3886
|
+
host = previewHost(parsed.owner, parsed.repo, options.ref ?? "main");
|
|
3887
|
+
}
|
|
3888
|
+
const link2 = editorLink({ host, path: pPath, org: options.org });
|
|
3889
|
+
logger.info(heading("Universal Editor"));
|
|
3890
|
+
logger.info(accentLine("page", `${host}${pPath.startsWith("/") ? "" : "/"}${pPath}`));
|
|
3891
|
+
logger.info(accentLine("open", link2));
|
|
3892
|
+
if (options.open !== false) await openUrl2(link2);
|
|
3893
|
+
else logger.info(chalk8.dim(" (use without --no-open to launch the browser)"));
|
|
3894
|
+
}
|
|
3895
|
+
var init_ue3 = __esm({
|
|
3896
|
+
"src/commands/ue.ts"() {
|
|
3897
|
+
"use strict";
|
|
3898
|
+
init_esm_shims();
|
|
3899
|
+
init_logger();
|
|
3900
|
+
init_project();
|
|
3901
|
+
init_check();
|
|
3902
|
+
init_link();
|
|
3903
|
+
init_ui();
|
|
3904
|
+
}
|
|
3905
|
+
});
|
|
3906
|
+
|
|
3022
3907
|
// src/commands/block/create.ts
|
|
3023
3908
|
var create_exports = {};
|
|
3024
3909
|
__export(create_exports, {
|
|
3025
3910
|
createBlock: () => createBlock,
|
|
3026
3911
|
ueModelFor: () => ueModelFor
|
|
3027
3912
|
});
|
|
3028
|
-
import { existsSync as
|
|
3029
|
-
import { mkdir as
|
|
3030
|
-
import
|
|
3031
|
-
import
|
|
3913
|
+
import { existsSync as existsSync19 } from "fs";
|
|
3914
|
+
import { mkdir as mkdir7, writeFile as writeFile14 } from "fs/promises";
|
|
3915
|
+
import path22 from "path";
|
|
3916
|
+
import chalk9 from "chalk";
|
|
3032
3917
|
function titleCase(name) {
|
|
3033
3918
|
return name.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
3034
3919
|
}
|
|
@@ -3106,13 +3991,13 @@ async function createBlock(name, options = {}) {
|
|
|
3106
3991
|
process.exitCode = 1;
|
|
3107
3992
|
return;
|
|
3108
3993
|
}
|
|
3109
|
-
const blocksDir =
|
|
3110
|
-
if (
|
|
3994
|
+
const blocksDir = path22.join(projectRoot, "blocks", name);
|
|
3995
|
+
if (existsSync19(blocksDir)) {
|
|
3111
3996
|
logger.error(`Block "${name}" already exists at ${blocksDir}`);
|
|
3112
3997
|
process.exitCode = 1;
|
|
3113
3998
|
return;
|
|
3114
3999
|
}
|
|
3115
|
-
await
|
|
4000
|
+
await mkdir7(blocksDir, { recursive: true });
|
|
3116
4001
|
const jsContent = `export default function decorate(block) {
|
|
3117
4002
|
// TODO: implement ${name} block
|
|
3118
4003
|
}
|
|
@@ -3121,14 +4006,14 @@ async function createBlock(name, options = {}) {
|
|
|
3121
4006
|
/* TODO: style ${name} block */
|
|
3122
4007
|
}
|
|
3123
4008
|
`;
|
|
3124
|
-
await
|
|
3125
|
-
await
|
|
4009
|
+
await writeFile14(path22.join(blocksDir, `${name}.js`), jsContent);
|
|
4010
|
+
await writeFile14(path22.join(blocksDir, `${name}.css`), cssContent);
|
|
3126
4011
|
const withUe = options.ueModel !== false;
|
|
3127
4012
|
if (withUe) {
|
|
3128
|
-
await
|
|
4013
|
+
await writeFile14(path22.join(blocksDir, `_${name}.json`), ueModelFor(name));
|
|
3129
4014
|
}
|
|
3130
4015
|
logger.success(
|
|
3131
|
-
`Block "${name}" created at ${
|
|
4016
|
+
`Block "${name}" created at ${chalk9.cyan(path22.relative(projectRoot, blocksDir))}${withUe ? chalk9.dim(" (+ Universal Editor model)") : ""}`
|
|
3132
4017
|
);
|
|
3133
4018
|
}
|
|
3134
4019
|
var init_create = __esm({
|
|
@@ -3142,10 +4027,10 @@ var init_create = __esm({
|
|
|
3142
4027
|
});
|
|
3143
4028
|
|
|
3144
4029
|
// src/lib/agents/detect.ts
|
|
3145
|
-
import { existsSync as
|
|
3146
|
-
import { readFile as
|
|
4030
|
+
import { existsSync as existsSync20 } from "fs";
|
|
4031
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
3147
4032
|
import { homedir } from "os";
|
|
3148
|
-
import
|
|
4033
|
+
import path23 from "path";
|
|
3149
4034
|
async function whichBinary(name) {
|
|
3150
4035
|
const { execaCommand } = await import("execa");
|
|
3151
4036
|
try {
|
|
@@ -3173,8 +4058,8 @@ function findFigmaServerKey(node) {
|
|
|
3173
4058
|
async function findFigmaMcpServer(configPaths) {
|
|
3174
4059
|
for (const configPath of configPaths) {
|
|
3175
4060
|
try {
|
|
3176
|
-
if (!
|
|
3177
|
-
const content = await
|
|
4061
|
+
if (!existsSync20(configPath)) continue;
|
|
4062
|
+
const content = await readFile14(configPath, "utf-8");
|
|
3178
4063
|
try {
|
|
3179
4064
|
const key = findFigmaServerKey(JSON.parse(content));
|
|
3180
4065
|
if (key) return key;
|
|
@@ -3196,7 +4081,7 @@ async function detectAgent(preferred) {
|
|
|
3196
4081
|
return {
|
|
3197
4082
|
type: def.type,
|
|
3198
4083
|
path: binPath,
|
|
3199
|
-
configDir:
|
|
4084
|
+
configDir: existsSync20(def.configDir) ? def.configDir : void 0,
|
|
3200
4085
|
hasFigmaMcp: !!figmaMcpServerName,
|
|
3201
4086
|
figmaMcpServerName
|
|
3202
4087
|
};
|
|
@@ -3211,7 +4096,7 @@ async function detectAgent(preferred) {
|
|
|
3211
4096
|
return {
|
|
3212
4097
|
type: def.type,
|
|
3213
4098
|
path: binPath,
|
|
3214
|
-
configDir:
|
|
4099
|
+
configDir: existsSync20(def.configDir) ? def.configDir : void 0,
|
|
3215
4100
|
hasFigmaMcp: !!figmaMcpServerName,
|
|
3216
4101
|
figmaMcpServerName
|
|
3217
4102
|
};
|
|
@@ -3228,7 +4113,7 @@ async function detectAllAgents() {
|
|
|
3228
4113
|
agents.push({
|
|
3229
4114
|
type: def.type,
|
|
3230
4115
|
path: binPath,
|
|
3231
|
-
configDir:
|
|
4116
|
+
configDir: existsSync20(def.configDir) ? def.configDir : void 0,
|
|
3232
4117
|
hasFigmaMcp: !!figmaMcpServerName,
|
|
3233
4118
|
figmaMcpServerName
|
|
3234
4119
|
});
|
|
@@ -3246,22 +4131,22 @@ var init_detect = __esm({
|
|
|
3246
4131
|
{
|
|
3247
4132
|
type: "claude",
|
|
3248
4133
|
binary: "claude",
|
|
3249
|
-
configDir:
|
|
4134
|
+
configDir: path23.join(homedir(), ".claude"),
|
|
3250
4135
|
mcpConfigPaths: [
|
|
3251
|
-
|
|
3252
|
-
|
|
4136
|
+
path23.join(homedir(), ".claude", "plugins.json"),
|
|
4137
|
+
path23.join(homedir(), ".claude.json")
|
|
3253
4138
|
]
|
|
3254
4139
|
},
|
|
3255
4140
|
{
|
|
3256
4141
|
type: "cursor",
|
|
3257
4142
|
binary: "cursor",
|
|
3258
|
-
configDir:
|
|
3259
|
-
mcpConfigPaths: [
|
|
4143
|
+
configDir: path23.join(homedir(), ".cursor"),
|
|
4144
|
+
mcpConfigPaths: [path23.join(homedir(), ".cursor", "mcp.json")]
|
|
3260
4145
|
},
|
|
3261
4146
|
{
|
|
3262
4147
|
type: "codex",
|
|
3263
4148
|
binary: "codex",
|
|
3264
|
-
configDir:
|
|
4149
|
+
configDir: path23.join(homedir(), ".codex"),
|
|
3265
4150
|
mcpConfigPaths: []
|
|
3266
4151
|
}
|
|
3267
4152
|
];
|
|
@@ -3269,27 +4154,27 @@ var init_detect = __esm({
|
|
|
3269
4154
|
});
|
|
3270
4155
|
|
|
3271
4156
|
// src/lib/agents/invoke.ts
|
|
3272
|
-
import { mkdir as
|
|
4157
|
+
import { mkdir as mkdir8, readFile as readFile15, writeFile as writeFile15 } from "fs/promises";
|
|
3273
4158
|
import { tmpdir } from "os";
|
|
3274
|
-
import
|
|
4159
|
+
import path24 from "path";
|
|
3275
4160
|
import readline from "readline";
|
|
3276
|
-
import
|
|
4161
|
+
import chalk10 from "chalk";
|
|
3277
4162
|
async function savePromptToFile(prompt, projectRoot) {
|
|
3278
|
-
const base = projectRoot ?
|
|
3279
|
-
const dir =
|
|
3280
|
-
await
|
|
4163
|
+
const base = projectRoot ? path24.join(projectRoot, "tmp") : tmpdir();
|
|
4164
|
+
const dir = path24.join(base, "eds-cli");
|
|
4165
|
+
await mkdir8(dir, { recursive: true });
|
|
3281
4166
|
const filename = `from-design-${Date.now()}.md`;
|
|
3282
|
-
const filePath =
|
|
3283
|
-
await
|
|
4167
|
+
const filePath = path24.join(dir, filename);
|
|
4168
|
+
await writeFile15(filePath, prompt, "utf-8");
|
|
3284
4169
|
return filePath;
|
|
3285
4170
|
}
|
|
3286
4171
|
async function invokeAgent(agent, promptFile, cwd) {
|
|
3287
4172
|
const { execa } = await import("execa");
|
|
3288
|
-
const prompt = await
|
|
4173
|
+
const prompt = await readFile15(promptFile, "utf-8");
|
|
3289
4174
|
try {
|
|
3290
4175
|
switch (agent.type) {
|
|
3291
4176
|
case "claude": {
|
|
3292
|
-
logger.info(`Running Claude Code ${
|
|
4177
|
+
logger.info(`Running Claude Code ${chalk10.dim("- live log:")}`);
|
|
3293
4178
|
const figmaServers = [
|
|
3294
4179
|
...agent.figmaMcpServerName ? [agent.figmaMcpServerName] : [],
|
|
3295
4180
|
"figma-sse",
|
|
@@ -3352,10 +4237,10 @@ async function renderClaudeStream(sub, cwd) {
|
|
|
3352
4237
|
const ora4 = (await import("ora")).default;
|
|
3353
4238
|
const start = Date.now();
|
|
3354
4239
|
const elapsed = () => `${Math.round((Date.now() - start) / 1e3)}s`;
|
|
3355
|
-
const spinner = ora4({ text: `thinking ${
|
|
4240
|
+
const spinner = ora4({ text: `thinking ${chalk10.dim(elapsed())}`, color: "yellow" }).start();
|
|
3356
4241
|
let activity = "thinking";
|
|
3357
4242
|
const timer = setInterval(() => {
|
|
3358
|
-
spinner.text = `${activity} ${
|
|
4243
|
+
spinner.text = `${activity} ${chalk10.dim(elapsed())}`;
|
|
3359
4244
|
}, 300);
|
|
3360
4245
|
const pending = /* @__PURE__ */ new Map();
|
|
3361
4246
|
const emit = (line) => {
|
|
@@ -3381,7 +4266,7 @@ async function renderClaudeStream(sub, cwd) {
|
|
|
3381
4266
|
for (const block2 of content) {
|
|
3382
4267
|
if (block2.type === "text" && "text" in block2) {
|
|
3383
4268
|
const first = block2.text.trim().split("\n")[0].slice(0, 100);
|
|
3384
|
-
if (first) emit(
|
|
4269
|
+
if (first) emit(chalk10.dim(first));
|
|
3385
4270
|
} else if (block2.type === "tool_use" && "name" in block2) {
|
|
3386
4271
|
const id = "id" in block2 && block2.id ? block2.id : `${Math.random()}`;
|
|
3387
4272
|
pending.set(id, {
|
|
@@ -3397,8 +4282,8 @@ async function renderClaudeStream(sub, cwd) {
|
|
|
3397
4282
|
const p = pending.get(block2.tool_use_id);
|
|
3398
4283
|
if (p) {
|
|
3399
4284
|
const dur = `${((Date.now() - p.start) / 1e3).toFixed(1)}s`;
|
|
3400
|
-
const failed = "is_error" in block2 && block2.is_error ? ` ${
|
|
3401
|
-
emit(`${p.label} ${
|
|
4285
|
+
const failed = "is_error" in block2 && block2.is_error ? ` ${chalk10.red("failed")}` : "";
|
|
4286
|
+
emit(`${p.label} ${chalk10.dim(dur)}${failed}`);
|
|
3402
4287
|
pending.delete(block2.tool_use_id);
|
|
3403
4288
|
}
|
|
3404
4289
|
}
|
|
@@ -3410,9 +4295,9 @@ async function renderClaudeStream(sub, cwd) {
|
|
|
3410
4295
|
}
|
|
3411
4296
|
}
|
|
3412
4297
|
}
|
|
3413
|
-
for (const p of pending.values()) emit(`${p.label} ${
|
|
4298
|
+
for (const p of pending.values()) emit(`${p.label} ${chalk10.dim("\u2026")}`);
|
|
3414
4299
|
clearInterval(timer);
|
|
3415
|
-
if (result) spinner.succeed(`${brand.accent("agent finished")} ${
|
|
4300
|
+
if (result) spinner.succeed(`${brand.accent("agent finished")} ${chalk10.dim(result)}`);
|
|
3416
4301
|
else spinner.stop();
|
|
3417
4302
|
} catch (err) {
|
|
3418
4303
|
clearInterval(timer);
|
|
@@ -3425,22 +4310,22 @@ function toolLabel(name, input, cwd) {
|
|
|
3425
4310
|
const rel = (key) => {
|
|
3426
4311
|
const p = input?.[key];
|
|
3427
4312
|
if (typeof p !== "string") return "";
|
|
3428
|
-
return p.startsWith(cwd) ?
|
|
4313
|
+
return p.startsWith(cwd) ? path24.relative(cwd, p) : path24.basename(p);
|
|
3429
4314
|
};
|
|
3430
4315
|
if (name.includes("figma")) {
|
|
3431
4316
|
const short = name.split("__").pop() ?? name;
|
|
3432
|
-
return `${icon.figma} ${
|
|
4317
|
+
return `${icon.figma} ${chalk10.bold(short)} ${chalk10.dim("Figma MCP")}`;
|
|
3433
4318
|
}
|
|
3434
4319
|
switch (name) {
|
|
3435
4320
|
case "Write":
|
|
3436
4321
|
case "Edit":
|
|
3437
4322
|
case "NotebookEdit":
|
|
3438
|
-
return `${brand.accent("\u270E")} ${
|
|
4323
|
+
return `${brand.accent("\u270E")} ${chalk10.cyan(rel("file_path"))}`;
|
|
3439
4324
|
case "Read":
|
|
3440
|
-
return
|
|
4325
|
+
return chalk10.dim(`\xB7 read ${rel("file_path")}`);
|
|
3441
4326
|
case "Bash": {
|
|
3442
4327
|
const cmd = typeof input?.command === "string" ? input.command.split("\n")[0].slice(0, 70) : "";
|
|
3443
|
-
return
|
|
4328
|
+
return chalk10.dim(`$ ${cmd}`);
|
|
3444
4329
|
}
|
|
3445
4330
|
default:
|
|
3446
4331
|
return `${brand.blue("\u2192")} ${name}`;
|
|
@@ -3456,13 +4341,13 @@ var init_invoke = __esm({
|
|
|
3456
4341
|
});
|
|
3457
4342
|
|
|
3458
4343
|
// src/lib/config.ts
|
|
3459
|
-
import { existsSync as
|
|
3460
|
-
import { readFile as
|
|
4344
|
+
import { existsSync as existsSync21 } from "fs";
|
|
4345
|
+
import { readFile as readFile16 } from "fs/promises";
|
|
3461
4346
|
import { homedir as homedir2 } from "os";
|
|
3462
|
-
import
|
|
4347
|
+
import path25 from "path";
|
|
3463
4348
|
async function loadJsonFile(filePath) {
|
|
3464
4349
|
try {
|
|
3465
|
-
const content = await
|
|
4350
|
+
const content = await readFile16(filePath, "utf-8");
|
|
3466
4351
|
return JSON.parse(content);
|
|
3467
4352
|
} catch {
|
|
3468
4353
|
return void 0;
|
|
@@ -3470,7 +4355,7 @@ async function loadJsonFile(filePath) {
|
|
|
3470
4355
|
}
|
|
3471
4356
|
async function loadConfig(projectRoot) {
|
|
3472
4357
|
const globalRaw = await loadJsonFile(GLOBAL_CONFIG_PATH);
|
|
3473
|
-
const projectRaw = projectRoot ? await loadJsonFile(
|
|
4358
|
+
const projectRaw = projectRoot ? await loadJsonFile(path25.join(projectRoot, PROJECT_CONFIG_NAME)) : void 0;
|
|
3474
4359
|
const merged = {
|
|
3475
4360
|
...typeof globalRaw === "object" && globalRaw !== null ? globalRaw : {},
|
|
3476
4361
|
...typeof projectRaw === "object" && projectRaw !== null ? projectRaw : {}
|
|
@@ -3489,7 +4374,7 @@ function globalConfigPath() {
|
|
|
3489
4374
|
return GLOBAL_CONFIG_PATH;
|
|
3490
4375
|
}
|
|
3491
4376
|
function globalConfigExists() {
|
|
3492
|
-
return
|
|
4377
|
+
return existsSync21(GLOBAL_CONFIG_PATH);
|
|
3493
4378
|
}
|
|
3494
4379
|
var GLOBAL_CONFIG_DIR, GLOBAL_CONFIG_PATH, PROJECT_CONFIG_NAME;
|
|
3495
4380
|
var init_config = __esm({
|
|
@@ -3498,8 +4383,8 @@ var init_config = __esm({
|
|
|
3498
4383
|
init_esm_shims();
|
|
3499
4384
|
init_logger();
|
|
3500
4385
|
init_schemas();
|
|
3501
|
-
GLOBAL_CONFIG_DIR =
|
|
3502
|
-
GLOBAL_CONFIG_PATH =
|
|
4386
|
+
GLOBAL_CONFIG_DIR = path25.join(homedir2(), ".eds");
|
|
4387
|
+
GLOBAL_CONFIG_PATH = path25.join(GLOBAL_CONFIG_DIR, "config.json");
|
|
3503
4388
|
PROJECT_CONFIG_NAME = ".edsrc.json";
|
|
3504
4389
|
}
|
|
3505
4390
|
});
|
|
@@ -3549,9 +4434,9 @@ var init_node_id = __esm({
|
|
|
3549
4434
|
});
|
|
3550
4435
|
|
|
3551
4436
|
// src/lib/figma/prompt-builder.ts
|
|
3552
|
-
import { existsSync as
|
|
3553
|
-
import { readFile as
|
|
3554
|
-
import
|
|
4437
|
+
import { existsSync as existsSync22 } from "fs";
|
|
4438
|
+
import { readFile as readFile17 } from "fs/promises";
|
|
4439
|
+
import path26 from "path";
|
|
3555
4440
|
function contentSourceGuidance(source, cfHint, blockName) {
|
|
3556
4441
|
if (!source || source === "document" || source === "ue") return "";
|
|
3557
4442
|
const q = cfHint ? ` (model / persisted query: \`${cfHint}\`)` : "";
|
|
@@ -3766,10 +4651,10 @@ ${tokensHint}
|
|
|
3766
4651
|
`;
|
|
3767
4652
|
}
|
|
3768
4653
|
async function getTokensHint(projectRoot) {
|
|
3769
|
-
const stylesPath =
|
|
3770
|
-
if (!
|
|
4654
|
+
const stylesPath = path26.join(projectRoot, "styles", "styles.css");
|
|
4655
|
+
if (!existsSync22(stylesPath)) return "";
|
|
3771
4656
|
try {
|
|
3772
|
-
const css = await
|
|
4657
|
+
const css = await readFile17(stylesPath, "utf-8");
|
|
3773
4658
|
const rootMatch = css.match(/:root\s*\{([^}]+)\}/);
|
|
3774
4659
|
if (!rootMatch) return "";
|
|
3775
4660
|
const props = rootMatch[1].split("\n").filter((line) => line.trim().startsWith("--")).map((line) => line.trim()).join("\n ");
|
|
@@ -3911,11 +4796,11 @@ var init_harness = __esm({
|
|
|
3911
4796
|
});
|
|
3912
4797
|
|
|
3913
4798
|
// src/lib/preview/server.ts
|
|
3914
|
-
import { createReadStream, existsSync as
|
|
4799
|
+
import { createReadStream, existsSync as existsSync23, statSync } from "fs";
|
|
3915
4800
|
import http from "http";
|
|
3916
|
-
import
|
|
4801
|
+
import path27 from "path";
|
|
3917
4802
|
function startServer(root, routes, port, host = "127.0.0.1") {
|
|
3918
|
-
const resolvedRoot =
|
|
4803
|
+
const resolvedRoot = path27.resolve(root);
|
|
3919
4804
|
const clients = /* @__PURE__ */ new Set();
|
|
3920
4805
|
const server = http.createServer((req, res) => {
|
|
3921
4806
|
const urlPath = decodeURIComponent((req.url ?? "/").split("?")[0]);
|
|
@@ -3941,19 +4826,19 @@ function startServer(root, routes, port, host = "127.0.0.1") {
|
|
|
3941
4826
|
res.end(route.body);
|
|
3942
4827
|
return;
|
|
3943
4828
|
}
|
|
3944
|
-
const filePath =
|
|
4829
|
+
const filePath = path27.join(resolvedRoot, urlPath);
|
|
3945
4830
|
if (!filePath.startsWith(resolvedRoot)) {
|
|
3946
4831
|
res.writeHead(403);
|
|
3947
4832
|
res.end("Forbidden");
|
|
3948
4833
|
return;
|
|
3949
4834
|
}
|
|
3950
|
-
if (!
|
|
4835
|
+
if (!existsSync23(filePath) || !statSync(filePath).isFile()) {
|
|
3951
4836
|
res.writeHead(404, { "content-type": "text/plain" });
|
|
3952
4837
|
res.end(`Not found: ${urlPath}`);
|
|
3953
4838
|
return;
|
|
3954
4839
|
}
|
|
3955
4840
|
res.writeHead(200, {
|
|
3956
|
-
"content-type": MIME[
|
|
4841
|
+
"content-type": MIME[path27.extname(filePath).toLowerCase()] ?? "application/octet-stream"
|
|
3957
4842
|
});
|
|
3958
4843
|
createReadStream(filePath).pipe(res);
|
|
3959
4844
|
});
|
|
@@ -4017,10 +4902,10 @@ var preview_exports = {};
|
|
|
4017
4902
|
__export(preview_exports, {
|
|
4018
4903
|
previewBlock: () => previewBlock
|
|
4019
4904
|
});
|
|
4020
|
-
import { existsSync as
|
|
4021
|
-
import { readFile as
|
|
4022
|
-
import
|
|
4023
|
-
import
|
|
4905
|
+
import { existsSync as existsSync24, watch } from "fs";
|
|
4906
|
+
import { readFile as readFile18 } from "fs/promises";
|
|
4907
|
+
import path28 from "path";
|
|
4908
|
+
import chalk11 from "chalk";
|
|
4024
4909
|
function labelFor(width) {
|
|
4025
4910
|
if (width <= 480) return `${width} mobile`;
|
|
4026
4911
|
if (width <= 1024) return `${width} tablet`;
|
|
@@ -4072,30 +4957,30 @@ async function previewBlock(name, options = {}) {
|
|
|
4072
4957
|
process.exitCode = 1;
|
|
4073
4958
|
return;
|
|
4074
4959
|
}
|
|
4075
|
-
const blockDir =
|
|
4076
|
-
if (!
|
|
4960
|
+
const blockDir = path28.join(projectRoot, "blocks", name);
|
|
4961
|
+
if (!existsSync24(blockDir)) {
|
|
4077
4962
|
logger.error(`Block "${name}" not found at blocks/${name}/.`);
|
|
4078
4963
|
process.exitCode = 1;
|
|
4079
4964
|
return;
|
|
4080
4965
|
}
|
|
4081
|
-
if (!
|
|
4966
|
+
if (!existsSync24(path28.join(blockDir, `${name}.css`)) && !existsSync24(path28.join(blockDir, `${name}.js`))) {
|
|
4082
4967
|
logger.error(`Block "${name}" has no ${name}.css / ${name}.js to preview.`);
|
|
4083
4968
|
process.exitCode = 1;
|
|
4084
4969
|
return;
|
|
4085
4970
|
}
|
|
4086
4971
|
let meta = null;
|
|
4087
|
-
const metaPath =
|
|
4088
|
-
if (
|
|
4972
|
+
const metaPath = path28.join(blockDir, ".eds-meta.json");
|
|
4973
|
+
if (existsSync24(metaPath)) {
|
|
4089
4974
|
try {
|
|
4090
|
-
meta = JSON.parse(await
|
|
4975
|
+
meta = JSON.parse(await readFile18(metaPath, "utf-8"));
|
|
4091
4976
|
} catch {
|
|
4092
4977
|
}
|
|
4093
4978
|
}
|
|
4094
|
-
const samplePath =
|
|
4979
|
+
const samplePath = path28.join(blockDir, `_${name}.preview.html`);
|
|
4095
4980
|
let sampleContent;
|
|
4096
4981
|
let placeholder = false;
|
|
4097
|
-
if (
|
|
4098
|
-
sampleContent = await
|
|
4982
|
+
if (existsSync24(samplePath)) {
|
|
4983
|
+
sampleContent = await readFile18(samplePath, "utf-8");
|
|
4099
4984
|
} else {
|
|
4100
4985
|
sampleContent = defaultSample(name);
|
|
4101
4986
|
placeholder = true;
|
|
@@ -4145,9 +5030,9 @@ async function previewBlock(name, options = {}) {
|
|
|
4145
5030
|
reloadTimer = setTimeout(() => server.reload(), 150);
|
|
4146
5031
|
});
|
|
4147
5032
|
logger.info("");
|
|
4148
|
-
logger.info(` ${
|
|
5033
|
+
logger.info(` ${chalk11.bold("\u279C")} ${chalk11.cyan(server.url)}`);
|
|
4149
5034
|
logger.info(statusLine("pass", "live reload", "edits to the block refresh the browser"));
|
|
4150
|
-
logger.info(
|
|
5035
|
+
logger.info(chalk11.dim(" Press Ctrl+C to stop.\n"));
|
|
4151
5036
|
if (options.open !== false) await openInBrowser(server.url);
|
|
4152
5037
|
const stop = async () => {
|
|
4153
5038
|
watcher.close();
|
|
@@ -4176,10 +5061,10 @@ var from_design_exports = {};
|
|
|
4176
5061
|
__export(from_design_exports, {
|
|
4177
5062
|
blockFromDesign: () => blockFromDesign
|
|
4178
5063
|
});
|
|
4179
|
-
import { existsSync as
|
|
4180
|
-
import { readFile as
|
|
4181
|
-
import
|
|
4182
|
-
import
|
|
5064
|
+
import { existsSync as existsSync25 } from "fs";
|
|
5065
|
+
import { readFile as readFile19, writeFile as writeFile16 } from "fs/promises";
|
|
5066
|
+
import path29 from "path";
|
|
5067
|
+
import chalk12 from "chalk";
|
|
4183
5068
|
import ora from "ora";
|
|
4184
5069
|
async function previewRunning(port = 8777) {
|
|
4185
5070
|
try {
|
|
@@ -4329,7 +5214,7 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
4329
5214
|
try {
|
|
4330
5215
|
figma2 = parseFigmaUrl(designUrl);
|
|
4331
5216
|
spinner.succeed(
|
|
4332
|
-
`Parsed Figma URL ${
|
|
5217
|
+
`Parsed Figma URL ${chalk12.dim(figma2.nodeId ? `node ${figma2.nodeId}` : `file ${figma2.fileKey}`)}`
|
|
4333
5218
|
);
|
|
4334
5219
|
} catch (err) {
|
|
4335
5220
|
spinner.fail(err.message);
|
|
@@ -4337,7 +5222,7 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
4337
5222
|
return;
|
|
4338
5223
|
}
|
|
4339
5224
|
} else {
|
|
4340
|
-
logger.info(` ${icon.pass} ${providerName} design ${
|
|
5225
|
+
logger.info(` ${icon.pass} ${providerName} design ${chalk12.dim(designRef)}`);
|
|
4341
5226
|
}
|
|
4342
5227
|
const blockName = options.name || (figma2 ? inferBlockName(figma2.fileName) : "");
|
|
4343
5228
|
if (!kebabCaseRegex.test(blockName)) {
|
|
@@ -4360,20 +5245,20 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
4360
5245
|
screenshot: options.screenshot !== false
|
|
4361
5246
|
});
|
|
4362
5247
|
const promptFile = await savePromptToFile(prompt, projectRoot);
|
|
4363
|
-
const hasTokens =
|
|
5248
|
+
const hasTokens = existsSync25(path29.join(projectRoot, "styles", "styles.css"));
|
|
4364
5249
|
promptSpinner.succeed(
|
|
4365
|
-
hasTokens ? `Loaded design tokens ${
|
|
5250
|
+
hasTokens ? `Loaded design tokens ${chalk12.dim("styles/styles.css")}` : `Built prompt ${chalk12.dim("no project tokens found")}`
|
|
4366
5251
|
);
|
|
4367
|
-
logger.info(
|
|
5252
|
+
logger.info(chalk12.dim(` prompt \u2192 ${promptFile}`));
|
|
4368
5253
|
if (options.dryRun) {
|
|
4369
5254
|
logger.info("");
|
|
4370
|
-
logger.info(
|
|
4371
|
-
logger.info(`Prompt file: ${
|
|
4372
|
-
logger.info(`Block name: ${
|
|
5255
|
+
logger.info(chalk12.yellow("Dry run mode \u2014 prompt generated but agent not invoked."));
|
|
5256
|
+
logger.info(`Prompt file: ${chalk12.cyan(promptFile)}`);
|
|
5257
|
+
logger.info(`Block name: ${chalk12.cyan(blockName)}`);
|
|
4373
5258
|
logger.info(`Prompt version: ${PROMPT_VERSION}`);
|
|
4374
5259
|
logger.info("");
|
|
4375
5260
|
logger.info("Preview:");
|
|
4376
|
-
logger.info(
|
|
5261
|
+
logger.info(chalk12.dim("\u2500".repeat(60)));
|
|
4377
5262
|
console.log(prompt);
|
|
4378
5263
|
return;
|
|
4379
5264
|
}
|
|
@@ -4381,22 +5266,22 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
4381
5266
|
const preferredAgent = options.agent || config.agent;
|
|
4382
5267
|
if (options.agent === "none") {
|
|
4383
5268
|
logger.info("Agent skipped (--agent none). Use the prompt file manually:");
|
|
4384
|
-
logger.info(` ${
|
|
5269
|
+
logger.info(` ${chalk12.cyan(promptFile)}`);
|
|
4385
5270
|
return;
|
|
4386
5271
|
}
|
|
4387
5272
|
const agentSpinner = ora("Detecting AI agent...").start();
|
|
4388
5273
|
const agent = await detectAgent(preferredAgent);
|
|
4389
5274
|
if (!agent) {
|
|
4390
5275
|
agentSpinner.fail("No AI agent found (claude, cursor, or codex).");
|
|
4391
|
-
logger.info(`Install Claude Code: ${
|
|
4392
|
-
logger.info(`Or use the prompt file manually: ${
|
|
5276
|
+
logger.info(`Install Claude Code: ${chalk12.cyan("npm install -g @anthropic-ai/claude-code")}`);
|
|
5277
|
+
logger.info(`Or use the prompt file manually: ${chalk12.cyan(promptFile)}`);
|
|
4393
5278
|
process.exitCode = 1;
|
|
4394
5279
|
return;
|
|
4395
5280
|
}
|
|
4396
|
-
agentSpinner.succeed(`Agent ready ${
|
|
5281
|
+
agentSpinner.succeed(`Agent ready ${chalk12.cyan(agent.type)} ${chalk12.dim(agent.path)}`);
|
|
4397
5282
|
if (provider === "figma" && !agent.hasFigmaMcp) {
|
|
4398
5283
|
logger.warn(`Figma MCP may not be configured for ${agent.type}.`);
|
|
4399
|
-
logger.info(`Run ${
|
|
5284
|
+
logger.info(`Run ${chalk12.cyan("eds figma setup")} to configure it.`);
|
|
4400
5285
|
}
|
|
4401
5286
|
if (!options.yes) {
|
|
4402
5287
|
const { confirm } = await import("@inquirer/prompts");
|
|
@@ -4405,24 +5290,24 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
4405
5290
|
default: true
|
|
4406
5291
|
});
|
|
4407
5292
|
if (!proceed) {
|
|
4408
|
-
logger.info(`Prompt saved to: ${
|
|
5293
|
+
logger.info(`Prompt saved to: ${chalk12.cyan(promptFile)}`);
|
|
4409
5294
|
return;
|
|
4410
5295
|
}
|
|
4411
5296
|
}
|
|
4412
5297
|
logger.info(
|
|
4413
|
-
` ${
|
|
5298
|
+
` ${chalk12.dim("\u2192")} Handing off to ${chalk12.cyan(agent.type)} ${chalk12.dim(`via ${providerName} MCP`)}`
|
|
4414
5299
|
);
|
|
4415
5300
|
if (provider === "figma") {
|
|
4416
|
-
logger.info(
|
|
5301
|
+
logger.info(chalk12.dim(" get_design_context \xB7 get_variable_defs \xB7 get_screenshot"));
|
|
4417
5302
|
}
|
|
4418
5303
|
const success = await invokeAgent(agent, promptFile, projectRoot);
|
|
4419
5304
|
if (!success) {
|
|
4420
5305
|
process.exitCode = 1;
|
|
4421
5306
|
return;
|
|
4422
5307
|
}
|
|
4423
|
-
const blockDir =
|
|
5308
|
+
const blockDir = path29.join(projectRoot, "blocks", blockName);
|
|
4424
5309
|
const ok = await verifyGeneratedBlock(blockDir, blockName, options.ueModel !== false);
|
|
4425
|
-
if (
|
|
5310
|
+
if (existsSync25(blockDir)) {
|
|
4426
5311
|
const meta = {
|
|
4427
5312
|
provider,
|
|
4428
5313
|
designRef,
|
|
@@ -4432,7 +5317,7 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
4432
5317
|
promptVersion: PROMPT_VERSION,
|
|
4433
5318
|
agentUsed: agent.type
|
|
4434
5319
|
};
|
|
4435
|
-
await
|
|
5320
|
+
await writeFile16(path29.join(blockDir, ".eds-meta.json"), `${JSON.stringify(meta, null, 2)}
|
|
4436
5321
|
`);
|
|
4437
5322
|
}
|
|
4438
5323
|
if (!ok) {
|
|
@@ -4457,7 +5342,7 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
4457
5342
|
if (options.serve !== false) {
|
|
4458
5343
|
if (await previewRunning()) {
|
|
4459
5344
|
logger.info(
|
|
4460
|
-
|
|
5345
|
+
chalk12.dim(
|
|
4461
5346
|
" A preview server is already running at http://127.0.0.1:8777 \u2014 your change will refresh there."
|
|
4462
5347
|
)
|
|
4463
5348
|
);
|
|
@@ -4467,15 +5352,15 @@ async function blockFromDesign(figmaUrlArg, optionsArg) {
|
|
|
4467
5352
|
await previewBlock2(blockName, {});
|
|
4468
5353
|
}
|
|
4469
5354
|
} else {
|
|
4470
|
-
logger.info(` ${
|
|
5355
|
+
logger.info(` ${chalk12.dim("\u2192")} Next: ${chalk12.cyan(`eds block preview ${blockName}`)}`);
|
|
4471
5356
|
}
|
|
4472
5357
|
}
|
|
4473
5358
|
async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
4474
5359
|
const checks = [];
|
|
4475
|
-
const jsPath =
|
|
4476
|
-
const cssPath =
|
|
4477
|
-
const hasJs =
|
|
4478
|
-
const hasCss =
|
|
5360
|
+
const jsPath = path29.join(blockDir, `${blockName}.js`);
|
|
5361
|
+
const cssPath = path29.join(blockDir, `${blockName}.css`);
|
|
5362
|
+
const hasJs = existsSync25(jsPath);
|
|
5363
|
+
const hasCss = existsSync25(cssPath);
|
|
4479
5364
|
checks.push({
|
|
4480
5365
|
status: hasJs ? "pass" : "fail",
|
|
4481
5366
|
name: `${blockName}.js`,
|
|
@@ -4487,7 +5372,7 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
|
4487
5372
|
message: hasCss ? "created" : "missing \u2014 agent did not write the CSS file"
|
|
4488
5373
|
});
|
|
4489
5374
|
if (hasJs) {
|
|
4490
|
-
const js = await
|
|
5375
|
+
const js = await readFile19(jsPath, "utf-8");
|
|
4491
5376
|
checks.push({
|
|
4492
5377
|
status: /export\s+default/.test(js) ? "pass" : "fail",
|
|
4493
5378
|
name: "decorate export",
|
|
@@ -4502,7 +5387,7 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
|
4502
5387
|
}
|
|
4503
5388
|
}
|
|
4504
5389
|
if (hasCss) {
|
|
4505
|
-
const css = await
|
|
5390
|
+
const css = await readFile19(cssPath, "utf-8");
|
|
4506
5391
|
checks.push({
|
|
4507
5392
|
status: css.includes(`.${blockName}`) ? "pass" : "warn",
|
|
4508
5393
|
name: "scoped CSS",
|
|
@@ -4510,12 +5395,12 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
|
4510
5395
|
});
|
|
4511
5396
|
}
|
|
4512
5397
|
if (withUeModel) {
|
|
4513
|
-
const hasPerBlock =
|
|
4514
|
-
const modelsFile =
|
|
5398
|
+
const hasPerBlock = existsSync25(path29.join(blockDir, `_${blockName}.json`));
|
|
5399
|
+
const modelsFile = path29.join(blockDir, "..", "..", "component-models.json");
|
|
4515
5400
|
let hasAggregated = false;
|
|
4516
|
-
if (!hasPerBlock &&
|
|
5401
|
+
if (!hasPerBlock && existsSync25(modelsFile)) {
|
|
4517
5402
|
try {
|
|
4518
|
-
hasAggregated = (await
|
|
5403
|
+
hasAggregated = (await readFile19(modelsFile, "utf-8")).includes(`"${blockName}"`);
|
|
4519
5404
|
} catch {
|
|
4520
5405
|
}
|
|
4521
5406
|
}
|
|
@@ -4527,8 +5412,8 @@ async function verifyGeneratedBlock(blockDir, blockName, withUeModel) {
|
|
|
4527
5412
|
});
|
|
4528
5413
|
if (hasPerBlock && hasJs) {
|
|
4529
5414
|
try {
|
|
4530
|
-
const modelRaw = await
|
|
4531
|
-
const js = await
|
|
5415
|
+
const modelRaw = await readFile19(path29.join(blockDir, `_${blockName}.json`), "utf-8");
|
|
5416
|
+
const js = await readFile19(jsPath, "utf-8");
|
|
4532
5417
|
const isRepeatable = /"resourceType"\s*:\s*"[^"]*\/item"/.test(modelRaw) || /"filter"\s*:/.test(modelRaw) || /"multi"\s*:\s*true/.test(modelRaw);
|
|
4533
5418
|
const iterates = /\.children/.test(js) || /\bfor\s*\(/.test(js) || /\.forEach\s*\(/.test(js);
|
|
4534
5419
|
if (isRepeatable) {
|
|
@@ -4571,10 +5456,10 @@ var list_exports = {};
|
|
|
4571
5456
|
__export(list_exports, {
|
|
4572
5457
|
listBlocks: () => listBlocks
|
|
4573
5458
|
});
|
|
4574
|
-
import { existsSync as
|
|
4575
|
-
import { readFile as
|
|
4576
|
-
import
|
|
4577
|
-
import
|
|
5459
|
+
import { existsSync as existsSync26 } from "fs";
|
|
5460
|
+
import { readFile as readFile20, readdir as readdir4 } from "fs/promises";
|
|
5461
|
+
import path30 from "path";
|
|
5462
|
+
import chalk13 from "chalk";
|
|
4578
5463
|
async function listBlocks(options = {}) {
|
|
4579
5464
|
const projectRoot = findProjectRoot();
|
|
4580
5465
|
if (!projectRoot) {
|
|
@@ -4582,27 +5467,27 @@ async function listBlocks(options = {}) {
|
|
|
4582
5467
|
process.exitCode = 1;
|
|
4583
5468
|
return;
|
|
4584
5469
|
}
|
|
4585
|
-
const blocksDir =
|
|
4586
|
-
if (!
|
|
5470
|
+
const blocksDir = path30.join(projectRoot, "blocks");
|
|
5471
|
+
if (!existsSync26(blocksDir)) {
|
|
4587
5472
|
logger.info("No blocks/ directory found.");
|
|
4588
5473
|
return;
|
|
4589
5474
|
}
|
|
4590
|
-
const entries = await
|
|
5475
|
+
const entries = await readdir4(blocksDir, { withFileTypes: true });
|
|
4591
5476
|
const blocks = [];
|
|
4592
5477
|
for (const entry of entries) {
|
|
4593
5478
|
if (!entry.isDirectory()) continue;
|
|
4594
5479
|
const name = entry.name;
|
|
4595
|
-
const blockDir =
|
|
5480
|
+
const blockDir = path30.join(blocksDir, name);
|
|
4596
5481
|
const info = {
|
|
4597
5482
|
name,
|
|
4598
|
-
hasJs:
|
|
4599
|
-
hasCss:
|
|
4600
|
-
hasUeModel:
|
|
5483
|
+
hasJs: existsSync26(path30.join(blockDir, `${name}.js`)),
|
|
5484
|
+
hasCss: existsSync26(path30.join(blockDir, `${name}.css`)),
|
|
5485
|
+
hasUeModel: existsSync26(path30.join(blockDir, `_${name}.json`))
|
|
4601
5486
|
};
|
|
4602
|
-
const metaFile =
|
|
4603
|
-
if (
|
|
5487
|
+
const metaFile = path30.join(blockDir, ".eds-meta.json");
|
|
5488
|
+
if (existsSync26(metaFile)) {
|
|
4604
5489
|
try {
|
|
4605
|
-
const meta = JSON.parse(await
|
|
5490
|
+
const meta = JSON.parse(await readFile20(metaFile, "utf-8"));
|
|
4606
5491
|
info.figmaSource = {
|
|
4607
5492
|
fileKey: meta.figmaFileKey,
|
|
4608
5493
|
nodeId: meta.figmaNodeId,
|
|
@@ -4625,17 +5510,17 @@ async function listBlocks(options = {}) {
|
|
|
4625
5510
|
const nameWidth = columnWidth(blocks.map((b) => b.name));
|
|
4626
5511
|
for (const b of blocks) {
|
|
4627
5512
|
const badges = [
|
|
4628
|
-
b.hasJs ? brand.ok("js") :
|
|
4629
|
-
b.hasCss ? brand.ok("css") :
|
|
5513
|
+
b.hasJs ? brand.ok("js") : chalk13.dim.strikethrough("js"),
|
|
5514
|
+
b.hasCss ? brand.ok("css") : chalk13.dim.strikethrough("css")
|
|
4630
5515
|
];
|
|
4631
5516
|
if (b.hasUeModel) badges.push(brand.blue("ue-model"));
|
|
4632
5517
|
if (b.figmaSource) badges.push(`${icon.figma} ${brand.dotPurple("figma")}`);
|
|
4633
|
-
const name = padEndVisible(
|
|
4634
|
-
logger.info(` ${name} ${
|
|
5518
|
+
const name = padEndVisible(chalk13.bold(b.name), nameWidth);
|
|
5519
|
+
logger.info(` ${name} ${chalk13.dim("[")}${badges.join(" ")}${chalk13.dim("]")}`);
|
|
4635
5520
|
if (b.figmaSource) {
|
|
4636
5521
|
const synced = new Date(b.figmaSource.lastSyncedAt).toLocaleDateString();
|
|
4637
5522
|
logger.info(
|
|
4638
|
-
` ${" ".repeat(nameWidth)} ${icon.arrow} ${
|
|
5523
|
+
` ${" ".repeat(nameWidth)} ${icon.arrow} ${chalk13.dim(
|
|
4639
5524
|
`figma:${b.figmaSource.fileKey} \xB7 synced ${synced}`
|
|
4640
5525
|
)}`
|
|
4641
5526
|
);
|
|
@@ -4815,10 +5700,10 @@ var integrate_exports = {};
|
|
|
4815
5700
|
__export(integrate_exports, {
|
|
4816
5701
|
integrate: () => integrate
|
|
4817
5702
|
});
|
|
4818
|
-
import { existsSync as
|
|
4819
|
-
import { mkdir as
|
|
4820
|
-
import
|
|
4821
|
-
import
|
|
5703
|
+
import { existsSync as existsSync27 } from "fs";
|
|
5704
|
+
import { mkdir as mkdir9, readFile as readFile21, writeFile as writeFile17 } from "fs/promises";
|
|
5705
|
+
import path31 from "path";
|
|
5706
|
+
import chalk14 from "chalk";
|
|
4822
5707
|
async function integrate(type) {
|
|
4823
5708
|
const projectRoot = findProjectRoot();
|
|
4824
5709
|
if (!projectRoot) {
|
|
@@ -4848,9 +5733,9 @@ async function integrate(type) {
|
|
|
4848
5733
|
const modulePaths = [];
|
|
4849
5734
|
if (integration.module) {
|
|
4850
5735
|
for (const mod of integration.module(values)) {
|
|
4851
|
-
const abs =
|
|
4852
|
-
await
|
|
4853
|
-
await
|
|
5736
|
+
const abs = path31.join(projectRoot, mod.file);
|
|
5737
|
+
await mkdir9(path31.dirname(abs), { recursive: true });
|
|
5738
|
+
await writeFile17(abs, mod.content);
|
|
4854
5739
|
modulePaths.push(mod.file);
|
|
4855
5740
|
}
|
|
4856
5741
|
}
|
|
@@ -4858,26 +5743,26 @@ async function integrate(type) {
|
|
|
4858
5743
|
// ${integration.name} (eds integrate)
|
|
4859
5744
|
${integration.code(values)}
|
|
4860
5745
|
` : wrapBlock(integration.name, integration.code(values));
|
|
4861
|
-
const delayedPath =
|
|
4862
|
-
await
|
|
4863
|
-
const existing =
|
|
4864
|
-
await
|
|
5746
|
+
const delayedPath = path31.join(projectRoot, "scripts", "delayed.js");
|
|
5747
|
+
await mkdir9(path31.dirname(delayedPath), { recursive: true });
|
|
5748
|
+
const existing = existsSync27(delayedPath) ? await readFile21(delayedPath, "utf-8") : DELAYED_HEADER;
|
|
5749
|
+
await writeFile17(delayedPath, `${existing.replace(/\s*$/, "")}
|
|
4865
5750
|
${block2}`);
|
|
4866
5751
|
logger.info(heading("Added"));
|
|
4867
5752
|
for (const p of modulePaths) logger.info(accentLine(integration.name, `wrote ${p}`));
|
|
4868
5753
|
logger.info(accentLine(integration.name, "wired into scripts/delayed.js"));
|
|
4869
5754
|
logger.info("");
|
|
4870
5755
|
logger.info(
|
|
4871
|
-
|
|
5756
|
+
chalk14.dim(" Loads after LCP (delayed phase) - zero impact on your Core Web Vitals.")
|
|
4872
5757
|
);
|
|
4873
5758
|
if (integration.id === "gtm" || integration.id === "ga4") {
|
|
4874
5759
|
logger.info(
|
|
4875
|
-
|
|
5760
|
+
chalk14.dim(" Tip: gate analytics behind user consent (add the Cookiebot integration).")
|
|
4876
5761
|
);
|
|
4877
5762
|
}
|
|
4878
5763
|
if (integration.id === "chat") {
|
|
4879
5764
|
logger.info(
|
|
4880
|
-
|
|
5765
|
+
chalk14.dim(" The widget loads only when the visitor clicks the button (facade pattern).")
|
|
4881
5766
|
);
|
|
4882
5767
|
}
|
|
4883
5768
|
logger.info("");
|
|
@@ -4903,9 +5788,9 @@ var setup_exports = {};
|
|
|
4903
5788
|
__export(setup_exports, {
|
|
4904
5789
|
figmaSetup: () => figmaSetup
|
|
4905
5790
|
});
|
|
4906
|
-
import { existsSync as
|
|
4907
|
-
import { mkdir as
|
|
4908
|
-
import
|
|
5791
|
+
import { existsSync as existsSync28 } from "fs";
|
|
5792
|
+
import { mkdir as mkdir10, writeFile as writeFile18 } from "fs/promises";
|
|
5793
|
+
import chalk15 from "chalk";
|
|
4909
5794
|
async function figmaSetup() {
|
|
4910
5795
|
const { select, confirm } = await import("@inquirer/prompts");
|
|
4911
5796
|
logger.logoOnce(logo());
|
|
@@ -4915,15 +5800,15 @@ async function figmaSetup() {
|
|
|
4915
5800
|
let selectedAgent;
|
|
4916
5801
|
if (agents.length === 0) {
|
|
4917
5802
|
logger.warn("No AI agents found (claude, cursor, codex).");
|
|
4918
|
-
logger.info(`Install Claude Code: ${
|
|
5803
|
+
logger.info(`Install Claude Code: ${chalk15.cyan("npm install -g @anthropic-ai/claude-code")}`);
|
|
4919
5804
|
selectedAgent = "none";
|
|
4920
5805
|
} else {
|
|
4921
|
-
logger.success(`Found: ${agents.map((a) =>
|
|
5806
|
+
logger.success(`Found: ${agents.map((a) => chalk15.cyan(a.type)).join(", ")}`);
|
|
4922
5807
|
selectedAgent = await select({
|
|
4923
5808
|
message: "Which agent do you want to use with eds-cli?",
|
|
4924
5809
|
choices: [
|
|
4925
5810
|
...agents.map((a) => ({
|
|
4926
|
-
name: `${a.type}${a.hasFigmaMcp ?
|
|
5811
|
+
name: `${a.type}${a.hasFigmaMcp ? chalk15.green(" (Figma MCP detected)") : ""}`,
|
|
4927
5812
|
value: a.type
|
|
4928
5813
|
})),
|
|
4929
5814
|
{ name: "None (manual mode)", value: "none" }
|
|
@@ -4939,17 +5824,17 @@ async function figmaSetup() {
|
|
|
4939
5824
|
switch (selectedAgent) {
|
|
4940
5825
|
case "claude":
|
|
4941
5826
|
logger.info(
|
|
4942
|
-
` ${
|
|
5827
|
+
` ${chalk15.cyan("claude mcp add --transport sse figma-sse https://mcp.figma.com/sse")}`
|
|
4943
5828
|
);
|
|
4944
5829
|
logger.info(" or for Figma Desktop app:");
|
|
4945
5830
|
logger.info(
|
|
4946
|
-
` ${
|
|
5831
|
+
` ${chalk15.cyan("claude mcp add --transport http figma-desktop http://127.0.0.1:3845/mcp")}`
|
|
4947
5832
|
);
|
|
4948
5833
|
break;
|
|
4949
5834
|
case "cursor":
|
|
4950
|
-
logger.info(` Add to ${
|
|
5835
|
+
logger.info(` Add to ${chalk15.cyan("~/.cursor/mcp.json")}:`);
|
|
4951
5836
|
logger.info(
|
|
4952
|
-
|
|
5837
|
+
chalk15.dim(
|
|
4953
5838
|
JSON.stringify(
|
|
4954
5839
|
{
|
|
4955
5840
|
mcpServers: {
|
|
@@ -4974,7 +5859,7 @@ async function figmaSetup() {
|
|
|
4974
5859
|
}
|
|
4975
5860
|
}
|
|
4976
5861
|
logger.info(
|
|
4977
|
-
|
|
5862
|
+
chalk15.dim(
|
|
4978
5863
|
"\nNote: Figma MCP requires a Figma seat (Dev or Full on paid plans for desktop server)."
|
|
4979
5864
|
)
|
|
4980
5865
|
);
|
|
@@ -4984,22 +5869,22 @@ async function figmaSetup() {
|
|
|
4984
5869
|
});
|
|
4985
5870
|
if (shouldSave) {
|
|
4986
5871
|
const configDir = globalConfigDir();
|
|
4987
|
-
if (!
|
|
4988
|
-
await
|
|
5872
|
+
if (!existsSync28(configDir)) {
|
|
5873
|
+
await mkdir10(configDir, { recursive: true });
|
|
4989
5874
|
}
|
|
4990
5875
|
const config = {};
|
|
4991
5876
|
if (selectedAgent !== "none") {
|
|
4992
5877
|
config.agent = selectedAgent;
|
|
4993
5878
|
}
|
|
4994
|
-
await
|
|
5879
|
+
await writeFile18(globalConfigPath(), `${JSON.stringify(config, null, 2)}
|
|
4995
5880
|
`, {
|
|
4996
5881
|
mode: 384
|
|
4997
5882
|
});
|
|
4998
|
-
logger.success(`Config saved to ${
|
|
5883
|
+
logger.success(`Config saved to ${chalk15.cyan(globalConfigPath())}`);
|
|
4999
5884
|
}
|
|
5000
|
-
logger.info(
|
|
5885
|
+
logger.info(chalk15.bold("\nSetup complete!"));
|
|
5001
5886
|
if (selectedAgent !== "none") {
|
|
5002
|
-
logger.info(`Try: ${
|
|
5887
|
+
logger.info(`Try: ${chalk15.cyan('eds block from-design "<figma-url>" --dry-run')}`);
|
|
5003
5888
|
}
|
|
5004
5889
|
}
|
|
5005
5890
|
var init_setup = __esm({
|
|
@@ -5018,9 +5903,9 @@ var doctor_exports = {};
|
|
|
5018
5903
|
__export(doctor_exports, {
|
|
5019
5904
|
doctor: () => doctor
|
|
5020
5905
|
});
|
|
5021
|
-
import { existsSync as
|
|
5022
|
-
import { readFile as
|
|
5023
|
-
import
|
|
5906
|
+
import { existsSync as existsSync29 } from "fs";
|
|
5907
|
+
import { readFile as readFile22, readdir as readdir5 } from "fs/promises";
|
|
5908
|
+
import path32 from "path";
|
|
5024
5909
|
import { parse as parseYaml2 } from "yaml";
|
|
5025
5910
|
async function doctor(options = {}) {
|
|
5026
5911
|
const projectRoot = findProjectRoot();
|
|
@@ -5042,7 +5927,7 @@ async function doctor(options = {}) {
|
|
|
5042
5927
|
});
|
|
5043
5928
|
if (project.hasFstab) {
|
|
5044
5929
|
try {
|
|
5045
|
-
const content = await
|
|
5930
|
+
const content = await readFile22(path32.join(projectRoot, "fstab.yaml"), "utf-8");
|
|
5046
5931
|
parseYaml2(content);
|
|
5047
5932
|
results.push({ name: "fstab.yaml valid YAML", status: "pass", message: "Valid" });
|
|
5048
5933
|
} catch {
|
|
@@ -5064,28 +5949,28 @@ async function doctor(options = {}) {
|
|
|
5064
5949
|
message: project.hasBlocks ? "Found" : "Missing - no blocks yet"
|
|
5065
5950
|
});
|
|
5066
5951
|
if (project.hasBlocks) {
|
|
5067
|
-
const blocksDir =
|
|
5068
|
-
const entries = await
|
|
5952
|
+
const blocksDir = path32.join(projectRoot, "blocks");
|
|
5953
|
+
const entries = await readdir5(blocksDir, { withFileTypes: true });
|
|
5069
5954
|
let aggregatedModels = "";
|
|
5070
|
-
const aggregatedPath =
|
|
5071
|
-
if (
|
|
5955
|
+
const aggregatedPath = path32.join(projectRoot, "component-models.json");
|
|
5956
|
+
if (existsSync29(aggregatedPath)) {
|
|
5072
5957
|
try {
|
|
5073
|
-
aggregatedModels = await
|
|
5958
|
+
aggregatedModels = await readFile22(aggregatedPath, "utf-8");
|
|
5074
5959
|
} catch {
|
|
5075
5960
|
}
|
|
5076
5961
|
}
|
|
5077
5962
|
for (const entry of entries) {
|
|
5078
5963
|
if (!entry.isDirectory()) continue;
|
|
5079
5964
|
const blockName = entry.name;
|
|
5080
|
-
const jsFile =
|
|
5081
|
-
const hasJs =
|
|
5965
|
+
const jsFile = path32.join(blocksDir, blockName, `${blockName}.js`);
|
|
5966
|
+
const hasJs = existsSync29(jsFile);
|
|
5082
5967
|
results.push({
|
|
5083
5968
|
name: `blocks/${blockName} has JS`,
|
|
5084
5969
|
status: hasJs ? "pass" : "warn",
|
|
5085
5970
|
message: hasJs ? "Found" : `Missing ${blockName}.js`
|
|
5086
5971
|
});
|
|
5087
5972
|
if (hasJs) {
|
|
5088
|
-
const jsContent = await
|
|
5973
|
+
const jsContent = await readFile22(jsFile, "utf-8");
|
|
5089
5974
|
if (jsContent.includes("console.log")) {
|
|
5090
5975
|
results.push({
|
|
5091
5976
|
name: `blocks/${blockName} no console.log`,
|
|
@@ -5094,17 +5979,17 @@ async function doctor(options = {}) {
|
|
|
5094
5979
|
});
|
|
5095
5980
|
}
|
|
5096
5981
|
}
|
|
5097
|
-
const hasPerBlockModel =
|
|
5982
|
+
const hasPerBlockModel = existsSync29(path32.join(blocksDir, blockName, `_${blockName}.json`));
|
|
5098
5983
|
const hasAggregatedModel = aggregatedModels.includes(`"${blockName}"`);
|
|
5099
5984
|
results.push({
|
|
5100
5985
|
name: `blocks/${blockName} UE model`,
|
|
5101
5986
|
status: hasPerBlockModel || hasAggregatedModel ? "pass" : "warn",
|
|
5102
5987
|
message: hasPerBlockModel || hasAggregatedModel ? hasPerBlockModel ? `_${blockName}.json` : "in component-models.json" : "no Universal Editor model - authors can't edit this block in UE"
|
|
5103
5988
|
});
|
|
5104
|
-
const metaFile =
|
|
5105
|
-
if (
|
|
5989
|
+
const metaFile = path32.join(blocksDir, blockName, ".eds-meta.json");
|
|
5990
|
+
if (existsSync29(metaFile)) {
|
|
5106
5991
|
try {
|
|
5107
|
-
const meta = JSON.parse(await
|
|
5992
|
+
const meta = JSON.parse(await readFile22(metaFile, "utf-8"));
|
|
5108
5993
|
const syncDate = new Date(meta.lastSyncedAt);
|
|
5109
5994
|
const daysSinceSync = Math.floor(
|
|
5110
5995
|
(Date.now() - syncDate.getTime()) / (1e3 * 60 * 60 * 24)
|
|
@@ -5124,10 +6009,10 @@ async function doctor(options = {}) {
|
|
|
5124
6009
|
}
|
|
5125
6010
|
}
|
|
5126
6011
|
}
|
|
5127
|
-
const queryFile =
|
|
5128
|
-
if (
|
|
6012
|
+
const queryFile = path32.join(projectRoot, "helix-query.yaml");
|
|
6013
|
+
if (existsSync29(queryFile)) {
|
|
5129
6014
|
try {
|
|
5130
|
-
const content = await
|
|
6015
|
+
const content = await readFile22(queryFile, "utf-8");
|
|
5131
6016
|
parseYaml2(content);
|
|
5132
6017
|
results.push({ name: "helix-query.yaml valid", status: "pass", message: "Valid YAML" });
|
|
5133
6018
|
} catch {
|
|
@@ -5184,10 +6069,10 @@ var audit_loading_exports = {};
|
|
|
5184
6069
|
__export(audit_loading_exports, {
|
|
5185
6070
|
auditLoading: () => auditLoading
|
|
5186
6071
|
});
|
|
5187
|
-
import { existsSync as
|
|
5188
|
-
import { readFile as
|
|
5189
|
-
import
|
|
5190
|
-
import
|
|
6072
|
+
import { existsSync as existsSync30 } from "fs";
|
|
6073
|
+
import { readFile as readFile23, readdir as readdir6, stat as stat3 } from "fs/promises";
|
|
6074
|
+
import path33 from "path";
|
|
6075
|
+
import chalk16 from "chalk";
|
|
5191
6076
|
async function auditLoading(options = {}) {
|
|
5192
6077
|
const projectRoot = findProjectRoot();
|
|
5193
6078
|
if (!projectRoot) {
|
|
@@ -5220,8 +6105,8 @@ async function auditLoading(options = {}) {
|
|
|
5220
6105
|
printResults(results);
|
|
5221
6106
|
}
|
|
5222
6107
|
async function checkHeadHtml(root, results) {
|
|
5223
|
-
const filePath =
|
|
5224
|
-
if (!
|
|
6108
|
+
const filePath = path33.join(root, "head.html");
|
|
6109
|
+
if (!existsSync30(filePath)) {
|
|
5225
6110
|
results.push({
|
|
5226
6111
|
rule: "head.html exists",
|
|
5227
6112
|
status: "warn",
|
|
@@ -5229,7 +6114,7 @@ async function checkHeadHtml(root, results) {
|
|
|
5229
6114
|
});
|
|
5230
6115
|
return;
|
|
5231
6116
|
}
|
|
5232
|
-
const content = await
|
|
6117
|
+
const content = await readFile23(filePath, "utf-8");
|
|
5233
6118
|
const lines = content.split("\n");
|
|
5234
6119
|
for (let i = 0; i < lines.length; i++) {
|
|
5235
6120
|
const line = lines[i];
|
|
@@ -5301,8 +6186,8 @@ async function checkHeadHtml(root, results) {
|
|
|
5301
6186
|
}
|
|
5302
6187
|
}
|
|
5303
6188
|
async function checkScriptsJs(root, results) {
|
|
5304
|
-
const filePath =
|
|
5305
|
-
if (!
|
|
6189
|
+
const filePath = path33.join(root, "scripts", "scripts.js");
|
|
6190
|
+
if (!existsSync30(filePath)) {
|
|
5306
6191
|
results.push({
|
|
5307
6192
|
rule: "scripts/scripts.js exists",
|
|
5308
6193
|
status: "warn",
|
|
@@ -5310,7 +6195,7 @@ async function checkScriptsJs(root, results) {
|
|
|
5310
6195
|
});
|
|
5311
6196
|
return;
|
|
5312
6197
|
}
|
|
5313
|
-
const content = await
|
|
6198
|
+
const content = await readFile23(filePath, "utf-8");
|
|
5314
6199
|
const lines = content.split("\n");
|
|
5315
6200
|
const hasLoadEager = /function\s+loadEager|loadEager\s*=/i.test(content);
|
|
5316
6201
|
const hasLoadLazy = /function\s+loadLazy|loadLazy\s*=/i.test(content);
|
|
@@ -5388,8 +6273,8 @@ async function checkScriptsJs(root, results) {
|
|
|
5388
6273
|
}
|
|
5389
6274
|
}
|
|
5390
6275
|
async function checkStylesCss(root, results) {
|
|
5391
|
-
const filePath =
|
|
5392
|
-
if (!
|
|
6276
|
+
const filePath = path33.join(root, "styles", "styles.css");
|
|
6277
|
+
if (!existsSync30(filePath)) return;
|
|
5393
6278
|
const stats = await stat3(filePath);
|
|
5394
6279
|
const sizeKb = stats.size / 1024;
|
|
5395
6280
|
results.push({
|
|
@@ -5398,7 +6283,7 @@ async function checkStylesCss(root, results) {
|
|
|
5398
6283
|
message: `${sizeKb.toFixed(1)} KB${sizeKb > 40 ? " \u2014 large CSS competes with 100KB LCP budget" : ""}`,
|
|
5399
6284
|
file: "styles/styles.css"
|
|
5400
6285
|
});
|
|
5401
|
-
const content = await
|
|
6286
|
+
const content = await readFile23(filePath, "utf-8");
|
|
5402
6287
|
if (/@import\s/.test(content)) {
|
|
5403
6288
|
results.push({
|
|
5404
6289
|
rule: "No @import in styles.css",
|
|
@@ -5409,8 +6294,8 @@ async function checkStylesCss(root, results) {
|
|
|
5409
6294
|
}
|
|
5410
6295
|
}
|
|
5411
6296
|
async function checkDelayedJs(root, results) {
|
|
5412
|
-
const filePath =
|
|
5413
|
-
if (!
|
|
6297
|
+
const filePath = path33.join(root, "scripts", "delayed.js");
|
|
6298
|
+
if (!existsSync30(filePath)) {
|
|
5414
6299
|
results.push({
|
|
5415
6300
|
rule: "scripts/delayed.js exists",
|
|
5416
6301
|
status: "info",
|
|
@@ -5418,7 +6303,7 @@ async function checkDelayedJs(root, results) {
|
|
|
5418
6303
|
});
|
|
5419
6304
|
return;
|
|
5420
6305
|
}
|
|
5421
|
-
const content = await
|
|
6306
|
+
const content = await readFile23(filePath, "utf-8");
|
|
5422
6307
|
const stripped = content.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").trim();
|
|
5423
6308
|
if (!stripped) {
|
|
5424
6309
|
results.push({
|
|
@@ -5430,15 +6315,15 @@ async function checkDelayedJs(root, results) {
|
|
|
5430
6315
|
}
|
|
5431
6316
|
}
|
|
5432
6317
|
async function checkBlocks(root, results) {
|
|
5433
|
-
const blocksDir =
|
|
5434
|
-
if (!
|
|
5435
|
-
const entries = await
|
|
6318
|
+
const blocksDir = path33.join(root, "blocks");
|
|
6319
|
+
if (!existsSync30(blocksDir)) return;
|
|
6320
|
+
const entries = await readdir6(blocksDir, { withFileTypes: true });
|
|
5436
6321
|
for (const entry of entries) {
|
|
5437
6322
|
if (!entry.isDirectory()) continue;
|
|
5438
6323
|
const blockName = entry.name;
|
|
5439
|
-
const jsFile =
|
|
5440
|
-
if (!
|
|
5441
|
-
const content = await
|
|
6324
|
+
const jsFile = path33.join(blocksDir, blockName, `${blockName}.js`);
|
|
6325
|
+
if (!existsSync30(jsFile)) continue;
|
|
6326
|
+
const content = await readFile23(jsFile, "utf-8");
|
|
5442
6327
|
const lines = content.split("\n");
|
|
5443
6328
|
const heavyLibs = [
|
|
5444
6329
|
"jquery",
|
|
@@ -5488,9 +6373,9 @@ async function checkBlocks(root, results) {
|
|
|
5488
6373
|
for (const entry of entries) {
|
|
5489
6374
|
if (!entry.isDirectory()) continue;
|
|
5490
6375
|
const blockName = entry.name;
|
|
5491
|
-
const cssFile =
|
|
5492
|
-
if (!
|
|
5493
|
-
const content = await
|
|
6376
|
+
const cssFile = path33.join(blocksDir, blockName, `${blockName}.css`);
|
|
6377
|
+
if (!existsSync30(cssFile)) continue;
|
|
6378
|
+
const content = await readFile23(cssFile, "utf-8");
|
|
5494
6379
|
const selectorPattern = /^([a-z][a-z0-9-]*)\s*\{/gm;
|
|
5495
6380
|
for (const selectorMatch of content.matchAll(selectorPattern)) {
|
|
5496
6381
|
const selector = selectorMatch[1];
|
|
@@ -5506,8 +6391,8 @@ async function checkBlocks(root, results) {
|
|
|
5506
6391
|
}
|
|
5507
6392
|
}
|
|
5508
6393
|
async function checkAemJs(root, results) {
|
|
5509
|
-
const filePath =
|
|
5510
|
-
if (!
|
|
6394
|
+
const filePath = path33.join(root, "scripts", "aem.js");
|
|
6395
|
+
if (!existsSync30(filePath)) {
|
|
5511
6396
|
results.push({
|
|
5512
6397
|
rule: "scripts/aem.js exists",
|
|
5513
6398
|
status: "warn",
|
|
@@ -5545,12 +6430,12 @@ function printResults(results) {
|
|
|
5545
6430
|
grouped.set(key, bucket);
|
|
5546
6431
|
}
|
|
5547
6432
|
for (const [file, fileResults] of grouped) {
|
|
5548
|
-
logger.info(` ${icon.bullet} ${
|
|
6433
|
+
logger.info(` ${icon.bullet} ${chalk16.underline(file)}`);
|
|
5549
6434
|
for (const r of fileResults) {
|
|
5550
|
-
const loc = r.line ?
|
|
6435
|
+
const loc = r.line ? chalk16.dim(`:${r.line}`) : "";
|
|
5551
6436
|
logger.info(` ${statusIcon(r.status)} ${r.rule}${loc}`);
|
|
5552
6437
|
if (r.status !== "pass") {
|
|
5553
|
-
logger.info(
|
|
6438
|
+
logger.info(chalk16.dim(` ${r.message}`));
|
|
5554
6439
|
}
|
|
5555
6440
|
}
|
|
5556
6441
|
logger.info("");
|
|
@@ -5579,14 +6464,14 @@ var audit_security_exports = {};
|
|
|
5579
6464
|
__export(audit_security_exports, {
|
|
5580
6465
|
auditSecurity: () => auditSecurity
|
|
5581
6466
|
});
|
|
5582
|
-
import { existsSync as
|
|
5583
|
-
import { readFile as
|
|
5584
|
-
import
|
|
5585
|
-
import
|
|
6467
|
+
import { existsSync as existsSync31 } from "fs";
|
|
6468
|
+
import { readFile as readFile24, readdir as readdir7 } from "fs/promises";
|
|
6469
|
+
import path34 from "path";
|
|
6470
|
+
import chalk17 from "chalk";
|
|
5586
6471
|
async function* walk2(dir) {
|
|
5587
|
-
const entries = await
|
|
6472
|
+
const entries = await readdir7(dir, { withFileTypes: true });
|
|
5588
6473
|
for (const entry of entries) {
|
|
5589
|
-
const full =
|
|
6474
|
+
const full = path34.join(dir, entry.name);
|
|
5590
6475
|
if (entry.isDirectory()) {
|
|
5591
6476
|
if (IGNORE_DIRS.has(entry.name)) continue;
|
|
5592
6477
|
yield* walk2(full);
|
|
@@ -5620,11 +6505,11 @@ function scanContent(content, relFile, ext) {
|
|
|
5620
6505
|
return findings;
|
|
5621
6506
|
}
|
|
5622
6507
|
async function auditDependencies(root) {
|
|
5623
|
-
if (!
|
|
6508
|
+
if (!existsSync31(path34.join(root, "package.json"))) {
|
|
5624
6509
|
return { status: "skipped", note: "no package.json" };
|
|
5625
6510
|
}
|
|
5626
|
-
const hasLock =
|
|
5627
|
-
if (!hasLock && !
|
|
6511
|
+
const hasLock = existsSync31(path34.join(root, "package-lock.json")) || existsSync31(path34.join(root, "npm-shrinkwrap.json"));
|
|
6512
|
+
if (!hasLock && !existsSync31(path34.join(root, "node_modules"))) {
|
|
5628
6513
|
return { status: "skipped", note: "no package-lock.json / node_modules \u2014 run npm install" };
|
|
5629
6514
|
}
|
|
5630
6515
|
try {
|
|
@@ -5656,11 +6541,11 @@ async function auditSecurity(options = {}) {
|
|
|
5656
6541
|
}
|
|
5657
6542
|
const findings = [];
|
|
5658
6543
|
for await (const file of walk2(projectRoot)) {
|
|
5659
|
-
const ext =
|
|
6544
|
+
const ext = path34.extname(file).slice(1).toLowerCase();
|
|
5660
6545
|
if (!SCAN_EXT.has(ext)) continue;
|
|
5661
|
-
const rel =
|
|
6546
|
+
const rel = path34.relative(projectRoot, file);
|
|
5662
6547
|
if (rel.endsWith("aem.js") || rel.endsWith("lib-franklin.js")) continue;
|
|
5663
|
-
const content = await
|
|
6548
|
+
const content = await readFile24(file, "utf-8");
|
|
5664
6549
|
findings.push(...scanContent(content, rel, ext));
|
|
5665
6550
|
}
|
|
5666
6551
|
const deps = await auditDependencies(projectRoot);
|
|
@@ -5708,9 +6593,9 @@ async function auditSecurity(options = {}) {
|
|
|
5708
6593
|
);
|
|
5709
6594
|
for (const f2 of sorted) {
|
|
5710
6595
|
const meta = SEVERITY_META[f2.severity];
|
|
5711
|
-
const loc =
|
|
5712
|
-
logger.info(` ${statusIcon(meta.status)} ${
|
|
5713
|
-
logger.info(
|
|
6596
|
+
const loc = chalk17.dim(`${f2.file}:${f2.line}`);
|
|
6597
|
+
logger.info(` ${statusIcon(meta.status)} ${chalk17.bold(meta.label)} ${f2.message} ${loc}`);
|
|
6598
|
+
logger.info(chalk17.dim(` ${f2.snippet}`));
|
|
5714
6599
|
}
|
|
5715
6600
|
}
|
|
5716
6601
|
const summaryLine = summary({
|
|
@@ -5917,7 +6802,7 @@ __export(preview_exports2, {
|
|
|
5917
6802
|
preview: () => preview,
|
|
5918
6803
|
resolveAdminConfig: () => resolveAdminConfig
|
|
5919
6804
|
});
|
|
5920
|
-
import
|
|
6805
|
+
import chalk18 from "chalk";
|
|
5921
6806
|
import ora2 from "ora";
|
|
5922
6807
|
async function preview(paths, options) {
|
|
5923
6808
|
const projectRoot = findProjectRoot();
|
|
@@ -5930,7 +6815,7 @@ async function preview(paths, options) {
|
|
|
5930
6815
|
let hasErrors = false;
|
|
5931
6816
|
for (const r of results) {
|
|
5932
6817
|
if (r.ok) {
|
|
5933
|
-
logger.success(`${r.path} \u2192 ${
|
|
6818
|
+
logger.success(`${r.path} \u2192 ${chalk18.cyan(r.previewUrl || "preview updated")}`);
|
|
5934
6819
|
} else {
|
|
5935
6820
|
logger.error(`${r.path} \u2014 ${r.message}`);
|
|
5936
6821
|
hasErrors = true;
|
|
@@ -5944,7 +6829,7 @@ function resolveAdminConfig(config, options) {
|
|
|
5944
6829
|
const ref = options.ref || config.admin?.ref || "main";
|
|
5945
6830
|
if (!org || !site) {
|
|
5946
6831
|
logger.error("Missing --org and --site. Set them via flags or in .edsrc.json:");
|
|
5947
|
-
logger.info(
|
|
6832
|
+
logger.info(chalk18.dim(' { "admin": { "org": "my-org", "site": "my-site" } }'));
|
|
5948
6833
|
process.exitCode = 1;
|
|
5949
6834
|
return void 0;
|
|
5950
6835
|
}
|
|
@@ -5966,7 +6851,7 @@ var publish_exports = {};
|
|
|
5966
6851
|
__export(publish_exports, {
|
|
5967
6852
|
publish: () => publish
|
|
5968
6853
|
});
|
|
5969
|
-
import
|
|
6854
|
+
import chalk19 from "chalk";
|
|
5970
6855
|
import ora3 from "ora";
|
|
5971
6856
|
async function publish(paths, options) {
|
|
5972
6857
|
const projectRoot = findProjectRoot();
|
|
@@ -5979,7 +6864,7 @@ async function publish(paths, options) {
|
|
|
5979
6864
|
let hasErrors = false;
|
|
5980
6865
|
for (const r of results) {
|
|
5981
6866
|
if (r.ok) {
|
|
5982
|
-
logger.success(`${r.path} \u2192 ${
|
|
6867
|
+
logger.success(`${r.path} \u2192 ${chalk19.cyan(r.liveUrl || "published")}`);
|
|
5983
6868
|
} else {
|
|
5984
6869
|
logger.error(`${r.path} \u2014 ${r.message}`);
|
|
5985
6870
|
hasErrors = true;
|
|
@@ -6078,13 +6963,13 @@ var menu_exports = {};
|
|
|
6078
6963
|
__export(menu_exports, {
|
|
6079
6964
|
mainMenu: () => mainMenu
|
|
6080
6965
|
});
|
|
6081
|
-
import { existsSync as
|
|
6082
|
-
import { readdir as
|
|
6083
|
-
import
|
|
6966
|
+
import { existsSync as existsSync32 } from "fs";
|
|
6967
|
+
import { readdir as readdir8 } from "fs/promises";
|
|
6968
|
+
import path35 from "path";
|
|
6084
6969
|
async function listBlockNames(projectRoot) {
|
|
6085
|
-
const dir =
|
|
6086
|
-
if (!
|
|
6087
|
-
const entries = await
|
|
6970
|
+
const dir = path35.join(projectRoot, "blocks");
|
|
6971
|
+
if (!existsSync32(dir)) return [];
|
|
6972
|
+
const entries = await readdir8(dir, { withFileTypes: true });
|
|
6088
6973
|
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
6089
6974
|
}
|
|
6090
6975
|
async function mainMenu() {
|
|
@@ -6095,6 +6980,7 @@ async function mainMenu() {
|
|
|
6095
6980
|
choices: [
|
|
6096
6981
|
{ name: "Set up this project (authoring model, CI, naming)", value: "init" },
|
|
6097
6982
|
{ name: "Generate a page template (initial content)", value: "template" },
|
|
6983
|
+
{ name: "Create a disposable sandbox repo on GitHub", value: "sandbox" },
|
|
6098
6984
|
{
|
|
6099
6985
|
name: "Generate a block from a design (Figma/Stitch/Canva/Sketch/Framer)",
|
|
6100
6986
|
value: "from-design"
|
|
@@ -6106,6 +6992,7 @@ async function mainMenu() {
|
|
|
6106
6992
|
{ name: "Add fields to a block model (partials)", value: "model" },
|
|
6107
6993
|
{ name: "Instrument a block for analytics (dataLayer)", value: "track" },
|
|
6108
6994
|
{ name: "Add JSON-LD structured data to a block", value: "schema" },
|
|
6995
|
+
{ name: "Check Universal Editor readiness / open the editor", value: "ue" },
|
|
6109
6996
|
{ name: "Preview a block in the browser", value: "preview-block" },
|
|
6110
6997
|
{ name: "Preview / publish pages (Admin API)", value: "admin" },
|
|
6111
6998
|
{ name: "Audit the project", value: "audit" },
|
|
@@ -6127,6 +7014,15 @@ async function mainMenu() {
|
|
|
6127
7014
|
await templateNew2(name.trim(), {});
|
|
6128
7015
|
return;
|
|
6129
7016
|
}
|
|
7017
|
+
case "sandbox": {
|
|
7018
|
+
const name = await input({
|
|
7019
|
+
message: "Sandbox repo name",
|
|
7020
|
+
validate: (v) => /^[a-z0-9][a-z0-9._-]*$/i.test(v.trim()) || "Use a valid repo name"
|
|
7021
|
+
});
|
|
7022
|
+
const { sandboxNew: sandboxNew2 } = await Promise.resolve().then(() => (init_sandbox(), sandbox_exports));
|
|
7023
|
+
await sandboxNew2(name.trim(), {});
|
|
7024
|
+
return;
|
|
7025
|
+
}
|
|
6130
7026
|
case "from-design": {
|
|
6131
7027
|
const { blockFromDesign: blockFromDesign2 } = await Promise.resolve().then(() => (init_from_design(), from_design_exports));
|
|
6132
7028
|
await blockFromDesign2(void 0, {});
|
|
@@ -6218,6 +7114,24 @@ async function mainMenu() {
|
|
|
6218
7114
|
await schemaBlock2(name, {});
|
|
6219
7115
|
return;
|
|
6220
7116
|
}
|
|
7117
|
+
case "ue": {
|
|
7118
|
+
const which = await select({
|
|
7119
|
+
message: "Universal Editor:",
|
|
7120
|
+
choices: [
|
|
7121
|
+
{ name: "Check readiness (which blocks are editable)", value: "check" },
|
|
7122
|
+
{ name: "Open a page in the editor", value: "open" }
|
|
7123
|
+
]
|
|
7124
|
+
});
|
|
7125
|
+
if (which === "check") {
|
|
7126
|
+
const { ueCheck: ueCheck2 } = await Promise.resolve().then(() => (init_ue3(), ue_exports));
|
|
7127
|
+
await ueCheck2();
|
|
7128
|
+
} else {
|
|
7129
|
+
const pagePath = await input({ message: "Page path", default: "/" });
|
|
7130
|
+
const { ueOpen: ueOpen2 } = await Promise.resolve().then(() => (init_ue3(), ue_exports));
|
|
7131
|
+
await ueOpen2(pagePath.trim() || "/", {});
|
|
7132
|
+
}
|
|
7133
|
+
return;
|
|
7134
|
+
}
|
|
6221
7135
|
case "admin": {
|
|
6222
7136
|
const { adminWizard: adminWizard2 } = await Promise.resolve().then(() => (init_admin(), admin_exports));
|
|
6223
7137
|
await adminWizard2();
|
|
@@ -6263,13 +7177,13 @@ var init_menu = __esm({
|
|
|
6263
7177
|
|
|
6264
7178
|
// src/bin/eds.ts
|
|
6265
7179
|
init_esm_shims();
|
|
6266
|
-
import
|
|
7180
|
+
import chalk20 from "chalk";
|
|
6267
7181
|
import { Command } from "commander";
|
|
6268
7182
|
|
|
6269
7183
|
// package.json
|
|
6270
7184
|
var package_default = {
|
|
6271
7185
|
name: "aem-eds-kit",
|
|
6272
|
-
version: "0.4.
|
|
7186
|
+
version: "0.4.22",
|
|
6273
7187
|
description: "CLI for AEM Edge Delivery Services with Figma MCP integration",
|
|
6274
7188
|
type: "module",
|
|
6275
7189
|
engines: {
|
|
@@ -6345,9 +7259,30 @@ program.name("eds").description("CLI for AEM Edge Delivery Services with Figma i
|
|
|
6345
7259
|
const opts = thisCommand.opts();
|
|
6346
7260
|
if (opts.verbose) logger.setLevel("debug");
|
|
6347
7261
|
if (opts.quiet) logger.setQuiet(true);
|
|
6348
|
-
if (opts.color === false)
|
|
7262
|
+
if (opts.color === false) chalk20.level = 0;
|
|
6349
7263
|
});
|
|
6350
7264
|
program.addHelpText("beforeAll", () => logo());
|
|
7265
|
+
var sandbox = program.command("sandbox").description("Disposable EDS sandbox repos on GitHub");
|
|
7266
|
+
sandbox.command("new <name>").description("Create a disposable EDS repo from a boilerplate (GitHub auth, no gh required)").option("--org <org>", "Create under a GitHub org (else your account)").option("--boilerplate <repo>", "Template repo (default adobe/aem-boilerplate)").option("--ue", "Use the crosswalk / Universal Editor boilerplate").option("--private", "Create a private repo").option("--yes", "Skip the guided prompts (use flags/defaults)").action(async (name, options) => {
|
|
7267
|
+
const { sandboxNew: sandboxNew2 } = await Promise.resolve().then(() => (init_sandbox(), sandbox_exports));
|
|
7268
|
+
await sandboxNew2(name, options);
|
|
7269
|
+
});
|
|
7270
|
+
sandbox.command("login").description("Authorize GitHub (OAuth device flow or a token) - no gh needed").action(async () => {
|
|
7271
|
+
const { sandboxLogin: sandboxLogin2 } = await Promise.resolve().then(() => (init_sandbox(), sandbox_exports));
|
|
7272
|
+
await sandboxLogin2();
|
|
7273
|
+
});
|
|
7274
|
+
sandbox.command("logout").description("Remove the stored GitHub token").action(async () => {
|
|
7275
|
+
const { sandboxLogout: sandboxLogout2 } = await Promise.resolve().then(() => (init_sandbox(), sandbox_exports));
|
|
7276
|
+
await sandboxLogout2();
|
|
7277
|
+
});
|
|
7278
|
+
sandbox.command("list").description("List your EDS sandboxes (repos tagged eds-sandbox)").action(async () => {
|
|
7279
|
+
const { sandboxList: sandboxList2 } = await Promise.resolve().then(() => (init_sandbox(), sandbox_exports));
|
|
7280
|
+
await sandboxList2();
|
|
7281
|
+
});
|
|
7282
|
+
sandbox.command("rm <name>").description("Delete a sandbox repo (destructive)").option("--org <org>", "Owner org (else your account)").option("--yes", "Skip the confirmation").action(async (name, options) => {
|
|
7283
|
+
const { sandboxRm: sandboxRm2 } = await Promise.resolve().then(() => (init_sandbox(), sandbox_exports));
|
|
7284
|
+
await sandboxRm2(name, options);
|
|
7285
|
+
});
|
|
6351
7286
|
var template = program.command("template").description("Generate page templates / initial content");
|
|
6352
7287
|
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
7288
|
"--blocks <list>",
|
|
@@ -6357,7 +7292,7 @@ template.command("new <name>").description("Generate EDS page initial content (s
|
|
|
6357
7292
|
const { templateNew: templateNew2 } = await Promise.resolve().then(() => (init_template(), template_exports));
|
|
6358
7293
|
await templateNew2(name, options);
|
|
6359
7294
|
});
|
|
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) => {
|
|
7295
|
+
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("--no-env", "Skip the local-dev .env (aem up config)").option("--no-xwalk", "Skip xwalk.json (UE multi-field)").option("--yes", "Skip prompts; apply with flags/defaults").action(async (options) => {
|
|
6361
7296
|
const { initProject: initProject2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
6362
7297
|
await initProject2(options);
|
|
6363
7298
|
});
|
|
@@ -6381,6 +7316,15 @@ track.command("block <name>").description("Add dataLayer tracking (click + form
|
|
|
6381
7316
|
const { trackBlock: trackBlock2 } = await Promise.resolve().then(() => (init_track2(), track_exports));
|
|
6382
7317
|
await trackBlock2(name, options);
|
|
6383
7318
|
});
|
|
7319
|
+
var ue = program.command("ue").description("Universal Editor - readiness check + editor deep link");
|
|
7320
|
+
ue.command("check").description("Check whether blocks are Universal-Editor-ready (offline)").action(async () => {
|
|
7321
|
+
const { ueCheck: ueCheck2 } = await Promise.resolve().then(() => (init_ue3(), ue_exports));
|
|
7322
|
+
await ueCheck2();
|
|
7323
|
+
});
|
|
7324
|
+
ue.command("open [path]").description("Open a page in the hosted Universal Editor (preview host from git)").option("--org <slug>", "IMS org slug (skips the org picker)").option("--ref <branch>", "Branch/ref for the preview host (default: main)").option("--url <host>", "Override the delivery host (e.g. main--repo--owner.aem.page)").option("--no-open", "Print the link without opening the browser").action(async (pagePath, options) => {
|
|
7325
|
+
const { ueOpen: ueOpen2 } = await Promise.resolve().then(() => (init_ue3(), ue_exports));
|
|
7326
|
+
await ueOpen2(pagePath, options);
|
|
7327
|
+
});
|
|
6384
7328
|
var block = program.command("block").description("Manage EDS blocks");
|
|
6385
7329
|
block.command("create <name>").description("Scaffold a new EDS block (with Universal Editor model by default)").option("--no-ue-model", "Skip the Universal Editor model (generated by default)").action(async (name, options) => {
|
|
6386
7330
|
const { createBlock: createBlock2 } = await Promise.resolve().then(() => (init_create(), create_exports));
|
|
@@ -6420,6 +7364,14 @@ scaffold.command("helpers [names...]").description("Scaffold scripts/utils.js wi
|
|
|
6420
7364
|
const { scaffoldHelpers: scaffoldHelpers2 } = await Promise.resolve().then(() => (init_scaffold(), scaffold_exports));
|
|
6421
7365
|
await scaffoldHelpers2(names);
|
|
6422
7366
|
});
|
|
7367
|
+
scaffold.command("env").description("Write a local-dev .env for `aem up` (AEM_OPEN / AEM_PORT / AEM_PAGES_URL)").option("--open <path>", "AEM_OPEN - path opened on start (default: /)").option("--port <port>", "AEM_PORT - proxy port (default: 3007)").option("--pages-url <url>", "AEM_PAGES_URL - content origin (default: from git remote)").option("--yes", "Skip prompts; use flags/defaults").action(async (options) => {
|
|
7368
|
+
const { scaffoldEnv: scaffoldEnv2 } = await Promise.resolve().then(() => (init_scaffold(), scaffold_exports));
|
|
7369
|
+
await scaffoldEnv2(options);
|
|
7370
|
+
});
|
|
7371
|
+
scaffold.command("xwalk").description("Write xwalk.json enabling Universal Editor multi-field support").action(async () => {
|
|
7372
|
+
const { scaffoldXwalk: scaffoldXwalk2 } = await Promise.resolve().then(() => (init_scaffold(), scaffold_exports));
|
|
7373
|
+
await scaffoldXwalk2();
|
|
7374
|
+
});
|
|
6423
7375
|
program.command("integrate [type]").description("Add a third-party service (GTM, GA4, chat, consent) to scripts/delayed.js").action(async (type) => {
|
|
6424
7376
|
const { integrate: integrate2 } = await Promise.resolve().then(() => (init_integrate(), integrate_exports));
|
|
6425
7377
|
await integrate2(type);
|