@uniformdev/transformer 1.1.3 → 1.1.5

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 CHANGED
@@ -372,13 +372,22 @@ var CompositionService = class {
372
372
  await this.fileSystem.writeFile(filePath, composition);
373
373
  }
374
374
  async findCompositionsByType(compositionsDir, compositionType, options = {}) {
375
+ return this.findCompositionsByTypes(compositionsDir, [compositionType], options);
376
+ }
377
+ async findCompositionsByTypes(compositionsDir, compositionTypes, options = {}) {
375
378
  const { strict = false } = options;
379
+ const normalizedTypes = this.normalizeCompositionTypes(compositionTypes, strict);
380
+ if (normalizedTypes.length === 0) {
381
+ return [];
382
+ }
376
383
  const files = await this.fileSystem.findFiles(compositionsDir, "**/*.{json,yaml,yml}");
377
384
  const results = [];
378
385
  for (const filePath of files) {
379
386
  try {
380
387
  const composition = await this.loadComposition(filePath);
381
- if (composition.composition?.type && this.compareTypes(composition.composition.type, compositionType, strict)) {
388
+ if (composition.composition?.type && normalizedTypes.some(
389
+ (type) => this.compareTypes(composition.composition.type, type, strict)
390
+ )) {
382
391
  results.push({ composition, filePath });
383
392
  }
384
393
  } catch {
@@ -386,6 +395,22 @@ var CompositionService = class {
386
395
  }
387
396
  return results;
388
397
  }
398
+ normalizeCompositionTypes(compositionTypes, strict) {
399
+ const normalized = [];
400
+ for (const type of compositionTypes) {
401
+ const trimmed = type.trim();
402
+ if (!trimmed) {
403
+ continue;
404
+ }
405
+ const exists = normalized.some(
406
+ (existing) => strict ? existing === trimmed : existing.toLowerCase() === trimmed.toLowerCase()
407
+ );
408
+ if (!exists) {
409
+ normalized.push(trimmed);
410
+ }
411
+ }
412
+ return normalized;
413
+ }
389
414
  findComponentInstances(composition, componentType, options = {}) {
390
415
  const { strict = false } = options;
391
416
  const results = [];
@@ -510,26 +535,51 @@ var PropertyPropagatorService = class {
510
535
  deleteSourceParameter
511
536
  } = options;
512
537
  const findOptions = { strict };
538
+ const compositionTypes = this.parsePipeSeparatedValues(compositionType, strict);
513
539
  const fullComponentsDir = this.fileSystem.resolvePath(rootDir, componentsDir);
514
540
  const fullCompositionsDir = this.fileSystem.resolvePath(rootDir, compositionsDir);
515
- this.logger.info(`Loading component: ${compositionType}`);
516
- const { component: sourceComponent, filePath: sourceFilePath } = await this.componentService.loadComponent(fullComponentsDir, compositionType, findOptions);
541
+ const sourceComponents = [];
542
+ for (const sourceType of compositionTypes) {
543
+ this.logger.info(`Loading component: ${sourceType}`);
544
+ const { component: sourceComponent, filePath: sourceFilePath } = await this.componentService.loadComponent(fullComponentsDir, sourceType, findOptions);
545
+ sourceComponents.push({ sourceType, sourceFilePath, sourceComponent });
546
+ }
517
547
  this.logger.info(`Loading component: ${targetComponentType}`);
518
548
  const { component: targetComponent, filePath: targetFilePath } = await this.componentService.loadComponent(fullComponentsDir, targetComponentType, findOptions);
519
- const propertyNames = property.split("|").map((p) => p.trim());
520
- const { parameters: resolvedParams, notFound } = this.componentService.resolveProperties(
521
- sourceComponent,
522
- propertyNames,
523
- findOptions
524
- );
525
- if (notFound.length > 0) {
526
- throw new PropertyNotFoundError(notFound.join(", "), compositionType);
549
+ const propertyNames = property.split("|").map((p) => p.trim()).filter((p) => p.length > 0);
550
+ const resolvedParams = [];
551
+ const resolvedNames = [];
552
+ for (const { sourceType, sourceComponent } of sourceComponents) {
553
+ const { parameters: sourceParams, notFound } = this.componentService.resolveProperties(
554
+ sourceComponent,
555
+ propertyNames,
556
+ findOptions
557
+ );
558
+ if (notFound.length > 0) {
559
+ throw new PropertyNotFoundError(notFound.join(", "), sourceType);
560
+ }
561
+ for (const param of sourceParams) {
562
+ const exists = resolvedParams.some(
563
+ (existing) => strict ? existing.id === param.id : existing.id.toLowerCase() === param.id.toLowerCase()
564
+ );
565
+ if (!exists) {
566
+ resolvedParams.push(param);
567
+ resolvedNames.push(param.id);
568
+ }
569
+ }
570
+ }
571
+ const groupSources = [];
572
+ for (const { sourceType, sourceComponent } of sourceComponents) {
573
+ for (const name of propertyNames) {
574
+ const param = this.componentService.findParameter(sourceComponent, name, findOptions);
575
+ if (param && this.componentService.isGroupParameter(param)) {
576
+ const groupSource = `${sourceType}.${name}`;
577
+ if (!groupSources.includes(groupSource)) {
578
+ groupSources.push(groupSource);
579
+ }
580
+ }
581
+ }
527
582
  }
528
- const resolvedNames = resolvedParams.map((p) => p.id);
529
- const groupSources = propertyNames.filter((name) => {
530
- const param = this.componentService.findParameter(sourceComponent, name, findOptions);
531
- return param && this.componentService.isGroupParameter(param);
532
- });
533
583
  if (groupSources.length > 0) {
534
584
  this.logger.info(
535
585
  `Resolved properties: ${resolvedNames.join(", ")} (from ${groupSources.join(", ")})`
@@ -604,9 +654,9 @@ var PropertyPropagatorService = class {
604
654
  if (componentModified && !whatIf) {
605
655
  await this.componentService.saveComponent(targetFilePath, modifiedComponent);
606
656
  }
607
- const compositions = await this.compositionService.findCompositionsByType(
657
+ const compositions = await this.compositionService.findCompositionsByTypes(
608
658
  fullCompositionsDir,
609
- compositionType,
659
+ compositionTypes,
610
660
  findOptions
611
661
  );
612
662
  let modifiedCompositions = 0;
@@ -653,31 +703,41 @@ var PropertyPropagatorService = class {
653
703
  modifiedCompositions++;
654
704
  }
655
705
  }
656
- let sourceComponentModified = false;
706
+ let modifiedSourceComponents = 0;
657
707
  if (deleteSourceParameter) {
658
- let modifiedSource = { ...sourceComponent };
659
- for (const param of resolvedParams) {
660
- this.logger.action(whatIf, "DELETE", `Parameter "${param.id}" from ${compositionType}`);
661
- modifiedSource = this.componentService.removeParameter(modifiedSource, param.id, findOptions);
662
- sourceComponentModified = true;
663
- }
664
- const beforeGroupCount = modifiedSource.parameters?.filter(
665
- (p) => this.componentService.isGroupParameter(p)
666
- ).length ?? 0;
667
- modifiedSource = this.componentService.removeEmptyGroups(modifiedSource);
668
- const afterGroupCount = modifiedSource.parameters?.filter(
669
- (p) => this.componentService.isGroupParameter(p)
670
- ).length ?? 0;
671
- if (afterGroupCount < beforeGroupCount) {
672
- const removedCount = beforeGroupCount - afterGroupCount;
673
- this.logger.action(
674
- whatIf,
675
- "DELETE",
676
- `${removedCount} empty group(s) from ${compositionType}`
677
- );
678
- }
679
- if (sourceComponentModified && !whatIf) {
680
- await this.componentService.saveComponent(sourceFilePath, modifiedSource);
708
+ for (const { sourceType, sourceFilePath, sourceComponent } of sourceComponents) {
709
+ let modifiedSource = { ...sourceComponent };
710
+ let sourceComponentModified = false;
711
+ for (const param of resolvedParams) {
712
+ const exists = this.componentService.findParameter(modifiedSource, param.id, findOptions);
713
+ if (exists) {
714
+ this.logger.action(whatIf, "DELETE", `Parameter "${param.id}" from ${sourceType}`);
715
+ modifiedSource = this.componentService.removeParameter(modifiedSource, param.id, findOptions);
716
+ sourceComponentModified = true;
717
+ }
718
+ }
719
+ const beforeGroupCount = modifiedSource.parameters?.filter(
720
+ (p) => this.componentService.isGroupParameter(p)
721
+ ).length ?? 0;
722
+ modifiedSource = this.componentService.removeEmptyGroups(modifiedSource);
723
+ const afterGroupCount = modifiedSource.parameters?.filter(
724
+ (p) => this.componentService.isGroupParameter(p)
725
+ ).length ?? 0;
726
+ if (afterGroupCount < beforeGroupCount) {
727
+ const removedCount = beforeGroupCount - afterGroupCount;
728
+ this.logger.action(
729
+ whatIf,
730
+ "DELETE",
731
+ `${removedCount} empty group(s) from ${sourceType}`
732
+ );
733
+ sourceComponentModified = true;
734
+ }
735
+ if (sourceComponentModified) {
736
+ modifiedSourceComponents++;
737
+ }
738
+ if (sourceComponentModified && !whatIf) {
739
+ await this.componentService.saveComponent(sourceFilePath, modifiedSource);
740
+ }
681
741
  }
682
742
  for (const { composition, filePath } of compositions) {
683
743
  const relativePath = filePath.replace(fullCompositionsDir, "").replace(/^[/\\]/, "");
@@ -696,11 +756,24 @@ var PropertyPropagatorService = class {
696
756
  }
697
757
  }
698
758
  return {
699
- modifiedComponents: (componentModified ? 1 : 0) + (sourceComponentModified ? 1 : 0),
759
+ modifiedComponents: (componentModified ? 1 : 0) + modifiedSourceComponents,
700
760
  modifiedCompositions,
701
761
  propagatedInstances
702
762
  };
703
763
  }
764
+ parsePipeSeparatedValues(value, strict) {
765
+ const entries = value.split("|").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
766
+ const normalized = [];
767
+ for (const entry of entries) {
768
+ const exists = normalized.some(
769
+ (existing) => strict ? existing === entry : existing.toLowerCase() === entry.toLowerCase()
770
+ );
771
+ if (!exists) {
772
+ normalized.push(entry);
773
+ }
774
+ }
775
+ return normalized;
776
+ }
704
777
  };
705
778
 
706
779
  // src/cli/logger.ts
@@ -732,7 +805,10 @@ function createPropagateRootComponentPropertyCommand() {
732
805
  const command = new Command("propagate-root-component-property");
733
806
  command.description(
734
807
  "Copies property definitions from a composition type's root component to a target component type, then propagates the actual values across all matching compositions."
735
- ).option("--compositionType <type>", "The composition type to process (e.g., HomePage)").option("--property <properties>", "Pipe-separated list of properties and/or groups to copy").option(
808
+ ).option(
809
+ "--compositionType <type>",
810
+ "The composition type(s) to process. Supports pipe-separated lists (e.g., HomePage|LandingPage)"
811
+ ).option("--property <properties>", "Pipe-separated list of properties and/or groups to copy").option(
736
812
  "--targetComponentType <type>",
737
813
  "The component type that will receive the copied properties"
738
814
  ).option(
@@ -2422,6 +2498,12 @@ var ComponentAdderService = class {
2422
2498
  }
2423
2499
  return id1.toLowerCase() === id2.toLowerCase();
2424
2500
  }
2501
+ parseParentComponentTypes(parentComponentType) {
2502
+ return parentComponentType.split("|").map((t) => t.trim()).filter((t) => t.length > 0);
2503
+ }
2504
+ matchesAnyParentType(instanceType, parentTypes, strict) {
2505
+ return parentTypes.some((pt) => this.compareIds(instanceType, pt, strict));
2506
+ }
2425
2507
  parseParameters(parameterString) {
2426
2508
  const result = {};
2427
2509
  if (!parameterString) return result;
@@ -2495,18 +2577,9 @@ var ComponentAdderService = class {
2495
2577
  const fullCompositionPatternsDir = this.fileSystem.resolvePath(rootDir, compositionPatternsDir);
2496
2578
  const fullComponentPatternsDir = this.fileSystem.resolvePath(rootDir, componentPatternsDir);
2497
2579
  const findOptions = { strict };
2498
- this.logger.info(`Loading parent component: ${parentComponentType}`);
2499
- const { component: parentComponent, filePath: parentComponentFilePath } = await this.componentService.loadComponent(fullComponentsDir, parentComponentType, findOptions);
2500
- if (!parentComponent.slots) {
2501
- parentComponent.slots = [];
2502
- }
2503
- let slotDef = parentComponent.slots.find(
2504
- (s) => this.compareIds(s.id, slot, strict)
2505
- );
2506
- if (!slotDef) {
2507
- this.logger.info(`Slot "${slot}" not found on component "${parentComponentType}", creating it`);
2508
- slotDef = { id: slot, name: slot, allowedComponents: [] };
2509
- parentComponent.slots.push(slotDef);
2580
+ const parentTypes = this.parseParentComponentTypes(parentComponentType);
2581
+ if (parentTypes.length === 0) {
2582
+ throw new TransformError("parentComponentType cannot be empty");
2510
2583
  }
2511
2584
  this.logger.info(`Validating new component: ${newComponentType}`);
2512
2585
  try {
@@ -2520,35 +2593,50 @@ var ComponentAdderService = class {
2520
2593
  throw error;
2521
2594
  }
2522
2595
  let allowedComponentsUpdated = false;
2523
- const slotIndex = parentComponent.slots?.findIndex(
2524
- (s) => this.compareIds(s.id, slotDef.id, strict)
2525
- );
2526
- if (slotIndex !== void 0 && slotIndex >= 0) {
2527
- const actualSlot = parentComponent.slots[slotIndex];
2528
- if (!actualSlot.allowedComponents) {
2529
- actualSlot.allowedComponents = [];
2596
+ for (const parentType of parentTypes) {
2597
+ this.logger.info(`Loading parent component: ${parentType}`);
2598
+ const { component: parentComponent, filePath: parentComponentFilePath } = await this.componentService.loadComponent(fullComponentsDir, parentType, findOptions);
2599
+ if (!parentComponent.slots) {
2600
+ parentComponent.slots = [];
2601
+ }
2602
+ let slotDef = parentComponent.slots.find(
2603
+ (s) => this.compareIds(s.id, slot, strict)
2604
+ );
2605
+ if (!slotDef) {
2606
+ this.logger.info(`Slot "${slot}" not found on component "${parentType}", creating it`);
2607
+ slotDef = { id: slot, name: slot, allowedComponents: [] };
2608
+ parentComponent.slots.push(slotDef);
2530
2609
  }
2531
- const isAlreadyAllowed = actualSlot.allowedComponents.some(
2532
- (c) => this.compareIds(c, newComponentType, strict)
2610
+ const slotIndex = parentComponent.slots?.findIndex(
2611
+ (s) => this.compareIds(s.id, slotDef.id, strict)
2533
2612
  );
2534
- if (!isAlreadyAllowed) {
2535
- this.logger.action(
2536
- whatIf,
2537
- "UPDATE",
2538
- `Adding "${newComponentType}" to allowedComponents for slot "${slot}" on component "${parentComponentType}"`
2613
+ if (slotIndex !== void 0 && slotIndex >= 0) {
2614
+ const actualSlot = parentComponent.slots[slotIndex];
2615
+ if (!actualSlot.allowedComponents) {
2616
+ actualSlot.allowedComponents = [];
2617
+ }
2618
+ const isAlreadyAllowed = actualSlot.allowedComponents.some(
2619
+ (c) => this.compareIds(c, newComponentType, strict)
2539
2620
  );
2540
- actualSlot.allowedComponents.push(newComponentType);
2541
- if (!whatIf) {
2542
- await this.componentService.saveComponent(parentComponentFilePath, parentComponent);
2621
+ if (!isAlreadyAllowed) {
2622
+ this.logger.action(
2623
+ whatIf,
2624
+ "UPDATE",
2625
+ `Adding "${newComponentType}" to allowedComponents for slot "${slot}" on component "${parentType}"`
2626
+ );
2627
+ actualSlot.allowedComponents.push(newComponentType);
2628
+ if (!whatIf) {
2629
+ await this.componentService.saveComponent(parentComponentFilePath, parentComponent);
2630
+ }
2631
+ allowedComponentsUpdated = true;
2543
2632
  }
2544
- allowedComponentsUpdated = true;
2545
2633
  }
2546
2634
  }
2547
2635
  const parsedParams = this.parseParameters(paramString);
2548
2636
  const newInstance = this.createComponentInstance(newComponentType, parsedParams);
2549
2637
  const compositionsResult = await this.addComponentToDirectory(
2550
2638
  fullCompositionsDir,
2551
- parentComponentType,
2639
+ parentTypes,
2552
2640
  slot,
2553
2641
  newInstance,
2554
2642
  whatIf,
@@ -2557,7 +2645,7 @@ var ComponentAdderService = class {
2557
2645
  );
2558
2646
  const compositionPatternsResult = await this.addComponentToDirectory(
2559
2647
  fullCompositionPatternsDir,
2560
- parentComponentType,
2648
+ parentTypes,
2561
2649
  slot,
2562
2650
  newInstance,
2563
2651
  whatIf,
@@ -2566,7 +2654,7 @@ var ComponentAdderService = class {
2566
2654
  );
2567
2655
  const componentPatternsResult = await this.addComponentToDirectory(
2568
2656
  fullComponentPatternsDir,
2569
- parentComponentType,
2657
+ parentTypes,
2570
2658
  slot,
2571
2659
  newInstance,
2572
2660
  whatIf,
@@ -2575,7 +2663,7 @@ var ComponentAdderService = class {
2575
2663
  );
2576
2664
  this.logger.info("");
2577
2665
  this.logger.info(
2578
- `Summary: ${allowedComponentsUpdated ? "1 component definition updated, " : ""}${compositionsResult} composition(s), ${compositionPatternsResult} composition pattern(s), ${componentPatternsResult} component pattern(s) updated. ${compositionsResult + compositionPatternsResult + componentPatternsResult} instance(s) added.`
2666
+ `Summary: ${allowedComponentsUpdated ? `${parentTypes.length} component definition(s) updated, ` : ""}${compositionsResult} composition(s), ${compositionPatternsResult} composition pattern(s), ${componentPatternsResult} component pattern(s) updated. ${compositionsResult + compositionPatternsResult + componentPatternsResult} instance(s) added.`
2579
2667
  );
2580
2668
  return {
2581
2669
  allowedComponentsUpdated,
@@ -2606,18 +2694,9 @@ var ComponentAdderService = class {
2606
2694
  const fullCompositionPatternsDir = this.fileSystem.resolvePath(rootDir, compositionPatternsDir);
2607
2695
  const fullComponentPatternsDir = this.fileSystem.resolvePath(rootDir, componentPatternsDir);
2608
2696
  const findOptions = { strict };
2609
- this.logger.info(`Loading parent component: ${parentComponentType}`);
2610
- const { component: parentComponent } = await this.componentService.loadComponent(fullComponentsDir, parentComponentType, findOptions);
2611
- if (!parentComponent.slots) {
2612
- parentComponent.slots = [];
2613
- }
2614
- let slotDef = parentComponent.slots.find(
2615
- (s) => this.compareIds(s.id, slot, strict)
2616
- );
2617
- if (!slotDef) {
2618
- this.logger.info(`Slot "${slot}" not found on component "${parentComponentType}", creating it`);
2619
- slotDef = { id: slot, name: slot, allowedComponents: [] };
2620
- parentComponent.slots.push(slotDef);
2697
+ const parentTypes = this.parseParentComponentTypes(parentComponentType);
2698
+ if (parentTypes.length === 0) {
2699
+ throw new TransformError("parentComponentType cannot be empty");
2621
2700
  }
2622
2701
  this.logger.info(`Loading component pattern: ${componentPatternId}`);
2623
2702
  const pattern = await this.loadComponentPattern(
@@ -2631,32 +2710,45 @@ var ComponentAdderService = class {
2631
2710
  `Component pattern "${componentPatternId}" has no valid definition or missing type`
2632
2711
  );
2633
2712
  }
2634
- let allowedComponentsUpdated = false;
2635
2713
  const componentTypeInPattern = patternDefinition.type;
2636
- const slotIndex = parentComponent.slots?.findIndex(
2637
- (s) => this.compareIds(s.id, slotDef.id, strict)
2638
- );
2639
- if (slotIndex !== void 0 && slotIndex >= 0) {
2640
- const actualSlot = parentComponent.slots[slotIndex];
2641
- if (!actualSlot.allowedComponents) {
2642
- actualSlot.allowedComponents = [];
2714
+ let allowedComponentsUpdated = false;
2715
+ for (const parentType of parentTypes) {
2716
+ this.logger.info(`Loading parent component: ${parentType}`);
2717
+ const { component: parentComponent, filePath: parentComponentFilePath } = await this.componentService.loadComponent(fullComponentsDir, parentType, findOptions);
2718
+ if (!parentComponent.slots) {
2719
+ parentComponent.slots = [];
2720
+ }
2721
+ let slotDef = parentComponent.slots.find(
2722
+ (s) => this.compareIds(s.id, slot, strict)
2723
+ );
2724
+ if (!slotDef) {
2725
+ this.logger.info(`Slot "${slot}" not found on component "${parentType}", creating it`);
2726
+ slotDef = { id: slot, name: slot, allowedComponents: [] };
2727
+ parentComponent.slots.push(slotDef);
2643
2728
  }
2644
- const isAlreadyAllowed = actualSlot.allowedComponents.some(
2645
- (c) => this.compareIds(c, componentTypeInPattern, strict)
2729
+ const slotIndex = parentComponent.slots?.findIndex(
2730
+ (s) => this.compareIds(s.id, slotDef.id, strict)
2646
2731
  );
2647
- if (!isAlreadyAllowed) {
2648
- this.logger.action(
2649
- whatIf,
2650
- "UPDATE",
2651
- `Adding "${componentTypeInPattern}" to allowedComponents for slot "${slot}" on component "${parentComponentType}"`
2732
+ if (slotIndex !== void 0 && slotIndex >= 0) {
2733
+ const actualSlot = parentComponent.slots[slotIndex];
2734
+ if (!actualSlot.allowedComponents) {
2735
+ actualSlot.allowedComponents = [];
2736
+ }
2737
+ const isAlreadyAllowed = actualSlot.allowedComponents.some(
2738
+ (c) => this.compareIds(c, componentTypeInPattern, strict)
2652
2739
  );
2653
- actualSlot.allowedComponents.push(componentTypeInPattern);
2654
- if (!whatIf) {
2655
- await this.componentService.loadComponent(fullComponentsDir, parentComponentType, findOptions).then(
2656
- ({ filePath }) => this.componentService.saveComponent(filePath, parentComponent)
2740
+ if (!isAlreadyAllowed) {
2741
+ this.logger.action(
2742
+ whatIf,
2743
+ "UPDATE",
2744
+ `Adding "${componentTypeInPattern}" to allowedComponents for slot "${slot}" on component "${parentType}"`
2657
2745
  );
2746
+ actualSlot.allowedComponents.push(componentTypeInPattern);
2747
+ if (!whatIf) {
2748
+ await this.componentService.saveComponent(parentComponentFilePath, parentComponent);
2749
+ }
2750
+ allowedComponentsUpdated = true;
2658
2751
  }
2659
- allowedComponentsUpdated = true;
2660
2752
  }
2661
2753
  }
2662
2754
  const newInstance = {
@@ -2666,7 +2758,7 @@ var ComponentAdderService = class {
2666
2758
  };
2667
2759
  const compositionsResult = await this.addComponentToDirectory(
2668
2760
  fullCompositionsDir,
2669
- parentComponentType,
2761
+ parentTypes,
2670
2762
  slot,
2671
2763
  newInstance,
2672
2764
  whatIf,
@@ -2675,7 +2767,7 @@ var ComponentAdderService = class {
2675
2767
  );
2676
2768
  const compositionPatternsResult = await this.addComponentToDirectory(
2677
2769
  fullCompositionPatternsDir,
2678
- parentComponentType,
2770
+ parentTypes,
2679
2771
  slot,
2680
2772
  newInstance,
2681
2773
  whatIf,
@@ -2684,7 +2776,7 @@ var ComponentAdderService = class {
2684
2776
  );
2685
2777
  const componentPatternsResult = await this.addComponentToDirectory(
2686
2778
  fullComponentPatternsDir,
2687
- parentComponentType,
2779
+ parentTypes,
2688
2780
  slot,
2689
2781
  newInstance,
2690
2782
  whatIf,
@@ -2693,7 +2785,7 @@ var ComponentAdderService = class {
2693
2785
  );
2694
2786
  this.logger.info("");
2695
2787
  this.logger.info(
2696
- `Summary: ${allowedComponentsUpdated ? "1 component definition updated, " : ""}${compositionsResult} composition(s), ${compositionPatternsResult} composition pattern(s), ${componentPatternsResult} component pattern(s) updated. ${compositionsResult + compositionPatternsResult + componentPatternsResult} instance(s) added.`
2788
+ `Summary: ${allowedComponentsUpdated ? `${parentTypes.length} component definition(s) updated, ` : ""}${compositionsResult} composition(s), ${compositionPatternsResult} composition pattern(s), ${componentPatternsResult} component pattern(s) updated. ${compositionsResult + compositionPatternsResult + componentPatternsResult} instance(s) added.`
2697
2789
  );
2698
2790
  return {
2699
2791
  allowedComponentsUpdated,
@@ -2744,7 +2836,7 @@ var ComponentAdderService = class {
2744
2836
  }
2745
2837
  return raw;
2746
2838
  }
2747
- async addComponentToDirectory(directory, parentComponentType, slot, newInstance, whatIf, strict, dirType) {
2839
+ async addComponentToDirectory(directory, parentTypes, slot, newInstance, whatIf, strict, dirType) {
2748
2840
  const exists = await this.fileSystem.fileExists(directory);
2749
2841
  if (!exists) {
2750
2842
  this.logger.detail(`${dirType} directory does not exist, skipping`);
@@ -2777,7 +2869,7 @@ var ComponentAdderService = class {
2777
2869
  }
2778
2870
  const rootInstance = composition.composition;
2779
2871
  let modified = false;
2780
- if (this.compareIds(rootInstance.type, parentComponentType, strict)) {
2872
+ if (this.matchesAnyParentType(rootInstance.type, parentTypes, strict)) {
2781
2873
  if (!rootInstance.slots) {
2782
2874
  rootInstance.slots = {};
2783
2875
  }
@@ -2789,7 +2881,7 @@ var ComponentAdderService = class {
2789
2881
  if (rootInstance.slots) {
2790
2882
  const nestedModified = this.addComponentToNestedSlots(
2791
2883
  rootInstance.slots,
2792
- parentComponentType,
2884
+ parentTypes,
2793
2885
  slot,
2794
2886
  newInstance,
2795
2887
  strict
@@ -2823,12 +2915,12 @@ var ComponentAdderService = class {
2823
2915
  }
2824
2916
  return filesModified;
2825
2917
  }
2826
- addComponentToNestedSlots(slots, parentComponentType, slot, newInstance, strict) {
2918
+ addComponentToNestedSlots(slots, parentTypes, slot, newInstance, strict) {
2827
2919
  let modified = false;
2828
2920
  for (const slotInstances of Object.values(slots)) {
2829
2921
  if (!Array.isArray(slotInstances)) continue;
2830
2922
  for (const instance of slotInstances) {
2831
- if (this.compareIds(instance.type, parentComponentType, strict)) {
2923
+ if (this.matchesAnyParentType(instance.type, parentTypes, strict)) {
2832
2924
  if (!instance.slots) {
2833
2925
  instance.slots = {};
2834
2926
  }
@@ -2840,7 +2932,7 @@ var ComponentAdderService = class {
2840
2932
  if (instance.slots) {
2841
2933
  const nestedModified = this.addComponentToNestedSlots(
2842
2934
  instance.slots,
2843
- parentComponentType,
2935
+ parentTypes,
2844
2936
  slot,
2845
2937
  newInstance,
2846
2938
  strict
@@ -2991,27 +3083,40 @@ var SlotPropagatorService = class {
2991
3083
  deleteSourceSlot
2992
3084
  } = options;
2993
3085
  const findOptions = { strict };
3086
+ const compositionTypes = this.parsePipeSeparatedValues(compositionType, strict);
2994
3087
  const fullComponentsDir = this.fileSystem.resolvePath(rootDir, componentsDir);
2995
3088
  const fullCompositionsDir = this.fileSystem.resolvePath(rootDir, compositionsDir);
2996
- this.logger.info(`Loading component: ${compositionType}`);
2997
- const { component: sourceComponent, filePath: sourceFilePath } = await this.componentService.loadComponent(fullComponentsDir, compositionType, findOptions);
3089
+ const sourceComponents = [];
3090
+ for (const sourceType of compositionTypes) {
3091
+ this.logger.info(`Loading component: ${sourceType}`);
3092
+ const { component: sourceComponent, filePath: sourceFilePath } = await this.componentService.loadComponent(fullComponentsDir, sourceType, findOptions);
3093
+ sourceComponents.push({ sourceType, sourceFilePath, sourceComponent });
3094
+ }
2998
3095
  this.logger.info(`Loading component: ${targetComponentType}`);
2999
3096
  const { component: targetComponent, filePath: targetFilePath } = await this.componentService.loadComponent(fullComponentsDir, targetComponentType, findOptions);
3000
- const slotNames = slot.split("|").map((s) => s.trim());
3097
+ const slotNames = slot.split("|").map((s) => s.trim()).filter((s) => s.length > 0);
3001
3098
  const resolvedSlots = [];
3002
- const notFound = [];
3003
- for (const slotName of slotNames) {
3004
- const slotDef = this.componentService.findSlot(sourceComponent, slotName, findOptions);
3005
- if (slotDef) {
3006
- resolvedSlots.push(slotDef);
3007
- } else {
3008
- notFound.push(slotName);
3099
+ const resolvedSlotIds = [];
3100
+ for (const { sourceType, sourceComponent } of sourceComponents) {
3101
+ const notFound = [];
3102
+ for (const slotName of slotNames) {
3103
+ const slotDef = this.componentService.findSlot(sourceComponent, slotName, findOptions);
3104
+ if (!slotDef) {
3105
+ notFound.push(slotName);
3106
+ continue;
3107
+ }
3108
+ const exists = resolvedSlots.some(
3109
+ (existing) => strict ? existing.id === slotDef.id : existing.id.toLowerCase() === slotDef.id.toLowerCase()
3110
+ );
3111
+ if (!exists) {
3112
+ resolvedSlots.push(slotDef);
3113
+ resolvedSlotIds.push(slotDef.id);
3114
+ }
3115
+ }
3116
+ if (notFound.length > 0) {
3117
+ throw new PropertyNotFoundError(`Slot "${notFound.join(", ")}"`, sourceType);
3009
3118
  }
3010
3119
  }
3011
- if (notFound.length > 0) {
3012
- throw new PropertyNotFoundError(`Slot "${notFound.join(", ")}"`, compositionType);
3013
- }
3014
- const resolvedSlotIds = resolvedSlots.map((s) => s.id);
3015
3120
  this.logger.info(`Resolved slots: ${resolvedSlotIds.join(", ")}`);
3016
3121
  let modifiedComponent = { ...targetComponent };
3017
3122
  let componentModified = false;
@@ -3048,9 +3153,9 @@ var SlotPropagatorService = class {
3048
3153
  if (componentModified && !whatIf) {
3049
3154
  await this.componentService.saveComponent(targetFilePath, modifiedComponent);
3050
3155
  }
3051
- const compositions = await this.compositionService.findCompositionsByType(
3156
+ const compositions = await this.compositionService.findCompositionsByTypes(
3052
3157
  fullCompositionsDir,
3053
- compositionType,
3158
+ compositionTypes,
3054
3159
  findOptions
3055
3160
  );
3056
3161
  let modifiedCompositions = 0;
@@ -3097,16 +3202,26 @@ var SlotPropagatorService = class {
3097
3202
  modifiedCompositions++;
3098
3203
  }
3099
3204
  }
3100
- let sourceComponentModified = false;
3205
+ let modifiedSourceComponents = 0;
3101
3206
  if (deleteSourceSlot) {
3102
- let modifiedSource = { ...sourceComponent };
3103
- for (const slotDef of resolvedSlots) {
3104
- this.logger.action(whatIf, "DELETE", `Slot "${slotDef.id}" from ${compositionType}`);
3105
- modifiedSource = this.componentService.removeSlot(modifiedSource, slotDef.id, findOptions);
3106
- sourceComponentModified = true;
3107
- }
3108
- if (sourceComponentModified && !whatIf) {
3109
- await this.componentService.saveComponent(sourceFilePath, modifiedSource);
3207
+ for (const { sourceType, sourceFilePath, sourceComponent } of sourceComponents) {
3208
+ let modifiedSource = { ...sourceComponent };
3209
+ let sourceComponentModified = false;
3210
+ for (const slotDef of resolvedSlots) {
3211
+ const slotToDelete = this.componentService.findSlot(modifiedSource, slotDef.id, findOptions);
3212
+ if (!slotToDelete) {
3213
+ continue;
3214
+ }
3215
+ this.logger.action(whatIf, "DELETE", `Slot "${slotDef.id}" from ${sourceType}`);
3216
+ modifiedSource = this.componentService.removeSlot(modifiedSource, slotDef.id, findOptions);
3217
+ sourceComponentModified = true;
3218
+ }
3219
+ if (sourceComponentModified) {
3220
+ modifiedSourceComponents++;
3221
+ }
3222
+ if (sourceComponentModified && !whatIf) {
3223
+ await this.componentService.saveComponent(sourceFilePath, modifiedSource);
3224
+ }
3110
3225
  }
3111
3226
  for (const { composition, filePath } of compositions) {
3112
3227
  const relativePath = filePath.replace(fullCompositionsDir, "").replace(/^[/\\]/, "");
@@ -3127,7 +3242,7 @@ var SlotPropagatorService = class {
3127
3242
  }
3128
3243
  }
3129
3244
  return {
3130
- modifiedComponents: (componentModified ? 1 : 0) + (sourceComponentModified ? 1 : 0),
3245
+ modifiedComponents: (componentModified ? 1 : 0) + modifiedSourceComponents,
3131
3246
  modifiedCompositions,
3132
3247
  propagatedInstances
3133
3248
  };
@@ -3153,6 +3268,19 @@ var SlotPropagatorService = class {
3153
3268
  }
3154
3269
  instance.slots[slotName].push(...components);
3155
3270
  }
3271
+ parsePipeSeparatedValues(value, strict) {
3272
+ const entries = value.split("|").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
3273
+ const normalized = [];
3274
+ for (const entry of entries) {
3275
+ const exists = normalized.some(
3276
+ (existing) => strict ? existing === entry : existing.toLowerCase() === entry.toLowerCase()
3277
+ );
3278
+ if (!exists) {
3279
+ normalized.push(entry);
3280
+ }
3281
+ }
3282
+ return normalized;
3283
+ }
3156
3284
  };
3157
3285
 
3158
3286
  // src/cli/commands/propagate-root-component-slot.ts
@@ -3160,7 +3288,10 @@ function createPropagateRootComponentSlotCommand() {
3160
3288
  const command = new Command9("propagate-root-component-slot");
3161
3289
  command.description(
3162
3290
  "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."
3163
- ).option("--compositionType <type>", "The composition type to process (e.g., HomePage)").option("--slot <slots>", "Pipe-separated list of slot names to copy from the source component").option(
3291
+ ).option(
3292
+ "--compositionType <type>",
3293
+ "The composition type(s) to process. Supports pipe-separated lists (e.g., HomePage|LandingPage)"
3294
+ ).option("--slot <slots>", "Pipe-separated list of slot names to copy from the source component").option(
3164
3295
  "--targetComponentType <type>",
3165
3296
  "The component type that will receive the copied slots"
3166
3297
  ).option(
@@ -3229,9 +3360,82 @@ function createPropagateRootComponentSlotCommand() {
3229
3360
  return command;
3230
3361
  }
3231
3362
 
3363
+ // package.json
3364
+ var package_default = {
3365
+ name: "@uniformdev/transformer",
3366
+ version: "1.1.5",
3367
+ description: "CLI tool for transforming Uniform.dev serialization files offline",
3368
+ type: "module",
3369
+ bin: {
3370
+ "uniform-transform": "./dist/cli/index.js"
3371
+ },
3372
+ main: "./dist/index.js",
3373
+ types: "./dist/index.d.ts",
3374
+ exports: {
3375
+ ".": {
3376
+ import: "./dist/index.js",
3377
+ types: "./dist/index.d.ts"
3378
+ }
3379
+ },
3380
+ files: [
3381
+ "dist",
3382
+ "LICENSE",
3383
+ "README.md"
3384
+ ],
3385
+ scripts: {
3386
+ build: "tsup",
3387
+ dev: "tsx src/cli/index.ts",
3388
+ test: "vitest run",
3389
+ "test:watch": "vitest",
3390
+ "test:e2e": "vitest run --config vitest.e2e.config.ts",
3391
+ "test:coverage": "vitest run --coverage",
3392
+ lint: "eslint --ext .ts src tests",
3393
+ format: "prettier --write .",
3394
+ prepublishOnly: "npm run build"
3395
+ },
3396
+ keywords: [
3397
+ "uniform",
3398
+ "uniform.dev",
3399
+ "cli",
3400
+ "transform",
3401
+ "content",
3402
+ "cms"
3403
+ ],
3404
+ author: "Uniform <https://uniform.dev>",
3405
+ license: "MIT",
3406
+ repository: {
3407
+ type: "git",
3408
+ url: "https://github.com/uniform-collab/transformer.git"
3409
+ },
3410
+ dependencies: {
3411
+ chalk: "^5.3.0",
3412
+ commander: "^12.1.0",
3413
+ glob: "^10.3.10",
3414
+ yaml: "^2.4.1",
3415
+ zod: "^3.22.4"
3416
+ },
3417
+ devDependencies: {
3418
+ "@types/node": "^20.11.24",
3419
+ "@typescript-eslint/eslint-plugin": "^7.18.0",
3420
+ "@typescript-eslint/parser": "^7.18.0",
3421
+ "@vitest/coverage-v8": "^1.3.1",
3422
+ eslint: "^8.57.0",
3423
+ prettier: "^3.2.5",
3424
+ tsup: "^8.0.2",
3425
+ tsx: "^4.7.1",
3426
+ typescript: "^5.4.2",
3427
+ vitest: "^1.3.1"
3428
+ },
3429
+ engines: {
3430
+ node: ">=18"
3431
+ }
3432
+ };
3433
+
3232
3434
  // src/cli/index.ts
3233
3435
  var program = new Command10();
3234
- program.name("uniform-transform").description("CLI tool for transforming Uniform.dev serialization files offline").version("1.0.0");
3436
+ var appVersion = package_default.version;
3437
+ console.error(`uniform-transform v${appVersion}`);
3438
+ program.name("uniform-transform").description("CLI tool for transforming Uniform.dev serialization files offline").version(appVersion);
3235
3439
  program.requiredOption("--rootDir <path>", "Path to the serialization project root").option("--what-if", "Dry run mode - preview changes without modifying files", false).option("--strict", "Enable strict mode for case-sensitive matching", false).option("--compositionsDir <dir>", "Compositions directory name", "composition").option("--componentsDir <dir>", "Components directory name", "component").option("--contentTypesDir <dir>", "Content types directory name", "contentType").option("--assetsDir <dir>", "Assets directory name", "asset").option("--categoriesDir <dir>", "Categories directory name", "category").option("--componentPatternsDir <dir>", "Component patterns directory name", "componentPattern").option(
3236
3440
  "--compositionPatternsDir <dir>",
3237
3441
  "Composition patterns directory name",