@uniformdev/transformer 1.1.16 → 1.1.18
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/dist/cli/index.js +1083 -1028
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +1368 -1310
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -523,301 +523,793 @@ var CompositionService = class {
|
|
|
523
523
|
}
|
|
524
524
|
};
|
|
525
525
|
|
|
526
|
-
// src/core/services/
|
|
527
|
-
|
|
526
|
+
// src/core/services/composition-converter.service.ts
|
|
527
|
+
import * as crypto from "crypto";
|
|
528
|
+
var CompositionConverterService = class {
|
|
528
529
|
constructor(fileSystem, componentService, compositionService, logger) {
|
|
529
530
|
this.fileSystem = fileSystem;
|
|
530
531
|
this.componentService = componentService;
|
|
531
532
|
this.compositionService = compositionService;
|
|
532
533
|
this.logger = logger;
|
|
533
534
|
}
|
|
534
|
-
async
|
|
535
|
+
async convert(options) {
|
|
535
536
|
const {
|
|
536
537
|
rootDir,
|
|
537
|
-
componentsDir,
|
|
538
538
|
compositionsDir,
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
539
|
+
componentsDir,
|
|
540
|
+
contentTypesDir,
|
|
541
|
+
entriesDir,
|
|
542
|
+
compositionTypes,
|
|
543
|
+
flattenComponentIds,
|
|
543
544
|
whatIf,
|
|
544
|
-
strict
|
|
545
|
-
deleteSourceParameter
|
|
545
|
+
strict
|
|
546
546
|
} = options;
|
|
547
|
-
const
|
|
548
|
-
const
|
|
549
|
-
const
|
|
550
|
-
const
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
547
|
+
const compositionsDirFull = this.fileSystem.resolvePath(rootDir, compositionsDir);
|
|
548
|
+
const componentsDirFull = this.fileSystem.resolvePath(rootDir, componentsDir);
|
|
549
|
+
const contentTypesDirFull = this.fileSystem.resolvePath(rootDir, contentTypesDir);
|
|
550
|
+
const entriesDirFull = this.fileSystem.resolvePath(rootDir, entriesDir);
|
|
551
|
+
let contentTypesWritten = 0;
|
|
552
|
+
let entriesFromCompositions = 0;
|
|
553
|
+
let entriesFromFlattened = 0;
|
|
554
|
+
let entriesReused = 0;
|
|
555
|
+
this.logger.info(`Composition types: ${compositionTypes.join(", ")}`);
|
|
556
|
+
if (flattenComponentIds.length > 0) {
|
|
557
|
+
this.logger.info(`Flatten component types: ${flattenComponentIds.join(", ")}`);
|
|
556
558
|
}
|
|
557
|
-
this.
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
const
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
559
|
+
const sourceItemMap = flattenComponentIds.length > 0 ? await this.buildSourceItemMap(entriesDirFull) : /* @__PURE__ */ new Map();
|
|
560
|
+
if (sourceItemMap.size > 0) {
|
|
561
|
+
this.logger.info(`Found ${sourceItemMap.size} existing entry(ies) with sourceItem values`);
|
|
562
|
+
}
|
|
563
|
+
const compositionResults = await this.compositionService.findCompositionsByTypes(
|
|
564
|
+
compositionsDirFull,
|
|
565
|
+
compositionTypes,
|
|
566
|
+
{ strict }
|
|
567
|
+
);
|
|
568
|
+
if (compositionResults.length === 0) {
|
|
569
|
+
this.logger.warn("No compositions found matching the specified types");
|
|
570
|
+
return { contentTypesWritten: 0, entriesFromCompositions: 0, entriesFromFlattened: 0, entriesReused: 0 };
|
|
571
|
+
}
|
|
572
|
+
this.logger.info(`Found ${compositionResults.length} composition(s)`);
|
|
573
|
+
const rootComponentTypes = /* @__PURE__ */ new Set();
|
|
574
|
+
for (const { composition } of compositionResults) {
|
|
575
|
+
rootComponentTypes.add(composition.composition.type);
|
|
576
|
+
}
|
|
577
|
+
const contentTypeMap = /* @__PURE__ */ new Map();
|
|
578
|
+
for (const rootType of rootComponentTypes) {
|
|
579
|
+
const { component } = await this.componentService.loadComponent(
|
|
580
|
+
componentsDirFull,
|
|
581
|
+
rootType,
|
|
582
|
+
{ strict }
|
|
567
583
|
);
|
|
568
|
-
|
|
569
|
-
|
|
584
|
+
const contentType = this.generateContentType(component);
|
|
585
|
+
contentTypeMap.set(rootType, contentType);
|
|
586
|
+
}
|
|
587
|
+
const flattenContentTypeMap = /* @__PURE__ */ new Map();
|
|
588
|
+
const missingFlattenTypes = [];
|
|
589
|
+
const foundMissingFlattenTypes = /* @__PURE__ */ new Set();
|
|
590
|
+
for (const flattenType of flattenComponentIds) {
|
|
591
|
+
const isRootType = [...rootComponentTypes].some(
|
|
592
|
+
(rt) => this.compareTypes(rt, flattenType, strict)
|
|
593
|
+
);
|
|
594
|
+
if (isRootType) {
|
|
570
595
|
continue;
|
|
571
596
|
}
|
|
572
|
-
|
|
573
|
-
const
|
|
574
|
-
|
|
597
|
+
try {
|
|
598
|
+
const { component } = await this.componentService.loadComponent(
|
|
599
|
+
componentsDirFull,
|
|
600
|
+
flattenType,
|
|
601
|
+
{ strict }
|
|
575
602
|
);
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
603
|
+
const contentType = this.generateContentType(component);
|
|
604
|
+
flattenContentTypeMap.set(flattenType, contentType);
|
|
605
|
+
} catch (error) {
|
|
606
|
+
if (error instanceof ComponentNotFoundError) {
|
|
607
|
+
this.logger.info(`Flatten component type not found: ${flattenType}`);
|
|
608
|
+
missingFlattenTypes.push(flattenType);
|
|
609
|
+
continue;
|
|
579
610
|
}
|
|
611
|
+
throw error;
|
|
580
612
|
}
|
|
581
613
|
}
|
|
582
|
-
const
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
614
|
+
for (const { composition } of compositionResults) {
|
|
615
|
+
const comp = composition.composition;
|
|
616
|
+
const compositionId = comp._id;
|
|
617
|
+
const compositionName = comp._name ?? compositionId;
|
|
618
|
+
const compositionType = comp.type;
|
|
619
|
+
const entry = this.generateEntryFromComposition(composition);
|
|
620
|
+
const flattenedByType = /* @__PURE__ */ new Map();
|
|
621
|
+
if (flattenComponentIds.length > 0 && comp.slots) {
|
|
622
|
+
for (const flattenType of flattenComponentIds) {
|
|
623
|
+
if (this.compareTypes(flattenType, compositionType, strict)) {
|
|
624
|
+
this.logger.warn(
|
|
625
|
+
`Skipping flatten of "${flattenType}" \u2014 same as root component type`
|
|
626
|
+
);
|
|
627
|
+
continue;
|
|
590
628
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
);
|
|
598
|
-
} else {
|
|
599
|
-
this.logger.info(`Resolved properties: ${resolvedNames.join(", ")}`);
|
|
600
|
-
}
|
|
601
|
-
let modifiedComponent = { ...targetComponent };
|
|
602
|
-
let componentModified = false;
|
|
603
|
-
const groupId = this.componentService.generateGroupId(targetGroup);
|
|
604
|
-
const existingGroup = this.componentService.findParameter(
|
|
605
|
-
modifiedComponent,
|
|
606
|
-
groupId,
|
|
607
|
-
findOptions
|
|
608
|
-
);
|
|
609
|
-
if (!existingGroup) {
|
|
610
|
-
this.logger.action(whatIf, "CREATE", `Group "${targetGroup}" on ${targetComponentType}`);
|
|
611
|
-
modifiedComponent = this.componentService.ensureGroupExists(
|
|
612
|
-
modifiedComponent,
|
|
613
|
-
groupId,
|
|
614
|
-
targetGroup,
|
|
615
|
-
findOptions
|
|
616
|
-
);
|
|
617
|
-
componentModified = true;
|
|
618
|
-
}
|
|
619
|
-
for (const param of resolvedParams) {
|
|
620
|
-
const existingParam = this.componentService.findParameter(
|
|
621
|
-
modifiedComponent,
|
|
622
|
-
param.id,
|
|
623
|
-
findOptions
|
|
624
|
-
);
|
|
625
|
-
if (!existingParam) {
|
|
626
|
-
this.logger.action(
|
|
627
|
-
whatIf,
|
|
628
|
-
"COPY",
|
|
629
|
-
`Parameter "${param.id}" \u2192 ${targetComponentType}.${targetGroup}`
|
|
630
|
-
);
|
|
631
|
-
modifiedComponent = this.componentService.addParameterToComponent(
|
|
632
|
-
modifiedComponent,
|
|
633
|
-
param,
|
|
634
|
-
findOptions
|
|
635
|
-
);
|
|
636
|
-
modifiedComponent = this.componentService.addParameterToGroup(
|
|
637
|
-
modifiedComponent,
|
|
638
|
-
groupId,
|
|
639
|
-
param.id,
|
|
640
|
-
findOptions
|
|
641
|
-
);
|
|
642
|
-
componentModified = true;
|
|
643
|
-
} else {
|
|
644
|
-
this.logger.info(`Parameter "${param.id}" already exists on ${targetComponentType}`);
|
|
645
|
-
const group = this.componentService.findParameter(
|
|
646
|
-
modifiedComponent,
|
|
647
|
-
groupId,
|
|
648
|
-
findOptions
|
|
649
|
-
);
|
|
650
|
-
if (group && this.componentService.isGroupParameter(group)) {
|
|
651
|
-
const isInGroup = group.typeConfig?.childrenParams?.some(
|
|
652
|
-
(id) => strict ? id === param.id : id.toLowerCase() === param.id.toLowerCase()
|
|
629
|
+
const instances = this.findFlattenTargets(
|
|
630
|
+
comp.slots,
|
|
631
|
+
flattenType,
|
|
632
|
+
compositionId,
|
|
633
|
+
compositionName,
|
|
634
|
+
strict
|
|
653
635
|
);
|
|
654
|
-
if (
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
findOptions
|
|
660
|
-
);
|
|
661
|
-
componentModified = true;
|
|
636
|
+
if (instances.length > 0) {
|
|
637
|
+
flattenedByType.set(flattenType, instances);
|
|
638
|
+
if (missingFlattenTypes.includes(flattenType)) {
|
|
639
|
+
foundMissingFlattenTypes.add(flattenType);
|
|
640
|
+
}
|
|
662
641
|
}
|
|
663
642
|
}
|
|
664
643
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
compositionTypes,
|
|
672
|
-
findOptions
|
|
673
|
-
);
|
|
674
|
-
let modifiedCompositions = 0;
|
|
675
|
-
let propagatedInstances = 0;
|
|
676
|
-
for (const { composition, filePath } of compositions) {
|
|
677
|
-
const rootOverrides = this.compositionService.getRootOverrides(composition);
|
|
678
|
-
const instances = this.compositionService.findComponentInstances(
|
|
679
|
-
composition,
|
|
680
|
-
targetComponentType,
|
|
681
|
-
findOptions
|
|
682
|
-
);
|
|
683
|
-
if (instances.length === 0) {
|
|
684
|
-
continue;
|
|
685
|
-
}
|
|
686
|
-
const valuesToPropagate = {};
|
|
687
|
-
for (const param of resolvedParams) {
|
|
688
|
-
if (rootOverrides[param.id]) {
|
|
689
|
-
valuesToPropagate[param.id] = rootOverrides[param.id];
|
|
644
|
+
const resolvedRefIds = /* @__PURE__ */ new Map();
|
|
645
|
+
for (const [flattenType, instances] of flattenedByType) {
|
|
646
|
+
const refIds = [];
|
|
647
|
+
for (const inst of instances) {
|
|
648
|
+
const existingId = this.findExistingEntryBySourceItem(inst, sourceItemMap);
|
|
649
|
+
refIds.push(existingId ?? inst.determinisiticId);
|
|
690
650
|
}
|
|
651
|
+
resolvedRefIds.set(flattenType, refIds);
|
|
691
652
|
}
|
|
692
|
-
|
|
693
|
-
|
|
653
|
+
for (const [flattenType] of flattenedByType) {
|
|
654
|
+
entry.entry.fields[flattenType] = {
|
|
655
|
+
type: "contentReference",
|
|
656
|
+
value: resolvedRefIds.get(flattenType)
|
|
657
|
+
};
|
|
694
658
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
const instanceUpdates = [];
|
|
698
|
-
for (const { instance, instanceId } of instances) {
|
|
699
|
-
const instanceName = instance._id ?? instanceId;
|
|
700
|
-
this.compositionService.setInstanceParameters(instance, valuesToPropagate);
|
|
701
|
-
compositionModified = true;
|
|
702
|
-
propagatedInstances++;
|
|
703
|
-
instanceUpdates.push(
|
|
704
|
-
`${targetComponentType} "${instanceName}": ${Object.keys(valuesToPropagate).join(", ")}`
|
|
705
|
-
);
|
|
659
|
+
if (flattenComponentIds.length > 0) {
|
|
660
|
+
this.transformContentReferences(entry);
|
|
706
661
|
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
662
|
+
const entryId = entry.entry._id;
|
|
663
|
+
const entryFilePath = this.fileSystem.joinPath(entriesDirFull, `${entryId}.json`);
|
|
664
|
+
this.logger.action(
|
|
665
|
+
whatIf,
|
|
666
|
+
"WRITE",
|
|
667
|
+
`${entriesDir}/${entryId}.json (${compositionType}, "${this.truncate(compositionName, 50)}")`
|
|
668
|
+
);
|
|
669
|
+
if (!whatIf) {
|
|
670
|
+
await this.fileSystem.writeFile(entryFilePath, entry);
|
|
716
671
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
modifiedSource = this.componentService.removeParameter(modifiedSource, param.id, findOptions);
|
|
728
|
-
sourceComponentModified = true;
|
|
672
|
+
entriesFromCompositions++;
|
|
673
|
+
for (const [flattenType, instances] of flattenedByType) {
|
|
674
|
+
for (const inst of instances) {
|
|
675
|
+
const existingId = this.findExistingEntryBySourceItem(inst, sourceItemMap);
|
|
676
|
+
if (existingId) {
|
|
677
|
+
this.logger.info(
|
|
678
|
+
`Reusing existing entry ${existingId} for ${flattenType} (sourceItem match)`
|
|
679
|
+
);
|
|
680
|
+
entriesReused++;
|
|
681
|
+
continue;
|
|
729
682
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
const afterGroupCount = modifiedSource.parameters?.filter(
|
|
736
|
-
(p) => this.componentService.isGroupParameter(p)
|
|
737
|
-
).length ?? 0;
|
|
738
|
-
if (afterGroupCount < beforeGroupCount) {
|
|
739
|
-
const removedCount = beforeGroupCount - afterGroupCount;
|
|
683
|
+
const flatEntry = this.generateEntryFromFlattenedInstance(inst);
|
|
684
|
+
const flatEntryPath = this.fileSystem.joinPath(
|
|
685
|
+
entriesDirFull,
|
|
686
|
+
`${inst.determinisiticId}.json`
|
|
687
|
+
);
|
|
740
688
|
this.logger.action(
|
|
741
689
|
whatIf,
|
|
742
|
-
"
|
|
743
|
-
`${
|
|
690
|
+
"WRITE",
|
|
691
|
+
`${entriesDir}/${inst.determinisiticId}.json (${flattenType} from "${this.truncate(compositionName, 50)}")`
|
|
744
692
|
);
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
}
|
|
750
|
-
if (sourceComponentModified && !whatIf) {
|
|
751
|
-
await this.componentService.saveComponent(sourceFilePath, modifiedSource);
|
|
693
|
+
if (!whatIf) {
|
|
694
|
+
await this.fileSystem.writeFile(flatEntryPath, flatEntry);
|
|
695
|
+
}
|
|
696
|
+
entriesFromFlattened++;
|
|
752
697
|
}
|
|
753
698
|
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
);
|
|
761
|
-
if (deleted) {
|
|
762
|
-
this.logger.action(whatIf, "DELETE", `Root overrides from composition/${relativePath}`);
|
|
763
|
-
this.logger.detail(`\u2192 Removed: ${resolvedNames.join(", ")}`);
|
|
764
|
-
if (!whatIf) {
|
|
765
|
-
await this.compositionService.saveComposition(filePath, composition);
|
|
699
|
+
}
|
|
700
|
+
if (flattenComponentIds.length > 0) {
|
|
701
|
+
for (const contentType of contentTypeMap.values()) {
|
|
702
|
+
for (const flattenType of flattenComponentIds) {
|
|
703
|
+
if (this.compareTypes(flattenType, contentType.id, strict)) {
|
|
704
|
+
continue;
|
|
766
705
|
}
|
|
706
|
+
if (missingFlattenTypes.includes(flattenType) && !foundMissingFlattenTypes.has(flattenType)) {
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
contentType.fields.push({
|
|
710
|
+
id: flattenType,
|
|
711
|
+
name: flattenType,
|
|
712
|
+
type: "contentReference",
|
|
713
|
+
typeConfig: {
|
|
714
|
+
isMulti: true,
|
|
715
|
+
allowedContentTypes: [flattenType]
|
|
716
|
+
},
|
|
717
|
+
localizable: false
|
|
718
|
+
});
|
|
767
719
|
}
|
|
768
720
|
}
|
|
769
721
|
}
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
for (const entry of entries) {
|
|
780
|
-
const exists = normalized.some(
|
|
781
|
-
(existing) => strict ? existing === entry : existing.toLowerCase() === entry.toLowerCase()
|
|
722
|
+
for (const [typeName, contentType] of contentTypeMap) {
|
|
723
|
+
const filePath = this.fileSystem.joinPath(contentTypesDirFull, `${typeName}.json`);
|
|
724
|
+
const fieldCount = contentType.fields.filter((f) => f.type !== "contentReference").length;
|
|
725
|
+
const refCount = contentType.fields.filter((f) => f.type === "contentReference").length;
|
|
726
|
+
const refInfo = refCount > 0 ? ` + ${refCount} reference(s)` : "";
|
|
727
|
+
this.logger.action(
|
|
728
|
+
whatIf,
|
|
729
|
+
"WRITE",
|
|
730
|
+
`${contentTypesDir}/${typeName}.json (${fieldCount} fields${refInfo})`
|
|
782
731
|
);
|
|
783
|
-
if (!
|
|
784
|
-
|
|
732
|
+
if (!whatIf) {
|
|
733
|
+
await this.fileSystem.writeFile(filePath, contentType);
|
|
785
734
|
}
|
|
735
|
+
contentTypesWritten++;
|
|
786
736
|
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
737
|
+
for (const [typeName, contentType] of flattenContentTypeMap) {
|
|
738
|
+
const filePath = this.fileSystem.joinPath(contentTypesDirFull, `${typeName}.json`);
|
|
739
|
+
this.logger.action(
|
|
740
|
+
whatIf,
|
|
741
|
+
"WRITE",
|
|
742
|
+
`${contentTypesDir}/${typeName}.json (${contentType.fields.length} fields)`
|
|
743
|
+
);
|
|
744
|
+
if (!whatIf) {
|
|
745
|
+
await this.fileSystem.writeFile(filePath, contentType);
|
|
746
|
+
}
|
|
747
|
+
contentTypesWritten++;
|
|
748
|
+
}
|
|
749
|
+
const neverFoundMissingTypes = missingFlattenTypes.filter(
|
|
750
|
+
(type) => !foundMissingFlattenTypes.has(type)
|
|
751
|
+
);
|
|
752
|
+
if (neverFoundMissingTypes.length > 0) {
|
|
753
|
+
this.logger.warn(
|
|
754
|
+
`Flatten component type(s) not found in any composition: ${neverFoundMissingTypes.join(", ")}`
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
return { contentTypesWritten, entriesFromCompositions, entriesFromFlattened, entriesReused };
|
|
796
758
|
}
|
|
797
|
-
|
|
798
|
-
|
|
759
|
+
// --- Content Type Generation ---
|
|
760
|
+
generateContentType(component) {
|
|
761
|
+
const fields = [];
|
|
762
|
+
if (component.parameters) {
|
|
763
|
+
for (const param of component.parameters) {
|
|
764
|
+
fields.push(this.parameterToField(param));
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return {
|
|
768
|
+
id: component.id,
|
|
769
|
+
name: component.name,
|
|
770
|
+
fields
|
|
771
|
+
};
|
|
799
772
|
}
|
|
800
|
-
|
|
801
|
-
|
|
773
|
+
parameterToField(param) {
|
|
774
|
+
const field = {
|
|
775
|
+
id: param.id,
|
|
776
|
+
name: param.name,
|
|
777
|
+
type: param.type
|
|
778
|
+
};
|
|
779
|
+
if (param.helpText !== void 0) {
|
|
780
|
+
field.helpText = param.helpText;
|
|
781
|
+
}
|
|
782
|
+
if (param.localizable !== void 0) {
|
|
783
|
+
field.localizable = param.localizable;
|
|
784
|
+
}
|
|
785
|
+
if (param.typeConfig !== void 0) {
|
|
786
|
+
field.typeConfig = param.typeConfig;
|
|
787
|
+
}
|
|
788
|
+
return field;
|
|
802
789
|
}
|
|
803
|
-
|
|
804
|
-
|
|
790
|
+
// --- Entry Generation ---
|
|
791
|
+
generateEntryFromComposition(composition) {
|
|
792
|
+
const comp = composition.composition;
|
|
793
|
+
const compositionSpecificKeys = /* @__PURE__ */ new Set(["_id", "_name", "type", "parameters", "slots", "_overrides"]);
|
|
794
|
+
const extraRootProps = {};
|
|
795
|
+
for (const [key, value] of Object.entries(comp)) {
|
|
796
|
+
if (!compositionSpecificKeys.has(key) && value != null) {
|
|
797
|
+
extraRootProps[key] = value;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
const wrapperKeys = /* @__PURE__ */ new Set(["composition"]);
|
|
801
|
+
const extraWrapperProps = {};
|
|
802
|
+
for (const [key, value] of Object.entries(composition)) {
|
|
803
|
+
if (!wrapperKeys.has(key) && value != null) {
|
|
804
|
+
extraWrapperProps[key] = value;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
const entryId = computeGuidHash(`entry${comp._id}`);
|
|
808
|
+
const entryName = this.truncateName(comp._name ?? comp._id, 60);
|
|
809
|
+
return {
|
|
810
|
+
entry: {
|
|
811
|
+
_id: entryId,
|
|
812
|
+
_name: entryName,
|
|
813
|
+
type: comp.type,
|
|
814
|
+
fields: { ...comp.parameters ?? {} },
|
|
815
|
+
...extraRootProps
|
|
816
|
+
},
|
|
817
|
+
...extraWrapperProps
|
|
818
|
+
};
|
|
805
819
|
}
|
|
806
|
-
|
|
807
|
-
const
|
|
808
|
-
|
|
820
|
+
generateEntryFromFlattenedInstance(inst) {
|
|
821
|
+
const entryName = this.truncateName(
|
|
822
|
+
`${inst.componentType} (from ${inst.compositionName})`,
|
|
823
|
+
60
|
|
824
|
+
);
|
|
825
|
+
return {
|
|
826
|
+
entry: {
|
|
827
|
+
_id: inst.determinisiticId,
|
|
828
|
+
_name: entryName,
|
|
829
|
+
type: inst.componentType,
|
|
830
|
+
fields: { ...inst.instance.parameters ?? {} }
|
|
831
|
+
}
|
|
832
|
+
};
|
|
809
833
|
}
|
|
810
|
-
|
|
811
|
-
|
|
834
|
+
// --- Flatten Tree Walking ---
|
|
835
|
+
findFlattenTargets(slots, targetType, compositionId, compositionName, strict) {
|
|
836
|
+
const results = [];
|
|
837
|
+
this.walkSlots(slots, targetType, compositionId, compositionName, "", results, strict);
|
|
838
|
+
return results;
|
|
812
839
|
}
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
840
|
+
walkSlots(slots, targetType, compositionId, compositionName, pathPrefix, results, strict) {
|
|
841
|
+
for (const [slotName, instances] of Object.entries(slots)) {
|
|
842
|
+
if (!Array.isArray(instances)) continue;
|
|
843
|
+
for (let i = 0; i < instances.length; i++) {
|
|
844
|
+
const instance = instances[i];
|
|
845
|
+
if (instance._pattern) {
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
const currentPath = pathPrefix ? `${pathPrefix}-${slotName}-[${i}]-${instance.type}` : `${slotName}-[${i}]-${instance.type}`;
|
|
849
|
+
if (this.compareTypes(instance.type, targetType, strict)) {
|
|
850
|
+
const fullPath = `${compositionId}-${currentPath}`;
|
|
851
|
+
const deterministicId = computeGuidHash(fullPath);
|
|
852
|
+
results.push({
|
|
853
|
+
instance,
|
|
854
|
+
path: fullPath,
|
|
855
|
+
determinisiticId: deterministicId,
|
|
856
|
+
componentType: instance.type,
|
|
857
|
+
compositionId,
|
|
858
|
+
compositionName
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
if (instance.slots) {
|
|
862
|
+
this.walkSlots(
|
|
863
|
+
instance.slots,
|
|
864
|
+
targetType,
|
|
865
|
+
compositionId,
|
|
866
|
+
compositionName,
|
|
867
|
+
currentPath,
|
|
868
|
+
results,
|
|
869
|
+
strict
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
// --- Content Reference Transformation ---
|
|
876
|
+
transformContentReferences(entry) {
|
|
877
|
+
const dataResources = {};
|
|
878
|
+
for (const [fieldName, field] of Object.entries(entry.entry.fields)) {
|
|
879
|
+
if (field.type === "contentReference" && Array.isArray(field.value)) {
|
|
880
|
+
const entryIds = field.value;
|
|
881
|
+
const resourceKey = `ref-${entry.entry._id}-${fieldName}`;
|
|
882
|
+
field.value = `\${#jptr:/${resourceKey}/entries}`;
|
|
883
|
+
dataResources[resourceKey] = {
|
|
884
|
+
type: "uniformContentInternalReference",
|
|
885
|
+
variables: {
|
|
886
|
+
locale: "${locale}",
|
|
887
|
+
entryIds: entryIds.join(",")
|
|
888
|
+
}
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
if (Object.keys(dataResources).length > 0) {
|
|
893
|
+
entry.entry._dataResources = {
|
|
894
|
+
...entry.entry._dataResources ?? {},
|
|
895
|
+
...dataResources
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
// --- Source Item Matching ---
|
|
900
|
+
async buildSourceItemMap(entriesDirFull) {
|
|
901
|
+
const sourceItemMap = /* @__PURE__ */ new Map();
|
|
902
|
+
const entryFiles = await this.fileSystem.findFiles(entriesDirFull, "*.json");
|
|
903
|
+
for (const filePath of entryFiles) {
|
|
904
|
+
try {
|
|
905
|
+
const entryData = await this.fileSystem.readFile(filePath);
|
|
906
|
+
const sourceItemField = entryData?.entry?.fields?.sourceItem;
|
|
907
|
+
if (sourceItemField?.value != null) {
|
|
908
|
+
sourceItemMap.set(String(sourceItemField.value), entryData.entry._id);
|
|
909
|
+
}
|
|
910
|
+
} catch {
|
|
911
|
+
continue;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
return sourceItemMap;
|
|
915
|
+
}
|
|
916
|
+
findExistingEntryBySourceItem(inst, sourceItemMap) {
|
|
917
|
+
const sourceItemParam = inst.instance.parameters?.sourceItem;
|
|
918
|
+
if (sourceItemParam?.value == null) {
|
|
919
|
+
return void 0;
|
|
920
|
+
}
|
|
921
|
+
return sourceItemMap.get(String(sourceItemParam.value));
|
|
922
|
+
}
|
|
923
|
+
// --- Utilities ---
|
|
924
|
+
compareTypes(type1, type2, strict) {
|
|
925
|
+
if (strict) {
|
|
926
|
+
return type1 === type2;
|
|
927
|
+
}
|
|
928
|
+
return type1.toLowerCase() === type2.toLowerCase();
|
|
929
|
+
}
|
|
930
|
+
truncate(str, maxLength) {
|
|
931
|
+
if (str.length <= maxLength) return str;
|
|
932
|
+
return str.substring(0, maxLength - 3) + "...";
|
|
933
|
+
}
|
|
934
|
+
truncateName(name, maxLength) {
|
|
935
|
+
if (name.length <= maxLength) return name;
|
|
936
|
+
return name.substring(0, maxLength);
|
|
937
|
+
}
|
|
938
|
+
};
|
|
939
|
+
function computeGuidHash(guidOrSeed) {
|
|
940
|
+
let uuidStr;
|
|
941
|
+
if (isValidUuid(guidOrSeed)) {
|
|
942
|
+
uuidStr = formatAsBracedUuid(guidOrSeed);
|
|
943
|
+
} else {
|
|
944
|
+
const hash = crypto.createHash("md5").update(guidOrSeed, "utf-8").digest();
|
|
945
|
+
const hex = [
|
|
946
|
+
// First 4 bytes: little-endian in .NET Guid
|
|
947
|
+
hash[3].toString(16).padStart(2, "0"),
|
|
948
|
+
hash[2].toString(16).padStart(2, "0"),
|
|
949
|
+
hash[1].toString(16).padStart(2, "0"),
|
|
950
|
+
hash[0].toString(16).padStart(2, "0"),
|
|
951
|
+
"-",
|
|
952
|
+
// Bytes 4-5: little-endian
|
|
953
|
+
hash[5].toString(16).padStart(2, "0"),
|
|
954
|
+
hash[4].toString(16).padStart(2, "0"),
|
|
955
|
+
"-",
|
|
956
|
+
// Bytes 6-7: little-endian
|
|
957
|
+
hash[7].toString(16).padStart(2, "0"),
|
|
958
|
+
hash[6].toString(16).padStart(2, "0"),
|
|
959
|
+
"-",
|
|
960
|
+
// Bytes 8-9: big-endian
|
|
961
|
+
hash[8].toString(16).padStart(2, "0"),
|
|
962
|
+
hash[9].toString(16).padStart(2, "0"),
|
|
963
|
+
"-",
|
|
964
|
+
// Bytes 10-15: big-endian
|
|
965
|
+
hash[10].toString(16).padStart(2, "0"),
|
|
966
|
+
hash[11].toString(16).padStart(2, "0"),
|
|
967
|
+
hash[12].toString(16).padStart(2, "0"),
|
|
968
|
+
hash[13].toString(16).padStart(2, "0"),
|
|
969
|
+
hash[14].toString(16).padStart(2, "0"),
|
|
970
|
+
hash[15].toString(16).padStart(2, "0")
|
|
971
|
+
].join("");
|
|
972
|
+
uuidStr = `{${hex}}`.toUpperCase();
|
|
973
|
+
}
|
|
974
|
+
const chars = uuidStr.split("");
|
|
975
|
+
chars[15] = "4";
|
|
976
|
+
const arr20 = ["8", "9", "A", "B"];
|
|
977
|
+
const hexVal = parseInt(chars[20], 16);
|
|
978
|
+
chars[20] = arr20[hexVal % 4];
|
|
979
|
+
return chars.join("").slice(1, -1).toLowerCase();
|
|
980
|
+
}
|
|
981
|
+
function isValidUuid(str) {
|
|
982
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(str);
|
|
983
|
+
}
|
|
984
|
+
function formatAsBracedUuid(uuid) {
|
|
985
|
+
const clean = uuid.replace(/[{}]/g, "").toUpperCase();
|
|
986
|
+
return `{${clean}}`;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
// src/core/services/id-regenerator.ts
|
|
990
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
991
|
+
function regenerateIds(value, basePath) {
|
|
992
|
+
return walkAndRegenerate(value, basePath);
|
|
993
|
+
}
|
|
994
|
+
function walkAndRegenerate(value, currentPath) {
|
|
995
|
+
if (value === null || value === void 0) {
|
|
996
|
+
return value;
|
|
997
|
+
}
|
|
998
|
+
if (Array.isArray(value)) {
|
|
999
|
+
return value.map((item, index) => walkAndRegenerate(item, `${currentPath}[${index}]`));
|
|
1000
|
+
}
|
|
1001
|
+
if (typeof value === "object") {
|
|
1002
|
+
const obj = value;
|
|
1003
|
+
const result = {};
|
|
1004
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
1005
|
+
const childPath = `${currentPath}.${key}`;
|
|
1006
|
+
if (key === "_id" && typeof val === "string" && UUID_PATTERN.test(val)) {
|
|
1007
|
+
result[key] = computeGuidHash(val + childPath);
|
|
1008
|
+
} else {
|
|
1009
|
+
result[key] = walkAndRegenerate(val, childPath);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
return result;
|
|
1013
|
+
}
|
|
1014
|
+
return value;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// src/core/services/property-propagator.service.ts
|
|
1018
|
+
var PropertyPropagatorService = class {
|
|
1019
|
+
constructor(fileSystem, componentService, compositionService, logger) {
|
|
1020
|
+
this.fileSystem = fileSystem;
|
|
1021
|
+
this.componentService = componentService;
|
|
1022
|
+
this.compositionService = compositionService;
|
|
1023
|
+
this.logger = logger;
|
|
1024
|
+
}
|
|
1025
|
+
async propagate(options) {
|
|
1026
|
+
const {
|
|
1027
|
+
rootDir,
|
|
1028
|
+
componentsDir,
|
|
1029
|
+
compositionsDir,
|
|
1030
|
+
compositionType,
|
|
1031
|
+
property,
|
|
1032
|
+
targetComponentType,
|
|
1033
|
+
targetGroup,
|
|
1034
|
+
whatIf,
|
|
1035
|
+
strict,
|
|
1036
|
+
deleteSourceParameter
|
|
1037
|
+
} = options;
|
|
1038
|
+
const findOptions = { strict };
|
|
1039
|
+
const compositionTypes = this.parsePipeSeparatedValues(compositionType, strict);
|
|
1040
|
+
const fullComponentsDir = this.fileSystem.resolvePath(rootDir, componentsDir);
|
|
1041
|
+
const fullCompositionsDir = this.fileSystem.resolvePath(rootDir, compositionsDir);
|
|
1042
|
+
const sourceComponents = [];
|
|
1043
|
+
for (const sourceType of compositionTypes) {
|
|
1044
|
+
this.logger.info(`Loading component: ${sourceType}`);
|
|
1045
|
+
const { component: sourceComponent, filePath: sourceFilePath } = await this.componentService.loadComponent(fullComponentsDir, sourceType, findOptions);
|
|
1046
|
+
sourceComponents.push({ sourceType, sourceFilePath, sourceComponent });
|
|
1047
|
+
}
|
|
1048
|
+
this.logger.info(`Loading component: ${targetComponentType}`);
|
|
1049
|
+
const { component: targetComponent, filePath: targetFilePath } = await this.componentService.loadComponent(fullComponentsDir, targetComponentType, findOptions);
|
|
1050
|
+
const propertyNames = property.split("|").map((p) => p.trim()).filter((p) => p.length > 0);
|
|
1051
|
+
const resolvedParams = [];
|
|
1052
|
+
const resolvedNames = [];
|
|
1053
|
+
for (const { sourceType, sourceComponent } of sourceComponents) {
|
|
1054
|
+
const { parameters: sourceParams, notFound } = this.componentService.resolveProperties(
|
|
1055
|
+
sourceComponent,
|
|
1056
|
+
propertyNames,
|
|
1057
|
+
findOptions
|
|
1058
|
+
);
|
|
1059
|
+
if (notFound.length > 0) {
|
|
1060
|
+
this.logger.warn(`Property "${notFound.join(", ")}" not found on component "${sourceType}"`);
|
|
1061
|
+
continue;
|
|
1062
|
+
}
|
|
1063
|
+
for (const param of sourceParams) {
|
|
1064
|
+
const exists = resolvedParams.some(
|
|
1065
|
+
(existing) => strict ? existing.id === param.id : existing.id.toLowerCase() === param.id.toLowerCase()
|
|
1066
|
+
);
|
|
1067
|
+
if (!exists) {
|
|
1068
|
+
resolvedParams.push(param);
|
|
1069
|
+
resolvedNames.push(param.id);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
const groupSources = [];
|
|
1074
|
+
for (const { sourceType, sourceComponent } of sourceComponents) {
|
|
1075
|
+
for (const name of propertyNames) {
|
|
1076
|
+
const param = this.componentService.findParameter(sourceComponent, name, findOptions);
|
|
1077
|
+
if (param && this.componentService.isGroupParameter(param)) {
|
|
1078
|
+
const groupSource = `${sourceType}.${name}`;
|
|
1079
|
+
if (!groupSources.includes(groupSource)) {
|
|
1080
|
+
groupSources.push(groupSource);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
if (groupSources.length > 0) {
|
|
1086
|
+
this.logger.info(
|
|
1087
|
+
`Resolved properties: ${resolvedNames.join(", ")} (from ${groupSources.join(", ")})`
|
|
1088
|
+
);
|
|
1089
|
+
} else {
|
|
1090
|
+
this.logger.info(`Resolved properties: ${resolvedNames.join(", ")}`);
|
|
1091
|
+
}
|
|
1092
|
+
let modifiedComponent = { ...targetComponent };
|
|
1093
|
+
let componentModified = false;
|
|
1094
|
+
const groupId = this.componentService.generateGroupId(targetGroup);
|
|
1095
|
+
const existingGroup = this.componentService.findParameter(
|
|
1096
|
+
modifiedComponent,
|
|
1097
|
+
groupId,
|
|
1098
|
+
findOptions
|
|
1099
|
+
);
|
|
1100
|
+
if (!existingGroup) {
|
|
1101
|
+
this.logger.action(whatIf, "CREATE", `Group "${targetGroup}" on ${targetComponentType}`);
|
|
1102
|
+
modifiedComponent = this.componentService.ensureGroupExists(
|
|
1103
|
+
modifiedComponent,
|
|
1104
|
+
groupId,
|
|
1105
|
+
targetGroup,
|
|
1106
|
+
findOptions
|
|
1107
|
+
);
|
|
1108
|
+
componentModified = true;
|
|
1109
|
+
}
|
|
1110
|
+
for (const param of resolvedParams) {
|
|
1111
|
+
const existingParam = this.componentService.findParameter(
|
|
1112
|
+
modifiedComponent,
|
|
1113
|
+
param.id,
|
|
1114
|
+
findOptions
|
|
1115
|
+
);
|
|
1116
|
+
if (!existingParam) {
|
|
1117
|
+
this.logger.action(
|
|
1118
|
+
whatIf,
|
|
1119
|
+
"COPY",
|
|
1120
|
+
`Parameter "${param.id}" \u2192 ${targetComponentType}.${targetGroup}`
|
|
1121
|
+
);
|
|
1122
|
+
modifiedComponent = this.componentService.addParameterToComponent(
|
|
1123
|
+
modifiedComponent,
|
|
1124
|
+
param,
|
|
1125
|
+
findOptions
|
|
1126
|
+
);
|
|
1127
|
+
modifiedComponent = this.componentService.addParameterToGroup(
|
|
1128
|
+
modifiedComponent,
|
|
1129
|
+
groupId,
|
|
1130
|
+
param.id,
|
|
1131
|
+
findOptions
|
|
1132
|
+
);
|
|
1133
|
+
componentModified = true;
|
|
1134
|
+
} else {
|
|
1135
|
+
this.logger.info(`Parameter "${param.id}" already exists on ${targetComponentType}`);
|
|
1136
|
+
const group = this.componentService.findParameter(
|
|
1137
|
+
modifiedComponent,
|
|
1138
|
+
groupId,
|
|
1139
|
+
findOptions
|
|
1140
|
+
);
|
|
1141
|
+
if (group && this.componentService.isGroupParameter(group)) {
|
|
1142
|
+
const isInGroup = group.typeConfig?.childrenParams?.some(
|
|
1143
|
+
(id) => strict ? id === param.id : id.toLowerCase() === param.id.toLowerCase()
|
|
1144
|
+
);
|
|
1145
|
+
if (!isInGroup) {
|
|
1146
|
+
modifiedComponent = this.componentService.addParameterToGroup(
|
|
1147
|
+
modifiedComponent,
|
|
1148
|
+
groupId,
|
|
1149
|
+
param.id,
|
|
1150
|
+
findOptions
|
|
1151
|
+
);
|
|
1152
|
+
componentModified = true;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
if (componentModified && !whatIf) {
|
|
1158
|
+
await this.componentService.saveComponent(targetFilePath, modifiedComponent);
|
|
1159
|
+
}
|
|
1160
|
+
const compositions = await this.compositionService.findCompositionsByTypes(
|
|
1161
|
+
fullCompositionsDir,
|
|
1162
|
+
compositionTypes,
|
|
1163
|
+
findOptions
|
|
1164
|
+
);
|
|
1165
|
+
let modifiedCompositions = 0;
|
|
1166
|
+
let propagatedInstances = 0;
|
|
1167
|
+
for (const { composition, filePath } of compositions) {
|
|
1168
|
+
const rootOverrides = this.compositionService.getRootOverrides(composition);
|
|
1169
|
+
const instances = this.compositionService.findComponentInstances(
|
|
1170
|
+
composition,
|
|
1171
|
+
targetComponentType,
|
|
1172
|
+
findOptions
|
|
1173
|
+
);
|
|
1174
|
+
if (instances.length === 0) {
|
|
1175
|
+
continue;
|
|
1176
|
+
}
|
|
1177
|
+
const valuesToPropagate = {};
|
|
1178
|
+
for (const param of resolvedParams) {
|
|
1179
|
+
if (rootOverrides[param.id]) {
|
|
1180
|
+
valuesToPropagate[param.id] = rootOverrides[param.id];
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
if (Object.keys(valuesToPropagate).length === 0) {
|
|
1184
|
+
continue;
|
|
1185
|
+
}
|
|
1186
|
+
let compositionModified = false;
|
|
1187
|
+
const relativePath = filePath.replace(fullCompositionsDir, "").replace(/^[/\\]/, "");
|
|
1188
|
+
const instanceUpdates = [];
|
|
1189
|
+
for (const { instance, instanceId } of instances) {
|
|
1190
|
+
const instanceName = instance._id ?? instanceId;
|
|
1191
|
+
const clonedValues = regenerateIds(valuesToPropagate, instanceName);
|
|
1192
|
+
this.compositionService.setInstanceParameters(instance, clonedValues);
|
|
1193
|
+
compositionModified = true;
|
|
1194
|
+
propagatedInstances++;
|
|
1195
|
+
instanceUpdates.push(
|
|
1196
|
+
`${targetComponentType} "${instanceName}": ${Object.keys(valuesToPropagate).join(", ")}`
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1199
|
+
if (compositionModified) {
|
|
1200
|
+
this.logger.action(whatIf, "UPDATE", `composition/${relativePath}`);
|
|
1201
|
+
for (const update of instanceUpdates) {
|
|
1202
|
+
this.logger.detail(`\u2192 ${update}`);
|
|
1203
|
+
}
|
|
1204
|
+
if (!whatIf) {
|
|
1205
|
+
await this.compositionService.saveComposition(filePath, composition);
|
|
1206
|
+
}
|
|
1207
|
+
modifiedCompositions++;
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
let modifiedSourceComponents = 0;
|
|
1211
|
+
if (deleteSourceParameter) {
|
|
1212
|
+
for (const { sourceType, sourceFilePath, sourceComponent } of sourceComponents) {
|
|
1213
|
+
let modifiedSource = { ...sourceComponent };
|
|
1214
|
+
let sourceComponentModified = false;
|
|
1215
|
+
for (const param of resolvedParams) {
|
|
1216
|
+
const exists = this.componentService.findParameter(modifiedSource, param.id, findOptions);
|
|
1217
|
+
if (exists) {
|
|
1218
|
+
this.logger.action(whatIf, "DELETE", `Parameter "${param.id}" from ${sourceType}`);
|
|
1219
|
+
modifiedSource = this.componentService.removeParameter(modifiedSource, param.id, findOptions);
|
|
1220
|
+
sourceComponentModified = true;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
const beforeGroupCount = modifiedSource.parameters?.filter(
|
|
1224
|
+
(p) => this.componentService.isGroupParameter(p)
|
|
1225
|
+
).length ?? 0;
|
|
1226
|
+
modifiedSource = this.componentService.removeEmptyGroups(modifiedSource);
|
|
1227
|
+
const afterGroupCount = modifiedSource.parameters?.filter(
|
|
1228
|
+
(p) => this.componentService.isGroupParameter(p)
|
|
1229
|
+
).length ?? 0;
|
|
1230
|
+
if (afterGroupCount < beforeGroupCount) {
|
|
1231
|
+
const removedCount = beforeGroupCount - afterGroupCount;
|
|
1232
|
+
this.logger.action(
|
|
1233
|
+
whatIf,
|
|
1234
|
+
"DELETE",
|
|
1235
|
+
`${removedCount} empty group(s) from ${sourceType}`
|
|
1236
|
+
);
|
|
1237
|
+
sourceComponentModified = true;
|
|
1238
|
+
}
|
|
1239
|
+
if (sourceComponentModified) {
|
|
1240
|
+
modifiedSourceComponents++;
|
|
1241
|
+
}
|
|
1242
|
+
if (sourceComponentModified && !whatIf) {
|
|
1243
|
+
await this.componentService.saveComponent(sourceFilePath, modifiedSource);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
for (const { composition, filePath } of compositions) {
|
|
1247
|
+
const relativePath = filePath.replace(fullCompositionsDir, "").replace(/^[/\\]/, "");
|
|
1248
|
+
const deleted = this.compositionService.deleteRootOverrides(
|
|
1249
|
+
composition,
|
|
1250
|
+
resolvedNames,
|
|
1251
|
+
findOptions
|
|
1252
|
+
);
|
|
1253
|
+
if (deleted) {
|
|
1254
|
+
this.logger.action(whatIf, "DELETE", `Root overrides from composition/${relativePath}`);
|
|
1255
|
+
this.logger.detail(`\u2192 Removed: ${resolvedNames.join(", ")}`);
|
|
1256
|
+
if (!whatIf) {
|
|
1257
|
+
await this.compositionService.saveComposition(filePath, composition);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
return {
|
|
1263
|
+
modifiedComponents: (componentModified ? 1 : 0) + modifiedSourceComponents,
|
|
1264
|
+
modifiedCompositions,
|
|
1265
|
+
propagatedInstances
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
parsePipeSeparatedValues(value, strict) {
|
|
1269
|
+
const entries = value.split("|").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
1270
|
+
const normalized = [];
|
|
1271
|
+
for (const entry of entries) {
|
|
1272
|
+
const exists = normalized.some(
|
|
1273
|
+
(existing) => strict ? existing === entry : existing.toLowerCase() === entry.toLowerCase()
|
|
1274
|
+
);
|
|
1275
|
+
if (!exists) {
|
|
1276
|
+
normalized.push(entry);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
return normalized;
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
|
|
1283
|
+
// src/cli/logger.ts
|
|
1284
|
+
import chalk from "chalk";
|
|
1285
|
+
var Logger = class {
|
|
1286
|
+
info(message) {
|
|
1287
|
+
console.log(`${chalk.blue("[INFO]")} ${message}`);
|
|
1288
|
+
}
|
|
1289
|
+
success(message) {
|
|
1290
|
+
console.log(`${chalk.green("[DONE]")} ${message}`);
|
|
1291
|
+
}
|
|
1292
|
+
error(message) {
|
|
1293
|
+
console.log(`${chalk.red("[ERROR]")} ${message}`);
|
|
1294
|
+
}
|
|
1295
|
+
warn(message) {
|
|
1296
|
+
console.log(`${chalk.yellow("[WARN]")} ${message}`);
|
|
1297
|
+
}
|
|
1298
|
+
action(whatIf, actionType, message) {
|
|
1299
|
+
const prefix = whatIf ? chalk.yellow("[WOULD]") : chalk.green(`[${actionType}]`);
|
|
1300
|
+
console.log(`${prefix} ${message}`);
|
|
1301
|
+
}
|
|
1302
|
+
detail(message) {
|
|
1303
|
+
console.log(` ${chalk.gray(message)}`);
|
|
1304
|
+
}
|
|
1305
|
+
};
|
|
1306
|
+
|
|
1307
|
+
// src/cli/commands/propagate-root-component-property.ts
|
|
1308
|
+
function createPropagateRootComponentPropertyCommand() {
|
|
1309
|
+
const command = new Command("propagate-root-component-property");
|
|
1310
|
+
command.description(
|
|
1311
|
+
"Copies property definitions from a composition type's root component to a target component type, then propagates the actual values across all matching compositions."
|
|
1312
|
+
).option(
|
|
821
1313
|
"--compositionType <type>",
|
|
822
1314
|
"The composition type(s) to process. Supports pipe-separated lists (e.g., HomePage|LandingPage)"
|
|
823
1315
|
).option("--property <properties>", "Pipe-separated list of properties and/or groups to copy").option(
|
|
@@ -2807,9 +3299,23 @@ var ComponentAdderService = class {
|
|
|
2807
3299
|
throw error;
|
|
2808
3300
|
}
|
|
2809
3301
|
let allowedComponentsUpdated = false;
|
|
3302
|
+
const resolvedParentTypes = [];
|
|
2810
3303
|
for (const parentType of parentTypes) {
|
|
2811
|
-
this.logger.info(`Loading
|
|
2812
|
-
|
|
3304
|
+
this.logger.info(`Loading component: ${parentType}`);
|
|
3305
|
+
let parentComponent;
|
|
3306
|
+
let parentComponentFilePath;
|
|
3307
|
+
try {
|
|
3308
|
+
const result = await this.componentService.loadComponent(fullComponentsDir, parentType, findOptions);
|
|
3309
|
+
parentComponent = result.component;
|
|
3310
|
+
parentComponentFilePath = result.filePath;
|
|
3311
|
+
} catch (error) {
|
|
3312
|
+
if (error instanceof ComponentNotFoundError) {
|
|
3313
|
+
this.logger.warn(`Component not found: ${parentType} (searched: ${fullComponentsDir})`);
|
|
3314
|
+
continue;
|
|
3315
|
+
}
|
|
3316
|
+
throw error;
|
|
3317
|
+
}
|
|
3318
|
+
resolvedParentTypes.push(parentType);
|
|
2813
3319
|
if (!parentComponent.slots) {
|
|
2814
3320
|
parentComponent.slots = [];
|
|
2815
3321
|
}
|
|
@@ -2877,7 +3383,7 @@ var ComponentAdderService = class {
|
|
|
2877
3383
|
);
|
|
2878
3384
|
this.logger.info("");
|
|
2879
3385
|
this.logger.info(
|
|
2880
|
-
`Summary: ${allowedComponentsUpdated ? `${
|
|
3386
|
+
`Summary: ${allowedComponentsUpdated ? `${resolvedParentTypes.length} component definition(s) updated, ` : ""}${compositionsResult} composition(s), ${compositionPatternsResult} composition pattern(s), ${componentPatternsResult} component pattern(s) updated. ${compositionsResult + compositionPatternsResult + componentPatternsResult} instance(s) added.`
|
|
2881
3387
|
);
|
|
2882
3388
|
return {
|
|
2883
3389
|
allowedComponentsUpdated,
|
|
@@ -2926,9 +3432,23 @@ var ComponentAdderService = class {
|
|
|
2926
3432
|
}
|
|
2927
3433
|
const componentTypeInPattern = patternDefinition.type;
|
|
2928
3434
|
let allowedComponentsUpdated = false;
|
|
3435
|
+
const resolvedParentTypes = [];
|
|
2929
3436
|
for (const parentType of parentTypes) {
|
|
2930
|
-
this.logger.info(`Loading
|
|
2931
|
-
|
|
3437
|
+
this.logger.info(`Loading component: ${parentType}`);
|
|
3438
|
+
let parentComponent;
|
|
3439
|
+
let parentComponentFilePath;
|
|
3440
|
+
try {
|
|
3441
|
+
const result = await this.componentService.loadComponent(fullComponentsDir, parentType, findOptions);
|
|
3442
|
+
parentComponent = result.component;
|
|
3443
|
+
parentComponentFilePath = result.filePath;
|
|
3444
|
+
} catch (error) {
|
|
3445
|
+
if (error instanceof ComponentNotFoundError) {
|
|
3446
|
+
this.logger.warn(`Component not found: ${parentType} (searched: ${fullComponentsDir})`);
|
|
3447
|
+
continue;
|
|
3448
|
+
}
|
|
3449
|
+
throw error;
|
|
3450
|
+
}
|
|
3451
|
+
resolvedParentTypes.push(parentType);
|
|
2932
3452
|
if (!parentComponent.slots) {
|
|
2933
3453
|
parentComponent.slots = [];
|
|
2934
3454
|
}
|
|
@@ -2999,7 +3519,7 @@ var ComponentAdderService = class {
|
|
|
2999
3519
|
);
|
|
3000
3520
|
this.logger.info("");
|
|
3001
3521
|
this.logger.info(
|
|
3002
|
-
`Summary: ${allowedComponentsUpdated ? `${
|
|
3522
|
+
`Summary: ${allowedComponentsUpdated ? `${resolvedParentTypes.length} component definition(s) updated, ` : ""}${compositionsResult} composition(s), ${compositionPatternsResult} composition pattern(s), ${componentPatternsResult} component pattern(s) updated. ${compositionsResult + compositionPatternsResult + componentPatternsResult} instance(s) added.`
|
|
3003
3523
|
);
|
|
3004
3524
|
return {
|
|
3005
3525
|
allowedComponentsUpdated,
|
|
@@ -3159,74 +3679,17 @@ var ComponentAdderService = class {
|
|
|
3159
3679
|
}
|
|
3160
3680
|
return modified;
|
|
3161
3681
|
}
|
|
3162
|
-
};
|
|
3163
|
-
|
|
3164
|
-
// src/cli/commands/add-component.ts
|
|
3165
|
-
function createAddComponentCommand() {
|
|
3166
|
-
const command = new Command7("add-component");
|
|
3167
|
-
command.description("Adds a component instance to existing component slots across all compositions.").option("--parentComponentType <type>", "The component type that owns the slot").option("--slot <slotId>", "The slot ID where the component will be added").option("--newComponentType <type>", "The component type to add").option("--parameters <params>", "Pipe-separated parameter assignments (key1:value1|key2:value2)").hook("preAction", (thisCommand) => {
|
|
3168
|
-
const opts = thisCommand.opts();
|
|
3169
|
-
const requiredOptions = [
|
|
3170
|
-
{ name: "parentComponentType", flag: "--parentComponentType" },
|
|
3171
|
-
{ name: "slot", flag: "--slot" },
|
|
3172
|
-
{ name: "newComponentType", flag: "--newComponentType" }
|
|
3173
|
-
];
|
|
3174
|
-
const missing = requiredOptions.filter((opt) => !opts[opt.name]).map((opt) => opt.flag);
|
|
3175
|
-
if (missing.length > 0) {
|
|
3176
|
-
console.error(`error: missing required options: ${missing.join(", ")}`);
|
|
3177
|
-
process.exit(1);
|
|
3178
|
-
}
|
|
3179
|
-
}).action(async (opts, cmd) => {
|
|
3180
|
-
const globalOpts = cmd.optsWithGlobals();
|
|
3181
|
-
const options = {
|
|
3182
|
-
...globalOpts,
|
|
3183
|
-
parentComponentType: opts.parentComponentType,
|
|
3184
|
-
slot: opts.slot,
|
|
3185
|
-
newComponentType: opts.newComponentType,
|
|
3186
|
-
parameters: opts.parameters
|
|
3187
|
-
};
|
|
3188
|
-
const logger = new Logger();
|
|
3189
|
-
const fileSystem = new FileSystemService();
|
|
3190
|
-
const componentService = new ComponentService(fileSystem);
|
|
3191
|
-
const adder = new ComponentAdderService(fileSystem, componentService, logger);
|
|
3192
|
-
try {
|
|
3193
|
-
const result = await adder.addComponent({
|
|
3194
|
-
rootDir: options.rootDir,
|
|
3195
|
-
componentsDir: options.componentsDir,
|
|
3196
|
-
compositionsDir: options.compositionsDir,
|
|
3197
|
-
compositionPatternsDir: options.compositionPatternsDir,
|
|
3198
|
-
componentPatternsDir: options.componentPatternsDir,
|
|
3199
|
-
parentComponentType: options.parentComponentType,
|
|
3200
|
-
slot: options.slot,
|
|
3201
|
-
newComponentType: options.newComponentType,
|
|
3202
|
-
parameters: options.parameters,
|
|
3203
|
-
whatIf: options.whatIf ?? false,
|
|
3204
|
-
strict: options.strict ?? false
|
|
3205
|
-
});
|
|
3206
|
-
logger.success(
|
|
3207
|
-
`Added component: ${result.allowedComponentsUpdated ? "1 component definition updated, " : ""}${result.instancesAdded} instance(s) added across ${result.compositionsModified} composition(s), ${result.compositionPatternsModified} composition pattern(s), ${result.componentPatternsModified} component pattern(s)`
|
|
3208
|
-
);
|
|
3209
|
-
} catch (error) {
|
|
3210
|
-
if (error instanceof TransformError) {
|
|
3211
|
-
logger.error(error.message);
|
|
3212
|
-
process.exit(1);
|
|
3213
|
-
}
|
|
3214
|
-
throw error;
|
|
3215
|
-
}
|
|
3216
|
-
});
|
|
3217
|
-
return command;
|
|
3218
|
-
}
|
|
3682
|
+
};
|
|
3219
3683
|
|
|
3220
|
-
// src/cli/commands/add-component
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
command.description("Adds a component pattern instance to existing component slots across all compositions.").option("--parentComponentType <type>", "The component type that owns the slot").option("--slot <slotId>", "The slot ID where the component pattern will be added").option("--componentPatternId <id>", "The component pattern ID to add").hook("preAction", (thisCommand) => {
|
|
3684
|
+
// src/cli/commands/add-component.ts
|
|
3685
|
+
function createAddComponentCommand() {
|
|
3686
|
+
const command = new Command7("add-component");
|
|
3687
|
+
command.description("Adds a component instance to existing component slots across all compositions.").option("--parentComponentType <type>", "The component type that owns the slot").option("--slot <slotId>", "The slot ID where the component will be added").option("--newComponentType <type>", "The component type to add").option("--parameters <params>", "Pipe-separated parameter assignments (key1:value1|key2:value2)").hook("preAction", (thisCommand) => {
|
|
3225
3688
|
const opts = thisCommand.opts();
|
|
3226
3689
|
const requiredOptions = [
|
|
3227
3690
|
{ name: "parentComponentType", flag: "--parentComponentType" },
|
|
3228
3691
|
{ name: "slot", flag: "--slot" },
|
|
3229
|
-
{ name: "
|
|
3692
|
+
{ name: "newComponentType", flag: "--newComponentType" }
|
|
3230
3693
|
];
|
|
3231
3694
|
const missing = requiredOptions.filter((opt) => !opts[opt.name]).map((opt) => opt.flag);
|
|
3232
3695
|
if (missing.length > 0) {
|
|
@@ -3239,288 +3702,51 @@ function createAddComponentPatternCommand() {
|
|
|
3239
3702
|
...globalOpts,
|
|
3240
3703
|
parentComponentType: opts.parentComponentType,
|
|
3241
3704
|
slot: opts.slot,
|
|
3242
|
-
|
|
3705
|
+
newComponentType: opts.newComponentType,
|
|
3706
|
+
parameters: opts.parameters
|
|
3243
3707
|
};
|
|
3244
3708
|
const logger = new Logger();
|
|
3245
3709
|
const fileSystem = new FileSystemService();
|
|
3246
3710
|
const componentService = new ComponentService(fileSystem);
|
|
3247
3711
|
const adder = new ComponentAdderService(fileSystem, componentService, logger);
|
|
3248
3712
|
try {
|
|
3249
|
-
const result = await adder.
|
|
3713
|
+
const result = await adder.addComponent({
|
|
3250
3714
|
rootDir: options.rootDir,
|
|
3251
|
-
componentsDir: options.componentsDir,
|
|
3252
|
-
compositionsDir: options.compositionsDir,
|
|
3253
|
-
compositionPatternsDir: options.compositionPatternsDir,
|
|
3254
|
-
componentPatternsDir: options.componentPatternsDir,
|
|
3255
|
-
parentComponentType: options.parentComponentType,
|
|
3256
|
-
slot: options.slot,
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
} catch (error) {
|
|
3265
|
-
if (error instanceof TransformError) {
|
|
3266
|
-
logger.error(error.message);
|
|
3267
|
-
process.exit(1);
|
|
3268
|
-
}
|
|
3269
|
-
throw error;
|
|
3270
|
-
}
|
|
3271
|
-
});
|
|
3272
|
-
return command;
|
|
3273
|
-
}
|
|
3274
|
-
|
|
3275
|
-
// src/cli/commands/propagate-root-component-slot.ts
|
|
3276
|
-
import { Command as Command9 } from "commander";
|
|
3277
|
-
|
|
3278
|
-
// src/core/services/slot-propagator.service.ts
|
|
3279
|
-
var SlotPropagatorService = class {
|
|
3280
|
-
constructor(fileSystem, componentService, compositionService, logger) {
|
|
3281
|
-
this.fileSystem = fileSystem;
|
|
3282
|
-
this.componentService = componentService;
|
|
3283
|
-
this.compositionService = compositionService;
|
|
3284
|
-
this.logger = logger;
|
|
3285
|
-
}
|
|
3286
|
-
async propagate(options) {
|
|
3287
|
-
const {
|
|
3288
|
-
rootDir,
|
|
3289
|
-
componentsDir,
|
|
3290
|
-
compositionsDir,
|
|
3291
|
-
compositionType,
|
|
3292
|
-
slot,
|
|
3293
|
-
targetComponentType,
|
|
3294
|
-
targetSlot,
|
|
3295
|
-
whatIf,
|
|
3296
|
-
strict,
|
|
3297
|
-
deleteSourceSlot
|
|
3298
|
-
} = options;
|
|
3299
|
-
const findOptions = { strict };
|
|
3300
|
-
const compositionTypes = this.parsePipeSeparatedValues(compositionType, strict);
|
|
3301
|
-
const fullComponentsDir = this.fileSystem.resolvePath(rootDir, componentsDir);
|
|
3302
|
-
const fullCompositionsDir = this.fileSystem.resolvePath(rootDir, compositionsDir);
|
|
3303
|
-
const sourceComponents = [];
|
|
3304
|
-
for (const sourceType of compositionTypes) {
|
|
3305
|
-
this.logger.info(`Loading component: ${sourceType}`);
|
|
3306
|
-
const { component: sourceComponent, filePath: sourceFilePath } = await this.componentService.loadComponent(fullComponentsDir, sourceType, findOptions);
|
|
3307
|
-
sourceComponents.push({ sourceType, sourceFilePath, sourceComponent });
|
|
3308
|
-
}
|
|
3309
|
-
this.logger.info(`Loading component: ${targetComponentType}`);
|
|
3310
|
-
const { component: targetComponent, filePath: targetFilePath } = await this.componentService.loadComponent(fullComponentsDir, targetComponentType, findOptions);
|
|
3311
|
-
const slotNames = slot.split("|").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
3312
|
-
const resolvedSlots = [];
|
|
3313
|
-
const resolvedSlotIds = [];
|
|
3314
|
-
for (const { sourceType, sourceComponent } of sourceComponents) {
|
|
3315
|
-
const notFound = [];
|
|
3316
|
-
for (const slotName of slotNames) {
|
|
3317
|
-
const slotDef = this.componentService.findSlot(sourceComponent, slotName, findOptions);
|
|
3318
|
-
if (!slotDef) {
|
|
3319
|
-
notFound.push(slotName);
|
|
3320
|
-
continue;
|
|
3321
|
-
}
|
|
3322
|
-
const exists = resolvedSlots.some(
|
|
3323
|
-
(existing) => strict ? existing.id === slotDef.id : existing.id.toLowerCase() === slotDef.id.toLowerCase()
|
|
3324
|
-
);
|
|
3325
|
-
if (!exists) {
|
|
3326
|
-
resolvedSlots.push(slotDef);
|
|
3327
|
-
resolvedSlotIds.push(slotDef.id);
|
|
3328
|
-
}
|
|
3329
|
-
}
|
|
3330
|
-
if (notFound.length > 0) {
|
|
3331
|
-
throw new PropertyNotFoundError(`Slot "${notFound.join(", ")}"`, sourceType);
|
|
3332
|
-
}
|
|
3333
|
-
}
|
|
3334
|
-
this.logger.info(`Resolved slots: ${resolvedSlotIds.join(", ")}`);
|
|
3335
|
-
let modifiedComponent = { ...targetComponent };
|
|
3336
|
-
let componentModified = false;
|
|
3337
|
-
const existingSlot = this.componentService.findSlot(modifiedComponent, targetSlot, findOptions);
|
|
3338
|
-
if (!existingSlot) {
|
|
3339
|
-
const mergedAllowedComponents = this.mergeAllowedComponents(resolvedSlots);
|
|
3340
|
-
this.logger.action(whatIf, "CREATE", `Slot "${targetSlot}" on ${targetComponentType}`);
|
|
3341
|
-
modifiedComponent = this.componentService.addSlot(modifiedComponent, {
|
|
3342
|
-
id: targetSlot,
|
|
3343
|
-
name: targetSlot,
|
|
3344
|
-
allowedComponents: mergedAllowedComponents
|
|
3345
|
-
});
|
|
3346
|
-
componentModified = true;
|
|
3347
|
-
} else {
|
|
3348
|
-
const mergedAllowedComponents = this.mergeAllowedComponents([existingSlot, ...resolvedSlots]);
|
|
3349
|
-
const existingAllowed = existingSlot.allowedComponents ?? [];
|
|
3350
|
-
if (mergedAllowedComponents.length > existingAllowed.length) {
|
|
3351
|
-
this.logger.action(
|
|
3352
|
-
whatIf,
|
|
3353
|
-
"UPDATE",
|
|
3354
|
-
`Slot "${targetSlot}" allowedComponents on ${targetComponentType}`
|
|
3355
|
-
);
|
|
3356
|
-
modifiedComponent = this.componentService.updateSlotAllowedComponents(
|
|
3357
|
-
modifiedComponent,
|
|
3358
|
-
targetSlot,
|
|
3359
|
-
mergedAllowedComponents,
|
|
3360
|
-
findOptions
|
|
3361
|
-
);
|
|
3362
|
-
componentModified = true;
|
|
3363
|
-
} else {
|
|
3364
|
-
this.logger.info(`Slot "${targetSlot}" already exists on ${targetComponentType}`);
|
|
3365
|
-
}
|
|
3366
|
-
}
|
|
3367
|
-
if (componentModified && !whatIf) {
|
|
3368
|
-
await this.componentService.saveComponent(targetFilePath, modifiedComponent);
|
|
3369
|
-
}
|
|
3370
|
-
const compositions = await this.compositionService.findCompositionsByTypes(
|
|
3371
|
-
fullCompositionsDir,
|
|
3372
|
-
compositionTypes,
|
|
3373
|
-
findOptions
|
|
3374
|
-
);
|
|
3375
|
-
let modifiedCompositions = 0;
|
|
3376
|
-
let propagatedInstances = 0;
|
|
3377
|
-
for (const { composition, filePath } of compositions) {
|
|
3378
|
-
const rootSlots = composition.composition.slots ?? {};
|
|
3379
|
-
const instances = this.compositionService.findComponentInstances(
|
|
3380
|
-
composition,
|
|
3381
|
-
targetComponentType,
|
|
3382
|
-
findOptions
|
|
3383
|
-
);
|
|
3384
|
-
if (instances.length === 0) {
|
|
3385
|
-
continue;
|
|
3386
|
-
}
|
|
3387
|
-
const componentsToPropagate = [];
|
|
3388
|
-
for (const slotDef of resolvedSlots) {
|
|
3389
|
-
const slotContent = rootSlots[slotDef.id] ?? [];
|
|
3390
|
-
componentsToPropagate.push(...slotContent);
|
|
3391
|
-
}
|
|
3392
|
-
if (componentsToPropagate.length === 0) {
|
|
3393
|
-
continue;
|
|
3394
|
-
}
|
|
3395
|
-
let compositionModified = false;
|
|
3396
|
-
const relativePath = filePath.replace(fullCompositionsDir, "").replace(/^[/\\]/, "");
|
|
3397
|
-
const instanceUpdates = [];
|
|
3398
|
-
for (const { instance, instanceId } of instances) {
|
|
3399
|
-
const instanceName = instance._id ?? instanceId;
|
|
3400
|
-
const clonedComponents = this.deepCloneComponents(componentsToPropagate);
|
|
3401
|
-
this.addComponentsToInstanceSlot(instance, targetSlot, clonedComponents);
|
|
3402
|
-
compositionModified = true;
|
|
3403
|
-
propagatedInstances++;
|
|
3404
|
-
instanceUpdates.push(
|
|
3405
|
-
`${targetComponentType} "${instanceName}": ${componentsToPropagate.length} component(s) \u2192 ${targetSlot}`
|
|
3406
|
-
);
|
|
3407
|
-
}
|
|
3408
|
-
if (compositionModified) {
|
|
3409
|
-
this.logger.action(whatIf, "UPDATE", `composition/${relativePath}`);
|
|
3410
|
-
for (const update of instanceUpdates) {
|
|
3411
|
-
this.logger.detail(`\u2192 ${update}`);
|
|
3412
|
-
}
|
|
3413
|
-
if (!whatIf) {
|
|
3414
|
-
await this.compositionService.saveComposition(filePath, composition);
|
|
3415
|
-
}
|
|
3416
|
-
modifiedCompositions++;
|
|
3417
|
-
}
|
|
3418
|
-
}
|
|
3419
|
-
let modifiedSourceComponents = 0;
|
|
3420
|
-
if (deleteSourceSlot) {
|
|
3421
|
-
for (const { sourceType, sourceFilePath, sourceComponent } of sourceComponents) {
|
|
3422
|
-
let modifiedSource = { ...sourceComponent };
|
|
3423
|
-
let sourceComponentModified = false;
|
|
3424
|
-
for (const slotDef of resolvedSlots) {
|
|
3425
|
-
const slotToDelete = this.componentService.findSlot(modifiedSource, slotDef.id, findOptions);
|
|
3426
|
-
if (!slotToDelete) {
|
|
3427
|
-
continue;
|
|
3428
|
-
}
|
|
3429
|
-
this.logger.action(whatIf, "DELETE", `Slot "${slotDef.id}" from ${sourceType}`);
|
|
3430
|
-
modifiedSource = this.componentService.removeSlot(modifiedSource, slotDef.id, findOptions);
|
|
3431
|
-
sourceComponentModified = true;
|
|
3432
|
-
}
|
|
3433
|
-
if (sourceComponentModified) {
|
|
3434
|
-
modifiedSourceComponents++;
|
|
3435
|
-
}
|
|
3436
|
-
if (sourceComponentModified && !whatIf) {
|
|
3437
|
-
await this.componentService.saveComponent(sourceFilePath, modifiedSource);
|
|
3438
|
-
}
|
|
3439
|
-
}
|
|
3440
|
-
for (const { composition, filePath } of compositions) {
|
|
3441
|
-
const relativePath = filePath.replace(fullCompositionsDir, "").replace(/^[/\\]/, "");
|
|
3442
|
-
let cleared = false;
|
|
3443
|
-
for (const slotDef of resolvedSlots) {
|
|
3444
|
-
if (composition.composition.slots?.[slotDef.id]?.length) {
|
|
3445
|
-
composition.composition.slots[slotDef.id] = [];
|
|
3446
|
-
cleared = true;
|
|
3447
|
-
}
|
|
3448
|
-
}
|
|
3449
|
-
if (cleared) {
|
|
3450
|
-
this.logger.action(whatIf, "CLEAR", `Root slots in composition/${relativePath}`);
|
|
3451
|
-
this.logger.detail(`\u2192 Cleared: ${resolvedSlotIds.join(", ")}`);
|
|
3452
|
-
if (!whatIf) {
|
|
3453
|
-
await this.compositionService.saveComposition(filePath, composition);
|
|
3454
|
-
}
|
|
3455
|
-
}
|
|
3456
|
-
}
|
|
3457
|
-
}
|
|
3458
|
-
return {
|
|
3459
|
-
modifiedComponents: (componentModified ? 1 : 0) + modifiedSourceComponents,
|
|
3460
|
-
modifiedCompositions,
|
|
3461
|
-
propagatedInstances
|
|
3462
|
-
};
|
|
3463
|
-
}
|
|
3464
|
-
mergeAllowedComponents(slots) {
|
|
3465
|
-
const allowed = /* @__PURE__ */ new Set();
|
|
3466
|
-
for (const slot of slots) {
|
|
3467
|
-
for (const comp of slot.allowedComponents ?? []) {
|
|
3468
|
-
allowed.add(comp);
|
|
3469
|
-
}
|
|
3470
|
-
}
|
|
3471
|
-
return Array.from(allowed).sort();
|
|
3472
|
-
}
|
|
3473
|
-
deepCloneComponents(components) {
|
|
3474
|
-
return JSON.parse(JSON.stringify(components));
|
|
3475
|
-
}
|
|
3476
|
-
addComponentsToInstanceSlot(instance, slotName, components) {
|
|
3477
|
-
if (!instance.slots) {
|
|
3478
|
-
instance.slots = {};
|
|
3479
|
-
}
|
|
3480
|
-
if (!instance.slots[slotName]) {
|
|
3481
|
-
instance.slots[slotName] = [];
|
|
3482
|
-
}
|
|
3483
|
-
instance.slots[slotName].push(...components);
|
|
3484
|
-
}
|
|
3485
|
-
parsePipeSeparatedValues(value, strict) {
|
|
3486
|
-
const entries = value.split("|").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
3487
|
-
const normalized = [];
|
|
3488
|
-
for (const entry of entries) {
|
|
3489
|
-
const exists = normalized.some(
|
|
3490
|
-
(existing) => strict ? existing === entry : existing.toLowerCase() === entry.toLowerCase()
|
|
3715
|
+
componentsDir: options.componentsDir,
|
|
3716
|
+
compositionsDir: options.compositionsDir,
|
|
3717
|
+
compositionPatternsDir: options.compositionPatternsDir,
|
|
3718
|
+
componentPatternsDir: options.componentPatternsDir,
|
|
3719
|
+
parentComponentType: options.parentComponentType,
|
|
3720
|
+
slot: options.slot,
|
|
3721
|
+
newComponentType: options.newComponentType,
|
|
3722
|
+
parameters: options.parameters,
|
|
3723
|
+
whatIf: options.whatIf ?? false,
|
|
3724
|
+
strict: options.strict ?? false
|
|
3725
|
+
});
|
|
3726
|
+
logger.success(
|
|
3727
|
+
`Added component: ${result.allowedComponentsUpdated ? "1 component definition updated, " : ""}${result.instancesAdded} instance(s) added across ${result.compositionsModified} composition(s), ${result.compositionPatternsModified} composition pattern(s), ${result.componentPatternsModified} component pattern(s)`
|
|
3491
3728
|
);
|
|
3492
|
-
|
|
3493
|
-
|
|
3729
|
+
} catch (error) {
|
|
3730
|
+
if (error instanceof TransformError) {
|
|
3731
|
+
logger.error(error.message);
|
|
3732
|
+
process.exit(1);
|
|
3494
3733
|
}
|
|
3734
|
+
throw error;
|
|
3495
3735
|
}
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
}
|
|
3736
|
+
});
|
|
3737
|
+
return command;
|
|
3738
|
+
}
|
|
3499
3739
|
|
|
3500
|
-
// src/cli/commands/
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
command
|
|
3504
|
-
|
|
3505
|
-
).option(
|
|
3506
|
-
"--compositionType <type>",
|
|
3507
|
-
"The composition type(s) to process. Supports pipe-separated lists (e.g., HomePage|LandingPage)"
|
|
3508
|
-
).option("--slot <slots>", "Pipe-separated list of slot names to copy from the source component").option(
|
|
3509
|
-
"--targetComponentType <type>",
|
|
3510
|
-
"The component type that will receive the copied slots"
|
|
3511
|
-
).option(
|
|
3512
|
-
"--targetSlot <slot>",
|
|
3513
|
-
"The slot name on the target component where the contents will be placed"
|
|
3514
|
-
).option(
|
|
3515
|
-
"--deleteSourceSlot",
|
|
3516
|
-
"Delete the original slots from the source component after propagation"
|
|
3517
|
-
).hook("preAction", (thisCommand) => {
|
|
3740
|
+
// src/cli/commands/add-component-pattern.ts
|
|
3741
|
+
import { Command as Command8 } from "commander";
|
|
3742
|
+
function createAddComponentPatternCommand() {
|
|
3743
|
+
const command = new Command8("add-component-pattern");
|
|
3744
|
+
command.description("Adds a component pattern instance to existing component slots across all compositions.").option("--parentComponentType <type>", "The component type that owns the slot").option("--slot <slotId>", "The slot ID where the component pattern will be added").option("--componentPatternId <id>", "The component pattern ID to add").hook("preAction", (thisCommand) => {
|
|
3518
3745
|
const opts = thisCommand.opts();
|
|
3519
3746
|
const requiredOptions = [
|
|
3520
|
-
{ name: "
|
|
3747
|
+
{ name: "parentComponentType", flag: "--parentComponentType" },
|
|
3521
3748
|
{ name: "slot", flag: "--slot" },
|
|
3522
|
-
{ name: "
|
|
3523
|
-
{ name: "targetSlot", flag: "--targetSlot" }
|
|
3749
|
+
{ name: "componentPatternId", flag: "--componentPatternId" }
|
|
3524
3750
|
];
|
|
3525
3751
|
const missing = requiredOptions.filter((opt) => !opts[opt.name]).map((opt) => opt.flag);
|
|
3526
3752
|
if (missing.length > 0) {
|
|
@@ -3531,37 +3757,29 @@ function createPropagateRootComponentSlotCommand() {
|
|
|
3531
3757
|
const globalOpts = cmd.optsWithGlobals();
|
|
3532
3758
|
const options = {
|
|
3533
3759
|
...globalOpts,
|
|
3534
|
-
|
|
3760
|
+
parentComponentType: opts.parentComponentType,
|
|
3535
3761
|
slot: opts.slot,
|
|
3536
|
-
|
|
3537
|
-
targetSlot: opts.targetSlot,
|
|
3538
|
-
deleteSourceSlot: opts.deleteSourceSlot
|
|
3762
|
+
componentPatternId: opts.componentPatternId
|
|
3539
3763
|
};
|
|
3540
3764
|
const logger = new Logger();
|
|
3541
3765
|
const fileSystem = new FileSystemService();
|
|
3542
3766
|
const componentService = new ComponentService(fileSystem);
|
|
3543
|
-
const
|
|
3544
|
-
const propagator = new SlotPropagatorService(
|
|
3545
|
-
fileSystem,
|
|
3546
|
-
componentService,
|
|
3547
|
-
compositionService,
|
|
3548
|
-
logger
|
|
3549
|
-
);
|
|
3767
|
+
const adder = new ComponentAdderService(fileSystem, componentService, logger);
|
|
3550
3768
|
try {
|
|
3551
|
-
const result = await
|
|
3769
|
+
const result = await adder.addComponentPattern({
|
|
3552
3770
|
rootDir: options.rootDir,
|
|
3553
3771
|
componentsDir: options.componentsDir,
|
|
3554
3772
|
compositionsDir: options.compositionsDir,
|
|
3555
|
-
|
|
3773
|
+
compositionPatternsDir: options.compositionPatternsDir,
|
|
3774
|
+
componentPatternsDir: options.componentPatternsDir,
|
|
3775
|
+
parentComponentType: options.parentComponentType,
|
|
3556
3776
|
slot: options.slot,
|
|
3557
|
-
|
|
3558
|
-
targetSlot: options.targetSlot,
|
|
3777
|
+
componentPatternId: options.componentPatternId,
|
|
3559
3778
|
whatIf: options.whatIf ?? false,
|
|
3560
|
-
strict: options.strict ?? false
|
|
3561
|
-
deleteSourceSlot: options.deleteSourceSlot ?? false
|
|
3779
|
+
strict: options.strict ?? false
|
|
3562
3780
|
});
|
|
3563
3781
|
logger.success(
|
|
3564
|
-
`
|
|
3782
|
+
`Added component pattern: ${result.allowedComponentsUpdated ? "1 component definition updated, " : ""}${result.instancesAdded} instance(s) added across ${result.compositionsModified} composition(s), ${result.compositionPatternsModified} composition pattern(s), ${result.componentPatternsModified} component pattern(s)`
|
|
3565
3783
|
);
|
|
3566
3784
|
} catch (error) {
|
|
3567
3785
|
if (error instanceof TransformError) {
|
|
@@ -3574,473 +3792,310 @@ function createPropagateRootComponentSlotCommand() {
|
|
|
3574
3792
|
return command;
|
|
3575
3793
|
}
|
|
3576
3794
|
|
|
3577
|
-
// src/cli/commands/
|
|
3578
|
-
import { Command as
|
|
3795
|
+
// src/cli/commands/propagate-root-component-slot.ts
|
|
3796
|
+
import { Command as Command9 } from "commander";
|
|
3579
3797
|
|
|
3580
|
-
// src/core/services/
|
|
3581
|
-
|
|
3582
|
-
var CompositionConverterService = class {
|
|
3798
|
+
// src/core/services/slot-propagator.service.ts
|
|
3799
|
+
var SlotPropagatorService = class {
|
|
3583
3800
|
constructor(fileSystem, componentService, compositionService, logger) {
|
|
3584
3801
|
this.fileSystem = fileSystem;
|
|
3585
3802
|
this.componentService = componentService;
|
|
3586
3803
|
this.compositionService = compositionService;
|
|
3587
3804
|
this.logger = logger;
|
|
3588
3805
|
}
|
|
3589
|
-
async
|
|
3806
|
+
async propagate(options) {
|
|
3590
3807
|
const {
|
|
3591
3808
|
rootDir,
|
|
3592
|
-
compositionsDir,
|
|
3593
3809
|
componentsDir,
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
const
|
|
3604
|
-
const
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
}
|
|
3613
|
-
const sourceItemMap = flattenComponentIds.length > 0 ? await this.buildSourceItemMap(entriesDirFull) : /* @__PURE__ */ new Map();
|
|
3614
|
-
if (sourceItemMap.size > 0) {
|
|
3615
|
-
this.logger.info(`Found ${sourceItemMap.size} existing entry(ies) with sourceItem values`);
|
|
3616
|
-
}
|
|
3617
|
-
const compositionResults = await this.compositionService.findCompositionsByTypes(
|
|
3618
|
-
compositionsDirFull,
|
|
3619
|
-
compositionTypes,
|
|
3620
|
-
{ strict }
|
|
3621
|
-
);
|
|
3622
|
-
if (compositionResults.length === 0) {
|
|
3623
|
-
this.logger.warn("No compositions found matching the specified types");
|
|
3624
|
-
return { contentTypesWritten: 0, entriesFromCompositions: 0, entriesFromFlattened: 0, entriesReused: 0 };
|
|
3625
|
-
}
|
|
3626
|
-
this.logger.info(`Found ${compositionResults.length} composition(s)`);
|
|
3627
|
-
const rootComponentTypes = /* @__PURE__ */ new Set();
|
|
3628
|
-
for (const { composition } of compositionResults) {
|
|
3629
|
-
rootComponentTypes.add(composition.composition.type);
|
|
3630
|
-
}
|
|
3631
|
-
const contentTypeMap = /* @__PURE__ */ new Map();
|
|
3632
|
-
for (const rootType of rootComponentTypes) {
|
|
3633
|
-
const { component } = await this.componentService.loadComponent(
|
|
3634
|
-
componentsDirFull,
|
|
3635
|
-
rootType,
|
|
3636
|
-
{ strict }
|
|
3637
|
-
);
|
|
3638
|
-
const contentType = this.generateContentType(component);
|
|
3639
|
-
contentTypeMap.set(rootType, contentType);
|
|
3640
|
-
}
|
|
3641
|
-
const flattenContentTypeMap = /* @__PURE__ */ new Map();
|
|
3642
|
-
const missingFlattenTypes = [];
|
|
3643
|
-
const foundMissingFlattenTypes = /* @__PURE__ */ new Set();
|
|
3644
|
-
for (const flattenType of flattenComponentIds) {
|
|
3645
|
-
const isRootType = [...rootComponentTypes].some(
|
|
3646
|
-
(rt) => this.compareTypes(rt, flattenType, strict)
|
|
3647
|
-
);
|
|
3648
|
-
if (isRootType) {
|
|
3649
|
-
continue;
|
|
3650
|
-
}
|
|
3651
|
-
try {
|
|
3652
|
-
const { component } = await this.componentService.loadComponent(
|
|
3653
|
-
componentsDirFull,
|
|
3654
|
-
flattenType,
|
|
3655
|
-
{ strict }
|
|
3656
|
-
);
|
|
3657
|
-
const contentType = this.generateContentType(component);
|
|
3658
|
-
flattenContentTypeMap.set(flattenType, contentType);
|
|
3659
|
-
} catch (error) {
|
|
3660
|
-
if (error instanceof ComponentNotFoundError) {
|
|
3661
|
-
this.logger.info(`Flatten component type not found: ${flattenType}`);
|
|
3662
|
-
missingFlattenTypes.push(flattenType);
|
|
3663
|
-
continue;
|
|
3664
|
-
}
|
|
3665
|
-
throw error;
|
|
3666
|
-
}
|
|
3667
|
-
}
|
|
3668
|
-
for (const { composition } of compositionResults) {
|
|
3669
|
-
const comp = composition.composition;
|
|
3670
|
-
const compositionId = comp._id;
|
|
3671
|
-
const compositionName = comp._name ?? compositionId;
|
|
3672
|
-
const compositionType = comp.type;
|
|
3673
|
-
const entry = this.generateEntryFromComposition(composition);
|
|
3674
|
-
const flattenedByType = /* @__PURE__ */ new Map();
|
|
3675
|
-
if (flattenComponentIds.length > 0 && comp.slots) {
|
|
3676
|
-
for (const flattenType of flattenComponentIds) {
|
|
3677
|
-
if (this.compareTypes(flattenType, compositionType, strict)) {
|
|
3678
|
-
this.logger.warn(
|
|
3679
|
-
`Skipping flatten of "${flattenType}" \u2014 same as root component type`
|
|
3680
|
-
);
|
|
3681
|
-
continue;
|
|
3682
|
-
}
|
|
3683
|
-
const instances = this.findFlattenTargets(
|
|
3684
|
-
comp.slots,
|
|
3685
|
-
flattenType,
|
|
3686
|
-
compositionId,
|
|
3687
|
-
compositionName,
|
|
3688
|
-
strict
|
|
3689
|
-
);
|
|
3690
|
-
if (instances.length > 0) {
|
|
3691
|
-
flattenedByType.set(flattenType, instances);
|
|
3692
|
-
if (missingFlattenTypes.includes(flattenType)) {
|
|
3693
|
-
foundMissingFlattenTypes.add(flattenType);
|
|
3694
|
-
}
|
|
3695
|
-
}
|
|
3696
|
-
}
|
|
3697
|
-
}
|
|
3698
|
-
const resolvedRefIds = /* @__PURE__ */ new Map();
|
|
3699
|
-
for (const [flattenType, instances] of flattenedByType) {
|
|
3700
|
-
const refIds = [];
|
|
3701
|
-
for (const inst of instances) {
|
|
3702
|
-
const existingId = this.findExistingEntryBySourceItem(inst, sourceItemMap);
|
|
3703
|
-
refIds.push(existingId ?? inst.determinisiticId);
|
|
3704
|
-
}
|
|
3705
|
-
resolvedRefIds.set(flattenType, refIds);
|
|
3706
|
-
}
|
|
3707
|
-
for (const [flattenType] of flattenedByType) {
|
|
3708
|
-
entry.entry.fields[flattenType] = {
|
|
3709
|
-
type: "contentReference",
|
|
3710
|
-
value: resolvedRefIds.get(flattenType)
|
|
3711
|
-
};
|
|
3712
|
-
}
|
|
3713
|
-
if (flattenComponentIds.length > 0) {
|
|
3714
|
-
this.transformContentReferences(entry);
|
|
3715
|
-
}
|
|
3716
|
-
const entryId = entry.entry._id;
|
|
3717
|
-
const entryFilePath = this.fileSystem.joinPath(entriesDirFull, `${entryId}.json`);
|
|
3718
|
-
this.logger.action(
|
|
3719
|
-
whatIf,
|
|
3720
|
-
"WRITE",
|
|
3721
|
-
`${entriesDir}/${entryId}.json (${compositionType}, "${this.truncate(compositionName, 50)}")`
|
|
3722
|
-
);
|
|
3723
|
-
if (!whatIf) {
|
|
3724
|
-
await this.fileSystem.writeFile(entryFilePath, entry);
|
|
3725
|
-
}
|
|
3726
|
-
entriesFromCompositions++;
|
|
3727
|
-
for (const [flattenType, instances] of flattenedByType) {
|
|
3728
|
-
for (const inst of instances) {
|
|
3729
|
-
const existingId = this.findExistingEntryBySourceItem(inst, sourceItemMap);
|
|
3730
|
-
if (existingId) {
|
|
3731
|
-
this.logger.info(
|
|
3732
|
-
`Reusing existing entry ${existingId} for ${flattenType} (sourceItem match)`
|
|
3733
|
-
);
|
|
3734
|
-
entriesReused++;
|
|
3735
|
-
continue;
|
|
3736
|
-
}
|
|
3737
|
-
const flatEntry = this.generateEntryFromFlattenedInstance(inst);
|
|
3738
|
-
const flatEntryPath = this.fileSystem.joinPath(
|
|
3739
|
-
entriesDirFull,
|
|
3740
|
-
`${inst.determinisiticId}.json`
|
|
3741
|
-
);
|
|
3742
|
-
this.logger.action(
|
|
3743
|
-
whatIf,
|
|
3744
|
-
"WRITE",
|
|
3745
|
-
`${entriesDir}/${inst.determinisiticId}.json (${flattenType} from "${this.truncate(compositionName, 50)}")`
|
|
3746
|
-
);
|
|
3747
|
-
if (!whatIf) {
|
|
3748
|
-
await this.fileSystem.writeFile(flatEntryPath, flatEntry);
|
|
3749
|
-
}
|
|
3750
|
-
entriesFromFlattened++;
|
|
3751
|
-
}
|
|
3752
|
-
}
|
|
3810
|
+
compositionsDir,
|
|
3811
|
+
compositionType,
|
|
3812
|
+
slot,
|
|
3813
|
+
targetComponentType,
|
|
3814
|
+
targetSlot,
|
|
3815
|
+
whatIf,
|
|
3816
|
+
strict,
|
|
3817
|
+
deleteSourceSlot
|
|
3818
|
+
} = options;
|
|
3819
|
+
const findOptions = { strict };
|
|
3820
|
+
const compositionTypes = this.parsePipeSeparatedValues(compositionType, strict);
|
|
3821
|
+
const fullComponentsDir = this.fileSystem.resolvePath(rootDir, componentsDir);
|
|
3822
|
+
const fullCompositionsDir = this.fileSystem.resolvePath(rootDir, compositionsDir);
|
|
3823
|
+
const sourceComponents = [];
|
|
3824
|
+
for (const sourceType of compositionTypes) {
|
|
3825
|
+
this.logger.info(`Loading component: ${sourceType}`);
|
|
3826
|
+
const { component: sourceComponent, filePath: sourceFilePath } = await this.componentService.loadComponent(fullComponentsDir, sourceType, findOptions);
|
|
3827
|
+
sourceComponents.push({ sourceType, sourceFilePath, sourceComponent });
|
|
3753
3828
|
}
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3829
|
+
this.logger.info(`Loading component: ${targetComponentType}`);
|
|
3830
|
+
const { component: targetComponent, filePath: targetFilePath } = await this.componentService.loadComponent(fullComponentsDir, targetComponentType, findOptions);
|
|
3831
|
+
const slotNames = slot.split("|").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
3832
|
+
const resolvedSlots = [];
|
|
3833
|
+
const resolvedSlotIds = [];
|
|
3834
|
+
for (const { sourceType, sourceComponent } of sourceComponents) {
|
|
3835
|
+
const notFound = [];
|
|
3836
|
+
for (const slotName of slotNames) {
|
|
3837
|
+
const slotDef = this.componentService.findSlot(sourceComponent, slotName, findOptions);
|
|
3838
|
+
if (!slotDef) {
|
|
3839
|
+
notFound.push(slotName);
|
|
3840
|
+
continue;
|
|
3841
|
+
}
|
|
3842
|
+
const exists = resolvedSlots.some(
|
|
3843
|
+
(existing) => strict ? existing.id === slotDef.id : existing.id.toLowerCase() === slotDef.id.toLowerCase()
|
|
3844
|
+
);
|
|
3845
|
+
if (!exists) {
|
|
3846
|
+
resolvedSlots.push(slotDef);
|
|
3847
|
+
resolvedSlotIds.push(slotDef.id);
|
|
3773
3848
|
}
|
|
3774
3849
|
}
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
const filePath = this.fileSystem.joinPath(contentTypesDirFull, `${typeName}.json`);
|
|
3778
|
-
const fieldCount = contentType.fields.filter((f) => f.type !== "contentReference").length;
|
|
3779
|
-
const refCount = contentType.fields.filter((f) => f.type === "contentReference").length;
|
|
3780
|
-
const refInfo = refCount > 0 ? ` + ${refCount} reference(s)` : "";
|
|
3781
|
-
this.logger.action(
|
|
3782
|
-
whatIf,
|
|
3783
|
-
"WRITE",
|
|
3784
|
-
`${contentTypesDir}/${typeName}.json (${fieldCount} fields${refInfo})`
|
|
3785
|
-
);
|
|
3786
|
-
if (!whatIf) {
|
|
3787
|
-
await this.fileSystem.writeFile(filePath, contentType);
|
|
3850
|
+
if (notFound.length > 0) {
|
|
3851
|
+
throw new PropertyNotFoundError(`Slot "${notFound.join(", ")}"`, sourceType);
|
|
3788
3852
|
}
|
|
3789
|
-
contentTypesWritten++;
|
|
3790
3853
|
}
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
);
|
|
3798
|
-
|
|
3799
|
-
|
|
3854
|
+
this.logger.info(`Resolved slots: ${resolvedSlotIds.join(", ")}`);
|
|
3855
|
+
let modifiedComponent = { ...targetComponent };
|
|
3856
|
+
let componentModified = false;
|
|
3857
|
+
const existingSlot = this.componentService.findSlot(modifiedComponent, targetSlot, findOptions);
|
|
3858
|
+
if (!existingSlot) {
|
|
3859
|
+
const mergedAllowedComponents = this.mergeAllowedComponents(resolvedSlots);
|
|
3860
|
+
this.logger.action(whatIf, "CREATE", `Slot "${targetSlot}" on ${targetComponentType}`);
|
|
3861
|
+
modifiedComponent = this.componentService.addSlot(modifiedComponent, {
|
|
3862
|
+
id: targetSlot,
|
|
3863
|
+
name: targetSlot,
|
|
3864
|
+
allowedComponents: mergedAllowedComponents
|
|
3865
|
+
});
|
|
3866
|
+
componentModified = true;
|
|
3867
|
+
} else {
|
|
3868
|
+
const mergedAllowedComponents = this.mergeAllowedComponents([existingSlot, ...resolvedSlots]);
|
|
3869
|
+
const existingAllowed = existingSlot.allowedComponents ?? [];
|
|
3870
|
+
if (mergedAllowedComponents.length > existingAllowed.length) {
|
|
3871
|
+
this.logger.action(
|
|
3872
|
+
whatIf,
|
|
3873
|
+
"UPDATE",
|
|
3874
|
+
`Slot "${targetSlot}" allowedComponents on ${targetComponentType}`
|
|
3875
|
+
);
|
|
3876
|
+
modifiedComponent = this.componentService.updateSlotAllowedComponents(
|
|
3877
|
+
modifiedComponent,
|
|
3878
|
+
targetSlot,
|
|
3879
|
+
mergedAllowedComponents,
|
|
3880
|
+
findOptions
|
|
3881
|
+
);
|
|
3882
|
+
componentModified = true;
|
|
3883
|
+
} else {
|
|
3884
|
+
this.logger.info(`Slot "${targetSlot}" already exists on ${targetComponentType}`);
|
|
3800
3885
|
}
|
|
3801
|
-
contentTypesWritten++;
|
|
3802
3886
|
}
|
|
3803
|
-
|
|
3804
|
-
|
|
3887
|
+
if (componentModified && !whatIf) {
|
|
3888
|
+
await this.componentService.saveComponent(targetFilePath, modifiedComponent);
|
|
3889
|
+
}
|
|
3890
|
+
const compositions = await this.compositionService.findCompositionsByTypes(
|
|
3891
|
+
fullCompositionsDir,
|
|
3892
|
+
compositionTypes,
|
|
3893
|
+
findOptions
|
|
3805
3894
|
);
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3895
|
+
let modifiedCompositions = 0;
|
|
3896
|
+
let propagatedInstances = 0;
|
|
3897
|
+
for (const { composition, filePath } of compositions) {
|
|
3898
|
+
const rootSlots = composition.composition.slots ?? {};
|
|
3899
|
+
const instances = this.compositionService.findComponentInstances(
|
|
3900
|
+
composition,
|
|
3901
|
+
targetComponentType,
|
|
3902
|
+
findOptions
|
|
3809
3903
|
);
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
}
|
|
3813
|
-
// --- Content Type Generation ---
|
|
3814
|
-
generateContentType(component) {
|
|
3815
|
-
const fields = [];
|
|
3816
|
-
if (component.parameters) {
|
|
3817
|
-
for (const param of component.parameters) {
|
|
3818
|
-
fields.push(this.parameterToField(param));
|
|
3904
|
+
if (instances.length === 0) {
|
|
3905
|
+
continue;
|
|
3819
3906
|
}
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
fields
|
|
3825
|
-
};
|
|
3826
|
-
}
|
|
3827
|
-
parameterToField(param) {
|
|
3828
|
-
const field = {
|
|
3829
|
-
id: param.id,
|
|
3830
|
-
name: param.name,
|
|
3831
|
-
type: param.type
|
|
3832
|
-
};
|
|
3833
|
-
if (param.helpText !== void 0) {
|
|
3834
|
-
field.helpText = param.helpText;
|
|
3835
|
-
}
|
|
3836
|
-
if (param.localizable !== void 0) {
|
|
3837
|
-
field.localizable = param.localizable;
|
|
3838
|
-
}
|
|
3839
|
-
if (param.typeConfig !== void 0) {
|
|
3840
|
-
field.typeConfig = param.typeConfig;
|
|
3841
|
-
}
|
|
3842
|
-
return field;
|
|
3843
|
-
}
|
|
3844
|
-
// --- Entry Generation ---
|
|
3845
|
-
generateEntryFromComposition(composition) {
|
|
3846
|
-
const comp = composition.composition;
|
|
3847
|
-
const compositionSpecificKeys = /* @__PURE__ */ new Set(["_id", "_name", "type", "parameters", "slots", "_overrides"]);
|
|
3848
|
-
const extraRootProps = {};
|
|
3849
|
-
for (const [key, value] of Object.entries(comp)) {
|
|
3850
|
-
if (!compositionSpecificKeys.has(key) && value != null) {
|
|
3851
|
-
extraRootProps[key] = value;
|
|
3907
|
+
const componentsToPropagate = [];
|
|
3908
|
+
for (const slotDef of resolvedSlots) {
|
|
3909
|
+
const slotContent = rootSlots[slotDef.id] ?? [];
|
|
3910
|
+
componentsToPropagate.push(...slotContent);
|
|
3852
3911
|
}
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
const extraWrapperProps = {};
|
|
3856
|
-
for (const [key, value] of Object.entries(composition)) {
|
|
3857
|
-
if (!wrapperKeys.has(key) && value != null) {
|
|
3858
|
-
extraWrapperProps[key] = value;
|
|
3912
|
+
if (componentsToPropagate.length === 0) {
|
|
3913
|
+
continue;
|
|
3859
3914
|
}
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
};
|
|
3873
|
-
}
|
|
3874
|
-
generateEntryFromFlattenedInstance(inst) {
|
|
3875
|
-
const entryName = this.truncateName(
|
|
3876
|
-
`${inst.componentType} (from ${inst.compositionName})`,
|
|
3877
|
-
60
|
|
3878
|
-
);
|
|
3879
|
-
return {
|
|
3880
|
-
entry: {
|
|
3881
|
-
_id: inst.determinisiticId,
|
|
3882
|
-
_name: entryName,
|
|
3883
|
-
type: inst.componentType,
|
|
3884
|
-
fields: { ...inst.instance.parameters ?? {} }
|
|
3915
|
+
let compositionModified = false;
|
|
3916
|
+
const relativePath = filePath.replace(fullCompositionsDir, "").replace(/^[/\\]/, "");
|
|
3917
|
+
const instanceUpdates = [];
|
|
3918
|
+
for (const { instance, instanceId } of instances) {
|
|
3919
|
+
const instanceName = instance._id ?? instanceId;
|
|
3920
|
+
const clonedComponents = regenerateIds(componentsToPropagate, `${instanceName}.${targetSlot}`);
|
|
3921
|
+
this.addComponentsToInstanceSlot(instance, targetSlot, clonedComponents);
|
|
3922
|
+
compositionModified = true;
|
|
3923
|
+
propagatedInstances++;
|
|
3924
|
+
instanceUpdates.push(
|
|
3925
|
+
`${targetComponentType} "${instanceName}": ${componentsToPropagate.length} component(s) \u2192 ${targetSlot}`
|
|
3926
|
+
);
|
|
3885
3927
|
}
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3928
|
+
if (compositionModified) {
|
|
3929
|
+
this.logger.action(whatIf, "UPDATE", `composition/${relativePath}`);
|
|
3930
|
+
for (const update of instanceUpdates) {
|
|
3931
|
+
this.logger.detail(`\u2192 ${update}`);
|
|
3932
|
+
}
|
|
3933
|
+
if (!whatIf) {
|
|
3934
|
+
await this.compositionService.saveComposition(filePath, composition);
|
|
3935
|
+
}
|
|
3936
|
+
modifiedCompositions++;
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
let modifiedSourceComponents = 0;
|
|
3940
|
+
if (deleteSourceSlot) {
|
|
3941
|
+
for (const { sourceType, sourceFilePath, sourceComponent } of sourceComponents) {
|
|
3942
|
+
let modifiedSource = { ...sourceComponent };
|
|
3943
|
+
let sourceComponentModified = false;
|
|
3944
|
+
for (const slotDef of resolvedSlots) {
|
|
3945
|
+
const slotToDelete = this.componentService.findSlot(modifiedSource, slotDef.id, findOptions);
|
|
3946
|
+
if (!slotToDelete) {
|
|
3947
|
+
continue;
|
|
3948
|
+
}
|
|
3949
|
+
this.logger.action(whatIf, "DELETE", `Slot "${slotDef.id}" from ${sourceType}`);
|
|
3950
|
+
modifiedSource = this.componentService.removeSlot(modifiedSource, slotDef.id, findOptions);
|
|
3951
|
+
sourceComponentModified = true;
|
|
3901
3952
|
}
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
const fullPath = `${compositionId}-${currentPath}`;
|
|
3905
|
-
const deterministicId = computeGuidHash(fullPath);
|
|
3906
|
-
results.push({
|
|
3907
|
-
instance,
|
|
3908
|
-
path: fullPath,
|
|
3909
|
-
determinisiticId: deterministicId,
|
|
3910
|
-
componentType: instance.type,
|
|
3911
|
-
compositionId,
|
|
3912
|
-
compositionName
|
|
3913
|
-
});
|
|
3953
|
+
if (sourceComponentModified) {
|
|
3954
|
+
modifiedSourceComponents++;
|
|
3914
3955
|
}
|
|
3915
|
-
if (
|
|
3916
|
-
this.
|
|
3917
|
-
instance.slots,
|
|
3918
|
-
targetType,
|
|
3919
|
-
compositionId,
|
|
3920
|
-
compositionName,
|
|
3921
|
-
currentPath,
|
|
3922
|
-
results,
|
|
3923
|
-
strict
|
|
3924
|
-
);
|
|
3956
|
+
if (sourceComponentModified && !whatIf) {
|
|
3957
|
+
await this.componentService.saveComponent(sourceFilePath, modifiedSource);
|
|
3925
3958
|
}
|
|
3926
3959
|
}
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
const entryIds = field.value;
|
|
3935
|
-
const resourceKey = `ref-${entry.entry._id}-${fieldName}`;
|
|
3936
|
-
field.value = `\${#jptr:/${resourceKey}/entries}`;
|
|
3937
|
-
dataResources[resourceKey] = {
|
|
3938
|
-
type: "uniformContentInternalReference",
|
|
3939
|
-
variables: {
|
|
3940
|
-
locale: "${locale}",
|
|
3941
|
-
entryIds: entryIds.join(",")
|
|
3960
|
+
for (const { composition, filePath } of compositions) {
|
|
3961
|
+
const relativePath = filePath.replace(fullCompositionsDir, "").replace(/^[/\\]/, "");
|
|
3962
|
+
let cleared = false;
|
|
3963
|
+
for (const slotDef of resolvedSlots) {
|
|
3964
|
+
if (composition.composition.slots?.[slotDef.id]?.length) {
|
|
3965
|
+
composition.composition.slots[slotDef.id] = [];
|
|
3966
|
+
cleared = true;
|
|
3942
3967
|
}
|
|
3943
|
-
}
|
|
3968
|
+
}
|
|
3969
|
+
if (cleared) {
|
|
3970
|
+
this.logger.action(whatIf, "CLEAR", `Root slots in composition/${relativePath}`);
|
|
3971
|
+
this.logger.detail(`\u2192 Cleared: ${resolvedSlotIds.join(", ")}`);
|
|
3972
|
+
if (!whatIf) {
|
|
3973
|
+
await this.compositionService.saveComposition(filePath, composition);
|
|
3974
|
+
}
|
|
3975
|
+
}
|
|
3944
3976
|
}
|
|
3945
3977
|
}
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
}
|
|
3978
|
+
return {
|
|
3979
|
+
modifiedComponents: (componentModified ? 1 : 0) + modifiedSourceComponents,
|
|
3980
|
+
modifiedCompositions,
|
|
3981
|
+
propagatedInstances
|
|
3982
|
+
};
|
|
3952
3983
|
}
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
const
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
try {
|
|
3959
|
-
const entryData = await this.fileSystem.readFile(filePath);
|
|
3960
|
-
const sourceItemField = entryData?.entry?.fields?.sourceItem;
|
|
3961
|
-
if (sourceItemField?.value != null) {
|
|
3962
|
-
sourceItemMap.set(String(sourceItemField.value), entryData.entry._id);
|
|
3963
|
-
}
|
|
3964
|
-
} catch {
|
|
3965
|
-
continue;
|
|
3984
|
+
mergeAllowedComponents(slots) {
|
|
3985
|
+
const allowed = /* @__PURE__ */ new Set();
|
|
3986
|
+
for (const slot of slots) {
|
|
3987
|
+
for (const comp of slot.allowedComponents ?? []) {
|
|
3988
|
+
allowed.add(comp);
|
|
3966
3989
|
}
|
|
3967
3990
|
}
|
|
3968
|
-
return
|
|
3991
|
+
return Array.from(allowed).sort();
|
|
3969
3992
|
}
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
if (sourceItemParam?.value == null) {
|
|
3973
|
-
return void 0;
|
|
3974
|
-
}
|
|
3975
|
-
return sourceItemMap.get(String(sourceItemParam.value));
|
|
3993
|
+
deepCloneComponents(components) {
|
|
3994
|
+
return JSON.parse(JSON.stringify(components));
|
|
3976
3995
|
}
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
return type1 === type2;
|
|
3996
|
+
addComponentsToInstanceSlot(instance, slotName, components) {
|
|
3997
|
+
if (!instance.slots) {
|
|
3998
|
+
instance.slots = {};
|
|
3981
3999
|
}
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
return str.substring(0, maxLength - 3) + "...";
|
|
4000
|
+
if (!instance.slots[slotName]) {
|
|
4001
|
+
instance.slots[slotName] = [];
|
|
4002
|
+
}
|
|
4003
|
+
instance.slots[slotName].push(...components);
|
|
3987
4004
|
}
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
4005
|
+
parsePipeSeparatedValues(value, strict) {
|
|
4006
|
+
const entries = value.split("|").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
4007
|
+
const normalized = [];
|
|
4008
|
+
for (const entry of entries) {
|
|
4009
|
+
const exists = normalized.some(
|
|
4010
|
+
(existing) => strict ? existing === entry : existing.toLowerCase() === entry.toLowerCase()
|
|
4011
|
+
);
|
|
4012
|
+
if (!exists) {
|
|
4013
|
+
normalized.push(entry);
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
return normalized;
|
|
3991
4017
|
}
|
|
3992
4018
|
};
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
"
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4019
|
+
|
|
4020
|
+
// src/cli/commands/propagate-root-component-slot.ts
|
|
4021
|
+
function createPropagateRootComponentSlotCommand() {
|
|
4022
|
+
const command = new Command9("propagate-root-component-slot");
|
|
4023
|
+
command.description(
|
|
4024
|
+
"Copies slot definitions from a composition type's root component to a target component type, then moves all component instances from that slot across all matching compositions."
|
|
4025
|
+
).option(
|
|
4026
|
+
"--compositionType <type>",
|
|
4027
|
+
"The composition type(s) to process. Supports pipe-separated lists (e.g., HomePage|LandingPage)"
|
|
4028
|
+
).option("--slot <slots>", "Pipe-separated list of slot names to copy from the source component").option(
|
|
4029
|
+
"--targetComponentType <type>",
|
|
4030
|
+
"The component type that will receive the copied slots"
|
|
4031
|
+
).option(
|
|
4032
|
+
"--targetSlot <slot>",
|
|
4033
|
+
"The slot name on the target component where the contents will be placed"
|
|
4034
|
+
).option(
|
|
4035
|
+
"--deleteSourceSlot",
|
|
4036
|
+
"Delete the original slots from the source component after propagation"
|
|
4037
|
+
).hook("preAction", (thisCommand) => {
|
|
4038
|
+
const opts = thisCommand.opts();
|
|
4039
|
+
const requiredOptions = [
|
|
4040
|
+
{ name: "compositionType", flag: "--compositionType" },
|
|
4041
|
+
{ name: "slot", flag: "--slot" },
|
|
4042
|
+
{ name: "targetComponentType", flag: "--targetComponentType" },
|
|
4043
|
+
{ name: "targetSlot", flag: "--targetSlot" }
|
|
4044
|
+
];
|
|
4045
|
+
const missing = requiredOptions.filter((opt) => !opts[opt.name]).map((opt) => opt.flag);
|
|
4046
|
+
if (missing.length > 0) {
|
|
4047
|
+
console.error(`error: missing required options: ${missing.join(", ")}`);
|
|
4048
|
+
process.exit(1);
|
|
4049
|
+
}
|
|
4050
|
+
}).action(async (opts, cmd) => {
|
|
4051
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
4052
|
+
const options = {
|
|
4053
|
+
...globalOpts,
|
|
4054
|
+
compositionType: opts.compositionType,
|
|
4055
|
+
slot: opts.slot,
|
|
4056
|
+
targetComponentType: opts.targetComponentType,
|
|
4057
|
+
targetSlot: opts.targetSlot,
|
|
4058
|
+
deleteSourceSlot: opts.deleteSourceSlot
|
|
4059
|
+
};
|
|
4060
|
+
const logger = new Logger();
|
|
4061
|
+
const fileSystem = new FileSystemService();
|
|
4062
|
+
const componentService = new ComponentService(fileSystem);
|
|
4063
|
+
const compositionService = new CompositionService(fileSystem);
|
|
4064
|
+
const propagator = new SlotPropagatorService(
|
|
4065
|
+
fileSystem,
|
|
4066
|
+
componentService,
|
|
4067
|
+
compositionService,
|
|
4068
|
+
logger
|
|
4069
|
+
);
|
|
4070
|
+
try {
|
|
4071
|
+
const result = await propagator.propagate({
|
|
4072
|
+
rootDir: options.rootDir,
|
|
4073
|
+
componentsDir: options.componentsDir,
|
|
4074
|
+
compositionsDir: options.compositionsDir,
|
|
4075
|
+
compositionType: options.compositionType,
|
|
4076
|
+
slot: options.slot,
|
|
4077
|
+
targetComponentType: options.targetComponentType,
|
|
4078
|
+
targetSlot: options.targetSlot,
|
|
4079
|
+
whatIf: options.whatIf ?? false,
|
|
4080
|
+
strict: options.strict ?? false,
|
|
4081
|
+
deleteSourceSlot: options.deleteSourceSlot ?? false
|
|
4082
|
+
});
|
|
4083
|
+
logger.success(
|
|
4084
|
+
`Modified ${result.modifiedComponents} component(s), ${result.modifiedCompositions} composition(s)`
|
|
4085
|
+
);
|
|
4086
|
+
} catch (error) {
|
|
4087
|
+
if (error instanceof TransformError) {
|
|
4088
|
+
logger.error(error.message);
|
|
4089
|
+
process.exit(1);
|
|
4090
|
+
}
|
|
4091
|
+
throw error;
|
|
4092
|
+
}
|
|
4093
|
+
});
|
|
4094
|
+
return command;
|
|
4041
4095
|
}
|
|
4042
4096
|
|
|
4043
4097
|
// src/cli/commands/convert-compositions-to-entries.ts
|
|
4098
|
+
import { Command as Command10 } from "commander";
|
|
4044
4099
|
function createConvertCompositionsToEntriesCommand() {
|
|
4045
4100
|
const command = new Command10("convert-compositions-to-entries");
|
|
4046
4101
|
command.description(
|
|
@@ -4649,7 +4704,7 @@ function createRemoveFieldCommand() {
|
|
|
4649
4704
|
// package.json
|
|
4650
4705
|
var package_default = {
|
|
4651
4706
|
name: "@uniformdev/transformer",
|
|
4652
|
-
version: "1.1.
|
|
4707
|
+
version: "1.1.18",
|
|
4653
4708
|
description: "CLI tool for transforming Uniform.dev serialization files offline",
|
|
4654
4709
|
type: "module",
|
|
4655
4710
|
bin: {
|