@uniformdev/transformer 1.1.44 → 1.1.45

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 Command21 } from "commander";
4
+ import { Command as Command22 } from "commander";
5
5
 
6
6
  // src/cli/commands/propagate-root-component-property.ts
7
7
  import { Command } from "commander";
@@ -7141,10 +7141,295 @@ function createSplitContentTypeCommand() {
7141
7141
  return command;
7142
7142
  }
7143
7143
 
7144
+ // src/cli/commands/propagate-root-slot.ts
7145
+ import { Command as Command21 } from "commander";
7146
+
7147
+ // src/core/services/root-slot-propagator.service.ts
7148
+ var RootSlotPropagatorService = class {
7149
+ constructor(fileSystem, componentService, compositionService, logger) {
7150
+ this.fileSystem = fileSystem;
7151
+ this.componentService = componentService;
7152
+ this.compositionService = compositionService;
7153
+ this.logger = logger;
7154
+ }
7155
+ async propagate(options) {
7156
+ const {
7157
+ rootDir,
7158
+ componentsDir,
7159
+ compositionsDir,
7160
+ compositionType,
7161
+ slot,
7162
+ targetComponentType,
7163
+ whatIf,
7164
+ strict
7165
+ } = options;
7166
+ const findOptions = { strict };
7167
+ const compositionTypes = this.parseSeparatedValues(compositionType, strict);
7168
+ const slotNames = splitList(slot);
7169
+ const fullComponentsDir = this.fileSystem.resolvePath(rootDir, componentsDir);
7170
+ const fullCompositionsDir = this.fileSystem.resolvePath(rootDir, compositionsDir);
7171
+ const sourceComponents = [];
7172
+ for (const sourceType of compositionTypes) {
7173
+ this.logger.info(`Loading component: ${sourceType}`);
7174
+ try {
7175
+ const { component: sourceComponent, filePath: sourceFilePath } = await this.componentService.loadComponent(fullComponentsDir, sourceType, findOptions);
7176
+ for (const slotName of slotNames) {
7177
+ const slotDef = this.componentService.findSlot(sourceComponent, slotName, findOptions);
7178
+ if (!slotDef) {
7179
+ this.logger.warn(`Slot "${slotName}" not found on component "${sourceType}" \u2014 skipping`);
7180
+ }
7181
+ }
7182
+ sourceComponents.push({ sourceType, sourceFilePath, sourceComponent });
7183
+ this.logger.debug(`Loaded source component "${sourceType}" from ${sourceFilePath}`);
7184
+ } catch (error) {
7185
+ if (error instanceof ComponentNotFoundError) {
7186
+ this.logger.warn(`Component not found: ${sourceType} (searched: ${fullComponentsDir})`);
7187
+ continue;
7188
+ }
7189
+ throw error;
7190
+ }
7191
+ }
7192
+ this.logger.info(`Loading component: ${targetComponentType}`);
7193
+ const { component: targetComponent, filePath: targetFilePath } = await this.componentService.loadComponent(fullComponentsDir, targetComponentType, findOptions);
7194
+ this.logger.debug(`Loaded target component "${targetComponentType}" from ${targetFilePath}`);
7195
+ const compositions = await this.compositionService.findCompositionsByTypes(
7196
+ fullCompositionsDir,
7197
+ compositionTypes,
7198
+ findOptions
7199
+ );
7200
+ this.logger.debug(
7201
+ `Found ${compositions.length} composition(s) matching types [${compositionTypes.join(", ")}]`
7202
+ );
7203
+ const componentTypesInSlot = /* @__PURE__ */ new Set();
7204
+ for (const { composition } of compositions) {
7205
+ const rootSlots = composition.composition.slots ?? {};
7206
+ for (const slotName of slotNames) {
7207
+ const slotContent = rootSlots[slotName] ?? [];
7208
+ for (const instance of slotContent) {
7209
+ if (instance.type) {
7210
+ componentTypesInSlot.add(instance.type);
7211
+ }
7212
+ }
7213
+ }
7214
+ }
7215
+ const collectedTypes = Array.from(componentTypesInSlot).sort();
7216
+ this.logger.info(
7217
+ `Collected component types from slot contents: [${collectedTypes.join(", ")}]`
7218
+ );
7219
+ let modifiedComponent = { ...targetComponent };
7220
+ let componentModified = false;
7221
+ for (const slotName of slotNames) {
7222
+ const existingSlot = this.componentService.findSlot(modifiedComponent, slotName, findOptions);
7223
+ if (!existingSlot) {
7224
+ this.logger.action(whatIf, "CREATE", `Slot "${slotName}" on ${targetComponentType}`);
7225
+ modifiedComponent = this.componentService.addSlot(modifiedComponent, {
7226
+ id: slotName,
7227
+ name: slotName,
7228
+ allowedComponents: collectedTypes
7229
+ });
7230
+ this.logger.debug(
7231
+ `Slot "${slotName}" created on "${targetComponentType}" with allowedComponents: [${collectedTypes.join(", ")}]`
7232
+ );
7233
+ componentModified = true;
7234
+ } else {
7235
+ const existingAllowed = existingSlot.allowedComponents ?? [];
7236
+ const newTypes = collectedTypes.filter(
7237
+ (t) => !existingAllowed.some(
7238
+ (a) => strict ? a === t : a.toLowerCase() === t.toLowerCase()
7239
+ )
7240
+ );
7241
+ if (newTypes.length > 0) {
7242
+ const merged = [...existingAllowed, ...newTypes].sort();
7243
+ this.logger.action(
7244
+ whatIf,
7245
+ "UPDATE",
7246
+ `Slot "${slotName}" allowedComponents on ${targetComponentType}`
7247
+ );
7248
+ this.logger.detail(`\u2192 Adding: ${newTypes.join(", ")}`);
7249
+ modifiedComponent = this.componentService.updateSlotAllowedComponents(
7250
+ modifiedComponent,
7251
+ slotName,
7252
+ merged,
7253
+ findOptions
7254
+ );
7255
+ componentModified = true;
7256
+ } else {
7257
+ this.logger.info(
7258
+ `Slot "${slotName}" on "${targetComponentType}" already has all required allowedComponents`
7259
+ );
7260
+ }
7261
+ }
7262
+ }
7263
+ if (componentModified && !whatIf) {
7264
+ await this.componentService.saveComponent(targetFilePath, modifiedComponent);
7265
+ }
7266
+ let modifiedCompositions = 0;
7267
+ let propagatedInstances = 0;
7268
+ for (const { composition, filePath } of compositions) {
7269
+ const rootSlots = composition.composition.slots ?? {};
7270
+ const instances = this.compositionService.findComponentInstances(
7271
+ composition,
7272
+ targetComponentType,
7273
+ findOptions
7274
+ );
7275
+ if (instances.length === 0) {
7276
+ this.logger.debug(
7277
+ `Skipping "${filePath}": no instances of ${targetComponentType} found`
7278
+ );
7279
+ continue;
7280
+ }
7281
+ const componentsToPropagate = [];
7282
+ for (const slotName of slotNames) {
7283
+ const slotContent = rootSlots[slotName] ?? [];
7284
+ componentsToPropagate.push(...slotContent);
7285
+ }
7286
+ if (componentsToPropagate.length === 0) {
7287
+ this.logger.debug(
7288
+ `Skipping "${filePath}": slot(s) [${slotNames.join(", ")}] are empty`
7289
+ );
7290
+ continue;
7291
+ }
7292
+ let compositionModified = false;
7293
+ const relativePath = filePath.replace(fullCompositionsDir, "").replace(/^[/\\]/, "");
7294
+ const instanceUpdates = [];
7295
+ for (const { instance, instanceId } of instances) {
7296
+ const instanceName = instance._id ?? instanceId;
7297
+ for (const slotName of slotNames) {
7298
+ const slotContent = rootSlots[slotName] ?? [];
7299
+ if (slotContent.length === 0) continue;
7300
+ const clonedComponents = regenerateIds(
7301
+ slotContent,
7302
+ `${instanceName}.${slotName}`
7303
+ );
7304
+ this.addComponentsToInstanceSlot(instance, slotName, clonedComponents);
7305
+ }
7306
+ compositionModified = true;
7307
+ propagatedInstances++;
7308
+ instanceUpdates.push(
7309
+ `${targetComponentType} "${instanceName}": ${componentsToPropagate.length} component(s) \u2192 [${slotNames.join(", ")}]`
7310
+ );
7311
+ this.logger.debug(
7312
+ `Component ${targetComponentType} "${instanceName}": slot(s) [${slotNames.join(", ")}] updated`
7313
+ );
7314
+ }
7315
+ if (compositionModified) {
7316
+ this.logger.action(whatIf, "UPDATE", `composition/${relativePath}`);
7317
+ for (const update of instanceUpdates) {
7318
+ this.logger.detail(`\u2192 ${update}`);
7319
+ }
7320
+ if (!whatIf) {
7321
+ await this.compositionService.saveComposition(filePath, composition);
7322
+ }
7323
+ modifiedCompositions++;
7324
+ }
7325
+ }
7326
+ return {
7327
+ modifiedComponents: componentModified ? 1 : 0,
7328
+ modifiedCompositions,
7329
+ propagatedInstances
7330
+ };
7331
+ }
7332
+ addComponentsToInstanceSlot(instance, slotName, components) {
7333
+ if (!instance.slots) {
7334
+ instance.slots = {};
7335
+ }
7336
+ if (!instance.slots[slotName]) {
7337
+ instance.slots[slotName] = [];
7338
+ }
7339
+ instance.slots[slotName].push(...components);
7340
+ }
7341
+ parseSeparatedValues(value, strict) {
7342
+ const entries = splitList(value);
7343
+ const normalized = [];
7344
+ for (const entry of entries) {
7345
+ const exists = normalized.some(
7346
+ (existing) => strict ? existing === entry : existing.toLowerCase() === entry.toLowerCase()
7347
+ );
7348
+ if (!exists) {
7349
+ normalized.push(entry);
7350
+ }
7351
+ }
7352
+ return normalized;
7353
+ }
7354
+ };
7355
+
7356
+ // src/cli/commands/propagate-root-slot.ts
7357
+ function createPropagateRootSlotCommand() {
7358
+ const command = new Command21("propagate-root-slot");
7359
+ command.description(
7360
+ "Propagates a named slot (with all its component instances) from the root of compositions to every instance of a target component type, keeping the same slot name and updating allowedComponents based on actual slot contents."
7361
+ ).option(
7362
+ "--compositionType <type>",
7363
+ "The composition type(s) to process. Supports pipe-separated lists (e.g., RelatedTopicPage|LandingPage)"
7364
+ ).option(
7365
+ "--slot <slot>",
7366
+ "The slot name to propagate. The same slot name is used on both the source root and the target component. Supports pipe-separated lists."
7367
+ ).option(
7368
+ "--targetComponentType <type>",
7369
+ "The component type that will receive the slot contents"
7370
+ ).option("--verbose", "Enable verbose output with detailed progress information").hook("preAction", (thisCommand) => {
7371
+ const opts = thisCommand.opts();
7372
+ const requiredOptions = [
7373
+ { name: "compositionType", flag: "--compositionType" },
7374
+ { name: "slot", flag: "--slot" },
7375
+ { name: "targetComponentType", flag: "--targetComponentType" }
7376
+ ];
7377
+ const missing = requiredOptions.filter((opt) => !opts[opt.name]).map((opt) => opt.flag);
7378
+ if (missing.length > 0) {
7379
+ console.error(`error: missing required options: ${missing.join(", ")}`);
7380
+ process.exit(1);
7381
+ }
7382
+ }).action(async (opts, cmd) => {
7383
+ const globalOpts = cmd.optsWithGlobals();
7384
+ const options = {
7385
+ ...globalOpts,
7386
+ compositionType: opts.compositionType,
7387
+ slot: opts.slot,
7388
+ targetComponentType: opts.targetComponentType
7389
+ };
7390
+ const logger = new Logger(opts.verbose ?? false);
7391
+ const fileSystem = new FileSystemService();
7392
+ const componentService = new ComponentService(fileSystem);
7393
+ const compositionService = new CompositionService(fileSystem);
7394
+ const propagator = new RootSlotPropagatorService(
7395
+ fileSystem,
7396
+ componentService,
7397
+ compositionService,
7398
+ logger
7399
+ );
7400
+ try {
7401
+ const result = await propagator.propagate({
7402
+ rootDir: options.rootDir,
7403
+ componentsDir: options.componentsDir,
7404
+ compositionsDir: options.compositionsDir,
7405
+ compositionType: options.compositionType,
7406
+ slot: options.slot,
7407
+ targetComponentType: options.targetComponentType,
7408
+ whatIf: options.whatIf ?? false,
7409
+ strict: options.strict ?? false
7410
+ });
7411
+ logger.success(
7412
+ `Modified ${result.modifiedComponents} component(s), ${result.modifiedCompositions} composition(s), propagated to ${result.propagatedInstances} instance(s)`
7413
+ );
7414
+ } catch (error) {
7415
+ if (error instanceof ComponentNotFoundError) {
7416
+ logger.warn(error.message);
7417
+ return;
7418
+ }
7419
+ if (error instanceof TransformError) {
7420
+ logger.error(error.message);
7421
+ process.exit(1);
7422
+ }
7423
+ throw error;
7424
+ }
7425
+ });
7426
+ return command;
7427
+ }
7428
+
7144
7429
  // package.json
7145
7430
  var package_default = {
7146
7431
  name: "@uniformdev/transformer",
7147
- version: "1.1.44",
7432
+ version: "1.1.45",
7148
7433
  description: "CLI tool for transforming Uniform.dev serialization files offline",
7149
7434
  type: "module",
7150
7435
  bin: {
@@ -7213,7 +7498,7 @@ var package_default = {
7213
7498
  };
7214
7499
 
7215
7500
  // src/cli/index.ts
7216
- var program = new Command21();
7501
+ var program = new Command22();
7217
7502
  var appVersion = package_default.version;
7218
7503
  console.error(`uniform-transform v${appVersion}`);
7219
7504
  program.name("uniform-transform").description("CLI tool for transforming Uniform.dev serialization files offline").version(appVersion);
@@ -7249,5 +7534,6 @@ program.addCommand(createRemoveUnusedContentTypesCommand());
7249
7534
  program.addCommand(createRemoveUnusedComponentTypesCommand());
7250
7535
  program.addCommand(createGenerateMissingProjectMapNodesCommand());
7251
7536
  program.addCommand(createSplitContentTypeCommand());
7537
+ program.addCommand(createPropagateRootSlotCommand());
7252
7538
  program.parse();
7253
7539
  //# sourceMappingURL=index.js.map