aem-eds-cli 1.0.0 → 1.1.0
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/bin/aem-eds.js +127 -21
- package/package.json +1 -1
package/bin/aem-eds.js
CHANGED
|
@@ -542,6 +542,43 @@ async function collectValidation(rl, field, indent = '') {
|
|
|
542
542
|
log.blank();
|
|
543
543
|
}
|
|
544
544
|
|
|
545
|
+
// ─── Field collector with tab grouping ───────────────────────────────────────
|
|
546
|
+
async function collectFieldsWithTabs(rl, context) {
|
|
547
|
+
log.info(`Define tab groups for ${c.bold}${context}${c.reset}. Each tab appears as a panel section in UE.`);
|
|
548
|
+
log.blank();
|
|
549
|
+
|
|
550
|
+
const allFields = [];
|
|
551
|
+
let tabCount = 0;
|
|
552
|
+
|
|
553
|
+
while (true) {
|
|
554
|
+
tabCount++;
|
|
555
|
+
log.divider();
|
|
556
|
+
|
|
557
|
+
const tabLabelIn = (await prompt(rl, `Tab ${tabCount} name (e.g. Media, Links, Body): `)).trim();
|
|
558
|
+
if (!tabLabelIn) { log.warn('Tab name cannot be blank.'); tabCount--; continue; }
|
|
559
|
+
|
|
560
|
+
const tabName = `tab${tabCount}`;
|
|
561
|
+
const tabLabel = tabLabelIn;
|
|
562
|
+
|
|
563
|
+
allFields.push({ name: tabName, label: tabLabel, type: 'tab' });
|
|
564
|
+
log.success(`Tab "${tabLabel}" created — define its fields:`);
|
|
565
|
+
log.blank();
|
|
566
|
+
|
|
567
|
+
const tabFields = await collectFields(rl, `"${tabLabel}" tab`);
|
|
568
|
+
allFields.push(...tabFields);
|
|
569
|
+
|
|
570
|
+
log.blank();
|
|
571
|
+
const addMore = await confirm(rl, `Add another tab?`, false);
|
|
572
|
+
if (!addMore) break;
|
|
573
|
+
log.blank();
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
log.blank();
|
|
577
|
+
log.info(`${tabCount} tab(s) defined with ${allFields.filter(f => f.type !== 'tab').length} total fields.`);
|
|
578
|
+
log.blank();
|
|
579
|
+
return allFields;
|
|
580
|
+
}
|
|
581
|
+
|
|
545
582
|
// ─── Field collector ──────────────────────────────────────────────────────────
|
|
546
583
|
async function collectFields(rl, context, depth = 0) {
|
|
547
584
|
const fields = [];
|
|
@@ -631,7 +668,7 @@ async function collectFields(rl, context, depth = 0) {
|
|
|
631
668
|
// definitions, models and filters — matching the boilerplate pattern.
|
|
632
669
|
// `npm run build:json` (merge-json-cli) assembles these into global files.
|
|
633
670
|
|
|
634
|
-
function genBlockJSON(blockName, isContainer, parentFields, childName, childFields, needsVariants = false) {
|
|
671
|
+
function genBlockJSON(blockName, isContainer, parentFields, childName, childFields, needsVariants = false, useExistingChild = false) {
|
|
635
672
|
// ── models ───────────────────────────────────────────────────────────────────
|
|
636
673
|
const models = [];
|
|
637
674
|
const parentSchema = buildFieldSchema(parentFields);
|
|
@@ -648,7 +685,10 @@ function genBlockJSON(blockName, isContainer, parentFields, childName, childFiel
|
|
|
648
685
|
}
|
|
649
686
|
|
|
650
687
|
models.push({ id: blockName, fields: parentSchema });
|
|
651
|
-
|
|
688
|
+
|
|
689
|
+
// Only create child model if NOT using an existing block
|
|
690
|
+
// When useExistingChild=true, the child block already has its own model
|
|
691
|
+
if (isContainer && !useExistingChild) {
|
|
652
692
|
models.push({ id: childName, fields: buildFieldSchema(childFields) });
|
|
653
693
|
}
|
|
654
694
|
|
|
@@ -747,32 +787,94 @@ async function cmdCreate(rl, cwd, argName, dryRun) {
|
|
|
747
787
|
// Block type
|
|
748
788
|
log.blank();
|
|
749
789
|
console.log(` ${c.bold}Block type:${c.reset}`);
|
|
750
|
-
console.log(` ${c.dim}1.${c.reset} ${c.cyan}Simple${c.reset}
|
|
751
|
-
console.log(` ${c.dim}2.${c.reset} ${c.cyan}
|
|
752
|
-
console.log(` ${c.dim}3.${c.reset} ${c.cyan}
|
|
790
|
+
console.log(` ${c.dim}1.${c.reset} ${c.cyan}Simple${c.reset} ${c.dim}Fixed fields — hero, banner, teaser${c.reset}`);
|
|
791
|
+
console.log(` ${c.dim}2.${c.reset} ${c.cyan}Simple with tabs${c.reset} ${c.dim}Fixed fields grouped into UE panel tabs${c.reset}`);
|
|
792
|
+
console.log(` ${c.dim}3.${c.reset} ${c.cyan}Multi-row${c.reset} ${c.dim}Same fields repeating — slider, cards${c.reset}`);
|
|
793
|
+
console.log(` ${c.dim}4.${c.reset} ${c.cyan}Container${c.reset} ${c.dim}Children with different fields — accordion, tabs${c.reset}`);
|
|
794
|
+
console.log(` ${c.dim}5.${c.reset} ${c.cyan}Container with tabs${c.reset} ${c.dim}Container where parent config has tab groups${c.reset}`);
|
|
753
795
|
log.blank();
|
|
754
|
-
const typeIn
|
|
755
|
-
const isContainer = typeIn === '
|
|
756
|
-
const isMultiRow = typeIn === '
|
|
796
|
+
const typeIn = (await prompt(rl, `Type [1]: `)).trim();
|
|
797
|
+
const isContainer = typeIn === '4' || typeIn === '5' || typeIn.toLowerCase().startsWith('container');
|
|
798
|
+
const isMultiRow = typeIn === '3' || typeIn.toLowerCase() === 'multi-row';
|
|
799
|
+
const useTabs = typeIn === '2' || typeIn === '5'
|
|
800
|
+
|| typeIn.toLowerCase() === 'simple with tabs'
|
|
801
|
+
|| typeIn.toLowerCase() === 'container with tabs';
|
|
757
802
|
log.blank();
|
|
758
803
|
|
|
759
|
-
// Parent fields
|
|
804
|
+
// Parent fields — with or without tab grouping based on block type
|
|
760
805
|
log.section(isContainer ? `Phase 1 — Parent config fields` : `Define block fields`);
|
|
761
|
-
const parentFields =
|
|
806
|
+
const parentFields = useTabs
|
|
807
|
+
? await collectFieldsWithTabs(rl, isContainer ? `${blockName} config` : blockName, true)
|
|
808
|
+
: await collectFields(rl, isContainer ? `${blockName} config` : blockName);
|
|
762
809
|
|
|
763
810
|
// Container: child setup
|
|
764
|
-
let childName = '', childFields = [];
|
|
811
|
+
let childName = '', childFields = [], useExistingChild = false;
|
|
765
812
|
if (isContainer) {
|
|
766
813
|
log.divider();
|
|
767
814
|
log.section(`Phase 2 — Child item`);
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
815
|
+
|
|
816
|
+
// Ask if they want to use an existing block as child
|
|
817
|
+
useExistingChild = await confirm(rl, `Use an existing block as child item?`, false);
|
|
818
|
+
|
|
819
|
+
if (useExistingChild) {
|
|
820
|
+
log.blank();
|
|
821
|
+
|
|
822
|
+
// Scan blocks/ directory for existing blocks
|
|
823
|
+
const blocksDir = path.join(cwd, 'blocks');
|
|
824
|
+
const allBlocks = fs.existsSync(blocksDir)
|
|
825
|
+
? fs.readdirSync(blocksDir, { withFileTypes: true })
|
|
826
|
+
.filter((d) => d.isDirectory())
|
|
827
|
+
.map((d) => d.name)
|
|
828
|
+
.filter((name) => fs.existsSync(path.join(blocksDir, name, `${name}.js`)))
|
|
829
|
+
.filter((name) => name !== blockName) // exclude current block
|
|
830
|
+
: [];
|
|
831
|
+
|
|
832
|
+
if (!allBlocks.length) {
|
|
833
|
+
log.warn('No existing blocks found in blocks/ — switching to manual entry.');
|
|
834
|
+
const existingIn = (await prompt(rl, `Block name: `)).trim().toLowerCase().replace(/\s+/g, '-');
|
|
835
|
+
childName = existingIn;
|
|
836
|
+
} else {
|
|
837
|
+
// Display numbered list
|
|
838
|
+
console.log(`\n ${c.bold}Available blocks in this project:${c.reset}\n`);
|
|
839
|
+
allBlocks.forEach((name, i) => {
|
|
840
|
+
const hasJSON = fs.existsSync(path.join(blocksDir, name, `_${name}.json`));
|
|
841
|
+
const tag = hasJSON ? `${c.green}(scaffolded)${c.reset}` : `${c.dim}(manual)${c.reset}`;
|
|
842
|
+
console.log(` ${c.dim}${String(i + 1).padStart(2)}.${c.reset} ${c.cyan}${name.padEnd(24)}${c.reset} ${tag}`);
|
|
843
|
+
});
|
|
844
|
+
log.blank();
|
|
845
|
+
|
|
846
|
+
const sel = (await prompt(rl, `Select number or type block name: `)).trim().toLowerCase();
|
|
847
|
+
const byNum = allBlocks[parseInt(sel, 10) - 1];
|
|
848
|
+
const chosen = byNum || (allBlocks.includes(sel) ? sel : null);
|
|
849
|
+
|
|
850
|
+
if (!chosen) {
|
|
851
|
+
log.warn(`"${sel}" not found in blocks/ — using as-is.`);
|
|
852
|
+
childName = sel.replace(/\s+/g, '-');
|
|
853
|
+
} else {
|
|
854
|
+
childName = chosen;
|
|
855
|
+
log.success(`Selected: ${c.bold}${childName}${c.reset}`);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
if (!isValidName(childName)) { log.error(`Invalid block name.`); rl.close(); process.exit(1); }
|
|
860
|
+
|
|
861
|
+
childFields = [];
|
|
862
|
+
log.blank();
|
|
863
|
+
log.info(`Child: ${c.bold}${childName}${c.reset} ${c.dim}(existing block — model already defined)${c.reset}`);
|
|
864
|
+
log.blank();
|
|
865
|
+
log.info(`${c.yellow}Note:${c.reset} A ${c.cyan}block/item${c.reset} definition for "${childName}" will be added`);
|
|
866
|
+
log.info(`to ${c.cyan}_${blockName}.json${c.reset} so UE knows it can be dropped inside ${blockName}.`);
|
|
867
|
+
log.blank();
|
|
868
|
+
} else {
|
|
869
|
+
const sugg = `${blockName}-item`;
|
|
870
|
+
const childIn = (await prompt(rl, `Child item name [${sugg}]: `)).trim().toLowerCase().replace(/\s+/g, '-');
|
|
871
|
+
childName = childIn || sugg;
|
|
872
|
+
if (!isValidName(childName)) { log.error(`Invalid child name.`); rl.close(); process.exit(1); }
|
|
873
|
+
log.blank();
|
|
874
|
+
log.info(`Child: ${c.bold}${childName}${c.reset}`);
|
|
875
|
+
log.blank();
|
|
876
|
+
childFields = await collectFields(rl, `${childName} fields`);
|
|
877
|
+
}
|
|
776
878
|
}
|
|
777
879
|
|
|
778
880
|
// Variants prompt — asked for all block types
|
|
@@ -801,13 +903,17 @@ async function cmdCreate(rl, cwd, argName, dryRun) {
|
|
|
801
903
|
writeFile(path.join(dir, `${blockName}.css`), genBlockCSS(blockName), `blocks/${blockName}/${blockName}.css`, dryRun);
|
|
802
904
|
writeFile(path.join(dir, 'README.md'), genReadme(blockName, isContainer, parentFields, childName, childFields, needsVariants), `blocks/${blockName}/README.md`, dryRun);
|
|
803
905
|
|
|
804
|
-
const blockJSON = genBlockJSON(blockName, isContainer, parentFields, childName, childFields, needsVariants);
|
|
906
|
+
const blockJSON = genBlockJSON(blockName, isContainer, parentFields, childName, childFields, needsVariants, useExistingChild);
|
|
805
907
|
writeBlockJSON(dir, blockName, blockJSON, dryRun);
|
|
806
908
|
|
|
807
909
|
// Summary
|
|
808
910
|
log.blank(); log.divider();
|
|
809
911
|
const prefix = dryRun ? `${c.yellow}~ DRY RUN${c.reset} ` : `${c.green}✔${c.reset} `;
|
|
810
|
-
const typeLabel = isContainer ? `container → ${childName}
|
|
912
|
+
const typeLabel = isContainer && useTabs ? `container with tabs → ${childName}${useExistingChild ? ' (existing)' : ''}`
|
|
913
|
+
: isContainer ? `container → ${childName}${useExistingChild ? ' (existing)' : ''}`
|
|
914
|
+
: isMultiRow ? 'multi-row'
|
|
915
|
+
: useTabs ? 'simple with tabs'
|
|
916
|
+
: 'simple';
|
|
811
917
|
console.log(`\n ${prefix}${c.bold}"${blockName}" ${dryRun ? 'would be' : 'scaffolded!'} (${typeLabel})${c.reset}\n`);
|
|
812
918
|
if (!dryRun) {
|
|
813
919
|
console.log(` ${c.dim}Files created:${c.reset}`);
|