@uniformdev/transformer 1.1.9 → 1.1.10

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
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli/index.ts
4
- import { Command as Command10 } from "commander";
4
+ import { Command as Command11 } from "commander";
5
5
 
6
6
  // src/cli/commands/propagate-root-component-property.ts
7
7
  import { Command } from "commander";
@@ -3574,10 +3574,425 @@ function createPropagateRootComponentSlotCommand() {
3574
3574
  return command;
3575
3575
  }
3576
3576
 
3577
+ // src/cli/commands/convert-compositions-to-entries.ts
3578
+ import { Command as Command10 } from "commander";
3579
+
3580
+ // src/core/services/composition-converter.service.ts
3581
+ import * as crypto from "crypto";
3582
+ var CompositionConverterService = class {
3583
+ constructor(fileSystem, componentService, compositionService, logger) {
3584
+ this.fileSystem = fileSystem;
3585
+ this.componentService = componentService;
3586
+ this.compositionService = compositionService;
3587
+ this.logger = logger;
3588
+ }
3589
+ async convert(options) {
3590
+ const {
3591
+ rootDir,
3592
+ compositionsDir,
3593
+ componentsDir,
3594
+ contentTypesDir,
3595
+ entriesDir,
3596
+ compositionTypes,
3597
+ flattenComponentIds,
3598
+ whatIf,
3599
+ strict
3600
+ } = options;
3601
+ const compositionsDirFull = this.fileSystem.resolvePath(rootDir, compositionsDir);
3602
+ const componentsDirFull = this.fileSystem.resolvePath(rootDir, componentsDir);
3603
+ const contentTypesDirFull = this.fileSystem.resolvePath(rootDir, contentTypesDir);
3604
+ const entriesDirFull = this.fileSystem.resolvePath(rootDir, entriesDir);
3605
+ let contentTypesWritten = 0;
3606
+ let entriesFromCompositions = 0;
3607
+ let entriesFromFlattened = 0;
3608
+ this.logger.info(`Composition types: ${compositionTypes.join(", ")}`);
3609
+ if (flattenComponentIds.length > 0) {
3610
+ this.logger.info(`Flatten component types: ${flattenComponentIds.join(", ")}`);
3611
+ }
3612
+ const compositionResults = await this.compositionService.findCompositionsByTypes(
3613
+ compositionsDirFull,
3614
+ compositionTypes,
3615
+ { strict }
3616
+ );
3617
+ if (compositionResults.length === 0) {
3618
+ this.logger.warn("No compositions found matching the specified types");
3619
+ return { contentTypesWritten: 0, entriesFromCompositions: 0, entriesFromFlattened: 0 };
3620
+ }
3621
+ this.logger.info(`Found ${compositionResults.length} composition(s)`);
3622
+ const rootComponentTypes = /* @__PURE__ */ new Set();
3623
+ for (const { composition } of compositionResults) {
3624
+ rootComponentTypes.add(composition.composition.type);
3625
+ }
3626
+ const contentTypeMap = /* @__PURE__ */ new Map();
3627
+ for (const rootType of rootComponentTypes) {
3628
+ const { component } = await this.componentService.loadComponent(
3629
+ componentsDirFull,
3630
+ rootType,
3631
+ { strict }
3632
+ );
3633
+ const contentType = this.generateContentType(component);
3634
+ contentTypeMap.set(rootType, contentType);
3635
+ }
3636
+ const flattenContentTypeMap = /* @__PURE__ */ new Map();
3637
+ for (const flattenType of flattenComponentIds) {
3638
+ const isRootType = [...rootComponentTypes].some(
3639
+ (rt) => this.compareTypes(rt, flattenType, strict)
3640
+ );
3641
+ if (isRootType) {
3642
+ continue;
3643
+ }
3644
+ try {
3645
+ const { component } = await this.componentService.loadComponent(
3646
+ componentsDirFull,
3647
+ flattenType,
3648
+ { strict }
3649
+ );
3650
+ const contentType = this.generateContentType(component);
3651
+ flattenContentTypeMap.set(flattenType, contentType);
3652
+ } catch (error) {
3653
+ if (error instanceof ComponentNotFoundError) {
3654
+ throw new ComponentNotFoundError(flattenType, componentsDirFull);
3655
+ }
3656
+ throw error;
3657
+ }
3658
+ }
3659
+ if (flattenComponentIds.length > 0) {
3660
+ for (const contentType of contentTypeMap.values()) {
3661
+ for (const flattenType of flattenComponentIds) {
3662
+ if (this.compareTypes(flattenType, contentType.id, strict)) {
3663
+ continue;
3664
+ }
3665
+ contentType.fields.push({
3666
+ id: flattenType,
3667
+ name: flattenType,
3668
+ type: "contentReference",
3669
+ typeConfig: {
3670
+ isMulti: true,
3671
+ allowedContentTypes: [flattenType]
3672
+ },
3673
+ localizable: false
3674
+ });
3675
+ }
3676
+ }
3677
+ }
3678
+ for (const [typeName, contentType] of contentTypeMap) {
3679
+ const filePath = this.fileSystem.joinPath(contentTypesDirFull, `${typeName}.json`);
3680
+ const fieldCount = contentType.fields.filter((f) => f.type !== "contentReference").length;
3681
+ const refCount = contentType.fields.filter((f) => f.type === "contentReference").length;
3682
+ const refInfo = refCount > 0 ? ` + ${refCount} reference(s)` : "";
3683
+ this.logger.action(
3684
+ whatIf,
3685
+ "WRITE",
3686
+ `${contentTypesDir}/${typeName}.json (${fieldCount} fields${refInfo})`
3687
+ );
3688
+ if (!whatIf) {
3689
+ await this.fileSystem.writeFile(filePath, contentType);
3690
+ }
3691
+ contentTypesWritten++;
3692
+ }
3693
+ for (const [typeName, contentType] of flattenContentTypeMap) {
3694
+ const filePath = this.fileSystem.joinPath(contentTypesDirFull, `${typeName}.json`);
3695
+ this.logger.action(
3696
+ whatIf,
3697
+ "WRITE",
3698
+ `${contentTypesDir}/${typeName}.json (${contentType.fields.length} fields)`
3699
+ );
3700
+ if (!whatIf) {
3701
+ await this.fileSystem.writeFile(filePath, contentType);
3702
+ }
3703
+ contentTypesWritten++;
3704
+ }
3705
+ for (const { composition } of compositionResults) {
3706
+ const comp = composition.composition;
3707
+ const compositionId = comp._id;
3708
+ const compositionName = comp._name ?? compositionId;
3709
+ const compositionType = comp.type;
3710
+ const entry = this.generateEntryFromComposition(composition);
3711
+ const flattenedByType = /* @__PURE__ */ new Map();
3712
+ if (flattenComponentIds.length > 0 && comp.slots) {
3713
+ for (const flattenType of flattenComponentIds) {
3714
+ if (this.compareTypes(flattenType, compositionType, strict)) {
3715
+ this.logger.warn(
3716
+ `Skipping flatten of "${flattenType}" \u2014 same as root component type`
3717
+ );
3718
+ continue;
3719
+ }
3720
+ const instances = this.findFlattenTargets(
3721
+ comp.slots,
3722
+ flattenType,
3723
+ compositionId,
3724
+ compositionName,
3725
+ strict
3726
+ );
3727
+ if (instances.length > 0) {
3728
+ flattenedByType.set(flattenType, instances);
3729
+ }
3730
+ }
3731
+ }
3732
+ for (const [flattenType, instances] of flattenedByType) {
3733
+ entry.entry.fields[flattenType] = {
3734
+ type: "contentReference",
3735
+ value: instances.map((inst) => inst.determinisiticId)
3736
+ };
3737
+ }
3738
+ const entryFilePath = this.fileSystem.joinPath(entriesDirFull, `${compositionId}.json`);
3739
+ this.logger.action(
3740
+ whatIf,
3741
+ "WRITE",
3742
+ `${entriesDir}/${compositionId}.json (${compositionType}, "${this.truncate(compositionName, 50)}")`
3743
+ );
3744
+ if (!whatIf) {
3745
+ await this.fileSystem.writeFile(entryFilePath, entry);
3746
+ }
3747
+ entriesFromCompositions++;
3748
+ for (const [flattenType, instances] of flattenedByType) {
3749
+ for (const inst of instances) {
3750
+ const flatEntry = this.generateEntryFromFlattenedInstance(inst);
3751
+ const flatEntryPath = this.fileSystem.joinPath(
3752
+ entriesDirFull,
3753
+ `${inst.determinisiticId}.json`
3754
+ );
3755
+ this.logger.action(
3756
+ whatIf,
3757
+ "WRITE",
3758
+ `${entriesDir}/${inst.determinisiticId}.json (${flattenType} from "${this.truncate(compositionName, 50)}")`
3759
+ );
3760
+ if (!whatIf) {
3761
+ await this.fileSystem.writeFile(flatEntryPath, flatEntry);
3762
+ }
3763
+ entriesFromFlattened++;
3764
+ }
3765
+ }
3766
+ }
3767
+ return { contentTypesWritten, entriesFromCompositions, entriesFromFlattened };
3768
+ }
3769
+ // --- Content Type Generation ---
3770
+ generateContentType(component) {
3771
+ const fields = [];
3772
+ if (component.parameters) {
3773
+ for (const param of component.parameters) {
3774
+ fields.push(this.parameterToField(param));
3775
+ }
3776
+ }
3777
+ return {
3778
+ id: component.id,
3779
+ name: component.name,
3780
+ fields
3781
+ };
3782
+ }
3783
+ parameterToField(param) {
3784
+ const field = {
3785
+ id: param.id,
3786
+ name: param.name,
3787
+ type: param.type
3788
+ };
3789
+ if (param.helpText !== void 0) {
3790
+ field.helpText = param.helpText;
3791
+ }
3792
+ if (param.localizable !== void 0) {
3793
+ field.localizable = param.localizable;
3794
+ }
3795
+ if (param.typeConfig !== void 0) {
3796
+ field.typeConfig = param.typeConfig;
3797
+ }
3798
+ return field;
3799
+ }
3800
+ // --- Entry Generation ---
3801
+ generateEntryFromComposition(composition) {
3802
+ const comp = composition.composition;
3803
+ return {
3804
+ entry: {
3805
+ _id: comp._id,
3806
+ _name: comp._name ?? comp._id,
3807
+ type: comp.type,
3808
+ fields: { ...comp.parameters ?? {} }
3809
+ }
3810
+ };
3811
+ }
3812
+ generateEntryFromFlattenedInstance(inst) {
3813
+ return {
3814
+ entry: {
3815
+ _id: inst.determinisiticId,
3816
+ _name: `${inst.componentType} (from ${inst.compositionName})`,
3817
+ type: inst.componentType,
3818
+ fields: { ...inst.instance.parameters ?? {} }
3819
+ }
3820
+ };
3821
+ }
3822
+ // --- Flatten Tree Walking ---
3823
+ findFlattenTargets(slots, targetType, compositionId, compositionName, strict) {
3824
+ const results = [];
3825
+ this.walkSlots(slots, targetType, compositionId, compositionName, "", results, strict);
3826
+ return results;
3827
+ }
3828
+ walkSlots(slots, targetType, compositionId, compositionName, pathPrefix, results, strict) {
3829
+ for (const [slotName, instances] of Object.entries(slots)) {
3830
+ if (!Array.isArray(instances)) continue;
3831
+ for (let i = 0; i < instances.length; i++) {
3832
+ const instance = instances[i];
3833
+ if (instance._pattern) {
3834
+ continue;
3835
+ }
3836
+ const currentPath = pathPrefix ? `${pathPrefix}-${slotName}-[${i}]-${instance.type}` : `${slotName}-[${i}]-${instance.type}`;
3837
+ if (this.compareTypes(instance.type, targetType, strict)) {
3838
+ const fullPath = `${compositionId}-${currentPath}`;
3839
+ const deterministicId = computeGuidHash(fullPath);
3840
+ results.push({
3841
+ instance,
3842
+ path: fullPath,
3843
+ determinisiticId: deterministicId,
3844
+ componentType: instance.type,
3845
+ compositionId,
3846
+ compositionName
3847
+ });
3848
+ }
3849
+ if (instance.slots) {
3850
+ this.walkSlots(
3851
+ instance.slots,
3852
+ targetType,
3853
+ compositionId,
3854
+ compositionName,
3855
+ currentPath,
3856
+ results,
3857
+ strict
3858
+ );
3859
+ }
3860
+ }
3861
+ }
3862
+ }
3863
+ // --- Utilities ---
3864
+ compareTypes(type1, type2, strict) {
3865
+ if (strict) {
3866
+ return type1 === type2;
3867
+ }
3868
+ return type1.toLowerCase() === type2.toLowerCase();
3869
+ }
3870
+ truncate(str, maxLength) {
3871
+ if (str.length <= maxLength) return str;
3872
+ return str.substring(0, maxLength - 3) + "...";
3873
+ }
3874
+ };
3875
+ function computeGuidHash(guidOrSeed) {
3876
+ let uuidStr;
3877
+ if (isValidUuid(guidOrSeed)) {
3878
+ uuidStr = formatAsBracedUuid(guidOrSeed);
3879
+ } else {
3880
+ const hash = crypto.createHash("md5").update(guidOrSeed, "utf-8").digest();
3881
+ const hex = [
3882
+ // First 4 bytes: little-endian in .NET Guid
3883
+ hash[3].toString(16).padStart(2, "0"),
3884
+ hash[2].toString(16).padStart(2, "0"),
3885
+ hash[1].toString(16).padStart(2, "0"),
3886
+ hash[0].toString(16).padStart(2, "0"),
3887
+ "-",
3888
+ // Bytes 4-5: little-endian
3889
+ hash[5].toString(16).padStart(2, "0"),
3890
+ hash[4].toString(16).padStart(2, "0"),
3891
+ "-",
3892
+ // Bytes 6-7: little-endian
3893
+ hash[7].toString(16).padStart(2, "0"),
3894
+ hash[6].toString(16).padStart(2, "0"),
3895
+ "-",
3896
+ // Bytes 8-9: big-endian
3897
+ hash[8].toString(16).padStart(2, "0"),
3898
+ hash[9].toString(16).padStart(2, "0"),
3899
+ "-",
3900
+ // Bytes 10-15: big-endian
3901
+ hash[10].toString(16).padStart(2, "0"),
3902
+ hash[11].toString(16).padStart(2, "0"),
3903
+ hash[12].toString(16).padStart(2, "0"),
3904
+ hash[13].toString(16).padStart(2, "0"),
3905
+ hash[14].toString(16).padStart(2, "0"),
3906
+ hash[15].toString(16).padStart(2, "0")
3907
+ ].join("");
3908
+ uuidStr = `{${hex}}`.toUpperCase();
3909
+ }
3910
+ const chars = uuidStr.split("");
3911
+ chars[15] = "4";
3912
+ const arr20 = ["8", "9", "A", "B"];
3913
+ const hexVal = parseInt(chars[20], 16);
3914
+ chars[20] = arr20[hexVal % 4];
3915
+ return chars.join("").slice(1, -1).toLowerCase();
3916
+ }
3917
+ function isValidUuid(str) {
3918
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(str);
3919
+ }
3920
+ function formatAsBracedUuid(uuid) {
3921
+ const clean = uuid.replace(/[{}]/g, "").toUpperCase();
3922
+ return `{${clean}}`;
3923
+ }
3924
+
3925
+ // src/cli/commands/convert-compositions-to-entries.ts
3926
+ function createConvertCompositionsToEntriesCommand() {
3927
+ const command = new Command10("convert-compositions-to-entries");
3928
+ command.description(
3929
+ "Converts compositions into content entries, optionally flattening nested components into separate referenced entries."
3930
+ ).option(
3931
+ "--compositionTypes <types>",
3932
+ "Pipe-separated list of composition types to convert (e.g., ProductDetailPage|ArticlePage)"
3933
+ ).option(
3934
+ "--flattenComponentIds <types>",
3935
+ "Pipe-separated list of component types to flatten into separate entries (e.g., DetailHero|ArticleDetail)"
3936
+ ).hook("preAction", (thisCommand) => {
3937
+ const opts = thisCommand.opts();
3938
+ const requiredOptions = [
3939
+ { name: "compositionTypes", flag: "--compositionTypes" }
3940
+ ];
3941
+ const missing = requiredOptions.filter((opt) => !opts[opt.name]).map((opt) => opt.flag);
3942
+ if (missing.length > 0) {
3943
+ console.error(`error: missing required options: ${missing.join(", ")}`);
3944
+ process.exit(1);
3945
+ }
3946
+ }).action(async (opts, cmd) => {
3947
+ const globalOpts = cmd.optsWithGlobals();
3948
+ const options = {
3949
+ ...globalOpts,
3950
+ compositionTypes: opts.compositionTypes,
3951
+ flattenComponentIds: opts.flattenComponentIds
3952
+ };
3953
+ const logger = new Logger();
3954
+ const fileSystem = new FileSystemService();
3955
+ const componentService = new ComponentService(fileSystem);
3956
+ const compositionService = new CompositionService(fileSystem);
3957
+ const converter = new CompositionConverterService(
3958
+ fileSystem,
3959
+ componentService,
3960
+ compositionService,
3961
+ logger
3962
+ );
3963
+ try {
3964
+ const compositionTypes = options.compositionTypes.split("|").map((t) => t.trim()).filter((t) => t.length > 0);
3965
+ const flattenComponentIds = options.flattenComponentIds ? options.flattenComponentIds.split("|").map((t) => t.trim()).filter((t) => t.length > 0) : [];
3966
+ const result = await converter.convert({
3967
+ rootDir: options.rootDir,
3968
+ compositionsDir: options.compositionsDir,
3969
+ componentsDir: options.componentsDir,
3970
+ contentTypesDir: options.contentTypesDir,
3971
+ entriesDir: options.entriesDir,
3972
+ compositionTypes,
3973
+ flattenComponentIds,
3974
+ whatIf: options.whatIf ?? false,
3975
+ strict: options.strict ?? false
3976
+ });
3977
+ const flatInfo = result.entriesFromFlattened > 0 ? `, ${result.entriesFromFlattened} from flattened components` : "";
3978
+ logger.success(
3979
+ `${result.contentTypesWritten} content type(s), ${result.entriesFromCompositions} entry(ies) from compositions${flatInfo}`
3980
+ );
3981
+ } catch (error) {
3982
+ if (error instanceof TransformError) {
3983
+ logger.error(error.message);
3984
+ process.exit(1);
3985
+ }
3986
+ throw error;
3987
+ }
3988
+ });
3989
+ return command;
3990
+ }
3991
+
3577
3992
  // package.json
3578
3993
  var package_default = {
3579
3994
  name: "@uniformdev/transformer",
3580
- version: "1.1.9",
3995
+ version: "1.1.10",
3581
3996
  description: "CLI tool for transforming Uniform.dev serialization files offline",
3582
3997
  type: "module",
3583
3998
  bin: {
@@ -3646,7 +4061,7 @@ var package_default = {
3646
4061
  };
3647
4062
 
3648
4063
  // src/cli/index.ts
3649
- var program = new Command10();
4064
+ var program = new Command11();
3650
4065
  var appVersion = package_default.version;
3651
4066
  console.error(`uniform-transform v${appVersion}`);
3652
4067
  program.name("uniform-transform").description("CLI tool for transforming Uniform.dev serialization files offline").version(appVersion);
@@ -3668,5 +4083,6 @@ program.addCommand(createRenameComponentCommand());
3668
4083
  program.addCommand(createAddComponentCommand());
3669
4084
  program.addCommand(createAddComponentPatternCommand());
3670
4085
  program.addCommand(createPropagateRootComponentSlotCommand());
4086
+ program.addCommand(createConvertCompositionsToEntriesCommand());
3671
4087
  program.parse();
3672
4088
  //# sourceMappingURL=index.js.map