@uniformdev/transformer 1.1.4 → 1.1.6

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,52 @@ 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
+ this.logger.warn(`Property "${notFound.join(", ")}" not found on component "${sourceType}"`);
560
+ continue;
561
+ }
562
+ for (const param of sourceParams) {
563
+ const exists = resolvedParams.some(
564
+ (existing) => strict ? existing.id === param.id : existing.id.toLowerCase() === param.id.toLowerCase()
565
+ );
566
+ if (!exists) {
567
+ resolvedParams.push(param);
568
+ resolvedNames.push(param.id);
569
+ }
570
+ }
571
+ }
572
+ const groupSources = [];
573
+ for (const { sourceType, sourceComponent } of sourceComponents) {
574
+ for (const name of propertyNames) {
575
+ const param = this.componentService.findParameter(sourceComponent, name, findOptions);
576
+ if (param && this.componentService.isGroupParameter(param)) {
577
+ const groupSource = `${sourceType}.${name}`;
578
+ if (!groupSources.includes(groupSource)) {
579
+ groupSources.push(groupSource);
580
+ }
581
+ }
582
+ }
527
583
  }
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
584
  if (groupSources.length > 0) {
534
585
  this.logger.info(
535
586
  `Resolved properties: ${resolvedNames.join(", ")} (from ${groupSources.join(", ")})`
@@ -604,9 +655,9 @@ var PropertyPropagatorService = class {
604
655
  if (componentModified && !whatIf) {
605
656
  await this.componentService.saveComponent(targetFilePath, modifiedComponent);
606
657
  }
607
- const compositions = await this.compositionService.findCompositionsByType(
658
+ const compositions = await this.compositionService.findCompositionsByTypes(
608
659
  fullCompositionsDir,
609
- compositionType,
660
+ compositionTypes,
610
661
  findOptions
611
662
  );
612
663
  let modifiedCompositions = 0;
@@ -653,31 +704,41 @@ var PropertyPropagatorService = class {
653
704
  modifiedCompositions++;
654
705
  }
655
706
  }
656
- let sourceComponentModified = false;
707
+ let modifiedSourceComponents = 0;
657
708
  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);
709
+ for (const { sourceType, sourceFilePath, sourceComponent } of sourceComponents) {
710
+ let modifiedSource = { ...sourceComponent };
711
+ let sourceComponentModified = false;
712
+ for (const param of resolvedParams) {
713
+ const exists = this.componentService.findParameter(modifiedSource, param.id, findOptions);
714
+ if (exists) {
715
+ this.logger.action(whatIf, "DELETE", `Parameter "${param.id}" from ${sourceType}`);
716
+ modifiedSource = this.componentService.removeParameter(modifiedSource, param.id, findOptions);
717
+ sourceComponentModified = true;
718
+ }
719
+ }
720
+ const beforeGroupCount = modifiedSource.parameters?.filter(
721
+ (p) => this.componentService.isGroupParameter(p)
722
+ ).length ?? 0;
723
+ modifiedSource = this.componentService.removeEmptyGroups(modifiedSource);
724
+ const afterGroupCount = modifiedSource.parameters?.filter(
725
+ (p) => this.componentService.isGroupParameter(p)
726
+ ).length ?? 0;
727
+ if (afterGroupCount < beforeGroupCount) {
728
+ const removedCount = beforeGroupCount - afterGroupCount;
729
+ this.logger.action(
730
+ whatIf,
731
+ "DELETE",
732
+ `${removedCount} empty group(s) from ${sourceType}`
733
+ );
734
+ sourceComponentModified = true;
735
+ }
736
+ if (sourceComponentModified) {
737
+ modifiedSourceComponents++;
738
+ }
739
+ if (sourceComponentModified && !whatIf) {
740
+ await this.componentService.saveComponent(sourceFilePath, modifiedSource);
741
+ }
681
742
  }
682
743
  for (const { composition, filePath } of compositions) {
683
744
  const relativePath = filePath.replace(fullCompositionsDir, "").replace(/^[/\\]/, "");
@@ -696,11 +757,24 @@ var PropertyPropagatorService = class {
696
757
  }
697
758
  }
698
759
  return {
699
- modifiedComponents: (componentModified ? 1 : 0) + (sourceComponentModified ? 1 : 0),
760
+ modifiedComponents: (componentModified ? 1 : 0) + modifiedSourceComponents,
700
761
  modifiedCompositions,
701
762
  propagatedInstances
702
763
  };
703
764
  }
765
+ parsePipeSeparatedValues(value, strict) {
766
+ const entries = value.split("|").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
767
+ const normalized = [];
768
+ for (const entry of entries) {
769
+ const exists = normalized.some(
770
+ (existing) => strict ? existing === entry : existing.toLowerCase() === entry.toLowerCase()
771
+ );
772
+ if (!exists) {
773
+ normalized.push(entry);
774
+ }
775
+ }
776
+ return normalized;
777
+ }
704
778
  };
705
779
 
706
780
  // src/cli/logger.ts
@@ -732,7 +806,10 @@ function createPropagateRootComponentPropertyCommand() {
732
806
  const command = new Command("propagate-root-component-property");
733
807
  command.description(
734
808
  "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(
809
+ ).option(
810
+ "--compositionType <type>",
811
+ "The composition type(s) to process. Supports pipe-separated lists (e.g., HomePage|LandingPage)"
812
+ ).option("--property <properties>", "Pipe-separated list of properties and/or groups to copy").option(
736
813
  "--targetComponentType <type>",
737
814
  "The component type that will receive the copied properties"
738
815
  ).option(
@@ -3007,27 +3084,40 @@ var SlotPropagatorService = class {
3007
3084
  deleteSourceSlot
3008
3085
  } = options;
3009
3086
  const findOptions = { strict };
3087
+ const compositionTypes = this.parsePipeSeparatedValues(compositionType, strict);
3010
3088
  const fullComponentsDir = this.fileSystem.resolvePath(rootDir, componentsDir);
3011
3089
  const fullCompositionsDir = this.fileSystem.resolvePath(rootDir, compositionsDir);
3012
- this.logger.info(`Loading component: ${compositionType}`);
3013
- const { component: sourceComponent, filePath: sourceFilePath } = await this.componentService.loadComponent(fullComponentsDir, compositionType, findOptions);
3090
+ const sourceComponents = [];
3091
+ for (const sourceType of compositionTypes) {
3092
+ this.logger.info(`Loading component: ${sourceType}`);
3093
+ const { component: sourceComponent, filePath: sourceFilePath } = await this.componentService.loadComponent(fullComponentsDir, sourceType, findOptions);
3094
+ sourceComponents.push({ sourceType, sourceFilePath, sourceComponent });
3095
+ }
3014
3096
  this.logger.info(`Loading component: ${targetComponentType}`);
3015
3097
  const { component: targetComponent, filePath: targetFilePath } = await this.componentService.loadComponent(fullComponentsDir, targetComponentType, findOptions);
3016
- const slotNames = slot.split("|").map((s) => s.trim());
3098
+ const slotNames = slot.split("|").map((s) => s.trim()).filter((s) => s.length > 0);
3017
3099
  const resolvedSlots = [];
3018
- const notFound = [];
3019
- for (const slotName of slotNames) {
3020
- const slotDef = this.componentService.findSlot(sourceComponent, slotName, findOptions);
3021
- if (slotDef) {
3022
- resolvedSlots.push(slotDef);
3023
- } else {
3024
- notFound.push(slotName);
3100
+ const resolvedSlotIds = [];
3101
+ for (const { sourceType, sourceComponent } of sourceComponents) {
3102
+ const notFound = [];
3103
+ for (const slotName of slotNames) {
3104
+ const slotDef = this.componentService.findSlot(sourceComponent, slotName, findOptions);
3105
+ if (!slotDef) {
3106
+ notFound.push(slotName);
3107
+ continue;
3108
+ }
3109
+ const exists = resolvedSlots.some(
3110
+ (existing) => strict ? existing.id === slotDef.id : existing.id.toLowerCase() === slotDef.id.toLowerCase()
3111
+ );
3112
+ if (!exists) {
3113
+ resolvedSlots.push(slotDef);
3114
+ resolvedSlotIds.push(slotDef.id);
3115
+ }
3116
+ }
3117
+ if (notFound.length > 0) {
3118
+ throw new PropertyNotFoundError(`Slot "${notFound.join(", ")}"`, sourceType);
3025
3119
  }
3026
3120
  }
3027
- if (notFound.length > 0) {
3028
- throw new PropertyNotFoundError(`Slot "${notFound.join(", ")}"`, compositionType);
3029
- }
3030
- const resolvedSlotIds = resolvedSlots.map((s) => s.id);
3031
3121
  this.logger.info(`Resolved slots: ${resolvedSlotIds.join(", ")}`);
3032
3122
  let modifiedComponent = { ...targetComponent };
3033
3123
  let componentModified = false;
@@ -3064,9 +3154,9 @@ var SlotPropagatorService = class {
3064
3154
  if (componentModified && !whatIf) {
3065
3155
  await this.componentService.saveComponent(targetFilePath, modifiedComponent);
3066
3156
  }
3067
- const compositions = await this.compositionService.findCompositionsByType(
3157
+ const compositions = await this.compositionService.findCompositionsByTypes(
3068
3158
  fullCompositionsDir,
3069
- compositionType,
3159
+ compositionTypes,
3070
3160
  findOptions
3071
3161
  );
3072
3162
  let modifiedCompositions = 0;
@@ -3113,16 +3203,26 @@ var SlotPropagatorService = class {
3113
3203
  modifiedCompositions++;
3114
3204
  }
3115
3205
  }
3116
- let sourceComponentModified = false;
3206
+ let modifiedSourceComponents = 0;
3117
3207
  if (deleteSourceSlot) {
3118
- let modifiedSource = { ...sourceComponent };
3119
- for (const slotDef of resolvedSlots) {
3120
- this.logger.action(whatIf, "DELETE", `Slot "${slotDef.id}" from ${compositionType}`);
3121
- modifiedSource = this.componentService.removeSlot(modifiedSource, slotDef.id, findOptions);
3122
- sourceComponentModified = true;
3123
- }
3124
- if (sourceComponentModified && !whatIf) {
3125
- await this.componentService.saveComponent(sourceFilePath, modifiedSource);
3208
+ for (const { sourceType, sourceFilePath, sourceComponent } of sourceComponents) {
3209
+ let modifiedSource = { ...sourceComponent };
3210
+ let sourceComponentModified = false;
3211
+ for (const slotDef of resolvedSlots) {
3212
+ const slotToDelete = this.componentService.findSlot(modifiedSource, slotDef.id, findOptions);
3213
+ if (!slotToDelete) {
3214
+ continue;
3215
+ }
3216
+ this.logger.action(whatIf, "DELETE", `Slot "${slotDef.id}" from ${sourceType}`);
3217
+ modifiedSource = this.componentService.removeSlot(modifiedSource, slotDef.id, findOptions);
3218
+ sourceComponentModified = true;
3219
+ }
3220
+ if (sourceComponentModified) {
3221
+ modifiedSourceComponents++;
3222
+ }
3223
+ if (sourceComponentModified && !whatIf) {
3224
+ await this.componentService.saveComponent(sourceFilePath, modifiedSource);
3225
+ }
3126
3226
  }
3127
3227
  for (const { composition, filePath } of compositions) {
3128
3228
  const relativePath = filePath.replace(fullCompositionsDir, "").replace(/^[/\\]/, "");
@@ -3143,7 +3243,7 @@ var SlotPropagatorService = class {
3143
3243
  }
3144
3244
  }
3145
3245
  return {
3146
- modifiedComponents: (componentModified ? 1 : 0) + (sourceComponentModified ? 1 : 0),
3246
+ modifiedComponents: (componentModified ? 1 : 0) + modifiedSourceComponents,
3147
3247
  modifiedCompositions,
3148
3248
  propagatedInstances
3149
3249
  };
@@ -3169,6 +3269,19 @@ var SlotPropagatorService = class {
3169
3269
  }
3170
3270
  instance.slots[slotName].push(...components);
3171
3271
  }
3272
+ parsePipeSeparatedValues(value, strict) {
3273
+ const entries = value.split("|").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
3274
+ const normalized = [];
3275
+ for (const entry of entries) {
3276
+ const exists = normalized.some(
3277
+ (existing) => strict ? existing === entry : existing.toLowerCase() === entry.toLowerCase()
3278
+ );
3279
+ if (!exists) {
3280
+ normalized.push(entry);
3281
+ }
3282
+ }
3283
+ return normalized;
3284
+ }
3172
3285
  };
3173
3286
 
3174
3287
  // src/cli/commands/propagate-root-component-slot.ts
@@ -3176,7 +3289,10 @@ function createPropagateRootComponentSlotCommand() {
3176
3289
  const command = new Command9("propagate-root-component-slot");
3177
3290
  command.description(
3178
3291
  "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."
3179
- ).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(
3292
+ ).option(
3293
+ "--compositionType <type>",
3294
+ "The composition type(s) to process. Supports pipe-separated lists (e.g., HomePage|LandingPage)"
3295
+ ).option("--slot <slots>", "Pipe-separated list of slot names to copy from the source component").option(
3180
3296
  "--targetComponentType <type>",
3181
3297
  "The component type that will receive the copied slots"
3182
3298
  ).option(
@@ -3245,9 +3361,82 @@ function createPropagateRootComponentSlotCommand() {
3245
3361
  return command;
3246
3362
  }
3247
3363
 
3364
+ // package.json
3365
+ var package_default = {
3366
+ name: "@uniformdev/transformer",
3367
+ version: "1.1.6",
3368
+ description: "CLI tool for transforming Uniform.dev serialization files offline",
3369
+ type: "module",
3370
+ bin: {
3371
+ "uniform-transform": "./dist/cli/index.js"
3372
+ },
3373
+ main: "./dist/index.js",
3374
+ types: "./dist/index.d.ts",
3375
+ exports: {
3376
+ ".": {
3377
+ import: "./dist/index.js",
3378
+ types: "./dist/index.d.ts"
3379
+ }
3380
+ },
3381
+ files: [
3382
+ "dist",
3383
+ "LICENSE",
3384
+ "README.md"
3385
+ ],
3386
+ scripts: {
3387
+ build: "tsup",
3388
+ dev: "tsx src/cli/index.ts",
3389
+ test: "vitest run",
3390
+ "test:watch": "vitest",
3391
+ "test:e2e": "vitest run --config vitest.e2e.config.ts",
3392
+ "test:coverage": "vitest run --coverage",
3393
+ lint: "eslint --ext .ts src tests",
3394
+ format: "prettier --write .",
3395
+ prepublishOnly: "npm run build"
3396
+ },
3397
+ keywords: [
3398
+ "uniform",
3399
+ "uniform.dev",
3400
+ "cli",
3401
+ "transform",
3402
+ "content",
3403
+ "cms"
3404
+ ],
3405
+ author: "Uniform <https://uniform.dev>",
3406
+ license: "MIT",
3407
+ repository: {
3408
+ type: "git",
3409
+ url: "https://github.com/uniform-collab/transformer.git"
3410
+ },
3411
+ dependencies: {
3412
+ chalk: "^5.3.0",
3413
+ commander: "^12.1.0",
3414
+ glob: "^10.3.10",
3415
+ yaml: "^2.4.1",
3416
+ zod: "^3.22.4"
3417
+ },
3418
+ devDependencies: {
3419
+ "@types/node": "^20.11.24",
3420
+ "@typescript-eslint/eslint-plugin": "^7.18.0",
3421
+ "@typescript-eslint/parser": "^7.18.0",
3422
+ "@vitest/coverage-v8": "^1.3.1",
3423
+ eslint: "^8.57.0",
3424
+ prettier: "^3.2.5",
3425
+ tsup: "^8.0.2",
3426
+ tsx: "^4.7.1",
3427
+ typescript: "^5.4.2",
3428
+ vitest: "^1.3.1"
3429
+ },
3430
+ engines: {
3431
+ node: ">=18"
3432
+ }
3433
+ };
3434
+
3248
3435
  // src/cli/index.ts
3249
3436
  var program = new Command10();
3250
- program.name("uniform-transform").description("CLI tool for transforming Uniform.dev serialization files offline").version("1.0.0");
3437
+ var appVersion = package_default.version;
3438
+ console.error(`uniform-transform v${appVersion}`);
3439
+ program.name("uniform-transform").description("CLI tool for transforming Uniform.dev serialization files offline").version(appVersion);
3251
3440
  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(
3252
3441
  "--compositionPatternsDir <dir>",
3253
3442
  "Composition patterns directory name",