aem-eds-cli 1.0.0 → 1.2.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 +76 -41
- package/package.json +1 -1
package/bin/aem-eds.js
CHANGED
|
@@ -68,7 +68,6 @@ const FIELD_TYPES = {
|
|
|
68
68
|
'radio-group': { label: 'Radio buttons — single value', valueType: 'string', multi: false },
|
|
69
69
|
'checkbox-group': { label: 'Checkboxes — multiple values', valueType: 'string[]', multi: false },
|
|
70
70
|
number: { label: 'Number input', valueType: 'number', multi: false },
|
|
71
|
-
'date-time': { label: 'Date / time picker', valueType: 'date', multi: false },
|
|
72
71
|
'aem-tag': { label: 'AEM Tag picker (cq:tags)', valueType: 'string', multi: false },
|
|
73
72
|
container: { label: 'Grouped / repeating field set', valueType: 'string', multi: false },
|
|
74
73
|
tab: { label: 'Tab — organises fields in panel', valueType: null, multi: false },
|
|
@@ -78,7 +77,7 @@ const FIELD_GROUPS = [
|
|
|
78
77
|
{ title: 'Text inputs', keys: ['text', 'textarea', 'richtext'] },
|
|
79
78
|
{ title: 'Media & references', keys: ['reference', 'aem-content', 'aem-content-fragment', 'aem-experience-fragment'] },
|
|
80
79
|
{ title: 'Selection & toggles', keys: ['boolean', 'select', 'multiselect', 'radio-group', 'checkbox-group'] },
|
|
81
|
-
{ title: 'Specialised', keys: ['number', '
|
|
80
|
+
{ title: 'Specialised', keys: ['number', 'aem-tag'] },
|
|
82
81
|
{ title: 'Layout (no value)', keys: ['container', 'tab'] },
|
|
83
82
|
];
|
|
84
83
|
|
|
@@ -114,7 +113,6 @@ const VALIDATIONS = {
|
|
|
114
113
|
'radio-group': ['required', 'description', 'readOnly', 'hidden'],
|
|
115
114
|
'checkbox-group': ['required', 'description', 'readOnly', 'hidden'],
|
|
116
115
|
number: ['required', 'description', 'readOnly', 'hidden', 'min', 'max', 'step'],
|
|
117
|
-
'date-time': ['required', 'description', 'readOnly', 'hidden', 'min', 'max'],
|
|
118
116
|
'aem-tag': ['required', 'description', 'readOnly', 'hidden'],
|
|
119
117
|
container: ['description', 'readOnly', 'hidden'],
|
|
120
118
|
tab: [],
|
|
@@ -148,7 +146,7 @@ function buildFieldSchema(fields) {
|
|
|
148
146
|
: f.defaultValue;
|
|
149
147
|
}
|
|
150
148
|
|
|
151
|
-
|
|
149
|
+
// number / date-time constraints go TOP-LEVEL — not inside validation.
|
|
152
150
|
if (f.type === 'aem-content-fragment') base.variationName = '';
|
|
153
151
|
|
|
154
152
|
const validation = {};
|
|
@@ -181,17 +179,6 @@ function genSimpleBlockJS(blockName) {
|
|
|
181
179
|
`;
|
|
182
180
|
}
|
|
183
181
|
|
|
184
|
-
// ─── JS template — multi-row block ───────────────────────────────────────────
|
|
185
|
-
function genMultiRowBlockJS(blockName) {
|
|
186
|
-
return `export default function decorate(block) {
|
|
187
|
-
const rows = [...block.children];
|
|
188
|
-
rows.forEach((row) => {
|
|
189
|
-
// TODO: implement ${blockName} row decoration
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
`;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
182
|
// ─── JS template — container block ───────────────────────────────────────────
|
|
196
183
|
function genContainerBlockJS(blockName, childName) {
|
|
197
184
|
return `export default function decorate(block) {
|
|
@@ -430,7 +417,7 @@ async function collectOptions(rl, fieldName, ind = '') {
|
|
|
430
417
|
const DEFAULT_VALUE_TYPES = new Set([
|
|
431
418
|
'text', 'textarea', 'richtext', 'number', 'boolean',
|
|
432
419
|
'select', 'multiselect', 'radio-group', 'checkbox-group',
|
|
433
|
-
'
|
|
420
|
+
'aem-tag',
|
|
434
421
|
]);
|
|
435
422
|
|
|
436
423
|
async function collectDefaultValue(rl, field, ind = '') {
|
|
@@ -463,9 +450,6 @@ async function collectDefaultValue(rl, field, ind = '') {
|
|
|
463
450
|
question = `${ind} Default selected values for "${field.name}" (comma-separated, Enter to skip): `;
|
|
464
451
|
break;
|
|
465
452
|
}
|
|
466
|
-
case 'date-time':
|
|
467
|
-
question = `${ind} Default date for "${field.name}" (YYYY-MM-DD, Enter to skip): `;
|
|
468
|
-
break;
|
|
469
453
|
default:
|
|
470
454
|
question = `${ind} Default value for "${field.name}" (Enter to skip): `;
|
|
471
455
|
}
|
|
@@ -534,14 +518,51 @@ async function collectValidation(rl, field, indent = '') {
|
|
|
534
518
|
if (available.includes('description')) { const d = (await prompt(rl, `${indent} Helper text (Enter to skip): `)).trim(); if (d) field.description = d; }
|
|
535
519
|
if (available.includes('minLength')) { const v = (await prompt(rl, `${indent} Min length (Enter to skip): `)).trim(); if (v && !isNaN(v)) field.minLength = +v; }
|
|
536
520
|
if (available.includes('maxLength')) { const v = (await prompt(rl, `${indent} Max length (Enter to skip): `)).trim(); if (v && !isNaN(v)) field.maxLength = +v; }
|
|
537
|
-
if (available.includes('min')) { const v = (await prompt(rl, `${indent} Min
|
|
538
|
-
if (available.includes('max')) { const v = (await prompt(rl, `${indent} Max
|
|
521
|
+
if (available.includes('min')) { const v = (await prompt(rl, `${indent} Min (Enter to skip): `)).trim(); if (v) field.min = field.type === 'number' ? +v : v; }
|
|
522
|
+
if (available.includes('max')) { const v = (await prompt(rl, `${indent} Max (Enter to skip): `)).trim(); if (v) field.max = field.type === 'number' ? +v : v; }
|
|
539
523
|
if (available.includes('step')) { const v = (await prompt(rl, `${indent} Step (Enter to skip): `)).trim(); if (v && !isNaN(v)) field.step = +v; }
|
|
540
524
|
if (available.includes('rootPath')) { const v = (await prompt(rl, `${indent} Root path (Enter to skip): `)).trim(); if (v) field.rootPath = v; }
|
|
541
525
|
if (available.includes('customErrorMsg')) { const v = (await prompt(rl, `${indent} Custom error message (Enter to skip): `)).trim(); if (v) field.customErrorMsg = v; }
|
|
542
526
|
log.blank();
|
|
543
527
|
}
|
|
544
528
|
|
|
529
|
+
// ─── Field collector with tab grouping ───────────────────────────────────────
|
|
530
|
+
async function collectFieldsWithTabs(rl, context) {
|
|
531
|
+
log.info(`Define tab groups for ${c.bold}${context}${c.reset}. Each tab appears as a panel section in UE.`);
|
|
532
|
+
log.blank();
|
|
533
|
+
|
|
534
|
+
const allFields = [];
|
|
535
|
+
let tabCount = 0;
|
|
536
|
+
|
|
537
|
+
while (true) {
|
|
538
|
+
tabCount++;
|
|
539
|
+
log.divider();
|
|
540
|
+
|
|
541
|
+
const tabLabelIn = (await prompt(rl, `Tab ${tabCount} name (e.g. Media, Links, Body): `)).trim();
|
|
542
|
+
if (!tabLabelIn) { log.warn('Tab name cannot be blank.'); tabCount--; continue; }
|
|
543
|
+
|
|
544
|
+
const tabName = `tab${tabCount}`;
|
|
545
|
+
const tabLabel = tabLabelIn;
|
|
546
|
+
|
|
547
|
+
allFields.push({ name: tabName, label: tabLabel, type: 'tab' });
|
|
548
|
+
log.success(`Tab "${tabLabel}" created — define its fields:`);
|
|
549
|
+
log.blank();
|
|
550
|
+
|
|
551
|
+
const tabFields = await collectFields(rl, `"${tabLabel}" tab`);
|
|
552
|
+
allFields.push(...tabFields);
|
|
553
|
+
|
|
554
|
+
log.blank();
|
|
555
|
+
const addMore = await confirm(rl, `Add another tab?`, false);
|
|
556
|
+
if (!addMore) break;
|
|
557
|
+
log.blank();
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
log.blank();
|
|
561
|
+
log.info(`${tabCount} tab(s) defined with ${allFields.filter(f => f.type !== 'tab').length} total fields.`);
|
|
562
|
+
log.blank();
|
|
563
|
+
return allFields;
|
|
564
|
+
}
|
|
565
|
+
|
|
545
566
|
// ─── Field collector ──────────────────────────────────────────────────────────
|
|
546
567
|
async function collectFields(rl, context, depth = 0) {
|
|
547
568
|
const fields = [];
|
|
@@ -636,7 +657,6 @@ function genBlockJSON(blockName, isContainer, parentFields, childName, childFiel
|
|
|
636
657
|
const models = [];
|
|
637
658
|
const parentSchema = buildFieldSchema(parentFields);
|
|
638
659
|
|
|
639
|
-
// classes field only added when author explicitly opted in during scaffolding
|
|
640
660
|
if (needsVariants) {
|
|
641
661
|
parentSchema.push({
|
|
642
662
|
component: 'text',
|
|
@@ -653,13 +673,15 @@ function genBlockJSON(blockName, isContainer, parentFields, childName, childFiel
|
|
|
653
673
|
}
|
|
654
674
|
|
|
655
675
|
// ── definitions ──────────────────────────────────────────────────────────────
|
|
676
|
+
// Must use groups.components format to match boilerplate's component-definition.json
|
|
677
|
+
// structure. merge-json-cli deep-merges this correctly into the global file.
|
|
678
|
+
// Using a flat "definitions" key would create a separate key that UE ignores.
|
|
656
679
|
const template = { name: toTitleCase(blockName), model: blockName };
|
|
657
680
|
if (isContainer) {
|
|
658
681
|
template.filter = blockName;
|
|
659
|
-
// No :items — pre-population causes 409 JCR conflict on insert
|
|
660
682
|
}
|
|
661
683
|
|
|
662
|
-
const
|
|
684
|
+
const components = [{
|
|
663
685
|
title: toTitleCase(blockName),
|
|
664
686
|
id: blockName,
|
|
665
687
|
plugins: {
|
|
@@ -672,11 +694,9 @@ function genBlockJSON(blockName, isContainer, parentFields, childName, childFiel
|
|
|
672
694
|
},
|
|
673
695
|
}];
|
|
674
696
|
|
|
675
|
-
//
|
|
676
|
-
// with resourceType block/item — this is what UE uses when author
|
|
677
|
-
// clicks + inside the block to add a child
|
|
697
|
+
// Child item definition — block/item resourceType required for UE + button
|
|
678
698
|
if (isContainer) {
|
|
679
|
-
|
|
699
|
+
components.push({
|
|
680
700
|
title: toTitleCase(childName),
|
|
681
701
|
id: childName,
|
|
682
702
|
plugins: {
|
|
@@ -693,6 +713,14 @@ function genBlockJSON(blockName, isContainer, parentFields, childName, childFiel
|
|
|
693
713
|
});
|
|
694
714
|
}
|
|
695
715
|
|
|
716
|
+
const definitions = {
|
|
717
|
+
groups: [{
|
|
718
|
+
title: 'Blocks',
|
|
719
|
+
id: 'blocks',
|
|
720
|
+
components,
|
|
721
|
+
}],
|
|
722
|
+
};
|
|
723
|
+
|
|
696
724
|
// ── filters ──────────────────────────────────────────────────────────────────
|
|
697
725
|
// Section filter is NOT generated here — it is a project-level concern
|
|
698
726
|
// maintained in the boilerplate's central component-filters.json.
|
|
@@ -747,18 +775,23 @@ async function cmdCreate(rl, cwd, argName, dryRun) {
|
|
|
747
775
|
// Block type
|
|
748
776
|
log.blank();
|
|
749
777
|
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}Container${c.reset}
|
|
778
|
+
console.log(` ${c.dim}1.${c.reset} ${c.cyan}Simple${c.reset} ${c.dim}Fixed fields — hero, banner, teaser${c.reset}`);
|
|
779
|
+
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}`);
|
|
780
|
+
console.log(` ${c.dim}3.${c.reset} ${c.cyan}Container${c.reset} ${c.dim}Children with different fields — accordion, tabs${c.reset}`);
|
|
781
|
+
console.log(` ${c.dim}4.${c.reset} ${c.cyan}Container with tabs${c.reset} ${c.dim}Container where parent config has tab groups${c.reset}`);
|
|
753
782
|
log.blank();
|
|
754
|
-
const typeIn
|
|
755
|
-
const isContainer = typeIn === '3' || typeIn.toLowerCase()
|
|
756
|
-
const
|
|
783
|
+
const typeIn = (await prompt(rl, `Type [1]: `)).trim();
|
|
784
|
+
const isContainer = typeIn === '3' || typeIn === '4' || typeIn.toLowerCase().startsWith('container');
|
|
785
|
+
const useTabs = typeIn === '2' || typeIn === '4'
|
|
786
|
+
|| typeIn.toLowerCase() === 'simple with tabs'
|
|
787
|
+
|| typeIn.toLowerCase() === 'container with tabs';
|
|
757
788
|
log.blank();
|
|
758
789
|
|
|
759
|
-
// Parent fields
|
|
790
|
+
// Parent fields — with or without tab grouping based on block type
|
|
760
791
|
log.section(isContainer ? `Phase 1 — Parent config fields` : `Define block fields`);
|
|
761
|
-
const parentFields =
|
|
792
|
+
const parentFields = useTabs
|
|
793
|
+
? await collectFieldsWithTabs(rl, isContainer ? `${blockName} config` : blockName, true)
|
|
794
|
+
: await collectFields(rl, isContainer ? `${blockName} config` : blockName);
|
|
762
795
|
|
|
763
796
|
// Container: child setup
|
|
764
797
|
let childName = '', childFields = [];
|
|
@@ -792,10 +825,9 @@ async function cmdCreate(rl, cwd, argName, dryRun) {
|
|
|
792
825
|
const dir = path.join(cwd, 'blocks', blockName);
|
|
793
826
|
|
|
794
827
|
// Minimal lint-free JS based on block type
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
else jsContent = genSimpleBlockJS(blockName);
|
|
828
|
+
const jsContent = isContainer
|
|
829
|
+
? genContainerBlockJS(blockName, childName)
|
|
830
|
+
: genSimpleBlockJS(blockName);
|
|
799
831
|
|
|
800
832
|
writeFile(path.join(dir, `${blockName}.js`), jsContent, `blocks/${blockName}/${blockName}.js`, dryRun);
|
|
801
833
|
writeFile(path.join(dir, `${blockName}.css`), genBlockCSS(blockName), `blocks/${blockName}/${blockName}.css`, dryRun);
|
|
@@ -807,7 +839,10 @@ async function cmdCreate(rl, cwd, argName, dryRun) {
|
|
|
807
839
|
// Summary
|
|
808
840
|
log.blank(); log.divider();
|
|
809
841
|
const prefix = dryRun ? `${c.yellow}~ DRY RUN${c.reset} ` : `${c.green}✔${c.reset} `;
|
|
810
|
-
const typeLabel = isContainer ? `container → ${childName}`
|
|
842
|
+
const typeLabel = isContainer && useTabs ? `container with tabs → ${childName}`
|
|
843
|
+
: isContainer ? `container → ${childName}`
|
|
844
|
+
: useTabs ? 'simple with tabs'
|
|
845
|
+
: 'simple';
|
|
811
846
|
console.log(`\n ${prefix}${c.bold}"${blockName}" ${dryRun ? 'would be' : 'scaffolded!'} (${typeLabel})${c.reset}\n`);
|
|
812
847
|
if (!dryRun) {
|
|
813
848
|
console.log(` ${c.dim}Files created:${c.reset}`);
|