@uniformdev/transformer 1.1.4 → 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(
@@ -3007,27 +3083,40 @@ var SlotPropagatorService = class {
3007
3083
  deleteSourceSlot
3008
3084
  } = options;
3009
3085
  const findOptions = { strict };
3086
+ const compositionTypes = this.parsePipeSeparatedValues(compositionType, strict);
3010
3087
  const fullComponentsDir = this.fileSystem.resolvePath(rootDir, componentsDir);
3011
3088
  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);
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
+ }
3014
3095
  this.logger.info(`Loading component: ${targetComponentType}`);
3015
3096
  const { component: targetComponent, filePath: targetFilePath } = await this.componentService.loadComponent(fullComponentsDir, targetComponentType, findOptions);
3016
- const slotNames = slot.split("|").map((s) => s.trim());
3097
+ const slotNames = slot.split("|").map((s) => s.trim()).filter((s) => s.length > 0);
3017
3098
  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);
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);
3025
3118
  }
3026
3119
  }
3027
- if (notFound.length > 0) {
3028
- throw new PropertyNotFoundError(`Slot "${notFound.join(", ")}"`, compositionType);
3029
- }
3030
- const resolvedSlotIds = resolvedSlots.map((s) => s.id);
3031
3120
  this.logger.info(`Resolved slots: ${resolvedSlotIds.join(", ")}`);
3032
3121
  let modifiedComponent = { ...targetComponent };
3033
3122
  let componentModified = false;
@@ -3064,9 +3153,9 @@ var SlotPropagatorService = class {
3064
3153
  if (componentModified && !whatIf) {
3065
3154
  await this.componentService.saveComponent(targetFilePath, modifiedComponent);
3066
3155
  }
3067
- const compositions = await this.compositionService.findCompositionsByType(
3156
+ const compositions = await this.compositionService.findCompositionsByTypes(
3068
3157
  fullCompositionsDir,
3069
- compositionType,
3158
+ compositionTypes,
3070
3159
  findOptions
3071
3160
  );
3072
3161
  let modifiedCompositions = 0;
@@ -3113,16 +3202,26 @@ var SlotPropagatorService = class {
3113
3202
  modifiedCompositions++;
3114
3203
  }
3115
3204
  }
3116
- let sourceComponentModified = false;
3205
+ let modifiedSourceComponents = 0;
3117
3206
  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);
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
+ }
3126
3225
  }
3127
3226
  for (const { composition, filePath } of compositions) {
3128
3227
  const relativePath = filePath.replace(fullCompositionsDir, "").replace(/^[/\\]/, "");
@@ -3143,7 +3242,7 @@ var SlotPropagatorService = class {
3143
3242
  }
3144
3243
  }
3145
3244
  return {
3146
- modifiedComponents: (componentModified ? 1 : 0) + (sourceComponentModified ? 1 : 0),
3245
+ modifiedComponents: (componentModified ? 1 : 0) + modifiedSourceComponents,
3147
3246
  modifiedCompositions,
3148
3247
  propagatedInstances
3149
3248
  };
@@ -3169,6 +3268,19 @@ var SlotPropagatorService = class {
3169
3268
  }
3170
3269
  instance.slots[slotName].push(...components);
3171
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
+ }
3172
3284
  };
3173
3285
 
3174
3286
  // src/cli/commands/propagate-root-component-slot.ts
@@ -3176,7 +3288,10 @@ function createPropagateRootComponentSlotCommand() {
3176
3288
  const command = new Command9("propagate-root-component-slot");
3177
3289
  command.description(
3178
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."
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(
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(
3180
3295
  "--targetComponentType <type>",
3181
3296
  "The component type that will receive the copied slots"
3182
3297
  ).option(
@@ -3245,9 +3360,82 @@ function createPropagateRootComponentSlotCommand() {
3245
3360
  return command;
3246
3361
  }
3247
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
+
3248
3434
  // src/cli/index.ts
3249
3435
  var program = new Command10();
3250
- 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);
3251
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(
3252
3440
  "--compositionPatternsDir <dir>",
3253
3441
  "Composition patterns directory name",