@uniformdev/transformer 1.1.34 → 1.1.36

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 Command16 } from "commander";
4
+ import { Command as Command17 } from "commander";
5
5
 
6
6
  // src/cli/commands/propagate-root-component-property.ts
7
7
  import { Command } from "commander";
@@ -659,6 +659,7 @@ var CompositionConverterService = class {
659
659
  );
660
660
  const contentType = this.generateContentType(component);
661
661
  contentTypeMap.set(rootType, contentType);
662
+ this.logger.debug(`Generated content type "${rootType}" with ${contentType.fields.length} field(s): ${contentType.fields.map((f) => f.id).join(", ")}`);
662
663
  } catch (error) {
663
664
  if (error instanceof ComponentNotFoundError) {
664
665
  this.logger.warn(`Component not found: ${rootType} \u2014 skipping content type generation`);
@@ -694,6 +695,7 @@ var CompositionConverterService = class {
694
695
  if (isRefType) {
695
696
  const contentType = this.generateContentType(component);
696
697
  targetContentTypeMap.set(targetType, contentType);
698
+ this.logger.debug(`Generated reference content type "${targetType}" with ${contentType.fields.length} field(s): ${contentType.fields.map((f) => f.id).join(", ")}`);
697
699
  }
698
700
  if (isBlockType) {
699
701
  let blockId = targetType;
@@ -723,6 +725,7 @@ var CompositionConverterService = class {
723
725
  blockContentType.id = blockId;
724
726
  blockContentType.name = needsRename ? `${blockContentType.name} Block` : blockContentType.name;
725
727
  targetContentTypeMap.set(blockId, blockContentType);
728
+ this.logger.debug(`Generated block content type "${blockId}" with ${blockContentType.fields.length} field(s): ${blockContentType.fields.map((f) => f.id).join(", ")}`);
726
729
  }
727
730
  if (!isBlockType) {
728
731
  if (!isRefType) {
@@ -764,6 +767,7 @@ var CompositionConverterService = class {
764
767
  }
765
768
  } else {
766
769
  entry = this.generateEntryFromComposition(composition);
770
+ this.logger.debug(`Generated entry "${entry.entry._id}" from composition "${compositionName}" (${compositionType}) with fields: ${Object.keys(entry.entry.fields).join(", ") || "(none)"}`);
767
771
  }
768
772
  const refsByType = /* @__PURE__ */ new Map();
769
773
  if (componentsToReferences.length > 0 && comp.slots) {
@@ -781,6 +785,7 @@ var CompositionConverterService = class {
781
785
  compositionName,
782
786
  strict
783
787
  );
788
+ this.logger.debug(`Composition "${compositionName}": found ${instances.length} instance(s) of reference type "${refType}"`);
784
789
  if (instances.length > 0) {
785
790
  refsByType.set(refType, instances);
786
791
  if (missingTargetTypes.includes(refType)) {
@@ -794,6 +799,7 @@ var CompositionConverterService = class {
794
799
  const refIds = [];
795
800
  for (const inst of instances) {
796
801
  const existingId = this.findExistingEntryBySourceItem(inst, sourceItemMap);
802
+ this.logger.debug(`Reference instance "${inst.determinisiticId}" (${refType}): ${existingId ? `reusing existing entry "${existingId}"` : `assigned new ID "${inst.determinisiticId}"`}`);
797
803
  refIds.push(existingId ?? inst.determinisiticId);
798
804
  }
799
805
  resolvedRefIds.set(refType, refIds);
@@ -823,6 +829,7 @@ var CompositionConverterService = class {
823
829
  compositionName,
824
830
  strict
825
831
  );
832
+ this.logger.debug(`Composition "${compositionName}": found ${instances.length} instance(s) of block type "${blockType}"`);
826
833
  if (instances.length > 0) {
827
834
  blocksByType.set(blockType, instances);
828
835
  if (missingTargetTypes.includes(blockType)) {
@@ -879,6 +886,7 @@ var CompositionConverterService = class {
879
886
  "UPDATE",
880
887
  `${entriesDir}/${existingId}.json (${refType}, merged fields from "${this.truncate(compositionName, 50)}")`
881
888
  );
889
+ this.logger.debug(`Merging fields [${Object.keys(inst.instance.parameters ?? {}).join(", ") || "(none)"}] into existing reference entry "${existingId}"`);
882
890
  if (!whatIf) {
883
891
  const existingEntry = await this.fileSystem.readFile(existingEntryPath2);
884
892
  if (existingEntry?.entry) {
@@ -903,6 +911,7 @@ var CompositionConverterService = class {
903
911
  "WRITE",
904
912
  `${entriesDir}/${inst.determinisiticId}.json (${refType} from "${this.truncate(compositionName, 50)}")`
905
913
  );
914
+ this.logger.debug(`Writing reference entry "${inst.determinisiticId}" (${refType}) with fields: ${Object.keys(flatEntry.entry.fields).join(", ") || "(none)"}`);
906
915
  if (!whatIf) {
907
916
  await this.fileSystem.writeFile(flatEntryPath, flatEntry);
908
917
  }
@@ -929,6 +938,7 @@ var CompositionConverterService = class {
929
938
  },
930
939
  localizable: false
931
940
  });
941
+ this.logger.debug(`Field "${refType}" (contentReference) added to content type "${contentType.id}"`);
932
942
  }
933
943
  }
934
944
  }
@@ -951,6 +961,7 @@ var CompositionConverterService = class {
951
961
  },
952
962
  localizable: false
953
963
  });
964
+ this.logger.debug(`Field "${resolvedBlockId}" ($block) added to content type "${contentType.id}"`);
954
965
  }
955
966
  }
956
967
  }
@@ -975,6 +986,7 @@ var CompositionConverterService = class {
975
986
  "WRITE",
976
987
  `${contentTypesDir}/${typeName}.json (${baseFieldCount} fields${extrasInfo})`
977
988
  );
989
+ this.logger.debug(`Content type "${typeName}" fields: ${contentType.fields.map((f) => `${f.id}:${f.type}`).join(", ")}`);
978
990
  if (!whatIf) {
979
991
  await this.fileSystem.writeFile(filePath, contentType);
980
992
  }
@@ -987,6 +999,7 @@ var CompositionConverterService = class {
987
999
  "WRITE",
988
1000
  `${contentTypesDir}/${typeName}.json (${contentType.fields.length} fields)`
989
1001
  );
1002
+ this.logger.debug(`Content type "${typeName}" fields: ${contentType.fields.map((f) => `${f.id}:${f.type}`).join(", ")}`);
990
1003
  if (!whatIf) {
991
1004
  await this.fileSystem.writeFile(filePath, contentType);
992
1005
  }
@@ -1352,6 +1365,7 @@ var PropertyPropagatorService = class {
1352
1365
  try {
1353
1366
  const { component: sourceComponent, filePath: sourceFilePath } = await this.componentService.loadComponent(fullComponentsDir, sourceType, findOptions);
1354
1367
  sourceComponents.push({ sourceType, sourceFilePath, sourceComponent });
1368
+ this.logger.debug(`Loaded source component "${sourceType}" from ${sourceFilePath}`);
1355
1369
  } catch (error) {
1356
1370
  if (error instanceof ComponentNotFoundError) {
1357
1371
  this.logger.warn(`Component not found: ${sourceType} (searched: ${fullComponentsDir})`);
@@ -1362,6 +1376,7 @@ var PropertyPropagatorService = class {
1362
1376
  }
1363
1377
  this.logger.info(`Loading component: ${targetComponentType}`);
1364
1378
  const { component: targetComponent, filePath: targetFilePath } = await this.componentService.loadComponent(fullComponentsDir, targetComponentType, findOptions);
1379
+ this.logger.debug(`Loaded target component "${targetComponentType}" from ${targetFilePath}`);
1365
1380
  const propertyNames = property.split("|").map((p) => p.trim()).filter((p) => p.length > 0);
1366
1381
  const resolvedParams = [];
1367
1382
  const resolvedNames = [];
@@ -1382,6 +1397,8 @@ var PropertyPropagatorService = class {
1382
1397
  if (!exists) {
1383
1398
  resolvedParams.push(param);
1384
1399
  resolvedNames.push(param.id);
1400
+ } else {
1401
+ this.logger.debug(`Parameter "${param.id}" already collected from another source \u2014 skipping duplicate`);
1385
1402
  }
1386
1403
  }
1387
1404
  }
@@ -1420,7 +1437,10 @@ var PropertyPropagatorService = class {
1420
1437
  targetGroup,
1421
1438
  findOptions
1422
1439
  );
1440
+ this.logger.debug(`Group "${targetGroup}" (id: ${groupId}) created on component ${targetComponentType}`);
1423
1441
  componentModified = true;
1442
+ } else {
1443
+ this.logger.debug(`Group "${targetGroup}" (id: ${groupId}) already exists on ${targetComponentType}`);
1424
1444
  }
1425
1445
  for (const param of resolvedParams) {
1426
1446
  const existingParam = this.componentService.findParameter(
@@ -1445,6 +1465,7 @@ var PropertyPropagatorService = class {
1445
1465
  param.id,
1446
1466
  findOptions
1447
1467
  );
1468
+ this.logger.debug(`Parameter "${param.id}" (type: ${param.type}) added to component ${targetComponentType} in group "${targetGroup}"`);
1448
1469
  componentModified = true;
1449
1470
  } else {
1450
1471
  this.logger.info(`Parameter "${param.id}" already exists on ${targetComponentType}`);
@@ -1464,7 +1485,10 @@ var PropertyPropagatorService = class {
1464
1485
  param.id,
1465
1486
  findOptions
1466
1487
  );
1488
+ this.logger.debug(`Parameter "${param.id}" added to group "${targetGroup}" on ${targetComponentType} (was missing from group)`);
1467
1489
  componentModified = true;
1490
+ } else {
1491
+ this.logger.debug(`Parameter "${param.id}" already in group "${targetGroup}" on ${targetComponentType} \u2014 no change`);
1468
1492
  }
1469
1493
  }
1470
1494
  }
@@ -1477,6 +1501,7 @@ var PropertyPropagatorService = class {
1477
1501
  compositionTypes,
1478
1502
  findOptions
1479
1503
  );
1504
+ this.logger.debug(`Found ${compositions.length} composition(s) matching types [${compositionTypes.join(", ")}]`);
1480
1505
  let modifiedCompositions = 0;
1481
1506
  let propagatedInstances = 0;
1482
1507
  for (const { composition, filePath } of compositions) {
@@ -1487,6 +1512,7 @@ var PropertyPropagatorService = class {
1487
1512
  findOptions
1488
1513
  );
1489
1514
  if (instances.length === 0) {
1515
+ this.logger.debug(`Skipping "${filePath}": no instances of ${targetComponentType} found`);
1490
1516
  continue;
1491
1517
  }
1492
1518
  const valuesToPropagate = {};
@@ -1496,6 +1522,7 @@ var PropertyPropagatorService = class {
1496
1522
  }
1497
1523
  }
1498
1524
  if (Object.keys(valuesToPropagate).length === 0) {
1525
+ this.logger.debug(`Skipping "${filePath}": ${instances.length} instance(s) of ${targetComponentType} found but none of [${resolvedNames.join(", ")}] present in root overrides`);
1499
1526
  continue;
1500
1527
  }
1501
1528
  let compositionModified = false;
@@ -1507,9 +1534,11 @@ var PropertyPropagatorService = class {
1507
1534
  this.compositionService.setInstanceParameters(instance, clonedValues);
1508
1535
  compositionModified = true;
1509
1536
  propagatedInstances++;
1537
+ const updatedParamNames = Object.keys(valuesToPropagate).join(", ");
1510
1538
  instanceUpdates.push(
1511
- `${targetComponentType} "${instanceName}": ${Object.keys(valuesToPropagate).join(", ")}`
1539
+ `${targetComponentType} "${instanceName}": ${updatedParamNames}`
1512
1540
  );
1541
+ this.logger.debug(`Component ${targetComponentType} "${instanceName}": parameters ${updatedParamNames} updated`);
1513
1542
  }
1514
1543
  if (compositionModified) {
1515
1544
  this.logger.action(whatIf, "UPDATE", `composition/${relativePath}`);
@@ -1532,7 +1561,10 @@ var PropertyPropagatorService = class {
1532
1561
  if (exists) {
1533
1562
  this.logger.action(whatIf, "DELETE", `Parameter "${param.id}" from ${sourceType}`);
1534
1563
  modifiedSource = this.componentService.removeParameter(modifiedSource, param.id, findOptions);
1564
+ this.logger.debug(`Parameter "${param.id}" removed from component ${sourceType}`);
1535
1565
  sourceComponentModified = true;
1566
+ } else {
1567
+ this.logger.debug(`Parameter "${param.id}" not found on source component "${sourceType}" \u2014 nothing to delete`);
1536
1568
  }
1537
1569
  }
1538
1570
  const beforeGroupCount = modifiedSource.parameters?.filter(
@@ -1571,6 +1603,8 @@ var PropertyPropagatorService = class {
1571
1603
  if (!whatIf) {
1572
1604
  await this.compositionService.saveComposition(filePath, composition);
1573
1605
  }
1606
+ } else {
1607
+ this.logger.debug(`No root overrides matching [${resolvedNames.join(", ")}] in composition/${relativePath}`);
1574
1608
  }
1575
1609
  }
1576
1610
  }
@@ -1598,9 +1632,16 @@ var PropertyPropagatorService = class {
1598
1632
  // src/cli/logger.ts
1599
1633
  import chalk from "chalk";
1600
1634
  var Logger = class {
1635
+ constructor(verbose = false) {
1636
+ this.verbose = verbose;
1637
+ }
1601
1638
  info(message) {
1602
1639
  console.log(`${chalk.blue("[INFO]")} ${message}`);
1603
1640
  }
1641
+ debug(message) {
1642
+ if (!this.verbose) return;
1643
+ console.log(`${chalk.magenta("[DEBUG]")} ${message}`);
1644
+ }
1604
1645
  success(message) {
1605
1646
  console.log(`${chalk.green("[DONE]")} ${message}`);
1606
1647
  }
@@ -1636,7 +1677,7 @@ function createPropagateRootComponentPropertyCommand() {
1636
1677
  ).option(
1637
1678
  "--deleteSourceParameter",
1638
1679
  "Delete the original parameters from the source component after propagation"
1639
- ).hook("preAction", (thisCommand) => {
1680
+ ).option("--verbose", "Enable verbose output with detailed progress information").hook("preAction", (thisCommand) => {
1640
1681
  const opts = thisCommand.opts();
1641
1682
  const requiredOptions = [
1642
1683
  { name: "compositionType", flag: "--compositionType" },
@@ -1659,7 +1700,7 @@ function createPropagateRootComponentPropertyCommand() {
1659
1700
  targetGroup: opts.targetGroup,
1660
1701
  deleteSourceParameter: opts.deleteSourceParameter
1661
1702
  };
1662
- const logger = new Logger();
1703
+ const logger = new Logger(opts.verbose ?? false);
1663
1704
  const fileSystem = new FileSystemService();
1664
1705
  const componentService = new ComponentService(fileSystem);
1665
1706
  const compositionService = new CompositionService(fileSystem);
@@ -1686,6 +1727,10 @@ function createPropagateRootComponentPropertyCommand() {
1686
1727
  `Modified ${result.modifiedComponents} component, ${result.modifiedCompositions} compositions`
1687
1728
  );
1688
1729
  } catch (error) {
1730
+ if (error instanceof ComponentNotFoundError || error instanceof PropertyNotFoundError) {
1731
+ logger.warn(error.message);
1732
+ return;
1733
+ }
1689
1734
  if (error instanceof TransformError) {
1690
1735
  logger.error(error.message);
1691
1736
  process.exit(1);
@@ -3215,6 +3260,10 @@ function createRenameSlotCommand() {
3215
3260
  `Renamed slot: ${result.compositionsModified} composition(s), ${result.compositionPatternsModified} composition pattern(s), ${result.componentPatternsModified} component pattern(s) updated`
3216
3261
  );
3217
3262
  } catch (error) {
3263
+ if (error instanceof ComponentNotFoundError) {
3264
+ logger.warn(error.message);
3265
+ return;
3266
+ }
3218
3267
  if (error instanceof TransformError) {
3219
3268
  logger.error(error.message);
3220
3269
  process.exit(1);
@@ -3492,6 +3541,10 @@ function createRenameComponentCommand() {
3492
3541
  `Renamed component: ${result.allowedComponentsUpdated} allowedComponents ref(s), ${result.compositionsModified} composition(s), ${result.compositionPatternsModified} composition pattern(s), ${result.componentPatternsModified} component pattern(s) updated`
3493
3542
  );
3494
3543
  } catch (error) {
3544
+ if (error instanceof ComponentNotFoundError) {
3545
+ logger.warn(error.message);
3546
+ return;
3547
+ }
3495
3548
  if (error instanceof TransformError) {
3496
3549
  logger.error(error.message);
3497
3550
  process.exit(1);
@@ -3603,16 +3656,7 @@ var ComponentAdderService = class {
3603
3656
  throw new TransformError("parentComponentType cannot be empty");
3604
3657
  }
3605
3658
  this.logger.info(`Validating new component: ${newComponentType}`);
3606
- try {
3607
- await this.componentService.loadComponent(fullComponentsDir, newComponentType, findOptions);
3608
- } catch (error) {
3609
- if (error instanceof ComponentNotFoundError) {
3610
- throw new TransformError(
3611
- `Component type "${newComponentType}" not found in ${fullComponentsDir}`
3612
- );
3613
- }
3614
- throw error;
3615
- }
3659
+ await this.componentService.loadComponent(fullComponentsDir, newComponentType, findOptions);
3616
3660
  let allowedComponentsUpdated = false;
3617
3661
  const resolvedParentTypes = [];
3618
3662
  for (const parentType of parentTypes) {
@@ -4042,6 +4086,10 @@ function createAddComponentCommand() {
4042
4086
  `Added component: ${result.allowedComponentsUpdated ? "1 component definition updated, " : ""}${result.instancesAdded} instance(s) added across ${result.compositionsModified} composition(s), ${result.compositionPatternsModified} composition pattern(s), ${result.componentPatternsModified} component pattern(s)`
4043
4087
  );
4044
4088
  } catch (error) {
4089
+ if (error instanceof ComponentNotFoundError) {
4090
+ logger.warn(error.message);
4091
+ return;
4092
+ }
4045
4093
  if (error instanceof TransformError) {
4046
4094
  logger.error(error.message);
4047
4095
  process.exit(1);
@@ -4097,6 +4145,10 @@ function createAddComponentPatternCommand() {
4097
4145
  `Added component pattern: ${result.allowedComponentsUpdated ? "1 component definition updated, " : ""}${result.instancesAdded} instance(s) added across ${result.compositionsModified} composition(s), ${result.compositionPatternsModified} composition pattern(s), ${result.componentPatternsModified} component pattern(s)`
4098
4146
  );
4099
4147
  } catch (error) {
4148
+ if (error instanceof ComponentNotFoundError) {
4149
+ logger.warn(error.message);
4150
+ return;
4151
+ }
4100
4152
  if (error instanceof TransformError) {
4101
4153
  logger.error(error.message);
4102
4154
  process.exit(1);
@@ -4407,6 +4459,10 @@ function createPropagateRootComponentSlotCommand() {
4407
4459
  `Modified ${result.modifiedComponents} component(s), ${result.modifiedCompositions} composition(s)`
4408
4460
  );
4409
4461
  } catch (error) {
4462
+ if (error instanceof ComponentNotFoundError) {
4463
+ logger.warn(error.message);
4464
+ return;
4465
+ }
4410
4466
  if (error instanceof TransformError) {
4411
4467
  logger.error(error.message);
4412
4468
  process.exit(1);
@@ -4432,7 +4488,7 @@ function createConvertCompositionsToEntriesCommand() {
4432
4488
  ).option(
4433
4489
  "--componentsToBlocks <types>",
4434
4490
  "Pipe-separated list of component types to convert into inline blocks (e.g., DetailHero|ArticleDetail)"
4435
- ).hook("preAction", (thisCommand) => {
4491
+ ).option("--verbose", "Enable verbose output with detailed progress information").hook("preAction", (thisCommand) => {
4436
4492
  const opts = thisCommand.opts();
4437
4493
  const requiredOptions = [
4438
4494
  { name: "compositionTypes", flag: "--compositionTypes" }
@@ -4450,7 +4506,7 @@ function createConvertCompositionsToEntriesCommand() {
4450
4506
  componentsToReferences: opts.componentsToReferences,
4451
4507
  componentsToBlocks: opts.componentsToBlocks
4452
4508
  };
4453
- const logger = new Logger();
4509
+ const logger = new Logger(opts.verbose ?? false);
4454
4510
  const fileSystem = new FileSystemService();
4455
4511
  const componentService = new ComponentService(fileSystem);
4456
4512
  const compositionService = new CompositionService(fileSystem);
@@ -4484,6 +4540,10 @@ function createConvertCompositionsToEntriesCommand() {
4484
4540
  `${result.contentTypesWritten} content type(s), ${result.entriesFromCompositions} entry(ies) from compositions${refInfo}${reusedInfo}${blocksInfo}`
4485
4541
  );
4486
4542
  } catch (error) {
4543
+ if (error instanceof ComponentNotFoundError) {
4544
+ logger.warn(error.message);
4545
+ return;
4546
+ }
4487
4547
  if (error instanceof TransformError) {
4488
4548
  logger.error(error.message);
4489
4549
  process.exit(1);
@@ -4811,7 +4871,7 @@ function createRemoveParameterCommand() {
4811
4871
  aggregate.compositionPatternsModified += result.compositionPatternsModified;
4812
4872
  aggregate.componentPatternsModified += result.componentPatternsModified;
4813
4873
  } catch (error) {
4814
- if (hasWildcard && (error instanceof ComponentNotFoundError || error instanceof PropertyNotFoundError)) {
4874
+ if (error instanceof ComponentNotFoundError || error instanceof PropertyNotFoundError) {
4815
4875
  logger.warn(error.message);
4816
4876
  continue;
4817
4877
  }
@@ -5254,6 +5314,10 @@ function createAddComponentParameterCommand() {
5254
5314
  `${result.parameterReplaced ? "Replaced" : "Added"} parameter "${options.parameterId}" on component "${options.componentId}"`
5255
5315
  );
5256
5316
  } catch (error) {
5317
+ if (error instanceof ComponentNotFoundError || error instanceof PropertyNotFoundError) {
5318
+ logger.warn(error.message);
5319
+ return;
5320
+ }
5257
5321
  if (error instanceof TransformError) {
5258
5322
  logger.error(error.message);
5259
5323
  process.exit(1);
@@ -5832,6 +5896,10 @@ function createFlattenBlockFieldCommand() {
5832
5896
  `Flattened parameter "${options.parameterId}" on component "${options.componentId}": ${result.compositionsModified} composition(s), ${result.compositionPatternsModified} composition pattern(s), ${result.componentPatternsModified} component pattern(s) updated`
5833
5897
  );
5834
5898
  } catch (error) {
5899
+ if (error instanceof ComponentNotFoundError || error instanceof PropertyNotFoundError) {
5900
+ logger.warn(error.message);
5901
+ return;
5902
+ }
5835
5903
  if (error instanceof TransformError) {
5836
5904
  logger.error(error.message);
5837
5905
  process.exit(1);
@@ -5842,10 +5910,171 @@ function createFlattenBlockFieldCommand() {
5842
5910
  return command;
5843
5911
  }
5844
5912
 
5913
+ // src/cli/commands/remove-orphan-entries.ts
5914
+ import { Command as Command16 } from "commander";
5915
+
5916
+ // src/core/services/orphan-entry-remover.service.ts
5917
+ var OrphanEntryRemoverService = class {
5918
+ constructor(fileSystem, logger) {
5919
+ this.fileSystem = fileSystem;
5920
+ this.logger = logger;
5921
+ }
5922
+ async remove(options) {
5923
+ const { rootDir, entriesDir, rootContentTypes, whatIf, strict } = options;
5924
+ const entriesDirFull = this.fileSystem.resolvePath(rootDir, entriesDir);
5925
+ const dirExists = await this.fileSystem.fileExists(entriesDirFull);
5926
+ if (!dirExists) {
5927
+ this.logger.warn(`Entries directory not found: ${entriesDir} \u2014 nothing to do`);
5928
+ return { totalEntries: 0, whitelistedEntries: 0, removedEntries: 0 };
5929
+ }
5930
+ const entryFiles = await this.fileSystem.findFiles(entriesDirFull, "**/*.{json,yaml,yml}");
5931
+ if (entryFiles.length === 0) {
5932
+ this.logger.warn("No entry files found \u2014 nothing to do");
5933
+ return { totalEntries: 0, whitelistedEntries: 0, removedEntries: 0 };
5934
+ }
5935
+ this.logger.info(`Loaded ${entryFiles.length} entry file(s)`);
5936
+ const entryMap = /* @__PURE__ */ new Map();
5937
+ for (const filePath of entryFiles) {
5938
+ let entryData;
5939
+ try {
5940
+ entryData = await this.fileSystem.readFile(filePath);
5941
+ } catch {
5942
+ this.logger.warn(`Could not read entry file: ${filePath} \u2014 skipping`);
5943
+ continue;
5944
+ }
5945
+ if (!entryData?.entry?._id) {
5946
+ this.logger.warn(`Entry file missing entry._id: ${filePath} \u2014 skipping`);
5947
+ continue;
5948
+ }
5949
+ entryMap.set(entryData.entry._id, { filePath, entryData });
5950
+ }
5951
+ this.logger.info(`Root content types: ${rootContentTypes.join(", ")}`);
5952
+ const whitelist = /* @__PURE__ */ new Set();
5953
+ for (const [entryId, { entryData }] of entryMap) {
5954
+ if (this.matchesContentType(entryData.entry.type, rootContentTypes, strict)) {
5955
+ whitelist.add(entryId);
5956
+ }
5957
+ }
5958
+ this.logger.info(`Whitelisted ${whitelist.size} entry(ies) from root content types`);
5959
+ let round = 0;
5960
+ let frontier = [...whitelist];
5961
+ while (frontier.length > 0) {
5962
+ round++;
5963
+ const nextFrontier = [];
5964
+ for (const entryId of frontier) {
5965
+ const entry = entryMap.get(entryId);
5966
+ if (!entry) continue;
5967
+ const referencedIds = this.extractReferencedEntryIds(entry.entryData);
5968
+ for (const refId of referencedIds) {
5969
+ if (!whitelist.has(refId) && entryMap.has(refId)) {
5970
+ whitelist.add(refId);
5971
+ nextFrontier.push(refId);
5972
+ }
5973
+ }
5974
+ }
5975
+ if (nextFrontier.length > 0) {
5976
+ this.logger.info(`BFS round ${round}: added ${nextFrontier.length} entry(ies) via references`);
5977
+ }
5978
+ frontier = nextFrontier;
5979
+ }
5980
+ this.logger.info(`BFS stable after ${round} round(s)`);
5981
+ let removedEntries = 0;
5982
+ for (const [entryId, { filePath, entryData }] of entryMap) {
5983
+ if (!whitelist.has(entryId)) {
5984
+ const relPath = this.fileSystem.joinPath(entriesDir, this.fileSystem.getBasename(filePath));
5985
+ this.logger.action(whatIf, "DELETE", `${relPath} (${entryData.entry.type})`);
5986
+ if (!whatIf) {
5987
+ this.fileSystem.deleteFile(filePath);
5988
+ }
5989
+ removedEntries++;
5990
+ }
5991
+ }
5992
+ return {
5993
+ totalEntries: entryMap.size,
5994
+ whitelistedEntries: whitelist.size,
5995
+ removedEntries
5996
+ };
5997
+ }
5998
+ extractReferencedEntryIds(entryData) {
5999
+ const ids = [];
6000
+ if (entryData.entry._dataResources) {
6001
+ for (const resource of Object.values(entryData.entry._dataResources)) {
6002
+ if (resource.type === "uniformContentInternalReference" && resource.variables?.entryIds) {
6003
+ const entryIds = resource.variables.entryIds.split(",").map((s) => s.trim()).filter(Boolean);
6004
+ ids.push(...entryIds);
6005
+ }
6006
+ }
6007
+ }
6008
+ if (entryData.entry.fields) {
6009
+ for (const field of Object.values(entryData.entry.fields)) {
6010
+ if (field.type === "contentReference" && Array.isArray(field.value)) {
6011
+ for (const id of field.value) {
6012
+ if (typeof id === "string") {
6013
+ ids.push(id);
6014
+ }
6015
+ }
6016
+ }
6017
+ }
6018
+ }
6019
+ return ids;
6020
+ }
6021
+ matchesContentType(entryType, rootContentTypes, strict) {
6022
+ for (const rootType of rootContentTypes) {
6023
+ if (strict) {
6024
+ if (entryType === rootType) return true;
6025
+ } else {
6026
+ if (entryType.toLowerCase() === rootType.toLowerCase()) return true;
6027
+ }
6028
+ }
6029
+ return false;
6030
+ }
6031
+ };
6032
+
6033
+ // src/cli/commands/remove-orphan-entries.ts
6034
+ function createRemoveOrphanEntriesCommand() {
6035
+ const command = new Command16("remove-orphan-entries");
6036
+ command.description(
6037
+ "Removes entry files that are not reachable from root content-type entries by following contentReference links transitively."
6038
+ ).option(
6039
+ "--rootContentTypes <types>",
6040
+ 'Pipe-separated list of content-type IDs whose entries are treated as roots and never deleted (e.g. "HomePage|BlogPost")'
6041
+ ).hook("preAction", (thisCommand) => {
6042
+ const opts = thisCommand.opts();
6043
+ const requiredOptions = [{ name: "rootContentTypes", flag: "--rootContentTypes" }];
6044
+ const missing = requiredOptions.filter((opt) => !opts[opt.name]).map((opt) => opt.flag);
6045
+ if (missing.length > 0) {
6046
+ console.error(`error: missing required options: ${missing.join(", ")}`);
6047
+ process.exit(1);
6048
+ }
6049
+ }).action(async (opts, cmd) => {
6050
+ const globalOpts = cmd.optsWithGlobals();
6051
+ const options = {
6052
+ ...globalOpts,
6053
+ rootContentTypes: opts.rootContentTypes
6054
+ };
6055
+ const logger = new Logger();
6056
+ logger.info(`rootContentTypes: ${options.rootContentTypes}`);
6057
+ const rootContentTypes = options.rootContentTypes.split("|").map((t) => t.trim()).filter(Boolean);
6058
+ const fileSystem = new FileSystemService();
6059
+ const service = new OrphanEntryRemoverService(fileSystem, logger);
6060
+ const result = await service.remove({
6061
+ rootDir: options.rootDir,
6062
+ entriesDir: options.entriesDir,
6063
+ rootContentTypes,
6064
+ whatIf: options.whatIf ?? false,
6065
+ strict: options.strict ?? false
6066
+ });
6067
+ logger.success(
6068
+ `Removed ${result.removedEntries} orphan entry(ies). ${result.whitelistedEntries} entry(ies) retained.`
6069
+ );
6070
+ });
6071
+ return command;
6072
+ }
6073
+
5845
6074
  // package.json
5846
6075
  var package_default = {
5847
6076
  name: "@uniformdev/transformer",
5848
- version: "1.1.34",
6077
+ version: "1.1.36",
5849
6078
  description: "CLI tool for transforming Uniform.dev serialization files offline",
5850
6079
  type: "module",
5851
6080
  bin: {
@@ -5914,7 +6143,7 @@ var package_default = {
5914
6143
  };
5915
6144
 
5916
6145
  // src/cli/index.ts
5917
- var program = new Command16();
6146
+ var program = new Command17();
5918
6147
  var appVersion = package_default.version;
5919
6148
  console.error(`uniform-transform v${appVersion}`);
5920
6149
  program.name("uniform-transform").description("CLI tool for transforming Uniform.dev serialization files offline").version(appVersion);
@@ -5942,5 +6171,6 @@ program.addCommand(createRemoveFieldCommand());
5942
6171
  program.addCommand(createAddComponentParameterCommand());
5943
6172
  program.addCommand(createAddContentTypeFieldCommand());
5944
6173
  program.addCommand(createFlattenBlockFieldCommand());
6174
+ program.addCommand(createRemoveOrphanEntriesCommand());
5945
6175
  program.parse();
5946
6176
  //# sourceMappingURL=index.js.map