aem-eds-cli 1.1.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 +40 -111
- 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,8 +518,8 @@ 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; }
|
|
@@ -668,12 +652,11 @@ async function collectFields(rl, context, depth = 0) {
|
|
|
668
652
|
// definitions, models and filters — matching the boilerplate pattern.
|
|
669
653
|
// `npm run build:json` (merge-json-cli) assembles these into global files.
|
|
670
654
|
|
|
671
|
-
function genBlockJSON(blockName, isContainer, parentFields, childName, childFields, needsVariants = false
|
|
655
|
+
function genBlockJSON(blockName, isContainer, parentFields, childName, childFields, needsVariants = false) {
|
|
672
656
|
// ── models ───────────────────────────────────────────────────────────────────
|
|
673
657
|
const models = [];
|
|
674
658
|
const parentSchema = buildFieldSchema(parentFields);
|
|
675
659
|
|
|
676
|
-
// classes field only added when author explicitly opted in during scaffolding
|
|
677
660
|
if (needsVariants) {
|
|
678
661
|
parentSchema.push({
|
|
679
662
|
component: 'text',
|
|
@@ -685,21 +668,20 @@ function genBlockJSON(blockName, isContainer, parentFields, childName, childFiel
|
|
|
685
668
|
}
|
|
686
669
|
|
|
687
670
|
models.push({ id: blockName, fields: parentSchema });
|
|
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) {
|
|
671
|
+
if (isContainer) {
|
|
692
672
|
models.push({ id: childName, fields: buildFieldSchema(childFields) });
|
|
693
673
|
}
|
|
694
674
|
|
|
695
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.
|
|
696
679
|
const template = { name: toTitleCase(blockName), model: blockName };
|
|
697
680
|
if (isContainer) {
|
|
698
681
|
template.filter = blockName;
|
|
699
|
-
// No :items — pre-population causes 409 JCR conflict on insert
|
|
700
682
|
}
|
|
701
683
|
|
|
702
|
-
const
|
|
684
|
+
const components = [{
|
|
703
685
|
title: toTitleCase(blockName),
|
|
704
686
|
id: blockName,
|
|
705
687
|
plugins: {
|
|
@@ -712,11 +694,9 @@ function genBlockJSON(blockName, isContainer, parentFields, childName, childFiel
|
|
|
712
694
|
},
|
|
713
695
|
}];
|
|
714
696
|
|
|
715
|
-
//
|
|
716
|
-
// with resourceType block/item — this is what UE uses when author
|
|
717
|
-
// clicks + inside the block to add a child
|
|
697
|
+
// Child item definition — block/item resourceType required for UE + button
|
|
718
698
|
if (isContainer) {
|
|
719
|
-
|
|
699
|
+
components.push({
|
|
720
700
|
title: toTitleCase(childName),
|
|
721
701
|
id: childName,
|
|
722
702
|
plugins: {
|
|
@@ -733,6 +713,14 @@ function genBlockJSON(blockName, isContainer, parentFields, childName, childFiel
|
|
|
733
713
|
});
|
|
734
714
|
}
|
|
735
715
|
|
|
716
|
+
const definitions = {
|
|
717
|
+
groups: [{
|
|
718
|
+
title: 'Blocks',
|
|
719
|
+
id: 'blocks',
|
|
720
|
+
components,
|
|
721
|
+
}],
|
|
722
|
+
};
|
|
723
|
+
|
|
736
724
|
// ── filters ──────────────────────────────────────────────────────────────────
|
|
737
725
|
// Section filter is NOT generated here — it is a project-level concern
|
|
738
726
|
// maintained in the boilerplate's central component-filters.json.
|
|
@@ -789,14 +777,12 @@ async function cmdCreate(rl, cwd, argName, dryRun) {
|
|
|
789
777
|
console.log(` ${c.bold}Block type:${c.reset}`);
|
|
790
778
|
console.log(` ${c.dim}1.${c.reset} ${c.cyan}Simple${c.reset} ${c.dim}Fixed fields — hero, banner, teaser${c.reset}`);
|
|
791
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}`);
|
|
792
|
-
console.log(` ${c.dim}3.${c.reset} ${c.cyan}
|
|
793
|
-
console.log(` ${c.dim}4.${c.reset} ${c.cyan}Container${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}`);
|
|
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}`);
|
|
795
782
|
log.blank();
|
|
796
783
|
const typeIn = (await prompt(rl, `Type [1]: `)).trim();
|
|
797
|
-
const isContainer = typeIn === '
|
|
798
|
-
const
|
|
799
|
-
const useTabs = typeIn === '2' || typeIn === '5'
|
|
784
|
+
const isContainer = typeIn === '3' || typeIn === '4' || typeIn.toLowerCase().startsWith('container');
|
|
785
|
+
const useTabs = typeIn === '2' || typeIn === '4'
|
|
800
786
|
|| typeIn.toLowerCase() === 'simple with tabs'
|
|
801
787
|
|| typeIn.toLowerCase() === 'container with tabs';
|
|
802
788
|
log.blank();
|
|
@@ -808,73 +794,18 @@ async function cmdCreate(rl, cwd, argName, dryRun) {
|
|
|
808
794
|
: await collectFields(rl, isContainer ? `${blockName} config` : blockName);
|
|
809
795
|
|
|
810
796
|
// Container: child setup
|
|
811
|
-
let childName = '', childFields = []
|
|
797
|
+
let childName = '', childFields = [];
|
|
812
798
|
if (isContainer) {
|
|
813
799
|
log.divider();
|
|
814
800
|
log.section(`Phase 2 — Child item`);
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
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
|
-
}
|
|
801
|
+
const sugg = `${blockName}-item`;
|
|
802
|
+
const childIn = (await prompt(rl, `Child item name [${sugg}]: `)).trim().toLowerCase().replace(/\s+/g, '-');
|
|
803
|
+
childName = childIn || sugg;
|
|
804
|
+
if (!isValidName(childName)) { log.error(`Invalid child name.`); rl.close(); process.exit(1); }
|
|
805
|
+
log.blank();
|
|
806
|
+
log.info(`Child: ${c.bold}${childName}${c.reset}`);
|
|
807
|
+
log.blank();
|
|
808
|
+
childFields = await collectFields(rl, `${childName} fields`);
|
|
878
809
|
}
|
|
879
810
|
|
|
880
811
|
// Variants prompt — asked for all block types
|
|
@@ -894,24 +825,22 @@ async function cmdCreate(rl, cwd, argName, dryRun) {
|
|
|
894
825
|
const dir = path.join(cwd, 'blocks', blockName);
|
|
895
826
|
|
|
896
827
|
// Minimal lint-free JS based on block type
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
else jsContent = genSimpleBlockJS(blockName);
|
|
828
|
+
const jsContent = isContainer
|
|
829
|
+
? genContainerBlockJS(blockName, childName)
|
|
830
|
+
: genSimpleBlockJS(blockName);
|
|
901
831
|
|
|
902
832
|
writeFile(path.join(dir, `${blockName}.js`), jsContent, `blocks/${blockName}/${blockName}.js`, dryRun);
|
|
903
833
|
writeFile(path.join(dir, `${blockName}.css`), genBlockCSS(blockName), `blocks/${blockName}/${blockName}.css`, dryRun);
|
|
904
834
|
writeFile(path.join(dir, 'README.md'), genReadme(blockName, isContainer, parentFields, childName, childFields, needsVariants), `blocks/${blockName}/README.md`, dryRun);
|
|
905
835
|
|
|
906
|
-
const blockJSON = genBlockJSON(blockName, isContainer, parentFields, childName, childFields, needsVariants
|
|
836
|
+
const blockJSON = genBlockJSON(blockName, isContainer, parentFields, childName, childFields, needsVariants);
|
|
907
837
|
writeBlockJSON(dir, blockName, blockJSON, dryRun);
|
|
908
838
|
|
|
909
839
|
// Summary
|
|
910
840
|
log.blank(); log.divider();
|
|
911
841
|
const prefix = dryRun ? `${c.yellow}~ DRY RUN${c.reset} ` : `${c.green}✔${c.reset} `;
|
|
912
|
-
const typeLabel = isContainer && useTabs ? `container with tabs → ${childName}
|
|
913
|
-
: isContainer ? `container → ${childName}
|
|
914
|
-
: isMultiRow ? 'multi-row'
|
|
842
|
+
const typeLabel = isContainer && useTabs ? `container with tabs → ${childName}`
|
|
843
|
+
: isContainer ? `container → ${childName}`
|
|
915
844
|
: useTabs ? 'simple with tabs'
|
|
916
845
|
: 'simple';
|
|
917
846
|
console.log(`\n ${prefix}${c.bold}"${blockName}" ${dryRun ? 'would be' : 'scaffolded!'} (${typeLabel})${c.reset}\n`);
|